I have some Java api that looks like this ```inter...
# coroutines
k
I have some Java api that looks like this
Copy code
interface Worker {
    // does the work, throws Exception promptly if cancel() is called
    fun doWork()  
    fun cancel()
}
What is the best way to convert it into
suspend fun Worker.doWorkCancellable()
? Preferably something that works with a single threaded executor.
j
When you say
doWork
does the work in a thread, is it asynchronous? Or does it block the current thread while doing so?
k
it does the work directly
Best I come up with is this but it looks extremely ugly
Copy code
suspend fun Worker.doWorkCancellable(): Unit = coroutineScope {
    launch { doWork() }
    // using another dispatcher so if the original dispatcher is single threaded, it still cancels
    withContext(<http://Dispatchers.IO|Dispatchers.IO>) {   
        try {
            awaitCancellation()
        } finally {
            cancel()
        }
    }
}
j
Unfortunately I'm not sure you have a choice here. Cancellation is cooperative in coroutines, so since
doWork
is blocking, you have to use another thread to call
cancel()
.
That being said, here it looks like
doWorkCancellable
cannot finish normally (with your current implementation)
n
I think it won't finish normally because it's already being cancelled 🙂 . I think the doWork exception is probably getting added as a suppressed exception to the already existing cancellation exception.
I do recommend swapping your dispatchers. Run the blocking code on
<http://Dispatchers.IO|Dispatchers.IO>
and the suspending
awaitCancellation()
can just run as-is.
FYI, I usually start out with
suspendCancellableCoroutine
when looking to convert existing APIs to coroutines:
Copy code
suspend fun Worker.awaitWork() {
        withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
            suspendCancellableCoroutine<Unit> { cont ->
                cont.invokeOnCancellation {
                    this@awaitWork.cancel()
                }
                try {
                    doWork()
                } catch (e: Exception) {}
                cont.resume(Unit)
            }
        }
    }
k
@Nick Allen isn't the block in
suspendCancellableCoroutine()
meant to finish quickly and not block? In all the examples of
suspendCancellableCoroutine()
, the block returns immediately after setting up some callback. Is there any problem using it your way?
âž• 1
n
It calls the lambda synchronously. So if the lambda blocks, then
suspendCancellableCoroutine
will block. That's why I used
<http://Dispatchers.IO|Dispatchers.IO>
.