https://kotlinlang.org logo
#coroutines
Title
# coroutines
m

Marko Mitic

05/08/2019, 12:28 PM
Is there a way to check whether coroutine (scope) was canceled from a suspend function (that function not being extension of the scope)?
a

altavir

05/08/2019, 12:29 PM
isActive
?
m

Marko Mitic

05/08/2019, 12:30 PM
isActive isn't visible in suspend fun, it's part of CoroutineScope
this is what I'd like to do
Copy code
fun CoroutineScope.launchSomething(){
    launch {
        ComplexClassWithState().run()
    }
}

class ComplexClassWithState {
    suspend fun run(){
        while (isActive){
            //do stuff
        }
    }
}
a

altavir

05/08/2019, 12:34 PM
If you do not have the scope, I would not recommend it since you can't guarantee, how the function will be called. You can add empty suspension points like
delay(0)
, it will throw exception if the scope is canceled.
m

Marko Mitic

05/08/2019, 12:35 PM
this is what I'm doing right now
Copy code
fun CoroutineScope.launchSomething(){
    launch {
        ComplexClassWithState().run(this)
    }
}

class ComplexClassWithState {
    suspend fun run(scope: CoroutineScope){
        while (scope.isActive){
            //do stuff
        }
    }
}
Looking at the code, doesn't looks like
delay(0)
does anything
Copy code
public suspend fun delay(timeMillis: Long) {
    if (timeMillis <= 0) return // don't delay
    ...
}
a

altavir

05/08/2019, 12:44 PM
it does not. It just creates a suspension point. If the coroutine is canceled, suspension point will throw an exception. Any suspension point will do.
I think that there was a function, that just creates a suspension point and does not do anything else... Just do not remember the name
m

Marko Mitic

05/08/2019, 12:56 PM
this is probably an overkill, right?
Copy code
val isActive = runCatching {
    suspendCoroutine<Boolean> { continuation ->
        continuation.resumeWith(Result.success(continuation.context.isActive))
    }
}.getOrDefault(false)
a

altavir

05/08/2019, 12:57 PM
probably. I do not recommend use naked coroutine primitives unless you really do understand what are you doing. I do not.
g

gergo

05/08/2019, 1:26 PM
wrapping the suspend function's body into a coroutineScope doesnt resolve this?
a

altavir

05/08/2019, 1:28 PM
No, it does not. You need to create a suspension point, otherwise kotlin would not know, where it can safely terminate computation.
g

gergo

05/08/2019, 1:32 PM
i meant like coroutineScope{ while(isActive) {} }. but i guess you are right adding yield() into the function resolves this. thank you!
a

altavir

05/08/2019, 1:32 PM
well, yes, you can. But it changes semantics a bit.
And no, if you just add scope without creating suspension point inside the cycle, it still won't work.
g

gergo

05/08/2019, 1:36 PM
i am not sure why it wouldn't work with scope, would you be kind enough to explain?
a

altavir

05/08/2019, 1:38 PM
If there are no suspension points in the cycle, then it just won't stop until it reaches the end of the scope. You need some place to throw an exception, you can't do it in a random point of the program.
g

gergo

05/08/2019, 1:40 PM
the isActive would change even if job of the outer scope were canceled, or it wouldnt be?
a

altavir

05/08/2019, 1:41 PM
isActive would work. It explicitly checks the coroutine state. And yes, it will in general show the state of outer scope as well. You need either
isActive
or suspension point.
g

gergo

05/08/2019, 1:43 PM
okay, so if i get it correctly you suggest something like
while(true) { yield(); /* do stuff */ }
, while
coroutineScope{ while(isActive) { /* do stuff */ } }
also works, but it's not your recommendation, is that right?
a

altavir

05/08/2019, 1:44 PM
Well, I think both ways are good. The one with coroutineScope just has a little bit different meaning.
g

gergo

05/08/2019, 1:45 PM
okay, i think i need to read a bit more about this. thank you for your help!
a

altavir

05/08/2019, 1:46 PM
Basically it changes how you work with exceptions. yield will drop the exception right where you got it.
coroutineScope
+
isActive
won't throw exception at all (it could be better, no stack trace), but even if it would, it would do it for the whole scope.
👍 1
g

gildor

05/08/2019, 1:53 PM
isActive isn't visible in suspend fun, it's part of CoroutineScope
isActive is extension property which isavailable in any suspend function, not only on scope, it just search for Job in coroutine context
a

altavir

05/08/2019, 1:55 PM
It is not extension if you do not have scope. It is internal API. I am not sure, it could be used outside inlines. Never tried.
g

gildor

05/08/2019, 1:56 PM
You shouldn't use coroutineScope builder function for this, it doesn't make sense, just use isActive directly
a

altavir

05/08/2019, 1:57 PM
I just tried. It is part of
Job
and is marked as internal api
Maybe I tried in project with old coroutines though...
g

gildor

05/08/2019, 1:58 PM
There is
coroutineContext.isActive
1
a

altavir

05/08/2019, 2:01 PM
I do not have explicit instance of context.
We are talking about
Copy code
suspend fun doA(){
    isActive
}
There is probably a way to get a context from continuation, but it is not direct.
g

gildor

05/08/2019, 2:03 PM
This is intrinsic property coroutineContext in any suspend function
💯 1
a

altavir

05/08/2019, 2:04 PM
Yes, you are right, I did not know about it
m

Marko Mitic

05/08/2019, 2:05 PM
that's what we we're looking for
g

gildor

05/08/2019, 2:20 PM
This is an example of isActive usage
But keep in mind that isActive has a bit different semantics than yield, if you use it in a loop and do not call any suspend functions, this loop will not get chance to reschedule coroutines and it will just spin on this thread, which may be fine, but not for main thread for example
But I'm a bit confused about top level isActive property:
kotlinx.coroutines.isActive
it was available before and even mentioned in docs now, but I cannot find it anymore in sources and even documentation link is broken
22 Views