fitermay
05/14/2023, 4:59 PMkevin.cianfarini
05/14/2023, 5:21 PMfitermay
05/15/2023, 2:44 AMfun allocateResource(): Closeable = TODO()
fun setupAndCreateResource(): Closeable{
//Do something
var a: Closeable? = null
try{
a = allocateResource()
doSmth()
}
catch(e: Throwable){
try {
a?.close()
} catch (c:Exception){
e.addSuppressed(c)
}
throw e
}
return a
}
suspend fun smth() = coroutineScope {
try {
val defferedRes = async {
setupAndCreateResource()
}
// Bug: will not dispose resource if allocated during cancel
defferedRes.await().use {
}
}
}
If the producer of the value returns a resource then the consumer is guaranteed to receive it, or if not able to receive it then the resource is guaranteed to be disposed
CLOVIS
05/15/2023, 8:47 AMfitermay
05/15/2023, 11:04 AMCLOVIS
05/15/2023, 11:39 AMkevin.cianfarini
05/15/2023, 12:43 PMDeferred
is a Job
you could install a cleanup handler with Job#invokeOnCompletion
to clean up that resource.
2. You could wrap the contents of the first async
block in withContext(NonCancellable)
3. You could decouple the actual creation of the closeable resource and the possibility for it to be cancelled. (Right now you don’t have it marked as suspend
but since you’ve got it wrapped in an async I assume that doSmth()
is a suspend fun).fitermay
05/15/2023, 12:46 PMkevin.cianfarini
05/15/2023, 12:50 PMuse
on it is essentially a call to finally { close() }
. Finally blocks get called on cancellation.fitermay
05/15/2023, 12:52 PMkevin.cianfarini
05/15/2023, 1:27 PMsuspend fun foo() = coroutineScope {
val resource = setupResourceSynchronous()
resource.use { r ->
val thing1 = async { r.thing1() }
val thing2 = async { r.thing2() }
return thing1.await() + thing2.await()
}
}
suspend fun foo() = coroutineScope {
val someAsyncValue = somethingAsync() // suspends
val resource = setupResourceSynchronous(someAsyncValue)
resource.use { r ->
val thing1 = async { r.thing1() }
val thing2 = async { r.thing2() }
return thing1.await() + thing2.await()
}
}