Kotlin Coroutines Reloaded Presented at JVM Language Summit, 2017 /Roman Elizarov @ JetBrains
KotlinCoroutinesReloadedPresentedatJVMLanguageSummit,2017
/RomanElizarov@JetBrains
Speaker:RomanElizarov
• 16+yearsexperience• Previouslydevelopedhigh-perftradingsoftware@Devexperts• Teachconcurrent&[email protected]• Chiefjudge@NortheasternEuropeanRegionofACMICPC• NowworkonKotlin@JetBrains
Agenda
• RecapofKotlincoroutinesprototype• Presented@JVMLS,2016byAndreyBreslav
• Theissuesintheprototypedesign• andthesolutionsthatwerefound
• DesignreleasedtopublicinKotlin1.1• Evolvedcoroutinessupportlibraries• Challenges&futuredirections
RecapofKotlincoroutinesprototypePresented@JVMLS,2016byAndreyBreslav
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}");
}
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}");
}
AsynctheKotlinway(prototype)
fun work(): CompletableFuture<String> { … }
fun moreWork() = async {println("Work started")val str = await(work())println("Work completed: $str")
}
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
SuspendingfunctionsAgrandunificationbetweenasync/awaitandgenerate/yield
Suspendingfunctions:use
val str = await(work())
Suspendingfunctions:use
val str = await(work())
CompletableFuture<String>
// String result
Suspendingfunctions:declare(prototype)
val str = await(work())
suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Unit
CompletableFuture<String>
// String result
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
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)
}
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
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
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
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
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!
AproblematicexampleWheregrandvisionmeetsreality
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())}
}
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?
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
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
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
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
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
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
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
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
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
AsolutionAdifferencebetweenknowingthepath&walkingthepath.
Asolution
val str = await(work())
suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Unit
CompletableFuture<String>
// String result
Asolution(0):stackunwindconvention
val str = await(work())
suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any?
CompletableFuture<String>
// String result
Asolution(0)
val str = await(work())
suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any?
CompletableFuture<String>
// String result
T | COROUTINE_SUSPENDED
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
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
Asolution(0)suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any? {
…}
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
}
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?
Asolution(1):Call/declarationfidelity
Asolution(1)suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any?
Asolution(1)suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any?
suspend fun <T> await(f: CompletableFuture<T>): Tnaturalsignature
Asolution(1)fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any?
suspend fun <T> await(f: CompletableFuture<T>): T
Compilesas(onJVM)
Asolution(1)fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Any?
suspend fun <T> await(f: CompletableFuture<T>): T
Compilesas(onJVM)CPSTransformation
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
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
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
Asolution(1)
suspend fun <T> await(f: CompletableFuture<T>): T =suspendCoroutineOrReturn { c ->
…}
Asolution(1)
suspend fun <T> await(f: CompletableFuture<T>): T =suspendCoroutineOrReturn { c ->
…}
inline suspend fun <T> suspendCoroutineOrReturn(crossinline block: (Continuation<T>) -> Any?): T
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
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
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
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
Asolution(2):Tailsuspension
Asolution(2)
suspend fun <T> await(f: CompletableFuture<T>): T =suspendCoroutineOrReturn { c ->
…}
Tailsuspendinvocation:Continuationpass-through
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
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
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
Asolution(3):Abstraction
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()
}
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()
}
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
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
Asolution(4):Puttingitalltogether
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!
Recapstepstosolution
• T|COROUTINE_SUSPENDED(Any?)toallowinvocationsthatdonotsuspendandthusavoidStackOverflowError• Call/declarationfidelityviaCPStransformation• Introducecall/cc(suspendCoroutineOrReturn)torecoverhiddencontinuation• Tailcallinvocationsupporttorecoverprototypesemantics• Useabstraction(suspendCoroutine)tohideimplementationcomplexitiesfromend-users
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)}
}
Thefinaltouch:awaitextension
suspend fun <T> CompletableFuture<T>.await(): T =suspendCoroutine { c ->
whenComplete { value, exception ->if (exception != null) c.resumeWithException(exception)
else c.resume(value)}
}
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")
}
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
CoroutinebuildersThemysteryofinception
Coroutinebuilders(prototype)fun <T> async(
coroutine c: FutureController<T>.() -> Continuation<Unit>): CompletableFuture<T> {
val controller = FutureController<T>()c(controller).resume(Unit)return controller.future
}
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
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
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
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)}
}
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)}
}
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
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)
}}
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>
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>
Coroutinebuilders:evolved
Coroutinebuildersfun <T> async(
c: suspend () -> T): CompletableFuture<T> {
val controller = FutureController<T>()c.startCoroutine(completion = controller)return controller.future
}
Coroutinebuildersfun <T> async(
c: suspend () -> T): CompletableFuture<T> {
val controller = FutureController<T>()c.startCoroutine(completion = controller)return controller.future
}
naturalsignature
Nospecialcoroutine keyword
Coroutinebuildersfun <T> async(
c: suspend () -> T): CompletableFuture<T> {
val controller = FutureController<T>()c.startCoroutine(completion = controller)return controller.future
}
Providedbystandardlibrary
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)
}}
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
BonusfeaturesFreeasincheese
Arbitrarysuspendingcallssuspend fun <T> suspending(block: suspend () -> T): T =
suspendCoroutine { continuation ->block.startCoroutine(completion = continuation)
}
Arbitrarysuspendingcallssuspend fun <T> suspending(block: suspend () -> T): T =
suspendCoroutine { continuation ->block.startCoroutine(completion = continuation)
}
Arbitrarysuspendingcallssuspend fun <T> suspending(block: suspend () -> T): T =
suspendCoroutine { continuation ->block.startCoroutine(completion = continuation)
}
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
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")
}
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
Arbitrarysuspendingcalls
suspend fun moreWork() {println("Work started")val str = work().await() println("Work completed: $str")
}
Non-tail(arbitrary)suspendinvocation
Stackless vsStackful coroutinesThecrucialdistinction
Stackless vsStackful coroutines
Stackless StackfulRestrictions Useinspecial ctx UseanywhereImplemented in C#,Scala,Kotlin, … Quasar, Javaflow,…
Stackless vsStackful coroutines
Stackless StackfulRestrictions Useinspecial ctx UseanywhereImplemented in C#,Scala,Kotlin, … Quasar, Javaflow,…
Stackless vsStackful coroutines
Stackless StackfulRestrictions Useinspecial ctx UseanywhereImplemented in C#,Scala,Kotlin, … Quasar, Javaflow,…Can suspendin? suspend
functionsthrows SuspendExecution /@Suspendable functions
Stackless vsStackful coroutines
Stackless StackfulRestrictions Useinspecial ctx UseanywhereImplemented in C#,Scala, … Kotlin,Quasar, Javaflow,…
Stackless vsStackful coroutines
Stackless StackfulRestrictions Useinspecial ctx UseanywhereImplemented in C#,Scala, Kotlin,
Quasar, JavaFlow,…LISP,Go,…
Stackless vsStackful coroutines
Stackless StackfulRestrictions Useinspecial ctx UseanywhereImplemented in C#,Scala, … Kotlin,Quasar, Javaflow,…
Falsedichotomy
AsyncvsSuspendingfunctionsTheactualdifference
fun work() = async { … }
suspend fun work() { … }
fun work(): CompletableFuture<String> = async { … }
suspend fun work(): String { … }
InKotlinyouhave achoice
fun workAsync(): CompletableFuture<String> = async { … }
suspend fun work(): String { … }
workAsync() VALID –>producesCompletableFuture<String>
workAsync().await() VALID –>producesString
concurrent&asyncbehavior
sequentialbehavior
Themostneeded one,yetthemostsyntacticallyclumsy,esp.forsuspend-heavy(CSP)codestyle
Theproblemwithasync
Kotlinsuspendingfunctionsimitatesequential behavior
bydefault
ConcurrencyishardConcurrencyhastobeexplicit
ComposabilityMakingthoselegos click
Buildersfun <T> async(
c: suspend () -> T): CompletableFuture<T> { … }
Buildersfun <T> async(
c: suspend () -> T): ListenableFuture<T> { … }
Buildersfun <T> async(
c: suspend () -> T): MyOwnFuture<T> { … }
Suspendingfunctionsfun <T> async(
c: suspend () -> T): MyOwnFuture<T> { … }
suspend fun <T> CompletableFuture<T>.await(): T { … }
Suspendingfunctionsfun <T> async(
c: suspend () -> T): MyOwnFuture<T> { … }
suspend fun <T> ListenableFuture<T>.await(): T { … }
Suspendingfunctionsfun <T> async(
c: suspend () -> T): MyOwnFuture<T> { … }
suspend fun <T> MyOwnFuture<T>.await(): T { … }
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
Composability:evolved
• Kotlinsuspendingfunctionsarecomposable bydefault• Asynchronous use-case• Defineasynchronoussuspendingfunctionsanywhere• Usetheminsideanyasynchronouscoroutinebuilder
• Orinsideothersuspendingfunctions
• generate/yield coroutinesaresynchronous• Restrictedviaaspecial@RestrictsSuspension annotation• Opt-intodefinesynchronouscoroutines
CoroutinecontextThelastpieceofcomposabilitypuzzle
asyncUI (prototype)asyncUI {
val image = await(loadImage(url))myUI.updateImage(image)
}
SupposedtoworkinUIthread
asyncUI (prototype)asyncUI {
val image = await(loadImage(url))myUI.updateImage(image)
}
Aspecialcoroutinebuilder
asyncUI (prototype)asyncUI {
val image = await(loadImage(url))myUI.updateImage(image)
}Aspecialsuspendingfunction initsscope
Composabilityproblemagain!
asyncUI:evolvedasync(UI) {
val image = loadImageAsync(url).await()myUI.updateImage(image)
}
asyncUI:evolvedasync(UI) {
val image = loadImageAsync(url).await()myUI.updateImage(image)
}
Explicitcontextconvention forallbuilders
Canintercept continuationtoresumeintheappropriatethread
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
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
Thread-safetyAmillion-dollarquestforcorrectly-synchronized(data-racefree)code
Aneedforhappens-beforerelationfun moreWork() = async {
val list = ArrayList<String>()val str = work().await()list.add(str)
}
Aneedforhappens-beforerelationfun moreWork() = async {
val list = ArrayList<String>()val str = work().await()list.add(str)
}
Onethread
Aneedforhappens-beforerelationfun moreWork() = async {
val list = ArrayList<String>()val str = work().await()list.add(str)
}
OnethreadSuspends/resumes
Aneedforhappens-beforerelationfun moreWork() = async {
val list = ArrayList<String>()val str = work().await()list.add(str)
}
OnethreadSuspends/resumesAnotherthread
Aneedforhappens-beforerelationfun moreWork() = async {
val list = ArrayList<String>()val str = work().await()list.add(str)
}
OnethreadSuspends/resumesAnotherthread
Isthereadatarace?Doweneedvolatilewhenspilling
localstoastatemachine?
Aneedforhappens-beforerelationfun moreWork() = async {
val list = ArrayList<String>()val str = work().await()list.add(str)
}
Thereisnodataracehere!await establisheshappens-beforerelation
happens-before
ChallengesIfitonlywasallthatsimple…
Threadconfinementvscoroutinesfun moreWork() = async {
synchronized(monitor) {val str = work().await()
}}
Threadconfinementvscoroutinesfun moreWork() = async {
synchronized(monitor) {val str = work().await()
}}
MONITORENTER inonethreadSuspends/resumesMONITOREXIT inanotherthread
IllegalMonitorStateException
Threadconfinementvscoroutines
• Monitors• Locks(j.u.c.l.ReentrantLock)• Thread-locals• …• Thread.currentThread()
ACoroutineContext isamapofcoroutine-localelementsasreplacement
Stacktracesinexceptions
Stacktracesinexceptionssuspend fun moreWork() {
work()}
suspend fun work() {someAsyncOp().await()throw Exception()
}stack
moreWork
Stacktracesinexceptionssuspend fun moreWork() {
work()}
suspend fun work() {someAsyncOp().await()throw Exception()
}stack
moreWorkwork
Stacktracesinexceptionssuspend fun moreWork() {
work()}
suspend fun work() {someAsyncOp().await()throw Exception()
}stack
moreWorkworkawait
Stacktracesinexceptionssuspend fun moreWork() {
work()}
suspend fun work() {someAsyncOp().await()throw Exception()
}stack
moreWorkwork
Work$StateMachine : Continuation----------
fun resume(v)
Stacktracesinexceptionssuspend fun moreWork() {
work()}
suspend fun work() {someAsyncOp().await()throw Exception()
}stack
Work$StateMachine.resumework
Stacktracesinexceptionssuspend fun moreWork() {
work()}
suspend fun work() {someAsyncOp().await()throw Exception()
}JVMstack
Work$StateMachine.resumework
Stacktracesinexceptionssuspend fun moreWork() {
work()}
suspend fun work() {someAsyncOp().await()throw Exception()
}JVMstack
Work$StateMachine.resumework
Coroutinestack
moreWorkwork
Stacktracesinexceptionssuspend fun moreWork() {
work()}
suspend fun work() {someAsyncOp().await()throw Exception()
}JVMstack
Work$StateMachine.resumework
Coroutinestack
moreWorkwork
Getsthrown
Stacktracesinexceptionssuspend fun moreWork() {
work()}
suspend fun work() {someAsyncOp().await()throw Exception()
}JVMstack
Work$StateMachine.resumework
Coroutinestack
moreWorkwork
Getsthrown Userwants
LibraryevolutionGoingbeyondthelanguage&stdlib
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
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)
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
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
Aclosingnoteonterminology
• Wedon’tusethetermfiber/strand/greenthreads/…• Thetermcoroutine worksjustaswell• ”Fibersdescribeessentiallythesameconceptascoroutines”©Wikipedia
KotlinCoroutinesarevery light-weightthreads
WrapupLet’scallitaday
Experimentalstatusofcoroutines
• Thisdesignisnew andunlikemainstream• Forsomeverygoodreasons
• Wewantcommunitytotryitforreal• Sowereleaseditasanexperimental feature
• Weguaranteebackwardscompatibility• Oldcodecompiledwithcoroutinescontinuestowork
• Wereservetherighttobreakforwardcompatibility• Wemayaddthingssonewcodemaynotrunw/oldRT
• Designwillbefinalizedatalaterpoint• Oldcodewillcontinuetoworkviasupportlibrary• Migrationaidstothefinaldesignwillbeprovided
opt-inflag
Thankyou
Anyquestions?
Slidesareavailableatwww.slideshare.net/elizarovemailelizarov atgmail
relizarov