I have a `Continuation` that I know I won't use an...
# coroutines
y
I have a
Continuation
that I know I won't use anymore. It was previously created because a call to
startCoroutine(myCont)
suspended. I'm sure that I want to get rid of that
Continuation
and instead resume
myCont
in a different way (with a different Coroutine). Is there a best practice to follow here?
suspendCancellableCoroutine
just gives me an
Already resumed
exception for some reason. What's the proper way to dispose of a
Continuation
? Does the garbage collector handles this on its own? Should I be using a
CancellableContinuation
and cancel it instead?
s
If the continuation represents a task that might be holding resources (e.g. it's suspended inside a
try
or
use
) then resuming it with an exception (e.g. cancellation) is the only way to make sure the resources get released
y
Hmmm, that makes a lot of sense. Perhaps I should resume it with a custom
CancellationException
that I then swallow? I don't want the cancellation to propagate to the
myCont
because, again, I will resume it on my own later.
s
That sounds plausible 👍 if you could add some indirection and replace
myCont
with some man-in-the-middle completion handler, it could decide whether to pass the completion on to the real continuation or not, depending on the outcome
y
Huh, that sounds like a much better idea actually! It can just ignore any
resume
calls that have my special exception, and thus it'd be able to resume multiple times until it gets an actual value. Is there a preferred way to do that? Would implementing a
Continuation
be it?
s
Or you could just throw away the continuation and accept the potential control flow violation -- the
sequence
and
iterator
functions do it, so it can't be that bad 😄🙈
Yeah, the continuation interface is super simple
I think you could just use a basic decorator pattern
This actually reminds me of how flows work -- they use a special AbortFlowException if they need to exit early, so they can still run completion handlers and finally blocks
The flow collector intercepts the exception and suppresses it so nothing actually gets thrown or cancelled
y
sequence
and
iterator
are
RestrictsSuspension
though so it makes some sense (although I didn't know they just throw it away). I'll check out the
AbortFlowException
code because that sounds like exactly what I need.
s
I actually wrote about the
sequence
issue a while ago -- not the most impressive thing I've ever written but it has some decent examples of the problem that needs to be avoided when making custom suspension points https://betterprogramming.pub/breaking-try-catch-finally-in-kotlin-b95059a5673f
y
That explains the issue pretty well! It's a shame that that's true, but I guess it makes some sense because
Iterator
can't really close. Maybe we need a
CloseableSequence
or similar, but it's likely too niche. I've made something that sort-of works now, but I'll check the flow code just in case.
e
Stream
and
Flow
are closeable sequences
s
Or you could just throw away the continuation and accept the potential control flow violation -- the
sequence
and
iterator
functions do it, so it can't be that bad 😄🙈
I think it's "not that bad" because it's RestrictSuspension, and this also single threaded but it probably breaks
try/finally
. Right? Afaik resuming with
CancellationException
is the only sane thing to do @Youssef Shoaib [MOD], not ideal but necessary evil for these kind of things IMHO. I think
sequence
and
iterator
should do the same, since
CancellationException
is now official part of the Kotlin Std. Only alternative is if we get cancellation control in Kotlin Std, which has been an issue somewhere for ages. This is the perfect use-case IMHO, and Arrow could leverage the same if it wanted.