Skip to content

Instantly share code, notes, and snippets.

@Munzey
Last active February 4, 2021 22:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Munzey/212f38879039987a4c819c53d1d393cd to your computer and use it in GitHub Desktop.
Save Munzey/212f38879039987a4c819c53d1d393cd to your computer and use it in GitHub Desktop.
launch inside async cancellation weirdness
import kotlinx.coroutines.*
import kotlin.coroutines.coroutineContext
// doesn't work as I expected :/
// this may just be bad practice to hide the launching of a new coroutine, but curious why it doesnt work the same as in the other example file
suspend fun coroutineScopeWrapper(block: suspend (parentScope: CoroutineScope) -> Unit) {
coroutineScope {
val job = async {
block(this)
}
try {
job.await()
} catch (ex: CancellationException) {
println("cancelled") //doesnt seem to reach here
}
}
}
fun main() {
runBlocking {
coroutineScopeWrapper { parentScope ->
launch {
delay(100)
println("reaches here")
parentScope.cancel()
}
launch {
delay(1000)
println("should never reach here but does") //prints, but would have thought cancelling scope would stop this
}
}
}
}
import kotlinx.coroutines.*
import kotlin.coroutines.coroutineContext
// works as I expected
fun main() {
runBlocking {
val job = async {
val parentScope = this
launch {
delay(100)
println("reaches here")
parentScope.cancel()
}
launch {
delay(1000)
println("never reaches here") // cancelling parent scope so expect this to never print
}
}
try {
job.await()
} catch (ex: CancellationException) {
println("reaches here") // job.await rethrows cancellation exception
}
}
}
@fluidsonic
Copy link

fluidsonic commented Feb 4, 2021

To properly pass async's CoroutineScope down to the launch invocations:

import kotlinx.coroutines.*

// doesn't work as I expected :/
// this may just be bad practice to hide the launching of a new coroutine, but curious why it doesnt work the same as in the other example file
suspend fun coroutineScopeWrapper(block: suspend CoroutineScope.() -> Unit) {
    coroutineScope {
        val job = async {
            block()
        }
        try {
            job.await()
        } catch (ex: CancellationException) {
            println("cancelled") //doesnt seem to reach here
        }
    }
}

fun main() {
    runBlocking {
        coroutineScopeWrapper {
            launch {
                delay(10)
                cancel()
            }
            launch {
                delay(1000)
                println("should never reach here but does") //prints, but would have thought cancelling scope would stop this
            }
        }
    }
}

Still doesn't explain why cancel() doesn't cancel the other launched coroutine though.

@fluidsonic
Copy link

fluidsonic commented Feb 4, 2021

Ah, looks like cancelling a launch-ed coroutine doesn't affect other coroutines 🤔
To cancel everything you'd use this:

import kotlinx.coroutines.*

// doesn't work as I expected :/
// this may just be bad practice to hide the launching of a new coroutine, but curious why it doesnt work the same as in the other example file
suspend fun coroutineScopeWrapper(block: suspend CoroutineScope.() -> Unit) {
    coroutineScope {
        val job = async {
            block()
        }
        try {
            job.await()
        } catch (ex: CancellationException) {
            println("cancelled") //doesnt seem to reach here
        }
    }
}

fun main() {
    runBlocking {
        coroutineScopeWrapper {
            launch {
                delay(10)
                this@coroutineScopeWrapper.cancel()
            }
            launch {
                delay(1000)
                println("should never reach here but does") //prints, but would have thought cancelling scope would stop this
            }
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment