Solution: CounterContext

Here is a simple solution:

class CounterContext : AbstractCoroutineContextElement(CounterContext){ private var counter = 0 fun next(): Int = counter++ companion object : CoroutineContext.Key<CounterContext> }

Here is a thread-safe solution:

class CounterContext : AbstractCoroutineContextElement(CounterContext){ private val counter = AtomicInteger(0) fun next(): Int = counter.getAndIncrement() companion object : CoroutineContext.Key<CounterContext> }

Example solution in playground

import kotlinx.coroutines.* import org.junit.Test import java.util.concurrent.atomic.AtomicInteger import kotlin.coroutines.* import kotlin.test.assertEquals class CounterContext : AbstractCoroutineContextElement(CounterContext){ private var counter = 0 fun next(): Int = counter++ companion object : CoroutineContext.Key<CounterContext> } fun main(): Unit = runBlocking(CounterContext()) { println(coroutineContext[CounterContext]?.next()) // 0 println(coroutineContext[CounterContext]?.next()) // 1 launch { println(coroutineContext[CounterContext]?.next())// 2 println(coroutineContext[CounterContext]?.next())// 3 } launch(CounterContext()) { println(coroutineContext[CounterContext]?.next())// 0 println(coroutineContext[CounterContext]?.next())// 1 } } class CounterContextTests { @Test fun `should return next numbers in the same coroutine`() = runBlocking<Unit>(CounterContext()) { assertEquals(0, coroutineContext[CounterContext]?.next()) assertEquals(1, coroutineContext[CounterContext]?.next()) assertEquals(2, coroutineContext[CounterContext]?.next()) assertEquals(3, coroutineContext[CounterContext]?.next()) assertEquals(4, coroutineContext[CounterContext]?.next()) } @Test fun `should have independent counter for each instance`() = runBlocking<Unit> { launch(CounterContext()) { assertEquals(0, coroutineContext[CounterContext]?.next()) assertEquals(1, coroutineContext[CounterContext]?.next()) assertEquals(2, coroutineContext[CounterContext]?.next()) } launch(CounterContext()) { assertEquals(0, coroutineContext[CounterContext]?.next()) assertEquals(1, coroutineContext[CounterContext]?.next()) assertEquals(2, coroutineContext[CounterContext]?.next()) } } @Test fun `should propagate to the child`() = runBlocking<Unit>(CounterContext()) { assertEquals(0, coroutineContext[CounterContext]?.next()) launch { assertEquals(1, coroutineContext[CounterContext]?.next()) launch { assertEquals(2, coroutineContext[CounterContext]?.next()) } launch(CounterContext()) { assertEquals(0, coroutineContext[CounterContext]?.next()) assertEquals(1, coroutineContext[CounterContext]?.next()) launch { assertEquals(2, coroutineContext[CounterContext]?.next()) } } } } }