I always thought that the following code would pas...
# coroutines
j
I always thought that the following code would pass (🧵), but I just discovered it does not. Can some explain to me why?
Copy code
val ctx1: CoroutineContext = StandardTestDispatcher()
val ctx2: CoroutineContext = MyContinuationInterceptor()
val ctx3 = ctx1 + ctx2
assert(ctx3[ContinuationInterceptor] == ctx2)
In other words, I thought that adding a
ContinuationInterceptor
to a context that has a
TestDispatcher
would replace that dispatcher, because a dispatcher is a ContinuationInterceptor
r
It's hard to tell without knowing your MyContinuationInterceptor implementation
e.g. this passes for me:
Copy code
@Test
    fun thing() {
        val ctx1: CoroutineContext = StandardTestDispatcher()
        val ctx2: CoroutineContext = object : ContinuationInterceptor {
            override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> {
                TODO("Not yet implemented")
            }

            override val key: CoroutineContext.Key<*>
                get() = ContinuationInterceptor
        }
        val ctx3 = ctx1 + ctx2
        assert(ctx3[ContinuationInterceptor] == ctx2)
    }
I assume your problem is because they keys are different, i.e.
ContinuationInterceptor.Key
is different to
CoroutineDispatcher.Key
j
I'm in the process of setting up a Kotlin Playground snippet
It is probably what you say. I can't remember now why 2 years ago I decided I needed an
AbstractCoroutineContextElement
, but that might be the problem?
r
So to be retrieved when calling ctx3[ContinuationInterceptor] your implementation should return something like
Copy code
override val key: CoroutineContext.Key<*>
get() = ContinuationInterceptor.Key`
or, since it's a delegate anyway I think you could do
Copy code
class DelegatingContinuationInterceptor(private val delegate: ContinuationInterceptor) :
    AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor by delegate
and don't override the key property
hmm though I didn't look at the impl of AbstractCoroutineContextKey - it does seem like baseKey should match as well
It looks like you just end up with two Keys - the Dispatcher and your Interceptor - which both are ContinuationInterceptors and ends up picking the Dispatcher first. It doesn't work so well as a Key in this regard. e.g. this works:
Copy code
fun main() {
    val ctx1: CoroutineContext = kotlinx.coroutines.Dispatchers.Default
    val ctx2 = DelegatingContinuationInterceptor(ctx1[ContinuationInterceptor]!!)
	val ctx3 = ctx1 + ctx2    
	assert(ctx3.minusKey(kotlinx.coroutines.CoroutineDispatcher)[ContinuationInterceptor] == ctx2)
}
so when attaching your Interceptor you could do something like:
Copy code
val ctx2 = DelegatingContinuationInterceptor(ctx1[ContinuationInterceptor]!!)
	val ctx3 = ctx1.minusKey(ContinuationInterceptor) + ctx2
which I'm sure you could wrap in a nice extension method