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

Arjan van Wieringen

06/30/2023, 5:57 PM
Okay, I have a very n00b coroutine question again. I have a
Runnable
which also implements
Closable
ino order to clean up which will fully block a thread (I think). I want to run this Runnable in a separate Job and be able to cancel it. I've brought it back to this:
Copy code
suspend fun main() = coroutineScope {
   val engine: Runnable = ....

   val job = launch(<http://Dispatchers.IO|Dispatchers.IO>) {
        try {
            println("Starting engine")
            launch { engine.run() }.join() // without join it would go to finally directly of course
        } finally {
            println("Closing engine")
            engine.close()
        }
    }

    delay(1000)
    job.cancel()
}
This appears to work, but it looks a bit weird having a seperate Job, only for cancelling. Just doing this:
Copy code
val job = launch(<http://Dispatchers.IO|Dispatchers.IO>) { engine.run() }
delay(1000)
job.cancel()
will not work, the engine will keep going. Also wrapping
engine.run()
in 'try / catch' in order to catch the cancellation exception doesn't work because engine.run() fully claims the thread. Is the above method the correct way? EDIT:
runInterruptable
seems to be handy here:
Copy code
val job = launch(<http://Dispatchers.IO|Dispatchers.IO>) {
        try {
            runInterruptible {
                engine.run()
            }
        } catch (e: CancellationException) {
            println("Cancelled")
        } finally {
            println("Finally")
            engine.close()
        }

    }
e

ephemient

06/30/2023, 6:04 PM
in principle you could
Copy code
val job = launch(<http://Dispatchers.IO|Dispatchers.IO>) {
    runInterruptible {
        engine.run()
    }
}
job.invokeOnCompletion { engine.close() }
but
try
-
finally
is clearer IMO
a

Arjan van Wieringen

06/30/2023, 6:06 PM
Ah, forgot to say. It will never complete 🙂
Unless completion also means interruption of course
I now have this:
Copy code
suspend fun <T>  T.runAndClose() where T : Runnable, T : Closeable {
    try {
        runInterruptible { run() }
    } finally {
        close()
    }
}
Which works pretty okay
e

ephemient

06/30/2023, 6:07 PM
note that you can't perform
suspend
calls from the
finally
block since they'll immediately fail if the job is cancelled (nor can you from the completion handler, statically). but probably not relevant to you if you're using Closable.close since that's not suspending
a

Arjan van Wieringen

06/30/2023, 6:07 PM
Yes, that's true. Good point. The finally block is purely for non-suspending cleanup
Even better:
Copy code
val job = launch(<http://Dispatchers.IO|Dispatchers.IO>) {
        engine.use {
            runInterruptible { it.run() }
        }
    }