Reputation: 4090
I created Custom Continuation like this
class LogContinuation<T>(
private val cont: Continuation<T>
) : Continuation<T> {
override val context: CoroutineContext
get() = cont.context
override fun resumeWith(result: Result<T>) {
println("Log: LogContinuation resume with thread: ${Thread.currentThread().name}")
cont.resumeWith(result)
}
}
And custom interceptor
val myInterceptor = object: ContinuationInterceptor{
override val key: CoroutineContext.Key<*>
get() = ContinuationInterceptor.Key
override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> {
println("3 interceptContinuation: ${continuation.context[CoroutineName].toString()}")
return LogContinuation(continuation)
}
override fun releaseInterceptedContinuation(continuation: Continuation<*>) {
println("4 releaseInterceptedContinuation: ${continuation.context[CoroutineName].toString()}")
}
}
val myInterceptor2 = object: ContinuationInterceptor{
override val key: CoroutineContext.Key<*>
get() = ContinuationInterceptor.Key
override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> {
println("5 interceptContinuation: ${continuation.context[CoroutineName].toString()}")
return Log2Continuation(continuation)
}
override fun releaseInterceptedContinuation(continuation: Continuation<*>) {
println("6 releaseInterceptedContinuation: ${continuation.context[CoroutineName].toString()}")
}
}
And I tried this:
fun main(){
runBlocking(context = myInterceptor + CoroutineName("myRunBlocking") + myInterceptor2 + CoroutineName("myRunBlocking2")){
println("1 runBlocking start: ${Thread.currentThread().name}")
delay(1000L)
println("2 runBlocking end: ${Thread.currentThread().name}")
repeat(1){
println("${7+it} runBlocking start: ${Thread.currentThread().name}")
println("${8+it} runBlocking end: ${Thread.currentThread().name}")
}
}
}
And I get
5 interceptContinuation: CoroutineName(myRunBlocking2)
Log: LogContinuation resume with thread: main
1 runBlocking start: main
Log: LogContinuation resume with thread: kotlinx.coroutines.DefaultExecutor
2 runBlocking end: kotlinx.coroutines.DefaultExecutor
7 runBlocking start: kotlinx.coroutines.DefaultExecutor
8 runBlocking end: kotlinx.coroutines.DefaultExecutor
6 releaseInterceptedContinuation: CoroutineName(myRunBlocking2)
What's happening here? I thought scope can pass only CoroutineContext but, CoroutineIntercepter and CoroutineName can be passed. Those are different types but how can it be done?
Upvotes: 0
Views: 571
Reputation: 28362
CoroutineContext
is basically a map of CoroutineContext.Element
items. To make the API a little smoother, coroutines authors introduced a smart trick. Each Element
is itself a CoroutineContext
of this single item.
You can verify that both CoroutineName
and ContinuationInterceptor
are actually subtypes of CoroutineContext
.
As an example, let's get your code:
myInterceptor + CoroutineName("myRunBlocking") + myInterceptor2 + CoroutineName("myRunBlocking2")
This could be understood as:
// not a true Kotlin code
coroutineContextOf(myInterceptor) +
coroutineContextOf(CoroutineName("myRunBlocking")) +
coroutineContextOf(myInterceptor2) +
coroutineContextOf(CoroutineName("myRunBlocking2"))
Here you have 4 different coroutine contexts and you merge them by overwriting the same items with newer ones. As a result, you pass to runBlocking()
something like:
// not a true Kotlin code
coroutineContextOf(myInterceptor2, CoroutineName("myRunBlocking2"))
Upvotes: 4