Slide 1

Slide 1 text

@rbusarow UNDERSTANDING
 COROUTINECONTEXT Rick Busarow

Slide 2

Slide 2 text

@rbusarow CoroutineScope.kt public interface CoroutineScope {~ public val coroutineContext: CoroutineContext }~

Slide 3

Slide 3 text

@rbusarow CoroutineScope.kt public interface CoroutineScope {~ public val coroutineContext: CoroutineContext }~

Slide 4

Slide 4 text

@rbusarow public fun CoroutineScope.launch( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() !-> Unit ): Job {~ val newContext = newCoroutineContext(context) val coroutine = if (start.isLazy) LazyStandaloneCoroutine(newContext, block) else StandaloneCoroutine(newContext, active = true) coroutine.start(start, coroutine, block) return coroutine }~ Builders.common

Slide 5

Slide 5 text

@rbusarow public fun CoroutineScope.launch( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() !-> Unit ): Job {~ val newContext = newCoroutineContext(context) val coroutine = if (start.isLazy) LazyStandaloneCoroutine(newContext, block) else StandaloneCoroutine(newContext, active = true) coroutine.start(start, coroutine, block) return coroutine }~ Builders.common

Slide 6

Slide 6 text

@rbusarow public fun~CoroutineScope.launch( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() !->~Unit ):~Job {~ val newContext = newCoroutineContext(context) val coroutine = if (start.isLazy) LazyStandaloneCoroutine(newContext, block) else StandaloneCoroutine(newContext, active = true) coroutine.start(start, coroutine, block) return coroutine }~ Builders.common

Slide 7

Slide 7 text

@rbusarow Builders.common public suspend fun withContext( context: CoroutineContext, block: suspend CoroutineScope.() !-> T ): T = suspendCoroutineUninterceptedOrReturn sc@ { uCont !-> … }

Slide 8

Slide 8 text

@rbusarow Builders.common public suspend fun withContext( context: CoroutineContext, block: suspend CoroutineScope.() !-> T ): T = suspendCoroutineUninterceptedOrReturn sc@ { uCont !-> … }

Slide 9

Slide 9 text

@rbusarow CoroutineContext.kt interface Element :~CoroutineContext

Slide 10

Slide 10 text

@rbusarow interface Element :~CoroutineContext interface Job :~CoroutineContext.Element

Slide 11

Slide 11 text

@rbusarow interface Element :~CoroutineContext interface Job :~CoroutineContext.Element interface ContinuationInterceptor :~CoroutineContext.Element

Slide 12

Slide 12 text

@rbusarow interface Element :~CoroutineContext interface Job :~CoroutineContext.Element interface ContinuationInterceptor :~CoroutineContext.Element interface CoroutineExceptionHandler :~CoroutineContext.Element

Slide 13

Slide 13 text

@rbusarow interface Element :~CoroutineContext interface Job :~CoroutineContext.Element interface ContinuationInterceptor :~CoroutineContext.Element interface CoroutineExceptionHandler :~CoroutineContext.Element data class CoroutineName( val name: String ) : CoroutineContext.Element

Slide 14

Slide 14 text

@rbusarow 4 TYPES OF COROUTINECONTEXT ▸ Job ▸ ContinuationInterceptor ▸ CoroutineExceptionHandler ▸ CoroutineName

Slide 15

Slide 15 text

@rbusarow SO WHAT IS COROUTINECONTEXT?

Slide 16

Slide 16 text

@rbusarow SO WHAT IS COROUTINECONTEXT? interface CoroutineContext :~ TypeSafeHeterogeneousMap

Slide 17

Slide 17 text

@rbusarow CoroutineContext.kt interface CoroutineContext {~ operator fun get(key: Key): E? operator fun plus(context:~CoroutineContext): CoroutineContext {~…~} fun fold(initial: R, operation: (R, Element) !-> R): R fun minusKey(key: Key!!<*>): CoroutineContext interface Key interface Element :~CoroutineContext {~…~} }~

Slide 18

Slide 18 text

@rbusarow CoroutineContext.kt interface CoroutineContext {~ operator fun get(key: Key): E? operator fun plus(context:~CoroutineContext): CoroutineContext {~…~} fun fold(initial: R, operation: (R, Element) !-> R): R fun minusKey(key: Key!!<*>): CoroutineContext interface Key interface Element :~CoroutineContext {~…~} }~

Slide 19

Slide 19 text

@rbusarow CoroutineContext.kt interface CoroutineContext { !// … interface Element : CoroutineContext { !// … override operator fun get(key: Key): E? = @Suppress("UNCHECKED_CAST") if (this.key !== key) this as E else null override fun fold( initial: R, operation: (R, Element) !-> R ): R = operation(initial, this) override fun minusKey(key: Key!!<*>): CoroutineContext = if (this.key !== key) EmptyCoroutineContext else this } }

Slide 20

Slide 20 text

@rbusarow CoroutineContext.kt interface CoroutineContext {~ operator fun get(key: Key): E? operator fun plus(context:~CoroutineContext): CoroutineContext {~…~} fun fold(initial: R, operation: (R, Element) !-> R): R fun minusKey(key: Key!!<*>): CoroutineContext interface Key interface Element :~CoroutineContext {~…~} }~

Slide 21

Slide 21 text

@rbusarow CoroutineContext.kt class MyClass : CoroutineScope { override val coroutineContext: CoroutineContext get() = Job() + Dispatchers.Main }

Slide 22

Slide 22 text

@rbusarow CoroutineContext.kt class MyClass : CoroutineScope { override val coroutineContext: CoroutineContext get() = Job() + Dispatchers.Main }

Slide 23

Slide 23 text

@rbusarow CoroutineContext.kt interface CoroutineContext {~ operator fun get(key: Key): E? operator~fun~plus(context:~CoroutineContext):~CoroutineContext {~…~} fun fold(initial: R, operation: (R, Element) !->~R): R fun minusKey(key: Key!!<*>): CoroutineContext interface Key interface Element :~CoroutineContext {~…~} }~

Slide 24

Slide 24 text

@rbusarow CoroutineContext.kt operator~fun~plus(context:~CoroutineContext):~CoroutineContext = if (context !!=== EmptyCoroutineContext) this else context.fold(this) { acc, element !-> val removed = acc.minusKey(element.key) if (removed !!=== EmptyCoroutineContext) element else { val interceptor = removed[ContinuationInterceptor] if (interceptor !== null) CombinedContext(removed, element) else { val left = removed.minusKey(ContinuationInterceptor) if (left !!=== EmptyCoroutineContext) CombinedContext( element, interceptor ) else CombinedContext(CombinedContext(left, element), interceptor) } } }

Slide 25

Slide 25 text

@rbusarow open class SimpleCoroutineContext( val job: Job =~Job(), val continuationInterceptor: ContinuationInterceptor =~ Dispatchers.Default, val coroutineExceptionHandler: CoroutineExceptionHandler =~ DefaultCoroutineExceptionHandler(), val coroutineName: CoroutineName =~CoroutineName("coroutine") )~

Slide 26

Slide 26 text

@rbusarow open class SimpleCoroutineContext( val job: Job =~Job(), val continuationInterceptor: ContinuationInterceptor =~ Dispatchers.Default, val coroutineExceptionHandler: CoroutineExceptionHandler =~ DefaultCoroutineExceptionHandler(), val coroutineName: CoroutineName =~CoroutineName(“coroutine") )~ { operator fun plus(other: CoroutineContext): SimpleCoroutineContext { return when (other) { is Job !-> SimpleCoroutineContext(job = other, continuationInterceptor = continuationInterceptor, coroutineExceptionHandler = coroutineExceptionHandler, coroutineName = coroutineName ) else !-> TODO(“definitely won’t fit on a slide") } } }

Slide 27

Slide 27 text

@rbusarow class MyCustomElement(val name: String) : CoroutineContext.Element {~ override val key: Key!!<*> get() =~MyCustomElement companion object :~Key }~

Slide 28

Slide 28 text

@rbusarow val foo = MyCustomElement("Hello") val ctx: CoroutineContext = EmptyCoroutineContext + foo val name: String = ctx[MyCustomElement]!!!.name

Slide 29

Slide 29 text

@rbusarow CoroutineContext.kt interface CoroutineContext {~ operator fun get(key: Key): E? operator~fun~plus(context:~CoroutineContext):~CoroutineContext {~…~} fun fold(initial: R, operation: (R, Element) !->~R): R fun minusKey(key: Key!!<*>): CoroutineContext interface Key interface Element :~CoroutineContext {~…~} }~

Slide 30

Slide 30 text

@rbusarow CoroutineContext.kt public interface CoroutineContext { !// … public interface Element : CoroutineContext { public val key: Key!!<*> public override operator fun get(key: Key): E? = @Suppress("UNCHECKED_CAST") if (this.key !== key) this as E else null public override fun fold( initial: R, operation: (R, Element) !-> R ): R = operation(initial, this) public override fun minusKey(key: Key!!<*>): CoroutineContext = if (this.key !== key) EmptyCoroutineContext else this } }

Slide 31

Slide 31 text

@rbusarow Builders.common public suspend fun withContext( context: CoroutineContext, block: suspend CoroutineScope.() !-> T ): T = suspendCoroutineUninterceptedOrReturn sc@ { uCont !-> val oldContext = uCont.context val newContext = oldContext + context !// … }

Slide 32

Slide 32 text

@rbusarow Builders.common public suspend fun withContext( context: CoroutineContext, block: suspend CoroutineScope.() !-> T ): T = suspendCoroutineUninterceptedOrReturn sc@ { uCont !-> val oldContext = uCont.context val newContext = oldContext + context !// … }

Slide 33

Slide 33 text

@rbusarow fun CoroutineScope.launchThenDoSomethingSlow() = launch { doSomethingSlow() } /** * This code always inherits: * 1. Job * 2. CoroutineExceptionHandler * 3. CoroutineName * * This code enforces its own: * 1. ContinuationInterceptor !*/ suspend fun doSomethingSlow() = withContext(Dispatchers.IO) { !// !!... }

Slide 34

Slide 34 text

@rbusarow fun CoroutineScope.launchThenDoSomethingSlow() = launch { doSomethingSlow() } /** * This code always inherits: * 1. CoroutineExceptionHandler * 2. CoroutineName * * This code enforces its own: * 1. Job * 2. ContinuationInterceptor !*/ suspend fun doSomethingSlow() = withContext(Job() + Dispatchers.IO) { !// !!... }

Slide 35

Slide 35 text

@rbusarow THANKS!