Comments (14)
@elizarov sorry to post here but I'm sure this could help a lot of other people.
I really struggle to understand how to properly cancel blocking IO calls since normal usage does not interrupt thread.
Goal would be convert some functions in a module to coroutines with blocking IO and some computation.
I get the isActive/yield and ways to cancel the computations, but not how to push the cancel to the blocking IO call.
If I understand correctly how to properly do the coroutines stuff it should be something like this
suspend fun x(): y {
....
val result = withContext(IO) {
}
do CPU computation checking for isActive()/yield
}
But from all that I read, cancelling the coroutine that call x() would still have to wait for the result to finish it's IO blocking call that only support thread interrupt)
Is there any usable documentation / sample that fully cover this need (for Android so without Guava if possible)
(Plan is not to use IO but an existing executor with asCoroutineDispatcher())
from kotlinx.coroutines.
UPDATE: I stand corrected. False alarm. This particular usage of Executors.newCachedThreadPool()
with future.cancel(true)
seems to be fine. All bases are covered. See here: https://twitter.com/relizarov/status/1184460504238100480
from kotlinx.coroutines.
I meet the issue like in the references, so the following workaround was implemented:
val externalThreadPool = Executors.newCachedThreadPool()
suspend fun <T> withTimeoutOrInterrupt(timeMillis: Long, block: () -> T) {
withTimeout(timeMillis) {
suspendCancellableCoroutine<T> { cont ->
val future = externalThreadPool.submit {
try {
val result = block()
cont.resumeWith(Result.success(result))
} catch (e: InterruptedException) {
cont.resumeWithException(CancellationException())
} catch (e: Throwable) {
cont.resumeWithException(e);
}
}
cont.invokeOnCancellation {
future.cancel(true)
}
}
}
}
It provides the similar behaviour like usual withTimeout
, but additionally it supports running code with blocking.
Note: It should be called only when you know, that the inner code use blocking and can correctly process throwed InterruptedException
. In most cases withTimeout
function is prefered.
from kotlinx.coroutines.
@khovanskiy This "works", but it is extremely dangerous code to use in a serious high-load production. Here's the implementation of that future.cancel
method from Open JDK for your reference:
https://github.com/openjdk/jdk/blob/67a89143dde6e545adbfc3c79bb89d954307f8bc/src/java.base/share/classes/java/util/concurrent/FutureTask.java#L164-L182
Ask you can see it does a classic "check & act" concurrent programming mistake: It interrupts a thread that was running this task at the time it had checked it, so under load it may, in fact, interrupt some other, unrelated piece of code.
from kotlinx.coroutines.
I think this ability is of great need on Android.
According to
- coroutine doc: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-main-scope.html
- Android guide: https://developer.android.com/kotlin/coroutines
The practical usage on Android would be something like:
class MyAndroidActivity {
private val scope = MainScope()
override fun onResume {
scope.launch {
var info: Info
withContext(Dispatchers.IO) {
// #1 may block
info = loadFromInternet()
}
showOnUI(info)
}
}
override fun onDestroy() {
super.onDestroy()
scope.cancel()
}
}
Line #1 may block for a relatively long time. If a lot of activities like this are entered and exited in a relatively short time, eg., someone is looking for interesting news and entering exiting news detail pages quickly without long stay, or someone is doing monkey test. The Dispatcher will be filled with canceled but blocked coroutines, so that new info
loading of new activities are starving and unable to run.
from kotlinx.coroutines.
To implement this, I think, we could
- Make it optional and default off -- explicitly use only.
- Watch the job for cancellation and interrupt the executing thread when, and only when, continuations of a coroutine is running.
- Transform InterruptedException to CancellationException at the end of the coroutine so that everything else goes as before.
- update/restore of ThreadContextElement is perfect place to implement 2
- To implement 3, transform before AbstractCoroutine#makeCompletingOnce requiring a little modification
- Implementing 1 with a context element is easy
from kotlinx.coroutines.
I made a PR to implement this #1922
The added context element is something like:
/**
* This [CoroutineContext] element makes a coroutine interruptible.
*
* With this element, the thread executing the coroutine is interrupted when the coroutine is canceled, making
* blocking procedures stop. Exceptions that indicate an interrupted procedure, eg., InterruptedException on JVM
* are transformed into [CancellationException] at the end of the coroutine. Thus, everything else goes as if this
* element is not present. In particular, the parent coroutine won't be canceled by those exceptions.
*
* This is an abstract element and will be implemented by each individual platform (or won't be implemented).
* The JVM implementation is named CoroutineInterruptible.
*
* Example:
* ```
* GlobalScope.launch(Dispatchers.IO + CoroutineInterruptible) {
* async {
* // This block will throw [CancellationException] instead of an exception indicating
* // interruption, such as InterruptedException on JVM.
* withContext(CoroutineName) {
* doSomethingUseful()
*
* // This blocking procedure will be interrupted when this coroutine is canceled
* // by Exception thrown by the below async block.
* doSomethingElseUsefulInterruptible()
* }
* }
*
* async {
* delay(500L)
* throw Exception()
* }
* }
* ```
*/
abstract class CoroutineInterruptController : AbstractCoroutineContextElement(Key)
from kotlinx.coroutines.
PR (non-intrusive implementation of this feature): #1934
As mentioned above. This feature is of great use at least on Android as I could see. There is still another non-intrusive implementation of this feature: Add a function that makes a blocking code block cancellable (become a cancellation point of the coroutine) by interrupting the blocking code block and throwing CancellationException on coroutine cancellation.
The added function is like:
/**
* Makes a blocking code block cancellable (become a cancellation point of the coroutine).
*
* The blocking code block will be interrupted and this function will throw [CancellationException]
* if the coroutine is cancelled.
*
* Example:
* ```
* GlobalScope.launch(Dispatchers.IO) {
* async {
* // This function will throw [CancellationException].
* interruptible {
* doSomethingUseful()
*
* // This blocking procedure will be interrupted when this coroutine is canceled
* // by Exception thrown by the below async block.
* doSomethingElseUsefulInterruptible()
* }
* }
*
* async {
* delay(500L)
* throw Exception()
* }
* }
* ```
*/
public suspend fun <T> interruptible(block: () -> T): T
from kotlinx.coroutines.
👍 interruptible { ... }
block seems to be lean an isolated way to implement it. Let's move it to a separate issue, though -> #1947 . I'm closing this one.
from kotlinx.coroutines.
Is it possible for Kotlin Coroutines to handle thread interruption?
If I call some function that has "sleep", for example, is it possible to cancel it so that it will also call "interrupt" ?
from kotlinx.coroutines.
@AndroidDeveloperLB yes, you could use runInterruptible
builder for this purpose
from kotlinx.coroutines.
@qwwdfsad This almost works.
Almost because then it forces you to cancel by interruption, always.
Via AsyncTask, you always had a choice of whether cancelling normally or by interruptions.
Here it seems you have to set it up and stay with one of them, forever.
from kotlinx.coroutines.
What do you mean by "forces you to cancel by interruption"? Can you elaborate, please?
from kotlinx.coroutines.
@elizarov Either you use runInterruptible
or you don't.
If you use it, cancel
will interrupt.
If you don't, cancel
will not interrupt.
The creation of the instance forces cancel
to work in one way or the other. You have no choice after it's created.
On AsyncTask, you have a choice for every instance. cancel(true)
will interrupt, and cancel(false)
will not.
The creation of the instance doesn't force you to choose how it will be canceled.
You can choose to cancel with interruption in some case, and without in another.
from kotlinx.coroutines.
Related Issues (20)
- StateFlow, different values, compareAndSet return false HOT 3
- 1.9.0-RC test task:compileDemoDebugUnitTestKotlin error Suspension functions can only be called within coroutine body. HOT 1
- Improve invokeOnCompletion and invokeOnCancellation API HOT 1
- Mention `testScope.backgroundScope` in `UncompletedCoroutinesError`
- Allow `Dispatchers.Unconfined` to use the event loops as time sources
- Add optional support for Micrometer Context Propagation
- `withContext` may execute code in the wrong context if the `coroutineContext` misleads it HOT 3
- SharedFlow doesn't have same parameters as in constructor function HOT 2
- Consider deprecation cycle for `CoroutineDispatcher.invoke` HOT 6
- How to prevent a SharedFlow collect values when the activity resumes? HOT 1
- Consider discouraging `CoroutineStart.LAZY`
- Some problem about `addLast` in LockFreeTaskQueue HOT 1
- Maybe it's reasonable to recommend wrapping a `callbackFlow` initialization into `try`-`finally`? HOT 4
- Dispatcher failures may leave coroutines uncompleted HOT 1
- `DelayWithTimeoutDiagnostics` missing from R8 rules? HOT 3
- Introduce Flow.all/any/none operators HOT 3
- [WASM] JsException: Exception was thrown while running JavaScript code kotlinx.coroutines.error_$external_fun HOT 6
- Suppressed exceptions are lost during stacktrace recovery HOT 1
- `runBlocking` executes other tasks instead of its own HOT 1
- "Kotlin Compiler Error: NoClassDefFoundError for kotlin/reflect/full/KClasses during Gradle build" HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from kotlinx.coroutines.