Mark
12/21/2022, 6:25 AMwithTimeoutOrNull to execute blocking code with a timeout. I’m doing this using a combination of runBlocking and async but I’m not clear about why using certain scopes results in the timeout not working. Here is code to demonstrate what I’m talking about: https://pl.kotl.in/af9mTAW9zbezrukov
12/21/2022, 6:43 AMasync(Dispatchers.Default) for work1bezrukov
12/21/2022, 6:43 AMgildor
12/21/2022, 6:45 AMMark
12/21/2022, 6:47 AMrunInterruptible which looks good. In reality, the withTimeout is being executed from a suspending function. So do I need to be careful with dispatchers regarding this?gildor
12/21/2022, 6:50 AMgildor
12/21/2022, 6:50 AMMark
12/21/2022, 6:52 AMblock is run on a different thread to the timeout code?gildor
12/21/2022, 6:52 AMgildor
12/21/2022, 6:53 AMMark
12/21/2022, 6:54 AMgildor
12/21/2022, 6:55 AMgildor
12/21/2022, 6:56 AMMark
12/21/2022, 6:57 AMblock?gildor
12/21/2022, 7:11 AMgildor
12/21/2022, 7:12 AMthere is a chance the timeout code is run on the same thread as theone thread dispatches one coroutine the same time
Mark
12/21/2022, 7:31 AM<http://Dispatchers.IO|Dispatchers.IO> to be used for both timeout code and block, not working rather because of the fact the blocking code is not cooperative and so withTimeoutOrNull() cannot finish, rather than because of some dispatcher choice issue: https://pl.kotl.in/jF9hnYSjqgildor
12/21/2022, 8:36 AMblock is finished, it’s not an issue of dispatchersgildor
12/21/2022, 8:37 AMMark
12/21/2022, 8:47 AMwithTimeoutOrNull . I thought by using Dispatchers.IO this would happen automatically, but if I printout Thread.currentThread().name I see the same thread is being used, so it cannot work this way. So my question is why is the dispatcher using the same thread, and how to stop it from doing so?Mark
12/21/2022, 8:48 AMGlobalScope.async to run the block but I’m just trying to understand why.gildor
12/21/2022, 8:56 AMgildor
12/21/2022, 8:56 AMbut in my case I don’t want to cancel the blockIn this case you need to run it on separate scope
Mark
12/21/2022, 8:59 AMgildor
12/21/2022, 9:14 AMNick Allen
12/21/2022, 5:42 PMrunBlocking creates a CoroutineScope with a CoroutineDispatcher that uses the current thread. It's essentially a thread pool of limit 1. While that thread pool is running Thread.sleep it can't resume any coroutines.
GlobalScope uses Dispatchers.Default which is essentially a thread pool with a limit equal to your CPU count. If Dispatchers.Default is running Thread.sleep then the runBlocking thread pool is not blocked, it can resume coroutines so withTimeoutOrNull can immediately cancel its children.
GlobalScope also has no Job. It creates coroutines that are "roots" and are not connected to any other existing coroutine. You need to cancel these individually as they aren't part of an existing scope with a Job. This makes it easy to start coroutines that "leak" meaning you don't need them to run any more but they are still going because they weren't explicitly cancelled.
All scoping suspend functions like withTimeoutOrNull or withContext or coroutineScope (little c) wait for child coroutines to finish before they resume. Even if withTimeoutOrNull is able to immediately cancel it's children, coroutine cancellation is asynchronous and cooperative. If that child take awhile to finish, withTimeoutOrNull will suspend until then.
So in one of your examples, the one thread is blocked sleeping so withTimeoutOrNull can't even try to cancel its child coroutines (thread is blocked sleeping).
In another example, withTimeoutOrNull succeeds in asking its children to cancel but the child doesn't cooperate so withTimeoutOrNull suspends until its child is finished.
In the examples where `withTimeoutOrNull resumes before the Thread.sleep finishes, you are both sleeping on a different thread pool AND you are sleeping in a coroutine that is not a child of withTimeoutOrNull.