Question: Can I in a threaddump see if a coroutine...
# coroutines
s
Question: Can I in a threaddump see if a coroutine is running on
<http://Dispatchers.IO|Dispatchers.IO>
? Context: I am debugging a deadlocked application. I have a threaddump with 96 ( = number of cores)
DefaultDispatcher-worker
threads all locked doing IO but I am unsure if they are running on
Dispatchers.Default
(which is obviously a problem) or
<http://Dispatchers.IO|Dispatchers.IO>
since the documentation mentions that they share threads.
Next steps. Is there a way to find out if a thread in non suspend code is part of the limited
Dispatchers.Default
thread set ? (in debugger, hacky way or otherwise). I assume there isnt a public
Map<Thread,CoroutineContext>
map anywhere.
e
e.g.,
Copy code
val isIO = Class.forName("kotlinx.coroutines.EventLoopKt").getDeclaredMethod("isIoDispatcherThread", Thread::class.java)
for (thread in Thread.getAllStackTraces().keys) {
    println("$thread isIO=${isIO.invoke(null, thread)}")
}
j
almost certainly not stable but i've been able to look at
Copy code
coroutineContext[CoroutineDispatcher].toString() !== "<http://Dispatchers.IO|Dispatchers.IO>"
from within a suspend function. you mention "thread in non suspend code" so not sure this helps
s
Thanks for tip @ephemient It returns true so its possible we just exhaust the IO dispatcher instead (which to my surprise isnt unbounded) or hit a bug. We are using a fairly old version of coroutines.
e
yes, Dispatchers.IO is bounded to 64 threads (by default, you can tune the kotlinx.coroutines.io.parallelism system property to change), while Dispatchers.Default is unbounded (well actually kotlinx.coroutines.scheduler.max.pool.size has a max of 2^21 but in practice you should not hit that)
if you have some external resource which you know can handle higher concurrency, you can make a
limitedParallelism
view of IO for it
s
Did this change at some point ? I was operating under the assumption that Default was like the ForkJoin pool for cpu bound work and then IO was the unbounded one since its threads would most likely be sleeping waiting for io.
e
no, it's always been this way. Default is "unbounded" but in practice is bounded by… well, your own CPU usage, assuming you're using it for computation
s
I see. So intuitively the only reason to use IO dispatcher is to limit parallelism ? I just find it odd that there is a global IO dispatcher at all if so. Maybe this is an Android thing in which case I would expect it to be a property of that platform and unbounded on a plain jvm
u
Default is bound to #cores. IO is bound to 64 by default, but you can use a view on the IO dispatcher which is not limited by it’s parent:
Copy code
// 100 threads for MySQL connection
val myMysqlDbDispatcher = Dispatchers.IO.limitedParallelism(100)
// 60 threads for MongoDB connection
val myMongoDbDispatcher = Dispatchers.IO.limitedParallelism(60)