Why does calling this code from the main thread, s...
# coroutines
m
Why does calling this code from the main thread, sometimes result in an ANR (Android example, but relevant to any blocking java call)
Copy code
runBlocking {
    withTimeoutOrNull(100) {
        runInterruptible(<http://Dispatchers.IO|Dispatchers.IO>) {
            telephonyManager.networkCountryIso
        }
    }
}
r
runBlocking
blocks the current thread. If that is the ui thread this will cause an ANR. To not block the ui thread you'll want to use a coroutine builder. Recommended scope to launch a coroutine in is in a lifecycle owner of a
ViewModel
,
Activity
,
Fragment
,
View
or
Process
depending on use case.
m
Thanks, but my understanding is that
runBlocking
will block the current thread until the passed code
block
has returned which, in this case, cannot last more than 100ms which is well under ANR threshold on Android. Why doesn’t the combination of
withTimeoutOrNull
and
runInterruptible
give the desired results?
r
I see. I'm not sure what the threshold for a ANR is. 100ms is a long time and will already drop 6 frames at 60hz. Can you move this work off the main thread and resume on main thread when it's done?
Slow devices will also get worse results.
m
On Android, ANRs are reported after 5 seconds, or 10 seconds depending on the scenario. This is why I have the impression this code is not WAI. Perhaps
runInterruptible
does not make java blocking code actually interruptible by
withTimeout
?
r
Oh right. Coroutines can only interrupt at certain points. If you run do a
Thread.sleep(10000)
inside
withTimeout
or
runInterruptable
it will still run to completion.
m
It shouldn’t matter if the
runInterruptible
block is run to completion. AFAICT,
runInterriptible
will anyway throw a cancellation exception when the timeout is reached. and so that should result in the
runBlocking
completing as intended.
r
You could try wrapping blocking code in a
suspendCoroutine
block. I think it will allow the outer coroutine to cancel.
I tried it with
Thread.sleep(10_000)
and it does get interrupted. Sorry I can't help.
But not using
runBlocking
on the main thread should prevent the ANR I think.
m
Thanks for confirming (regarding
Thread.sleep
). Yeah, it’s just common code that’s often called from non-suspending code.
BTW, here’s the main thread stacktrace if that helps:
Copy code
jdk.internal.misc.Unsafe.park (Native method)
java.util.concurrent.locks.LockSupport.parkNanos (LockSupport.java:234)

kotlinx.coroutines.BuildersKt.runBlocking$default (Builders.kt:1)
Actually, going through the ANR dump, I see about 100 threads (mostly
waiting
). Most of those are SQLDelight queries. So I wonder it’s an issue with running out of threads.
r
That seems excessive. Device might be slowing down due to too much IO?
r
Sorry for the delay, a recent question reminded me of this and thought I'd copy here for completeness
Java interruption, much like coroutine cancellation, is cooperative https://docs.oracle.com/javase/tutorial/essential/concurrency/interrupt.html
If the function you're calling doesn't support interruption (which almost always means throwing InterruptedException in its signature because it's a checked exception) there's a good chance that runInterruptible will do nothing with regards to execution time
Note if you try to test it with a common blocking function like Thread.sleep it'll probably work fine because that does support interruption but most Java isn't that well written 😅
thank you color 1
m
Is there a way to simulate such a non-interruptible call?
r
Definitely hard to do with a predictable delay because most of the common blocking operations (Thread.join, Future.get, BlockingQueue...) do handle interruption
I think your best bet is some good old fashioned number crunching
Copy code
for (i in 1..10000) {
            BigInteger("$i").nextProbablePrime()
   }
(takes about 1.5 seconds on kotlin playground)
👍 1
182 Views