I've been getting familiar with the ContinuationInterceptor
section of coroutines
I wrote the following section of code to verify my idea
class MyContext: CoroutineContext.Element{
override val key : CoroutineContext.Key<*>
get() = object : CoroutineContext.Key<MyContext>{}
}
val myInterceptor = object : ContinuationInterceptor {
//Key is set to a non-interceptor type
override val key : CoroutineContext.Key<MyContext>
get() = object : CoroutineContext.Key<MyContext>{}
override fun <T> interceptContinuation(continuation:
Continuation<T>): Continuation<T> {
Log.i(TAG,"interceptor:"+continuation.context[CoroutineName].toString())
return Continuation(EmptyCoroutineContext) {
thread(name = "myThread") {
continuation.resumeWith(it)
}
}
}
}
lifecycleScope.launch(myInterceptor + CoroutineName("MY1")) {
Log.i(TAG,"MY1 start:"+Thread.currentThread().name)
fun1()
launch(CoroutineName("MY2")) {
Log.i(TAG,"MY2 run:"+Thread.currentThread().name)
}
withContext(Dispatchers.Main+CoroutineName("MY3")) {
Log.i(TAG,"MY3 run:"+Thread.currentThread().name)
}
Log.i(TAG,"MY1 end:"+Thread.currentThread().name)
}
I don't have the myInterceptor Key specified as ContinuationInterceptor.Key
.
So coroutines should not recognize it as an Interceptor.
MY1
and MY2
prove this idea, but MY3
is intercepted by myInterceptor
and runs in a new thread.
I wonder:
Why is myInterceptor
recognized as an interceptor
Why does not Dispatcher.Main
work
The behavior of the shown code is weird. I couldn't find the exact reason why.
I fixed some obvious problems with your code, such as returning a new object from key
every time, and not implementing equals
. I return a singleton, so the default equals
works:
class MyContext: CoroutineContext.Element {
companion object Key : CoroutineContext.Key<MyContext>
override val key: CoroutineContext.Key<MyContext> = Key
}
val myInterceptor = object : ContinuationInterceptor {
override val key: CoroutineContext.Key<MyContext> = MyContext.Key
override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> {
println("interceptContinuation: ${continuation.context[CoroutineName]?.name}")
return Continuation(EmptyCoroutineContext) {
thread(name = "myThread") {
continuation.resumeWith(it)
}
}
}
override fun toString() = "My Interceptor"
}
These fixes don't change the behavior, though, and we can see it's broken even before messing with the dispatching:
val goodCtx = CoroutineName("A") + CoroutineName("B")
val badCtx = myInterceptor + myInterceptor
println(goodCtx)
println(badCtx)
This prints
CoroutineName(B)
[My Interceptor, My Interceptor]
The first line shows you what the behavior is supposed to be, and the second one shows what it is with your implementation. You broke the equality relation. I didn't get to realizing why.
Even this finding doesn't directly explain how come your interceptor ends up being used, but it does give you some insight into what kind of things start happening when you use the framework against the intention of the authors.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With