Kotlin Coroutines Reloaded

156
Kotlin Coroutines Reloaded Presented at JVM Language Summit, 2017 /Roman Elizarov @ JetBrains

Transcript of Kotlin Coroutines Reloaded

Page 1: Kotlin Coroutines Reloaded

KotlinCoroutinesReloadedPresentedatJVMLanguageSummit,2017

/RomanElizarov@JetBrains

Page 2: Kotlin Coroutines Reloaded

Speaker:RomanElizarov

• 16+yearsexperience• Previouslydevelopedhigh-perftradingsoftware@Devexperts• Teachconcurrent&[email protected]• Chiefjudge@NortheasternEuropeanRegionofACMICPC• NowworkonKotlin@JetBrains

Page 3: Kotlin Coroutines Reloaded

Agenda

• RecapofKotlincoroutinesprototype• Presented@JVMLS,2016byAndreyBreslav

• Theissuesintheprototypedesign• andthesolutionsthatwerefound

• DesignreleasedtopublicinKotlin1.1• Evolvedcoroutinessupportlibraries• Challenges&futuredirections

Page 4: Kotlin Coroutines Reloaded

RecapofKotlincoroutinesprototypePresented@JVMLS,2016byAndreyBreslav

Page 5: Kotlin Coroutines Reloaded

AsynctheC#(JS,TS,Dart,etc)way

async Task<String> Work() { … }

async Task MoreWork(){

Console.WriteLine("Work started");var str = await Work();Console.WriteLine($"Work completed {str}");

}

Page 6: Kotlin Coroutines Reloaded

AsynctheC#(JS,TS,Dart,etc)way

async Task<String> work() { … }

async Task MoreWork(){

Console.WriteLine("Work started");var str = await Work();Console.WriteLine($"Work completed {str}");

}

Page 7: Kotlin Coroutines Reloaded

AsynctheKotlinway(prototype)

fun work(): CompletableFuture<String> { … }

fun moreWork() = async {println("Work started")val str = await(work())println("Work completed: $str")

}

Page 8: Kotlin Coroutines Reloaded

AsynctheKotlinway(prototype)

fun work(): CompletableFuture<String> { … }

fun moreWork() = async {println("Work started")val str = await(work())println("Work completed: $str")

}

FunctionsvsKeywords

Extensibility

RunsonstockJVM1.6+

Purelylocal-- noglobalbytecodetransforms

Page 9: Kotlin Coroutines Reloaded

SuspendingfunctionsAgrandunificationbetweenasync/awaitandgenerate/yield

Page 10: Kotlin Coroutines Reloaded

Suspendingfunctions:use

val str = await(work())

Page 11: Kotlin Coroutines Reloaded

Suspendingfunctions:use

val str = await(work())

CompletableFuture<String>

// String result

Page 12: Kotlin Coroutines Reloaded

Suspendingfunctions:declare(prototype)

val str = await(work())

suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Unit

CompletableFuture<String>

// String result

Page 13: Kotlin Coroutines Reloaded

Suspendingfunctions:declare(prototype)

val str = await(work())

suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Unit

CompletableFuture<String>

callback void// String result

Magicsignaturetransformation

Page 14: Kotlin Coroutines Reloaded

Suspendingfunctions:declare(prototype)

val str = await(work())

suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Unit

CompletableFuture<String>

callback void// String result

Continuationisagenericcallbackinterface

interface Continuation<in T> {fun resume(value: T)fun resumeWithException(exception: Throwable)

}

Page 15: Kotlin Coroutines Reloaded

Suspendingfunctions:implement(prototype)

val str = await(work())

suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>) {f.whenComplete { value, exception ->

if (exception != null) c.resumeWithException(exception)else c.resume(value)

}}

CompletableFuture<String>

// String result

Page 16: Kotlin Coroutines Reloaded

Suspendingfunctions:implement(prototype)

val str = await(work())

suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>) {f.whenComplete { value, exception ->

if (exception != null) c.resumeWithException(exception)else c.resume(value)

}}

CompletableFuture<String>

// String result

Page 17: Kotlin Coroutines Reloaded

Suspendingfunctions:implement(prototype)

val str = await(work())

suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>) {f.whenComplete { value, exception ->

if (exception != null) c.resumeWithException(exception)else c.resume(value)

}}

CompletableFuture<String>

// String result

Page 18: Kotlin Coroutines Reloaded

Suspendingfunctions:implement(prototype)

val str = await(work())

suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>) {f.whenComplete { value, exception ->

if (exception != null) c.resumeWithException(exception)else c.resume(value)

}}

CompletableFuture<String>

// String result

Page 19: Kotlin Coroutines Reloaded

Suspendingfunctions:implement(prototype)

val str = await(work())

suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>) {f.whenComplete { value, exception ->

if (exception != null) c.resumeWithException(exception)else c.resume(value)

}}

CompletableFuture<String>

// String result

Simple,butwrong!

Page 20: Kotlin Coroutines Reloaded

AproblematicexampleWheregrandvisionmeetsreality

Page 21: Kotlin Coroutines Reloaded

Aproblematicexamplesuspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>) {

f.whenComplete { value, exception ->if (exception != null) c.resumeWithException(exception)

else c.resume(value)}

}

fun problem() = async {repeat(10_000) {

await(work())}

}

Page 22: Kotlin Coroutines Reloaded

Aproblematicexamplesuspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>) {

f.whenComplete { value, exception ->if (exception != null) c.resumeWithException(exception)

else c.resume(value)}

}

fun problem() = async {repeat(10_000) {

await(work())}

}

Whatifwork()alwaysreturnsafuturethatisalreadycomplete?

Page 23: Kotlin Coroutines Reloaded

Aproblematicexamplesuspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>) {

f.whenComplete { value, exception ->if (exception != null) c.resumeWithException(exception)

else c.resume(value)}

}

fun problem() = async {repeat(10_000) {

await(work())}

}

stack

problem$stateMachine

Page 24: Kotlin Coroutines Reloaded

Aproblematicexamplesuspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>) {

f.whenComplete { value, exception ->if (exception != null) c.resumeWithException(exception)

else c.resume(value)}

}

fun problem() = async {repeat(10_000) {

await(work())}

}

stack

problem$stateMachineawait

Page 25: Kotlin Coroutines Reloaded

Aproblematicexamplesuspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>) {

f.whenComplete { value, exception ->if (exception != null) c.resumeWithException(exception)

else c.resume(value)}

}

fun problem() = async {repeat(10_000) {

await(work())}

}

stack

problem$stateMachineawaitCompletableFuture.whenComplete

Page 26: Kotlin Coroutines Reloaded

Aproblematicexamplesuspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>) {

f.whenComplete { value, exception ->if (exception != null) c.resumeWithException(exception)

else c.resume(value)}

}

fun problem() = async {repeat(10_000) {

await(work())}

}

stack

problem$stateMachineawaitCompletableFuture.whenCompleteawait$lambda

Page 27: Kotlin Coroutines Reloaded

Aproblematicexamplesuspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>) {

f.whenComplete { value, exception ->if (exception != null) c.resumeWithException(exception)

else c.resume(value)}

}

fun problem() = async {repeat(10_000) {

await(work())}

}

stack

problem$stateMachineawaitCompletableFuture.whenCompleteawait$lambda

Page 28: Kotlin Coroutines Reloaded

Aproblematicexamplesuspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>) {

f.whenComplete { value, exception ->if (exception != null) c.resumeWithException(exception)

else c.resume(value)}

}

fun problem() = async {repeat(10_000) {

await(work())}

}

stack

problem$stateMachineawaitCompletableFuture.whenCompleteawait$lambdaContinuationImpl.resume

Page 29: Kotlin Coroutines Reloaded

Aproblematicexamplesuspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>) {

f.whenComplete { value, exception ->if (exception != null) c.resumeWithException(exception)

else c.resume(value)}

}

fun problem() = async {repeat(10_000) {

await(work()) }

}

stack

problem$stateMachineawaitCompletableFuture.whenCompleteawait$lambdaContinuationImpl.resumeproblem$stateMachine

Page 30: Kotlin Coroutines Reloaded

Aproblematicexamplesuspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>) {

f.whenComplete { value, exception ->if (exception != null) c.resumeWithException(exception)

else c.resume(value)}

}

fun problem() = async {repeat(10_000) {

await(work())}

}

stack

problem$stateMachineawaitCompletableFuture.whenCompleteawait$lambdaContinuationImpl.resumeproblem$stateMachineawait

Page 31: Kotlin Coroutines Reloaded

Aproblematicexamplesuspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>) {

f.whenComplete { value, exception ->if (exception != null) c.resumeWithException(exception)

else c.resume(value)}

}

fun problem() = async {repeat(10_000) {

await(work())}

}

stack

problem$stateMachineawaitCompletableFuture.whenCompleteawait$lambdaContinuationImpl.resumeproblem$stateMachineawait…

StackOverflowError

Page 32: Kotlin Coroutines Reloaded

AsolutionAdifferencebetweenknowingthepath&walkingthepath.

Page 33: Kotlin Coroutines Reloaded

Asolution

val str = await(work())

suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Unit

CompletableFuture<String>

// String result

Page 34: Kotlin Coroutines Reloaded

Asolution(0):stackunwindconvention

val str = await(work())

suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any?

CompletableFuture<String>

// String result

Page 35: Kotlin Coroutines Reloaded

Asolution(0)

val str = await(work())

suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any?

CompletableFuture<String>

// String result

T | COROUTINE_SUSPENDED

Page 36: Kotlin Coroutines Reloaded

Asolution(0)

val str = await(work())

suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any?

CompletableFuture<String>

// String result

T | COROUTINE_SUSPENDED

Didnotsuspend->Returnsresult

Page 37: Kotlin Coroutines Reloaded

Asolution(0)

val str = await(work())

suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any?

CompletableFuture<String>

// String result

T | COROUTINE_SUSPENDED

Didnotsuspend->Returnsresult

Didsuspend->WILLinvokecontinuation

Page 38: Kotlin Coroutines Reloaded

Asolution(0)suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any? {

…}

Page 39: Kotlin Coroutines Reloaded

Asolution?suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any? {

val consensus = AtomicReference<Any?>(UNDECIDED)f.whenComplete { value, exception ->

if (exception != null) {if (!consensus.compareAndSet(UNDECIDED, Fail(exception)))

c.resumeWithException(exception)} else {

if (!consensus.compareAndSet(UNDECIDED, value))c.resume(value)

}}consensus.compareAndSet(UNDECIDED, COROUTINE_SUSPENDED)val result = consensus.get()if (result is Fail) throw result.exceptionreturn result

}

Page 40: Kotlin Coroutines Reloaded

Asolution?suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any? {

val consensus = AtomicReference<Any?>(UNDECIDED)f.whenComplete { value, exception ->

if (exception != null) {if (!consensus.compareAndSet(UNDECIDED, Fail(exception)))

c.resumeWithException(exception)} else {

if (!consensus.compareAndSet(UNDECIDED, value))c.resume(value)

}}consensus.compareAndSet(UNDECIDED, COROUTINE_SUSPENDED)val result = consensus.get()if (result is Fail) throw result.exceptionreturn result

}

Wat?

Page 41: Kotlin Coroutines Reloaded

Asolution(1):Call/declarationfidelity

Page 42: Kotlin Coroutines Reloaded

Asolution(1)suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any?

Page 43: Kotlin Coroutines Reloaded

Asolution(1)suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any?

suspend fun <T> await(f: CompletableFuture<T>): Tnaturalsignature

Page 44: Kotlin Coroutines Reloaded

Asolution(1)fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any?

suspend fun <T> await(f: CompletableFuture<T>): T

Compilesas(onJVM)

Page 45: Kotlin Coroutines Reloaded

Asolution(1)fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any?

suspend fun <T> await(f: CompletableFuture<T>): T

Compilesas(onJVM)CPSTransformation

Page 46: Kotlin Coroutines Reloaded

Asolution(1)fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any?

suspend fun <T> await(f: CompletableFuture<T>)

suspend fun <T> await(f: CompletableFuture<T>): T =suspendCoroutineOrReturn { c ->

…}

Recovercontinuation

Page 47: Kotlin Coroutines Reloaded

Asolution(1)fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any?

suspend fun <T> await(f: CompletableFuture<T>)

suspend fun <T> await(f: CompletableFuture<T>): T =suspendCoroutineOrReturn { c ->

…} Inspiredbycall/ccfromScheme

Recovercontinuation

Page 48: Kotlin Coroutines Reloaded

Asolution(1)fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any?

suspend fun <T> await(f: CompletableFuture<T>)

suspend fun <T> await(f: CompletableFuture<T>): T =suspendCoroutineOrReturn { c ->

…}

Worksas

Inspiredbycall/ccfromScheme

Page 49: Kotlin Coroutines Reloaded

Asolution(1)

suspend fun <T> await(f: CompletableFuture<T>): T =suspendCoroutineOrReturn { c ->

…}

Page 50: Kotlin Coroutines Reloaded

Asolution(1)

suspend fun <T> await(f: CompletableFuture<T>): T =suspendCoroutineOrReturn { c ->

…}

inline suspend fun <T> suspendCoroutineOrReturn(crossinline block: (Continuation<T>) -> Any?): T

Page 51: Kotlin Coroutines Reloaded

Asolution(1)

suspend fun <T> await(f: CompletableFuture<T>): T =suspendCoroutineOrReturn { c ->

…}

inline suspend fun <T> suspendCoroutineOrReturn(crossinline block: (Continuation<T>) -> Any?): T

T | COROUTINE_SUSPENDED

Page 52: Kotlin Coroutines Reloaded

Asolution(1)

suspend fun <T> await(f: CompletableFuture<T>): T =suspendCoroutineOrReturn { c ->

…}

inline suspend fun <T> suspendCoroutineOrReturn(crossinline block: (Continuation<T>) -> Any?): T

Intrinsic

T | COROUTINE_SUSPENDED

Page 53: Kotlin Coroutines Reloaded

Asolution(1)

suspend fun <T> await(f: CompletableFuture<T>): T =suspendCoroutineOrReturn { c ->

…}

inline suspend fun <T> suspendCoroutineOrReturn(crossinline block: (Continuation<T>) -> Any?): T

fun <T> suspendCoroutineOrReturn(crossinline block: (Continuation<T>) -> Any?, c: Continuation<T>): Any? =

block(c)

Compilesas(onJVM)

Intrinsic

CPSTransformation

Page 54: Kotlin Coroutines Reloaded

Asolution(1)

suspend fun <T> await(f: CompletableFuture<T>): T =suspendCoroutineOrReturn { c ->

…}

inline suspend fun <T> suspendCoroutineOrReturn(crossinline block: (Continuation<T>) -> Any?): T

fun <T> suspendCoroutineOrReturn(crossinline block: (Continuation<T>) -> Any?, c: Continuation<T>): Any? =

block(c)

Compilesas(onJVM)

Intrinsic

CPSTransformation

Page 55: Kotlin Coroutines Reloaded

Asolution(2):Tailsuspension

Page 56: Kotlin Coroutines Reloaded

Asolution(2)

suspend fun <T> await(f: CompletableFuture<T>): T =suspendCoroutineOrReturn { c ->

…}

Tailsuspendinvocation:Continuationpass-through

Page 57: Kotlin Coroutines Reloaded

Asolution(2)

suspend fun <T> await(f: CompletableFuture<T>): T =suspendCoroutineOrReturn { c ->

…}

fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any? =suspendCoroutineOrReturn(c) { c ->

…}

Tailsuspendinvocation:Continuationpass-through

Compilesas(onJVM)CPSTransformation

Page 58: Kotlin Coroutines Reloaded

Asolution(2)

suspend fun <T> await(f: CompletableFuture<T>): T =suspendCoroutineOrReturn { c ->

…}

fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any? =suspendCoroutineOrReturn(c) { c ->

…}

Tailsuspendinvocation:Continuationpass-through

Compilesas(onJVM)CPSTransformation

Page 59: Kotlin Coroutines Reloaded

Asolution(2)

suspend fun <T> await(f: CompletableFuture<T>): T =suspendCoroutineOrReturn { c ->

…}

fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any? { …

}

Tailsuspendinvocation:Continuationpass-through

Compilesas(onJVM)CPSTransformation

Page 60: Kotlin Coroutines Reloaded

Asolution(3):Abstraction

Page 61: Kotlin Coroutines Reloaded

Asolution(3)

public inline suspend fun <T> suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T =

suspendCoroutineOrReturn { c: Continuation<T> ->val safe = SafeContinuation(c)block(safe)safe.getResult()

}

Page 62: Kotlin Coroutines Reloaded

Asolution(3)

public inline suspend fun <T> suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T =

suspendCoroutineOrReturn { c: Continuation<T> ->val safe = SafeContinuation(c)block(safe)safe.getResult()

}

Page 63: Kotlin Coroutines Reloaded

Asolution(3)

public inline suspend fun <T> suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T =

suspendCoroutineOrReturn { c: Continuation<T> ->val safe = SafeContinuation(c)block(safe)safe.getResult()

}

Any?Isgone

Page 64: Kotlin Coroutines Reloaded

Asolution(3)

public inline suspend fun <T> suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T =

suspendCoroutineOrReturn { c: Continuation<T> ->val safe = SafeContinuation(c)block(safe)safe.getResult()

}

Encapsulatesresultconsensusalgorithm

Page 65: Kotlin Coroutines Reloaded

Asolution(4):Puttingitalltogether

Page 66: Kotlin Coroutines Reloaded

Asolution(4)

suspend fun <T> await(f: CompletableFuture<T>): T =suspendCoroutine { c ->

f.whenComplete { value, exception ->if (exception != null) c.resumeWithException(exception)

else c.resume(value)}

}

Lookssimple,workscorrectly!

Page 67: Kotlin Coroutines Reloaded

Recapstepstosolution

• T|COROUTINE_SUSPENDED(Any?)toallowinvocationsthatdonotsuspendandthusavoidStackOverflowError• Call/declarationfidelityviaCPStransformation• Introducecall/cc(suspendCoroutineOrReturn)torecoverhiddencontinuation• Tailcallinvocationsupporttorecoverprototypesemantics• Useabstraction(suspendCoroutine)tohideimplementationcomplexitiesfromend-users

Page 68: Kotlin Coroutines Reloaded

Thefinaltouch:awaitextension

suspend fun <T> await(f: CompletableFuture<T>): T =suspendCoroutine { c ->

f.whenComplete { value, exception ->if (exception != null) c.resumeWithException(exception)

else c.resume(value)}

}

Page 69: Kotlin Coroutines Reloaded

Thefinaltouch:awaitextension

suspend fun <T> CompletableFuture<T>.await(): T =suspendCoroutine { c ->

whenComplete { value, exception ->if (exception != null) c.resumeWithException(exception)

else c.resume(value)}

}

Page 70: Kotlin Coroutines Reloaded

Thefinaltouch:awaitextension

suspend fun <T> CompletableFuture<T>.await(): T =suspendCoroutine { c ->

whenComplete { value, exception ->if (exception != null) c.resumeWithException(exception)

else c.resume(value)}

}

fun moreWork() = async {println("Work started")val str = await(work())println("Work completed: $str")

}

Page 71: Kotlin Coroutines Reloaded

Thefinaltouch:awaitextension

suspend fun <T> CompletableFuture<T>.await(): T =suspendCoroutine { c ->

whenComplete { value, exception ->if (exception != null) c.resumeWithException(exception)

else c.resume(value)}

}

fun moreWork() = async {println("Work started")val str = work().await()println("Work completed: $str")

}

Readsleft-to-rightjustlikeitexecutes

Page 72: Kotlin Coroutines Reloaded

CoroutinebuildersThemysteryofinception

Page 73: Kotlin Coroutines Reloaded

Coroutinebuilders(prototype)fun <T> async(

coroutine c: FutureController<T>.() -> Continuation<Unit>): CompletableFuture<T> {

val controller = FutureController<T>()c(controller).resume(Unit)return controller.future

}

Page 74: Kotlin Coroutines Reloaded

Coroutinebuilders(prototype)fun <T> async(

coroutine c: FutureController<T>.() -> Continuation<Unit>): CompletableFuture<T> {

val controller = FutureController<T>()c(controller).resume(Unit)return controller.future

}

Aspecialmodifier

Page 75: Kotlin Coroutines Reloaded

Coroutinebuilders(prototype)fun <T> async(

coroutine c: FutureController<T>.() -> Continuation<Unit>): CompletableFuture<T> {

val controller = FutureController<T>()c(controller).resume(Unit)return controller.future

}

Magicsignaturetransformation

Page 76: Kotlin Coroutines Reloaded

Coroutinebuilders(prototype)fun <T> async(

coroutine c: FutureController<T>.() -> Continuation<Unit>): CompletableFuture<T> {

val controller = FutureController<T>()c(controller).resume(Unit)return controller.future

}Aboilerplatetostartcoroutine

Page 77: Kotlin Coroutines Reloaded

Coroutinebuilders(prototype)fun <T> async(

coroutine c: FutureController<T>.() -> Continuation<Unit>): CompletableFuture<T> { … }

class FutureController<T> {val future = CompletableFuture<T>()

operator fun handleResult(value: T, c: Continuation<Nothing>) {future.complete(value)

}

operator fun handleException(exception: Throwable, c: Continuation<Nothing>) {

future.completeExceptionally(exception)}

}

Page 78: Kotlin Coroutines Reloaded

Coroutinebuilders(prototype)fun <T> async(

coroutine c: FutureController<T>.() -> Continuation<Unit>): CompletableFuture<T> { … }

class FutureController<T> {val future = CompletableFuture<T>()

operator fun handleResult(value: T, c: Continuation<Nothing>) {future.complete(value)

}

operator fun handleException(exception: Throwable, c: Continuation<Nothing>) {

future.completeExceptionally(exception)}

}

Page 79: Kotlin Coroutines Reloaded

Coroutinebuilders(prototype)fun <T> async(

coroutine c: FutureController<T>.() -> Continuation<Unit>): CompletableFuture<T> { … }

class FutureController<T> {val future = CompletableFuture<T>()

operator fun handleResult(value: T, c: Continuation<Nothing>) {future.complete(value)

}

operator fun handleException(exception: Throwable, c: Continuation<Nothing>) {

future.completeExceptionally(exception)}

}

wasneverused

Page 80: Kotlin Coroutines Reloaded

Coroutinebuilders(prototype)fun <T> async(

coroutine c: FutureController<T>.() -> Continuation<Unit>): CompletableFuture<T> { … }

class FutureController<T> {val future = CompletableFuture<T>()

operator fun handleResult(value: T) {future.complete(value)

}

operator fun handleException(exception: Throwable) {future.completeExceptionally(exception)

}}

Page 81: Kotlin Coroutines Reloaded

Coroutinebuilders(prototype)fun <T> async(

coroutine c: FutureController<T>.() -> Continuation<Unit>): CompletableFuture<T> { … }

class FutureController<T> {val future = CompletableFuture<T>()

operator fun handleResult(value: T) {future.complete(value)

}

operator fun handleException(exception: Throwable) {future.completeExceptionally(exception)

}}

LookslikeContinuation<T>

Page 82: Kotlin Coroutines Reloaded

Coroutinebuilders(prototype)fun <T> async(

coroutine c: FutureController<T>.() -> Continuation<Unit>): CompletableFuture<T> { … }

class FutureController<T> {val future = CompletableFuture<T>()

operator fun handleResult(value: T) {future.complete(value)

}

operator fun handleException(exception: Throwable) {future.completeExceptionally(exception)

}}

SomethingthattakesContinuation<T>

Page 83: Kotlin Coroutines Reloaded

Coroutinebuilders:evolved

Page 84: Kotlin Coroutines Reloaded

Coroutinebuildersfun <T> async(

c: suspend () -> T): CompletableFuture<T> {

val controller = FutureController<T>()c.startCoroutine(completion = controller)return controller.future

}

Page 85: Kotlin Coroutines Reloaded

Coroutinebuildersfun <T> async(

c: suspend () -> T): CompletableFuture<T> {

val controller = FutureController<T>()c.startCoroutine(completion = controller)return controller.future

}

naturalsignature

Nospecialcoroutine keyword

Page 86: Kotlin Coroutines Reloaded

Coroutinebuildersfun <T> async(

c: suspend () -> T): CompletableFuture<T> {

val controller = FutureController<T>()c.startCoroutine(completion = controller)return controller.future

}

Providedbystandardlibrary

Page 87: Kotlin Coroutines Reloaded

Coroutinebuildersfun <T> async(

c: suspend () -> T): CompletableFuture<T> { … }

class FutureController<T> : Continuation<T> {val future = CompletableFuture<T>()

override fun resume(value: T) {future.complete(value)

}

override fun resumeWithException(exception: Throwable) {future.completeExceptionally(exception)

}}

Page 88: Kotlin Coroutines Reloaded

Coroutinebuildersfun <T> async(

c: suspend () -> T): CompletableFuture<T> { … }

class FutureController<T> : Continuation<T> {val future = CompletableFuture<T>()

override fun resume(value: T) {future.complete(value)

}

override fun resumeWithException(exception: Throwable) {future.completeExceptionally(exception)

}}

Servesascompletioncontinuationforcoroutine

Page 89: Kotlin Coroutines Reloaded

BonusfeaturesFreeasincheese

Page 90: Kotlin Coroutines Reloaded

Arbitrarysuspendingcallssuspend fun <T> suspending(block: suspend () -> T): T =

suspendCoroutine { continuation ->block.startCoroutine(completion = continuation)

}

Page 91: Kotlin Coroutines Reloaded

Arbitrarysuspendingcallssuspend fun <T> suspending(block: suspend () -> T): T =

suspendCoroutine { continuation ->block.startCoroutine(completion = continuation)

}

Page 92: Kotlin Coroutines Reloaded

Arbitrarysuspendingcallssuspend fun <T> suspending(block: suspend () -> T): T =

suspendCoroutine { continuation ->block.startCoroutine(completion = continuation)

}

Page 93: Kotlin Coroutines Reloaded

Arbitrarysuspendingcallssuspend fun <T> suspending(block: suspend () -> T): T =

suspendCoroutine { continuation ->block.startCoroutine(completion = continuation)

}

fun moreWork() = async {println("Work started")val str = work().await()println("Work completed: $str")

}

ReturnsCompletableFuture

Page 94: Kotlin Coroutines Reloaded

Arbitrarysuspendingcallssuspend fun <T> suspending(block: suspend () -> T): T =

suspendCoroutine { continuation ->block.startCoroutine(completion = continuation)

}

suspend fun moreWork(): T = suspending {println("Work started")val str = work().await() println("Work completed: $str")

}

Page 95: Kotlin Coroutines Reloaded

Arbitrarysuspendingcallssuspend fun <T> suspending(block: suspend () -> T): T =

suspendCoroutine { continuation ->block.startCoroutine(completion = continuation)

}

suspend fun moreWork(): T = suspending {println("Work started")val str = work().await() println("Work completed: $str")

}

Tailsuspendinvocation

Non-tail(arbitrary)suspendinvocation

Page 96: Kotlin Coroutines Reloaded

Arbitrarysuspendingcalls

suspend fun moreWork() {println("Work started")val str = work().await() println("Work completed: $str")

}

Non-tail(arbitrary)suspendinvocation

Page 97: Kotlin Coroutines Reloaded

Stackless vsStackful coroutinesThecrucialdistinction

Page 98: Kotlin Coroutines Reloaded

Stackless vsStackful coroutines

Stackless StackfulRestrictions Useinspecial ctx UseanywhereImplemented in C#,Scala,Kotlin, … Quasar, Javaflow,…

Page 99: Kotlin Coroutines Reloaded

Stackless vsStackful coroutines

Stackless StackfulRestrictions Useinspecial ctx UseanywhereImplemented in C#,Scala,Kotlin, … Quasar, Javaflow,…

Page 100: Kotlin Coroutines Reloaded

Stackless vsStackful coroutines

Stackless StackfulRestrictions Useinspecial ctx UseanywhereImplemented in C#,Scala,Kotlin, … Quasar, Javaflow,…Can suspendin? suspend

functionsthrows SuspendExecution /@Suspendable functions

Page 101: Kotlin Coroutines Reloaded

Stackless vsStackful coroutines

Stackless StackfulRestrictions Useinspecial ctx UseanywhereImplemented in C#,Scala, … Kotlin,Quasar, Javaflow,…

Page 102: Kotlin Coroutines Reloaded

Stackless vsStackful coroutines

Stackless StackfulRestrictions Useinspecial ctx UseanywhereImplemented in C#,Scala, Kotlin,

Quasar, JavaFlow,…LISP,Go,…

Page 103: Kotlin Coroutines Reloaded

Stackless vsStackful coroutines

Stackless StackfulRestrictions Useinspecial ctx UseanywhereImplemented in C#,Scala, … Kotlin,Quasar, Javaflow,…

Falsedichotomy

Page 104: Kotlin Coroutines Reloaded

AsyncvsSuspendingfunctionsTheactualdifference

Page 105: Kotlin Coroutines Reloaded

fun work() = async { … }

suspend fun work() { … }

Page 106: Kotlin Coroutines Reloaded

fun work(): CompletableFuture<String> = async { … }

suspend fun work(): String { … }

InKotlinyouhave achoice

Page 107: Kotlin Coroutines Reloaded

fun workAsync(): CompletableFuture<String> = async { … }

suspend fun work(): String { … }

Page 108: Kotlin Coroutines Reloaded

workAsync() VALID –>producesCompletableFuture<String>

workAsync().await() VALID –>producesString

concurrent&asyncbehavior

sequentialbehavior

Themostneeded one,yetthemostsyntacticallyclumsy,esp.forsuspend-heavy(CSP)codestyle

Theproblemwithasync

Page 109: Kotlin Coroutines Reloaded

Kotlinsuspendingfunctionsimitatesequential behavior

bydefault

ConcurrencyishardConcurrencyhastobeexplicit

Page 110: Kotlin Coroutines Reloaded

ComposabilityMakingthoselegos click

Page 111: Kotlin Coroutines Reloaded

Buildersfun <T> async(

c: suspend () -> T): CompletableFuture<T> { … }

Page 112: Kotlin Coroutines Reloaded

Buildersfun <T> async(

c: suspend () -> T): ListenableFuture<T> { … }

Page 113: Kotlin Coroutines Reloaded

Buildersfun <T> async(

c: suspend () -> T): MyOwnFuture<T> { … }

Page 114: Kotlin Coroutines Reloaded

Suspendingfunctionsfun <T> async(

c: suspend () -> T): MyOwnFuture<T> { … }

suspend fun <T> CompletableFuture<T>.await(): T { … }

Page 115: Kotlin Coroutines Reloaded

Suspendingfunctionsfun <T> async(

c: suspend () -> T): MyOwnFuture<T> { … }

suspend fun <T> ListenableFuture<T>.await(): T { … }

Page 116: Kotlin Coroutines Reloaded

Suspendingfunctionsfun <T> async(

c: suspend () -> T): MyOwnFuture<T> { … }

suspend fun <T> MyOwnFuture<T>.await(): T { … }

Page 117: Kotlin Coroutines Reloaded

Suspendingfunctionsfun <T> async(

c: suspend () -> T): MyOwnFuture<T> { … }

suspend fun <T> MyOwnFuture<T>.await(): T { … }

fun moreWorkAsync() = async {println("Work started")val str = workAsync().await()println("Work completed: $str")

}

Allcombinationsneedtocompose

Page 118: Kotlin Coroutines Reloaded

Composability:evolved

• Kotlinsuspendingfunctionsarecomposable bydefault• Asynchronous use-case• Defineasynchronoussuspendingfunctionsanywhere• Usetheminsideanyasynchronouscoroutinebuilder

• Orinsideothersuspendingfunctions

• generate/yield coroutinesaresynchronous• Restrictedviaaspecial@RestrictsSuspension annotation• Opt-intodefinesynchronouscoroutines

Page 119: Kotlin Coroutines Reloaded

CoroutinecontextThelastpieceofcomposabilitypuzzle

Page 120: Kotlin Coroutines Reloaded

asyncUI (prototype)asyncUI {

val image = await(loadImage(url))myUI.updateImage(image)

}

SupposedtoworkinUIthread

Page 121: Kotlin Coroutines Reloaded

asyncUI (prototype)asyncUI {

val image = await(loadImage(url))myUI.updateImage(image)

}

Aspecialcoroutinebuilder

Page 122: Kotlin Coroutines Reloaded

asyncUI (prototype)asyncUI {

val image = await(loadImage(url))myUI.updateImage(image)

}Aspecialsuspendingfunction initsscope

Composabilityproblemagain!

Page 123: Kotlin Coroutines Reloaded

asyncUI:evolvedasync(UI) {

val image = loadImageAsync(url).await()myUI.updateImage(image)

}

Page 124: Kotlin Coroutines Reloaded

asyncUI:evolvedasync(UI) {

val image = loadImageAsync(url).await()myUI.updateImage(image)

}

Explicitcontextconvention forallbuilders

Canintercept continuationtoresumeintheappropriatethread

Page 125: Kotlin Coroutines Reloaded

TheactualContinuationinterfaceasync(UI) {

val image = loadImageAsync(url).await()myUI.updateImage(image)

}

interface Continuation<in T> {val context: CoroutineContextfun resume(value: T)fun resumeWithException(exception: Throwable)

}

Isusedtotransparentlylookupaninterceptorforresumes

Page 126: Kotlin Coroutines Reloaded

TheactualContinuationinterfaceasync(UI) {

val image = loadImageAsync(url).await()myUI.updateImage(image)

}

interface Continuation<in T> {val context: CoroutineContextfun resume(value: T)fun resumeWithException(exception: Throwable)

}

Doesnothavetobeaware– receivesinterceptedcontinuationtoworkwith

Page 127: Kotlin Coroutines Reloaded

Thread-safetyAmillion-dollarquestforcorrectly-synchronized(data-racefree)code

Page 128: Kotlin Coroutines Reloaded

Aneedforhappens-beforerelationfun moreWork() = async {

val list = ArrayList<String>()val str = work().await()list.add(str)

}

Page 129: Kotlin Coroutines Reloaded

Aneedforhappens-beforerelationfun moreWork() = async {

val list = ArrayList<String>()val str = work().await()list.add(str)

}

Onethread

Page 130: Kotlin Coroutines Reloaded

Aneedforhappens-beforerelationfun moreWork() = async {

val list = ArrayList<String>()val str = work().await()list.add(str)

}

OnethreadSuspends/resumes

Page 131: Kotlin Coroutines Reloaded

Aneedforhappens-beforerelationfun moreWork() = async {

val list = ArrayList<String>()val str = work().await()list.add(str)

}

OnethreadSuspends/resumesAnotherthread

Page 132: Kotlin Coroutines Reloaded

Aneedforhappens-beforerelationfun moreWork() = async {

val list = ArrayList<String>()val str = work().await()list.add(str)

}

OnethreadSuspends/resumesAnotherthread

Isthereadatarace?Doweneedvolatilewhenspilling

localstoastatemachine?

Page 133: Kotlin Coroutines Reloaded

Aneedforhappens-beforerelationfun moreWork() = async {

val list = ArrayList<String>()val str = work().await()list.add(str)

}

Thereisnodataracehere!await establisheshappens-beforerelation

happens-before

Page 134: Kotlin Coroutines Reloaded

ChallengesIfitonlywasallthatsimple…

Page 135: Kotlin Coroutines Reloaded

Threadconfinementvscoroutinesfun moreWork() = async {

synchronized(monitor) {val str = work().await()

}}

Page 136: Kotlin Coroutines Reloaded

Threadconfinementvscoroutinesfun moreWork() = async {

synchronized(monitor) {val str = work().await()

}}

MONITORENTER inonethreadSuspends/resumesMONITOREXIT inanotherthread

IllegalMonitorStateException

Page 137: Kotlin Coroutines Reloaded

Threadconfinementvscoroutines

• Monitors• Locks(j.u.c.l.ReentrantLock)• Thread-locals• …• Thread.currentThread()

ACoroutineContext isamapofcoroutine-localelementsasreplacement

Page 138: Kotlin Coroutines Reloaded

Stacktracesinexceptions

Page 139: Kotlin Coroutines Reloaded

Stacktracesinexceptionssuspend fun moreWork() {

work()}

suspend fun work() {someAsyncOp().await()throw Exception()

}stack

moreWork

Page 140: Kotlin Coroutines Reloaded

Stacktracesinexceptionssuspend fun moreWork() {

work()}

suspend fun work() {someAsyncOp().await()throw Exception()

}stack

moreWorkwork

Page 141: Kotlin Coroutines Reloaded

Stacktracesinexceptionssuspend fun moreWork() {

work()}

suspend fun work() {someAsyncOp().await()throw Exception()

}stack

moreWorkworkawait

Page 142: Kotlin Coroutines Reloaded

Stacktracesinexceptionssuspend fun moreWork() {

work()}

suspend fun work() {someAsyncOp().await()throw Exception()

}stack

moreWorkwork

Work$StateMachine : Continuation----------

fun resume(v)

Page 143: Kotlin Coroutines Reloaded

Stacktracesinexceptionssuspend fun moreWork() {

work()}

suspend fun work() {someAsyncOp().await()throw Exception()

}stack

Work$StateMachine.resumework

Page 144: Kotlin Coroutines Reloaded

Stacktracesinexceptionssuspend fun moreWork() {

work()}

suspend fun work() {someAsyncOp().await()throw Exception()

}JVMstack

Work$StateMachine.resumework

Page 145: Kotlin Coroutines Reloaded

Stacktracesinexceptionssuspend fun moreWork() {

work()}

suspend fun work() {someAsyncOp().await()throw Exception()

}JVMstack

Work$StateMachine.resumework

Coroutinestack

moreWorkwork

Page 146: Kotlin Coroutines Reloaded

Stacktracesinexceptionssuspend fun moreWork() {

work()}

suspend fun work() {someAsyncOp().await()throw Exception()

}JVMstack

Work$StateMachine.resumework

Coroutinestack

moreWorkwork

Getsthrown

Page 147: Kotlin Coroutines Reloaded

Stacktracesinexceptionssuspend fun moreWork() {

work()}

suspend fun work() {someAsyncOp().await()throw Exception()

}JVMstack

Work$StateMachine.resumework

Coroutinestack

moreWorkwork

Getsthrown Userwants

Page 148: Kotlin Coroutines Reloaded

LibraryevolutionGoingbeyondthelanguage&stdlib

Page 149: Kotlin Coroutines Reloaded

Libraryevolution:alreadythere

• Communication&synchronizationprimitives• Job:Acompletable &cancellableentitytorepresentacoroutine• Deferred:Afuturewithsuspend funawait (andno thread-blockingmethods)• Mutex:withsuspend funlock• Channel:SendChannel &ReceiveChannel

• RendezvousChannel – synchronousrendezvous• ArrayChannel – fixedsizebuffer• LinkedListChannel – unlimitedbuffer• ConflatedChannel – onlythemostrecentsentelementiskept

• BroadcastChannel:multiplesubscribes,allreceive

Page 150: Kotlin Coroutines Reloaded

Libraryevolution:alreadythere

• Coroutinebuilders• launch (fire&forget,returnsaJob)• async (returnsDeferred)• future(integrationwithCompletableFuture &ListenableFuture)• runBlocking (toexplicitlydelimitcodethatblocksathread)• actor/ produce(consume&producemessagesoverchannels)• publish (integrationwithreactivestreams,returnsPublisher)• rxCompletable,rxSingle,rxObservable,rxFlowable (RxJava 1/2)

Page 151: Kotlin Coroutines Reloaded

Libraryevolution:alreadythere

• Top-levelfunctions• select expressiontoawaitmultipleeventsforfullCSP-styleprogramming• delayfortime-basedlogicincoroutines

• Extensions• await forallkindsoffutures(JDK,Guava,RxJava)• aRead,aWrite,etc forAsynchronousXxxChannel inNIO

• Cancellation&job(coroutine/actor)hierarchies• withTimeout foracomposable wayforanysuspendfunctionrunw/timeout

Page 152: Kotlin Coroutines Reloaded

Libraryevolution:WIP

• Serialization/migrationofcoroutines• MigratinglibrariestoKotlin/JS&Kotlin/Native(lang support– done)• Fullscopeofpipeliningoperatorsonchannels(filter,map,etc)• Optimizationsforsingle-producerand/orsingle-consumercases• Allow3rd partyprimitivestoparticipateinselect (alternatives)• ByteChannel forsuspendable IO(httpclient/server,websocket,etc)• Seealsoktor.io

Page 153: Kotlin Coroutines Reloaded

Aclosingnoteonterminology

• Wedon’tusethetermfiber/strand/greenthreads/…• Thetermcoroutine worksjustaswell• ”Fibersdescribeessentiallythesameconceptascoroutines”©Wikipedia

KotlinCoroutinesarevery light-weightthreads

Page 154: Kotlin Coroutines Reloaded

WrapupLet’scallitaday

Page 155: Kotlin Coroutines Reloaded

Experimentalstatusofcoroutines

• Thisdesignisnew andunlikemainstream• Forsomeverygoodreasons

• Wewantcommunitytotryitforreal• Sowereleaseditasanexperimental feature

• Weguaranteebackwardscompatibility• Oldcodecompiledwithcoroutinescontinuestowork

• Wereservetherighttobreakforwardcompatibility• Wemayaddthingssonewcodemaynotrunw/oldRT

• Designwillbefinalizedatalaterpoint• Oldcodewillcontinuetoworkviasupportlibrary• Migrationaidstothefinaldesignwillbeprovided

opt-inflag

Page 156: Kotlin Coroutines Reloaded

Thankyou

Anyquestions?

Slidesareavailableatwww.slideshare.net/elizarovemailelizarov atgmail

relizarov