Say I have a `val scope = GlobalScope + Job()` tha...
# coroutines
s
Say I have a
val scope = GlobalScope + Job()
that is no longer active. If I call
scope.launch
the new coroutine will never get a chance to execute but caller of
launch
is oblivious to this. Would it not make more sense have a fail fast behaviour akin to
Executor.execute
that throws
RejectedExecutionException
in a similar situation ?
e
That would lead to a race. So, when you cancel a coroutine that is trying to start you child you’d sometimes get this exception, and sometimes not.
You should use structured concurrency. You should not start child from a random place in your code, but only from a parent code. So, if the parent job is already cancelled, it should not be trying to start more children
(it should be shutting down instead)
g
You also can implement such scope yourself. Just check that job is not cancelled on each call of coroutineContext. I didn’t try, maybe there are some pitfalls
s
Cant see why the race is a problem.
ConcurrentModificationErrors
and similar fail fast checks are not guaranteed either.
The calls to
launch
occur outside of the parent as network events come in. Not sure how to change that.
e
CME
is thrown when you have bug in your code. With a proposed
REE
you’ll get it from time to time from a perfectly good code without any bug. That is not Ok
It simply means you are launching in wrong scope. Use structured concurrency. Don’t launch child outside of parent.
s
I can see how it would be a poor fit if calls to
launch
should only happen from inside a parent.
I guess I need to look into how to avoid this need I seem to have then
e
If you are launching from parent, then your bases are covered. If you cannot launch, it means you (parent) is already cancelled and should be shutting down
Now, there is one corner-case:
Copy code
val resource = openSomeResource()
launch { // child will close resource
    try { ... } finally { resource.close }
}
You cannot afford this child not to start, or you loose a resource.
To fix that, use
launch(start = CoroutineStart.ATOMIC)
.
s
Thanks for your time. Good to know about
ATOMIC
. I have something similar myself.
The Activity class in https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/coroutine-context-and-dispatchers.md#cancellation-via-explicit-job is similar to what I am doing currently. The difference is that I would want an exception synchronously if
doSomething()
is either called before
create()
, after
destroy()
or if the
job
is cancelled due to a coding error. Seems like a simple requirement but how would one extend the example to handle that ?
e
We cannot support that, unfortunately
You’ll have to use your own functions that throw the corresponding exceptions to achieve this behavior
s
ie. blocking the thread until I get a signal from the launched block that it started executing OR the returned job from launch is cancelled ? (assuming the returned job from
launch
isnt synchronously set to
isCancelled
)
e
Whatever your use-cases calls for.