custom-CoroutineContextElement
Custom co-routine context elements, Typically implemented by extending AbstractCoroutineContextElement
From example-add-request-id
Go to text →
GT-Sandbox-Snapshot: Example adding Request ID to Co-Routine Context.
Code
package com.glassthought.sandbox
import com.glassthought.sandbox.util.out.impl.out
import kotlinx.coroutines.*
import kotlin.coroutines.AbstractCoroutineContextElement
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.coroutineContext
// Define a context element to hold request ID
data class RequestId(val id: String) : AbstractCoroutineContextElement(RequestId) {
companion object Key : CoroutineContext.Key<RequestId>
}
// Any suspend function can access the request ID from context
suspend fun doWork(about: String) {
val requestId = currentCoroutineContext()[RequestId]?.id
out.info("[${about}]: Working on request_id=[$requestId]")
delay(50)
}
fun main(): kotlin.Unit = runBlocking {
println("--------------------------------------------------------------------------------")
println("MAIN started")
// Add request ID to coroutine context
withContext(RequestId("req-12345")) {
out.info("Request ID added to context")
// All suspend calls in this scope can access it
doWork("direct-call-within-context")
// Child inherits parent context (RequestId + CoroutineName)
launch { doWork("in-launch") }
}
}
Command to reproduce:
gt.sandbox.checkout.commit 86fa348a8e09123d80d8 \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./gradlew run --quiet"
Recorded output of command:
Picked up JAVA_TOOL_OPTIONS: -Dkotlinx.coroutines.debug
Picked up JAVA_TOOL_OPTIONS: -Dkotlinx.coroutines.debug
--------------------------------------------------------------------------------
MAIN started
[INFO][elapsed: 15ms][①][coroutname:@coroutine#1] Request ID added to context
[INFO][elapsed: 27ms][①][coroutname:@coroutine#1] [direct-call-within-context]: Working on request_id=[req-12345]
[INFO][elapsed: 81ms][⓶][coroutname:@coroutine#2] [in-launch]: Working on request_id=[req-12345]
NOTE: if you launch on another scope as expected the fields will NOT transfer over UNLESS you pass through your coroutine context.
GT-Sandbox-Snapshot: Example passing through custom element to a task spawned on background scope.
Code
package com.glassthought.sandbox
import kotlinx.coroutines.*
import kotlin.coroutines.AbstractCoroutineContextElement
import kotlin.coroutines.CoroutineContext
data class RequestId(val id: String) : AbstractCoroutineContextElement(RequestId) {
companion object Key : CoroutineContext.Key<RequestId>
}
suspend fun doWork(about: String) {
val requestId = currentCoroutineContext()[RequestId]?.id
println("[$about]: request_id=[$requestId]")
delay(50)
}
// Different scope (like a singleton or background processor)
val backgroundScope = CoroutineScope(Dispatchers.Default)
fun main() = runBlocking {
println("=== Context Inheritance ===\n")
withContext(RequestId("req-12345")) {
println("Request context set to: req-12345\n")
// ✅ INHERITS - uses current scope
launch { doWork("launch") }
// ❌ DOES NOT INHERIT - uses backgroundScope instead
backgroundScope.launch { doWork("backgroundScope.launch") }
// ✅ FIX - explicitly pass context to different scope
backgroundScope.launch(currentCoroutineContext()) {
doWork("backgroundScope.launch + context")
}
delay(200)
}
backgroundScope.cancel()
}
Command to reproduce:
gt.sandbox.checkout.commit 052b2be0dc8e73ae2259 \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./gradlew run --quiet"
Recorded output of command:
Picked up JAVA_TOOL_OPTIONS: -Dkotlinx.coroutines.debug
Picked up JAVA_TOOL_OPTIONS: -Dkotlinx.coroutines.debug
=== Context Inheritance ===
Request context set to: req-12345
[backgroundScope.launch]: request_id=[null]
[launch]: request_id=[req-12345]
[backgroundScope.launch + context]: request_id=[req-12345]
Children
Backlinks