I have a long running CPU-heavy task. I want it to...
# coroutines
s
I have a long running CPU-heavy task. I want it to be cancellable, so I need to pass in something that will let it know when its
Job
becomes cancelled. • Adding the
suspend
modifier would give access to the
currentCoroutineContext()
, but it doesn't seem appropriate since the function doesn't actually suspend. • Adding a
CoroutineScope
receiver would also give access to the coroutine context, but that also doesn't seem appropriate since the function doesn't launch any coroutines. • I could use thread interrupts and call the function from a
runInterruptible
block, but that seems like a lot of hard work, and it's JVM only Which would you pick? Any established conventions for this? Maybe there's another option I haven't considered?
In an ideal world, the function wouldn't need to know about coroutines
e
suspend and call
checkActive()
periodically?
1
regular code just isn't interruptible
👆 1
s
Right, I'd need to modify the code to include the check somehow. I'm just trying to decide on the best way to do the check. Whether that's by checking the
Job
, using
ensureActive()
like you said, checking the thread interrupt status manually, or setting up some other variable to check.
At the moment the function doesn't know anything about coroutines or suspension points, and it'd be nice to keep it that way so it can be called from other places too
e
thread interruption is JVM only, and also only happens at specific points
s
Yeah, it'd be my first choice if it wasn't JVM specific. I can check
Thread.interrupted()
without introducing any coroutine dependencies, but only on JVM 😞
e
coroutine cancellation and thread interruption are cooperative, there isn't a (safe) way to stop uncooperative code
so either way you have to choose what to interop with
👍 1
s
Maybe I'll just pass in a
isActive: () -> Boolean
to my function to break the explicit dependency on coroutines
e
okhttp has a https://square.github.io/okio/3.x/okio/okio/okio/-timeout/index.html abstraction, although it only works on JVM the idea could be applied elsewhere
👀 1
s
I might actually be changing my mind about the dependency on coroutines; I think my task should probably be a good neighbour and use
yield()
, which will do the trick for cancellability too. Maybe that's why there isn't another common pattern for this.
👍 1
k
Is this CPU intensive task running on a separate thread? Is it a loop? If so, it might make sense to have two functions. 1. The inner loop code is a separate function. This does not know about coroutines. 2. The loop itself is
suspend
and does a
while(isActive)
to ensure it cancels cooperatively.
👍 2
Though, I think your solution of
isActive: () -> Boolean
is probably better.
l
currentCoroutineContext().isActive is probably what you need
g
I think there is nothing bad from making it suspend, even if it not actually suspend Coroutine cancellation sematics is clear and easy to use from any suspend function, than custom
isActive
, which anyway would be implemented like { currentCoroutineContext().isActive } Also if CPU heavy, explicitly wrapping content to appropriate dispatcher (like Default) makes it safe to call from any suspend function