2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) {...

107
elizarov at JetBrains Roman Elizarov Deep dive into Coroutines on JVM

Transcript of 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) {...

Page 1: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

elizarov at JetBrainsRoman Elizarov

Deep dive into Coroutines on JVM

Page 2: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

There is no magic

Page 3: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Continuation Passing Style (CPS)

Page 4: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

A toy problem

fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)

}Direct style

Page 5: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Direct style

fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)

}

Page 6: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Direct style

fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)

}Continuation

Page 7: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Continuation-Passing Style

fun postItem(item: Item) {requestToken { token ->

val post = createPost(token, item)processPost(post)

}}

Continuation

CPS == Callbacks

Page 8: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Continuation-Passing Style

fun postItem(item: Item) {requestToken { token ->

createPost(token, item) { post ->processPost(post)

}}

}

Page 9: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Coroutines Direct Style

suspend fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)

}

Page 10: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

How does it work?Behind the scenes

Page 11: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Kotlin suspending functionsKotlin

suspend fun createPost(token: Token, item: Item): Post { … }

Page 12: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

CPS Transformation

Java/JVM

suspend fun createPost(token: Token, item: Item): Post { … }

Object createPost(Token token, Item item, Continuation<Post> cont) { … }

Page 13: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

CPS Transformation

callbackJava/JVM

suspend fun createPost(token: Token, item: Item): Post { … }

Object createPost(Token token, Item item, Continuation<Post> cont) { … }

Page 14: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Continuation

suspend fun createPost(token: Token, item: Item): Post { … }

Object createPost(Token token, Item item, Continuation<Post> cont) { … }

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

}

Page 15: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Continuation

suspend fun createPost(token: Token, item: Item): Post { … }

Object createPost(Token token, Item item, Continuation<Post> cont) { … }

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

}

Page 16: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Continuation

suspend fun createPost(token: Token, item: Item): Post { … }

Object createPost(Token token, Item item, Continuation<Post> cont) { … }

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

}

Page 17: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Continuation

suspend fun createPost(token: Token, item: Item): Post { … }

Object createPost(Token token, Item item, Continuation<Post> cont) { … }

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

}

Page 18: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Continuation

Continuation is a generic callback interface

suspend fun createPost(token: Token, item: Item): Post { … }

Object createPost(Token token, Item item, Continuation<Post> cont) { … }

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

}

Page 19: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Direct to CPS

Page 20: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Direct code

suspend fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)

}

Page 21: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Continuations

suspend fun postItem(item: Item) {

val token = requestToken()

val post = createPost(token, item)

processPost(post)}

Initial continuation

Page 22: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Continuations

suspend fun postItem(item: Item) {

val token = requestToken()

val post = createPost(token, item)

processPost(post)}

Continuation

Page 23: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Continuations

suspend fun postItem(item: Item) {

val token = requestToken()

val post = createPost(token, item)

processPost(post)} Continuation

Convert to CPS?

Page 24: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Callbacks?

fun postItem(item: Item) {

requestToken { token ->

createPost(token, item) { post ->

processPost(post)

}}

}

Page 25: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Labels

suspend fun postItem(item: Item) {// LABEL 0

val token = requestToken()// LABEL 1

val post = createPost(token, item)// LABEL 2

processPost(post)}

Page 26: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Labels

suspend fun postItem(item: Item) {switch (label) {case 0:

val token = requestToken()case 1:

val post = createPost(token, item)case 2:

processPost(post)}

}

Page 27: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

suspend fun postItem(item: Item) {val sm = object : CoroutineImpl { … }switch (sm.label) {case 0:

val token = requestToken()case 1:

val post = createPost(token, item)case 2:

processPost(post)}

}

State

Page 28: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

fun postItem(item: Item, cont: Continuation) {val sm = object : CoroutineImpl { … }switch (sm.label) {case 0:

requestToken(sm)case 1:

createPost(token, item, sm)case 2:

processPost(post)}

}

CPS Transform

Page 29: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

fun postItem(item: Item, cont: Continuation) {val sm = …switch (sm.label) {case 0:

sm.item = itemsm.label = 1requestToken(sm)

case 1:createPost(token, item, sm)

case 2:processPost(post)

}}

Save state

Page 30: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

fun postItem(item: Item, cont: Continuation) {val sm = object : CoroutineImpl { … }switch (sm.label) {case 0:

sm.item = itemsm.label = 1requestToken(sm)

case 1:createPost(token, item, sm)

case 2:processPost(post)

}}

Callback

State Machine as Continuation

Page 31: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

fun postItem(item: Item, cont: Continuation) {val sm = object : CoroutineImpl {

fun resume(…) {postItem(null, this)

}}switch (sm.label) {case 0:

sm.item = itemsm.label = 1requestToken(sm)

case 1:createPost(token, item, sm)

…}

Callback

Page 32: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

fun postItem(item: Item, cont: Continuation) {val sm = cont as? ThisSM ?: object : ThisSM {

fun resume(…) {postItem(null, this)

}}switch (sm.label) {case 0:

sm.item = itemsm.label = 1requestToken(sm)

case 1:createPost(token, item, sm)

…}

Callback

Page 33: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

fun postItem(item: Item, cont: Continuation) {val sm = …switch (sm.label) {case 0:

sm.item = itemsm.label = 1requestToken(sm)

case 1:val item = sm.itemval token = sm.result as Tokensm.label = 2createPost(token, item, sm)

…}

Restore state

Page 34: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

fun postItem(item: Item, cont: Continuation) {val sm = …switch (sm.label) {case 0:

sm.item = itemsm.label = 1requestToken(sm)

case 1:val item = sm.itemval token = sm.result as Tokensm.label = 2createPost(token, item, sm)

…}

Continue

Page 35: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

State Machine vs Callbacks

fun postItem(item: Item) {requestToken { token ->

createPost(token, item) { post ->processPost(post)

}}

}

suspend fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)

}

Page 36: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

State Machine vs Callbacks

fun postItem(item: Item) {requestToken { token ->

createPost(token, item) { post ->processPost(post)

}}

}

suspend fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)

}

Reuse closure / state object

Create new closure

Page 37: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

State Machine vs Callbackssuspend fun postItems(items: List<Item>) {

for (item in items) {val token = requestToken()val post = createPost(token, item)processPost(post)

}}

Easy loops and higher-order functions

Page 38: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

State Machine vs Callbacks

fun postItems(items: List<Item>) {…

}

suspend fun postItems(items: List<Item>) {for (item in items) {

val token = requestToken()val post = createPost(token, item)processPost(post)

}}

A horrid callback mess

Easy loops and higher-order functions

Page 39: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Integration

Page 40: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Zoo of futures on JVM

Page 41: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

interface Service {fun createPost(token: Token, item: Item): Call<Post>

}

Page 42: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

interface Service {fun createPost(token: Token, item: Item): Call<Post>

}

suspend fun createPost(token: Token, item: Item): Post =serviceInstance.createPost(token, item).await()

Page 43: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

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

}

Page 44: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Callbacks everywheresuspend fun <T> Call<T>.await(): T {

enqueue(object : Callback<T> {override fun onResponse(call: Call<T>, response: Response<T>) {

// todo}

override fun onFailure(call: Call<T>, t: Throwable) {// todo

}})

}

Page 45: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

suspend fun <T> Call<T>.await(): T = suspendCoroutine { cont ->enqueue(object : Callback<T> {

override fun onResponse(call: Call<T>, response: Response<T>) {if (response.isSuccessful)

cont.resume(response.body()!!)else

cont.resumeWithException(ErrorResponse(response))}

override fun onFailure(call: Call<T>, t: Throwable) {cont.resumeWithException(t)

}})

}

Page 46: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

suspend fun <T> suspendCoroutine(block: (Continuation<T>) -> Unit): T

Page 47: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

suspend fun <T> suspendCoroutine(block: (Continuation<T>) -> Unit): T

Regular function

Inspired by call/cc from Scheme

Page 48: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Install callbacksuspend fun <T> Call<T>.await(): T = suspendCoroutine { cont ->

enqueue(object : Callback<T> {override fun onResponse(call: Call<T>, response: Response<T>) {

if (response.isSuccessful)cont.resume(response.body()!!)

elsecont.resumeWithException(ErrorResponse(response))

}

override fun onFailure(call: Call<T>, t: Throwable) {cont.resumeWithException(t)

}})

}

Page 49: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Install callbacksuspend fun <T> Call<T>.await(): T = suspendCoroutine { cont ->

enqueue(object : Callback<T> {override fun onResponse(call: Call<T>, response: Response<T>) {

if (response.isSuccessful)cont.resume(response.body()!!)

elsecont.resumeWithException(ErrorResponse(response))

}

override fun onFailure(call: Call<T>, t: Throwable) {cont.resumeWithException(t)

}})

}

Page 50: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Analyze responsesuspend fun <T> Call<T>.await(): T = suspendCoroutine { cont ->

enqueue(object : Callback<T> {override fun onResponse(call: Call<T>, response: Response<T>) {

if (response.isSuccessful)cont.resume(response.body()!!)

elsecont.resumeWithException(ErrorResponse(response))

}

override fun onFailure(call: Call<T>, t: Throwable) {cont.resumeWithException(t)

}})

}

Page 51: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Analyze responsesuspend fun <T> Call<T>.await(): T = suspendCoroutine { cont ->

enqueue(object : Callback<T> {override fun onResponse(call: Call<T>, response: Response<T>) {

if (response.isSuccessful)cont.resume(response.body()!!)

elsecont.resumeWithException(ErrorResponse(response))

}

override fun onFailure(call: Call<T>, t: Throwable) {cont.resumeWithException(t)

}})

}That’s all

Page 52: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Out-of-the box integrations

kotlinx-coroutines-core

jdk8

guava

nio

reactor

rx1

rx2

Contributions are welcome

Page 53: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Coroutine context

Page 54: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

What thread it resumes on?

suspend fun postItem(item: Item) {

val token = requestToken()

val post = createPost(token, item)

processPost(post)}

Continuation

It depends!

Page 55: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

What thread it resumes on?

fun postItem(item: Item) {launch(UI) {

val token = requestToken()

val post = createPost(token, item)

processPost(post)}

}

Continuation

Page 56: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Continuation Interceptorinterface ContinuationInterceptor : CoroutineContext.Element {

companion object Key : CoroutineContext.Key<ContinuationInterceptor>

fun <T> interceptContinuation(continuation: Continuation<T>):Continuation<T>

}

Page 57: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Continuation Interceptorinterface ContinuationInterceptor : CoroutineContext.Element {

companion object Key : CoroutineContext.Key<ContinuationInterceptor>

fun <T> interceptContinuation(continuation: Continuation<T>):Continuation<T>

}

Page 58: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Continuation Interceptorinterface ContinuationInterceptor : CoroutineContext.Element {

companion object Key : CoroutineContext.Key<ContinuationInterceptor>

fun <T> interceptContinuation(continuation: Continuation<T>):Continuation<T>

}

Page 59: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Dispatched continuation

class DispatchedContinuation<in T>(val dispatcher: CoroutineDispatcher,val continuation: Continuation<T>

): Continuation<T> by continuation {

override fun resume(value: T) {dispatcher.dispatch(context, DispatchTask(…))

}

…} Dispatches execution to another thread

Page 60: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Starting coroutines

Page 61: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

fun <T> future(context: CoroutineContext = DefaultDispatcher,block: suspend () -> T

): CompletableFuture<T>

Coroutine builder

Page 62: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

fun <T> future(context: CoroutineContext = DefaultDispatcher,block: suspend () -> T

): CompletableFuture<T>

A regular function

Page 63: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

fun <T> future(context: CoroutineContext = DefaultDispatcher,block: suspend () -> T

): CompletableFuture<T>

Page 64: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

fun <T> future(context: CoroutineContext = DefaultDispatcher,block: suspend () -> T

): CompletableFuture<T> suspending lambda

Page 65: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

fun <T> future(context: CoroutineContext = DefaultDispatcher,block: suspend () -> T

): CompletableFuture<T> {val future = CompletableFuture<T>()block.startCoroutine(…)return future

}

Page 66: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

fun <T> future(context: CoroutineContext = DefaultDispatcher,block: suspend () -> T

): CompletableFuture<T> {val future = CompletableFuture<T>()block.startCoroutine(…)return future

}

Page 67: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

fun <T> future(context: CoroutineContext = DefaultDispatcher,block: suspend () -> T

): CompletableFuture<T> {val future = CompletableFuture<T>()block.startCoroutine(completion = object : Continuation<T> {

…})return future

}

Page 68: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

fun <T> future(…): CompletableFuture<T> {val future = CompletableFuture<T>()block.startCoroutine(completion = object : Continuation<T> {

override val context: CoroutineContext get() = context

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

}

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

}})return future

}

Page 69: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

fun <T> future(…): CompletableFuture<T> {val future = CompletableFuture<T>()block.startCoroutine(completion = object : Continuation<T> {

override val context: CoroutineContext get() = context

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

}

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

}})return future

}That’s all, folks!

Page 70: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Job cancellation

Page 71: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Launch coroutine builderfun launch(

context: CoroutineContext = DefaultDispatcher,block: suspend () -> Unit

): Job { … }

Page 72: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Launching coroutineval job = launch {

…}

Page 73: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

val job = launch { …

}

job.join()

Page 74: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

val job = launch { …

}

job.join()job.cancel()

Page 75: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Jobinterface Job : CoroutineContext.Element {

companion object Key : CoroutineContext.Key<Job>

…}

Page 76: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Using coroutine contextlaunch {

val job = coroutineContext[Job]!!…

}

Page 77: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Using coroutine contextlaunch {

val job = coroutineContext[Job]!!val interceptor = coroutineContext[CoroutineInterceptor]!!…

}

Page 78: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Timeoutslaunch {

withTimeout(10, TimeUnit.SECONDS) {…

}}

Page 79: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Cooperative cancellation

Page 80: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Cooperative cancellationlaunch {

while (true) {…

}}

Page 81: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Cooperative cancellationlaunch {

while (isActive) {…

}}

Page 82: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Cooperative cancellationlaunch {

while (true) {delay(…)…

}}

Page 83: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Cancellable suspensionsuspend fun <T> Call<T>.await(): T =

suspendCancellableCoroutine { cont ->enqueue(…)

}

Page 84: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Cancellable continuationsuspend fun <T> Call<T>.await(): T =

suspendCancellableCoroutine { cont: CancellableContinuation<T> ->enqueue(…)

}

Page 85: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Completion handlersuspend fun <T> Call<T>.await(): T =

suspendCancellableCoroutine { cont: CancellableContinuation<T> ->enqueue(…)cont.invokeOnCompletion {

[email protected]()}

}

Page 86: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Completion handlersuspend fun <T> Call<T>.await(): T =

suspendCancellableCoroutine { cont: CancellableContinuation<T> ->enqueue(…)cont.invokeOnCompletion {

[email protected]()}

}

Page 87: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Communicating Sequential Processes (CSP)

Page 88: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Shared Mutable State

@stefanobaghino

Page 89: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

The choice

SharedMutable State

Share by Communicating

Page 90: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Examplefun main(args: Array<String>) = runBlocking<Unit> {

val chan = Channel<Int>()launch(coroutineContext) {

repeat(10) { i ->delay(100)chan.send(i)

}chan.close()

}launch(coroutineContext) {

for (i in chan) {println(i)

}}

}

Page 91: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Main coroutinefun main(args: Array<String>) = runBlocking<Unit> {

val chan = Channel<Int>()launch(coroutineContext) {

repeat(10) { i ->delay(100)chan.send(i)

}chan.close()

}launch(coroutineContext) {

for (i in chan) {println(i)

}}

}

Page 92: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Channelfun main(args: Array<String>) = runBlocking<Unit> {

val chan = Channel<Int>()launch(coroutineContext) {

repeat(10) { i ->delay(100)chan.send(i)

}chan.close()

}launch(coroutineContext) {

for (i in chan) {println(i)

}}

}

Page 93: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Launchfun main(args: Array<String>) = runBlocking<Unit> {

val chan = Channel<Int>()launch(coroutineContext) {

repeat(10) { i ->delay(100)chan.send(i)

}chan.close()

}launch(coroutineContext) {

for (i in chan) {println(i)

}}

}

Child coroutine

Page 94: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Coroutine bodyfun main(args: Array<String>) = runBlocking<Unit> {

val chan = Channel<Int>()launch(coroutineContext) {

repeat(10) { i ->delay(100)chan.send(i)

}chan.close()

}launch(coroutineContext) {

for (i in chan) {println(i)

}}

}

Sequential code!

Page 95: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Sendfun main(args: Array<String>) = runBlocking<Unit> {

val chan = Channel<Int>()launch(coroutineContext) {

repeat(10) { i ->delay(100)chan.send(i)

}chan.close()

}launch(coroutineContext) {

for (i in chan) {println(i)

}}

}

Page 96: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Closefun main(args: Array<String>) = runBlocking<Unit> {

val chan = Channel<Int>()launch(coroutineContext) {

repeat(10) { i ->delay(100)chan.send(i)

}chan.close()

}launch(coroutineContext) {

for (i in chan) {println(i)

}}

}

Page 97: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Receive for loopfun main(args: Array<String>) = runBlocking<Unit> {

val chan = Channel<Int>()launch(coroutineContext) {

repeat(10) { i ->delay(100)chan.send(i)

}chan.close()

}launch(coroutineContext) {

for (i in chan) {println(i)

}}

}

Page 98: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Demo

Page 99: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

ActorsThe other way to look at CSP

Page 100: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

The choice

Named channels

Namedcoroutines

Actor == named coroutine & inbox channel

Page 101: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Examplefun main(args: Array<String>) = runBlocking<Unit> {

val printer = actor<Int>(coroutineContext) {for (i in channel) {

println(i)}

}launch(coroutineContext) {

repeat(10) { i ->delay(100)printer.send(i)

}printer.close()

}}

Page 102: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Actor coroutine builderfun main(args: Array<String>) = runBlocking<Unit> {

val printer = actor<Int>(coroutineContext) {for (i in channel) {

println(i)}

}launch(coroutineContext) {

repeat(10) { i ->delay(100)printer.send(i)

}printer.close()

}}

Page 103: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Actor bodyfun main(args: Array<String>) = runBlocking<Unit> {

val printer = actor<Int>(coroutineContext) {for (i in channel) {

println(i)}

}launch(coroutineContext) {

repeat(10) { i ->delay(100)printer.send(i)

}printer.close()

}}

Sequential!

Page 104: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Interacting with an actorfun main(args: Array<String>) = runBlocking<Unit> {

val printer = actor<Int>(coroutineContext) {for (i in channel) {

println(i)}

}launch(coroutineContext) {

repeat(10) { i ->delay(100)printer.send(i)

}printer.close()

}}

Page 105: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

References

Page 106: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

Guide to kotlinx.coroutinesby example

https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md

• Basics• Cancellation and Timeouts• Composition• Coroutine contexts• Channels• Shared Mutable State and Concurrency• Select expressions

Page 107: 2017 KotlinConf - Deep dive into Coroutines on JVM · A toy problem fun postItem(item: Item) { valtoken = requestToken() valpost = createPost(token, item) processPost(post) Direct

#kotlinconf17

relizarovelizarov at JetBrains

Roman Elizarov

Thank you

Any questions?