SlideShare a Scribd company logo
elizarov at JetBrains
Roman Elizarov
Deep dive into Coroutines
on JVM
There is no magic
Continuation Passing
Style (CPS)
A toy problem
fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
Direct style
Direct style
fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
Direct style
fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
Continuation
Continuation-Passing Style
fun postItem(item: Item) {
requestToken { token ->
val post = createPost(token, item)
processPost(post)
}
}
Continuation
CPS == Callbacks
Continuation-Passing Style
fun postItem(item: Item) {
requestToken { token ->
createPost(token, item) { post ->
processPost(post)
}
}
}
Coroutines Direct Style
suspend fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
How does it work?
Behind the scenes
Kotlin suspending functions
Kotlin
suspend fun createPost(token: Token, item: Item): Post { … }
CPS Transformation
Java/JVM
suspend fun createPost(token: Token, item: Item): Post { … }
Object createPost(Token token, Item item, Continuation<Post> cont) { … }
CPS Transformation
callback
Java/JVM
suspend fun createPost(token: Token, item: Item): Post { … }
Object createPost(Token token, Item item, Continuation<Post> cont) { … }
Continuation
suspend fun createPost(token: Token, item: Item): Post { … }
Object createPost(Token token, Item item, Continuation<Post> cont) { … }
interface Continuation<in T> {
val context: CoroutineContext
fun resume(value: T)
fun resumeWithException(exception: Throwable)
}
Continuation
suspend fun createPost(token: Token, item: Item): Post { … }
Object createPost(Token token, Item item, Continuation<Post> cont) { … }
interface Continuation<in T> {
val context: CoroutineContext
fun resume(value: T)
fun resumeWithException(exception: Throwable)
}
Continuation
suspend fun createPost(token: Token, item: Item): Post { … }
Object createPost(Token token, Item item, Continuation<Post> cont) { … }
interface Continuation<in T> {
val context: CoroutineContext
fun resume(value: T)
fun resumeWithException(exception: Throwable)
}
Continuation
suspend fun createPost(token: Token, item: Item): Post { … }
Object createPost(Token token, Item item, Continuation<Post> cont) { … }
interface Continuation<in T> {
val context: CoroutineContext
fun resume(value: T)
fun resumeWithException(exception: Throwable)
}
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: CoroutineContext
fun resume(value: T)
fun resumeWithException(exception: Throwable)
}
Direct to CPS
Direct code
suspend fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
Continuations
suspend fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
Initial continuation
Continuations
suspend fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
Continuation
Continuations
suspend fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
Continuation
Convert to CPS?
Callbacks?
fun postItem(item: Item) {
requestToken { token ->
createPost(token, item) { post ->
processPost(post)
}
}
}
Labels
suspend fun postItem(item: Item) {
// LABEL 0
val token = requestToken()
// LABEL 1
val post = createPost(token, item)
// LABEL 2
processPost(post)
}
Labels
suspend fun postItem(item: Item) {
switch (label) {
case 0:
val token = requestToken()
case 1:
val post = createPost(token, item)
case 2:
processPost(post)
}
}
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
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
fun postItem(item: Item, cont: Continuation) {
val sm = …
switch (sm.label) {
case 0:
sm.item = item
sm.label = 1
requestToken(sm)
case 1:
createPost(token, item, sm)
case 2:
processPost(post)
}
}
Save state
fun postItem(item: Item, cont: Continuation) {
val sm = object : CoroutineImpl { … }
switch (sm.label) {
case 0:
sm.item = item
sm.label = 1
requestToken(sm)
case 1:
createPost(token, item, sm)
case 2:
processPost(post)
}
}
Callback
State Machine as Continuation
fun postItem(item: Item, cont: Continuation) {
val sm = object : CoroutineImpl {
fun resume(…) {
postItem(null, this)
}
}
switch (sm.label) {
case 0:
sm.item = item
sm.label = 1
requestToken(sm)
case 1:
createPost(token, item, sm)
…
}
Callback
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 = item
sm.label = 1
requestToken(sm)
case 1:
createPost(token, item, sm)
…
}
Callback
fun postItem(item: Item, cont: Continuation) {
val sm = …
switch (sm.label) {
case 0:
sm.item = item
sm.label = 1
requestToken(sm)
case 1:
val item = sm.item
val token = sm.result as Token
sm.label = 2
createPost(token, item, sm)
…
}
Restore state
fun postItem(item: Item, cont: Continuation) {
val sm = …
switch (sm.label) {
case 0:
sm.item = item
sm.label = 1
requestToken(sm)
case 1:
val item = sm.item
val token = sm.result as Token
sm.label = 2
createPost(token, item, sm)
…
}
Continue
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)
}
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
State Machine vs Callbacks
suspend 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
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
Integration
Zoo of futures on JVM
interface Service {
fun createPost(token: Token, item: Item): Call<Post>
}
interface Service {
fun createPost(token: Token, item: Item): Call<Post>
}
suspend fun createPost(token: Token, item: Item): Post =
serviceInstance.createPost(token, item).await()
suspend fun <T> Call<T>.await(): T {
…
}
Callbacks everywhere
suspend 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
}
})
}
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)
}
})
}
suspend fun <T> suspendCoroutine(block: (Continuation<T>) -> Unit): T
suspend fun <T> suspendCoroutine(block: (Continuation<T>) -> Unit): T
Regular function
Inspired by call/cc from Scheme
Install callback
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)
}
})
}
Install callback
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)
}
})
}
Analyze response
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)
}
})
}
Analyze response
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)
}
})
}
That’s all
Out-of-the box integrations
kotlinx-coroutines-core
jdk8
guava
nio
reactor
rx1
rx2
Contributions are welcome
Coroutine context
What thread it resumes on?
suspend fun postItem(item: Item) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
Continuation
It depends!
What thread it resumes on?
fun postItem(item: Item) {
launch(UI) {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
}
Continuation
Continuation Interceptor
interface ContinuationInterceptor : CoroutineContext.Element {
companion object Key : CoroutineContext.Key<ContinuationInterceptor>
fun <T> interceptContinuation(continuation: Continuation<T>):
Continuation<T>
}
Continuation Interceptor
interface ContinuationInterceptor : CoroutineContext.Element {
companion object Key : CoroutineContext.Key<ContinuationInterceptor>
fun <T> interceptContinuation(continuation: Continuation<T>):
Continuation<T>
}
Continuation Interceptor
interface ContinuationInterceptor : CoroutineContext.Element {
companion object Key : CoroutineContext.Key<ContinuationInterceptor>
fun <T> interceptContinuation(continuation: Continuation<T>):
Continuation<T>
}
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
Starting coroutines
fun <T> future(
context: CoroutineContext = DefaultDispatcher,
block: suspend () -> T
): CompletableFuture<T>
Coroutine builder
fun <T> future(
context: CoroutineContext = DefaultDispatcher,
block: suspend () -> T
): CompletableFuture<T>
A regular function
fun <T> future(
context: CoroutineContext = DefaultDispatcher,
block: suspend () -> T
): CompletableFuture<T>
fun <T> future(
context: CoroutineContext = DefaultDispatcher,
block: suspend () -> T
): CompletableFuture<T> suspending lambda
fun <T> future(
context: CoroutineContext = DefaultDispatcher,
block: suspend () -> T
): CompletableFuture<T> {
val future = CompletableFuture<T>()
block.startCoroutine(…)
return future
}
fun <T> future(
context: CoroutineContext = DefaultDispatcher,
block: suspend () -> T
): CompletableFuture<T> {
val future = CompletableFuture<T>()
block.startCoroutine(…)
return future
}
fun <T> future(
context: CoroutineContext = DefaultDispatcher,
block: suspend () -> T
): CompletableFuture<T> {
val future = CompletableFuture<T>()
block.startCoroutine(completion = object : Continuation<T> {
…
})
return future
}
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
}
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!
Job cancellation
Launch coroutine builder
fun launch(
context: CoroutineContext = DefaultDispatcher,
block: suspend () -> Unit
): Job { … }
Launching coroutine
val job = launch {
…
}
val job = launch {
…
}
job.join()
val job = launch {
…
}
job.join()
job.cancel()
Job
interface Job : CoroutineContext.Element {
companion object Key : CoroutineContext.Key<Job>
…
}
Using coroutine context
launch {
val job = coroutineContext[Job]!!
…
}
Using coroutine context
launch {
val job = coroutineContext[Job]!!
val interceptor = coroutineContext[CoroutineInterceptor]!!
…
}
Timeouts
launch {
withTimeout(10, TimeUnit.SECONDS) {
…
}
}
Cooperative cancellation
Cooperative cancellation
launch {
while (true) {
…
}
}
Cooperative cancellation
launch {
while (isActive) {
…
}
}
Cooperative cancellation
launch {
while (true) {
delay(…)
…
}
}
Cancellable suspension
suspend fun <T> Call<T>.await(): T =
suspendCancellableCoroutine { cont ->
enqueue(…)
}
Cancellable continuation
suspend fun <T> Call<T>.await(): T =
suspendCancellableCoroutine { cont: CancellableContinuation<T> ->
enqueue(…)
}
Completion handler
suspend fun <T> Call<T>.await(): T =
suspendCancellableCoroutine { cont: CancellableContinuation<T> ->
enqueue(…)
cont.invokeOnCompletion {
this@await.cancel()
}
}
Completion handler
suspend fun <T> Call<T>.await(): T =
suspendCancellableCoroutine { cont: CancellableContinuation<T> ->
enqueue(…)
cont.invokeOnCompletion {
this@await.cancel()
}
}
Communicating Sequential
Processes (CSP)
Shared Mutable State
@stefanobaghino
The choice
Shared
Mutable State
Share by
Communicating
Example
fun 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)
}
}
}
Main coroutine
fun 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)
}
}
}
Channel
fun 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)
}
}
}
Launch
fun 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
Coroutine body
fun 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!
Send
fun 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)
}
}
}
Close
fun 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)
}
}
}
Receive for loop
fun 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)
}
}
}
Demo
Actors
The other way to look at CSP
The choice
Named
channels
Named
coroutines
Actor == named coroutine & inbox channel
Example
fun 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()
}
}
Actor coroutine builder
fun 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()
}
}
Actor body
fun 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!
Interacting with an actor
fun 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()
}
}
References
Guide to kotlinx.coroutines
by 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
#kotlinconf17
relizarov
elizarov at JetBrains
Roman Elizarov
Thank you
Any questions?

More Related Content

PDF
Introduction to Coroutines @ KotlinConf 2017
PDF
Apache Iceberg - A Table Format for Hige Analytic Datasets
PDF
Introduction to Kotlin coroutines
PDF
Apache Druid 101
PPTX
Android Binder: Deep Dive
PDF
Introduction to kotlin for android app development gdg ahmedabad dev fest 2017
PDF
Apache Iceberg: An Architectural Look Under the Covers
PDF
17 Ways to Design a Presentation People Want to View
Introduction to Coroutines @ KotlinConf 2017
Apache Iceberg - A Table Format for Hige Analytic Datasets
Introduction to Kotlin coroutines
Apache Druid 101
Android Binder: Deep Dive
Introduction to kotlin for android app development gdg ahmedabad dev fest 2017
Apache Iceberg: An Architectural Look Under the Covers
17 Ways to Design a Presentation People Want to View

What's hot (20)

PDF
Kotlin Coroutines in Practice @ KotlinConf 2018
PDF
Kotlin Coroutines Reloaded
PPTX
Coroutines in Kotlin
PDF
Introduction to kotlin coroutines
PDF
Coroutines in Kotlin. In-depth review
PDF
Kotlin Coroutines. Flow is coming
PDF
Java 8 Lambda Built-in Functional Interfaces
PPTX
Capabilities for Resources and Effects
PDF
Retrofit
PPTX
Android kotlin coroutines
PPTX
JavaScript Event Loop
PDF
Intro to Asynchronous Javascript
PDF
Domain Driven Design with the F# type System -- F#unctional Londoners 2014
PDF
Hibernate Presentation
PDF
Qt Application Programming with C++ - Part 2
PDF
Deep Dive async/await in Unity with UniTask(EN)
PDF
Declarative UIs with Jetpack Compose
PDF
Idiomatic Kotlin
PPTX
JS Event Loop
PDF
L'API Collector dans tous ses états
Kotlin Coroutines in Practice @ KotlinConf 2018
Kotlin Coroutines Reloaded
Coroutines in Kotlin
Introduction to kotlin coroutines
Coroutines in Kotlin. In-depth review
Kotlin Coroutines. Flow is coming
Java 8 Lambda Built-in Functional Interfaces
Capabilities for Resources and Effects
Retrofit
Android kotlin coroutines
JavaScript Event Loop
Intro to Asynchronous Javascript
Domain Driven Design with the F# type System -- F#unctional Londoners 2014
Hibernate Presentation
Qt Application Programming with C++ - Part 2
Deep Dive async/await in Unity with UniTask(EN)
Declarative UIs with Jetpack Compose
Idiomatic Kotlin
JS Event Loop
L'API Collector dans tous ses états
Ad

Viewers also liked (20)

PPT
Using Simplicity to Make Hard Big Data Problems Easy
PDF
Анализ количества посетителей на сайте [Считаем уникальные элементы]
PDF
ReqLabs PechaKucha Евгений Сафроненко
PDF
HyperLogLog in Hive - How to count sheep efficiently?
PDF
Probabilistic data structures. Part 2. Cardinality
PDF
Big Data aggregation techniques
PDF
Big Data Day LA 2015 - Large Scale Distinct Count -- The HyperLogLog algorith...
PPTX
Probabilistic data structures
PPTX
Hyper loglog
PPTX
Walk through an enterprise Linux migration
PPTX
Graduating To Go - A Jumpstart into the Go Programming Language
PDF
Scale Up with Lock-Free Algorithms @ JavaOne
PDF
numPYNQ @ NGCLE@e-Novia 15.11.2017
PPTX
What in the World is Going on at The Linux Foundation?
PPTX
Docker Networking
PPTX
Communication hardware
PPTX
In-Memory Computing Essentials for Architects and Engineers
PPT
DevRomagna / Golang Intro
PDF
Advanced memory allocation
PDF
[若渴計畫] Challenges and Solutions of Window Remote Shellcode
Using Simplicity to Make Hard Big Data Problems Easy
Анализ количества посетителей на сайте [Считаем уникальные элементы]
ReqLabs PechaKucha Евгений Сафроненко
HyperLogLog in Hive - How to count sheep efficiently?
Probabilistic data structures. Part 2. Cardinality
Big Data aggregation techniques
Big Data Day LA 2015 - Large Scale Distinct Count -- The HyperLogLog algorith...
Probabilistic data structures
Hyper loglog
Walk through an enterprise Linux migration
Graduating To Go - A Jumpstart into the Go Programming Language
Scale Up with Lock-Free Algorithms @ JavaOne
numPYNQ @ NGCLE@e-Novia 15.11.2017
What in the World is Going on at The Linux Foundation?
Docker Networking
Communication hardware
In-Memory Computing Essentials for Architects and Engineers
DevRomagna / Golang Intro
Advanced memory allocation
[若渴計畫] Challenges and Solutions of Window Remote Shellcode
Ad

Similar to Deep dive into Coroutines on JVM @ KotlinConf 2017 (20)

PDF
Kotlin coroutine - behind the scenes
PDF
200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드
PDF
Fresh Async with Kotlin @ QConSF 2017
PDF
Coroutines in Kotlin. UA Mobile 2017.
PDF
Kotlin의 코루틴은 어떻게 동작하는가
PDF
Dive into kotlins coroutines
PDF
Fresh Async with Kotlin
PDF
Dip into Coroutines - KTUG Munich 202303
PDF
Kotlin : Advanced Tricks - Ubiratan Soares
PDF
Programação assíncrona utilizando Coroutines
PPTX
Kotlin coroutines
PDF
TDC2018SP | Trilha Kotlin - Programacao assincrona utilizando Coroutines
PDF
Atomically { Delete Your Actors }
PDF
Programação Assíncrona com Kotlin Coroutines
PDF
Are we ready to Go?
PDF
TDC218SP | Trilha Kotlin - DSLs in a Kotlin Way
PDF
Generics and Inference
PPTX
Concurrent Application Development using Scala
Kotlin coroutine - behind the scenes
200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드
Fresh Async with Kotlin @ QConSF 2017
Coroutines in Kotlin. UA Mobile 2017.
Kotlin의 코루틴은 어떻게 동작하는가
Dive into kotlins coroutines
Fresh Async with Kotlin
Dip into Coroutines - KTUG Munich 202303
Kotlin : Advanced Tricks - Ubiratan Soares
Programação assíncrona utilizando Coroutines
Kotlin coroutines
TDC2018SP | Trilha Kotlin - Programacao assincrona utilizando Coroutines
Atomically { Delete Your Actors }
Programação Assíncrona com Kotlin Coroutines
Are we ready to Go?
TDC218SP | Trilha Kotlin - DSLs in a Kotlin Way
Generics and Inference
Concurrent Application Development using Scala

More from Roman Elizarov (17)

PDF
Lock-free algorithms for Kotlin Coroutines
PPTX
Non blocking programming and waiting
PDF
ACM ICPC 2016 NEERC (Northeastern European Regional Contest) Problems Review
PDF
Многопоточное Программирование - Теория и Практика
PDF
Wait for your fortune without Blocking!
PDF
ACM ICPC 2015 NEERC (Northeastern European Regional Contest) Problems Review
PDF
ACM ICPC 2014 NEERC (Northeastern European Regional Contest) Problems Review
PDF
Why GC is eating all my CPU?
PDF
Многопоточные Алгоритмы (для BitByte 2014)
PDF
Теоретический минимум для понимания Java Memory Model (для JPoint 2014)
PPTX
DIY Java Profiling
PDF
ACM ICPC 2013 NEERC (Northeastern European Regional Contest) Problems Review
PPTX
Java Serialization Facts and Fallacies
PPTX
Millions quotes per second in pure java
PPTX
ACM ICPC 2012 NEERC (Northeastern European Regional Contest) Problems Review
PPTX
The theory of concurrent programming for a seasoned programmer
PPTX
Пишем самый быстрый хеш для кэширования данных
Lock-free algorithms for Kotlin Coroutines
Non blocking programming and waiting
ACM ICPC 2016 NEERC (Northeastern European Regional Contest) Problems Review
Многопоточное Программирование - Теория и Практика
Wait for your fortune without Blocking!
ACM ICPC 2015 NEERC (Northeastern European Regional Contest) Problems Review
ACM ICPC 2014 NEERC (Northeastern European Regional Contest) Problems Review
Why GC is eating all my CPU?
Многопоточные Алгоритмы (для BitByte 2014)
Теоретический минимум для понимания Java Memory Model (для JPoint 2014)
DIY Java Profiling
ACM ICPC 2013 NEERC (Northeastern European Regional Contest) Problems Review
Java Serialization Facts and Fallacies
Millions quotes per second in pure java
ACM ICPC 2012 NEERC (Northeastern European Regional Contest) Problems Review
The theory of concurrent programming for a seasoned programmer
Пишем самый быстрый хеш для кэширования данных

Recently uploaded (20)

PDF
Software Development Methodologies in 2025
PDF
BLW VOCATIONAL TRAINING SUMMER INTERNSHIP REPORT
PDF
Revolutionize Operations with Intelligent IoT Monitoring and Control
PDF
REPORT: Heating appliances market in Poland 2024
PDF
Google’s NotebookLM Unveils Video Overviews
PDF
creating-agentic-ai-solutions-leveraging-aws.pdf
PPTX
ChatGPT's Deck on The Enduring Legacy of Fax Machines
PDF
GamePlan Trading System Review: Professional Trader's Honest Take
PDF
NewMind AI Weekly Chronicles - July'25 - Week IV
PDF
CIFDAQ's Token Spotlight: SKY - A Forgotten Giant's Comeback?
PDF
Building High-Performance Oracle Teams: Strategic Staffing for Database Manag...
PDF
solutions_manual_-_materials___processing_in_manufacturing__demargo_.pdf
PDF
Test Bank, Solutions for Java How to Program, An Objects-Natural Approach, 12...
PDF
Automating ArcGIS Content Discovery with FME: A Real World Use Case
PDF
Why Endpoint Security Is Critical in a Remote Work Era?
PPTX
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
PDF
Smarter Business Operations Powered by IoT Remote Monitoring
PDF
Orbitly Pitch Deck|A Mission-Driven Platform for Side Project Collaboration (...
PDF
SparkLabs Primer on Artificial Intelligence 2025
PDF
madgavkar20181017ppt McKinsey Presentation.pdf
Software Development Methodologies in 2025
BLW VOCATIONAL TRAINING SUMMER INTERNSHIP REPORT
Revolutionize Operations with Intelligent IoT Monitoring and Control
REPORT: Heating appliances market in Poland 2024
Google’s NotebookLM Unveils Video Overviews
creating-agentic-ai-solutions-leveraging-aws.pdf
ChatGPT's Deck on The Enduring Legacy of Fax Machines
GamePlan Trading System Review: Professional Trader's Honest Take
NewMind AI Weekly Chronicles - July'25 - Week IV
CIFDAQ's Token Spotlight: SKY - A Forgotten Giant's Comeback?
Building High-Performance Oracle Teams: Strategic Staffing for Database Manag...
solutions_manual_-_materials___processing_in_manufacturing__demargo_.pdf
Test Bank, Solutions for Java How to Program, An Objects-Natural Approach, 12...
Automating ArcGIS Content Discovery with FME: A Real World Use Case
Why Endpoint Security Is Critical in a Remote Work Era?
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
Smarter Business Operations Powered by IoT Remote Monitoring
Orbitly Pitch Deck|A Mission-Driven Platform for Side Project Collaboration (...
SparkLabs Primer on Artificial Intelligence 2025
madgavkar20181017ppt McKinsey Presentation.pdf

Deep dive into Coroutines on JVM @ KotlinConf 2017

  • 1. elizarov at JetBrains Roman Elizarov Deep dive into Coroutines on JVM
  • 2. There is no magic
  • 4. A toy problem fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) } Direct style
  • 5. Direct style fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) }
  • 6. Direct style fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) } Continuation
  • 7. Continuation-Passing Style fun postItem(item: Item) { requestToken { token -> val post = createPost(token, item) processPost(post) } } Continuation CPS == Callbacks
  • 8. Continuation-Passing Style fun postItem(item: Item) { requestToken { token -> createPost(token, item) { post -> processPost(post) } } }
  • 9. Coroutines Direct Style suspend fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) }
  • 10. How does it work? Behind the scenes
  • 11. Kotlin suspending functions Kotlin suspend fun createPost(token: Token, item: Item): Post { … }
  • 12. CPS Transformation Java/JVM suspend fun createPost(token: Token, item: Item): Post { … } Object createPost(Token token, Item item, Continuation<Post> cont) { … }
  • 13. CPS Transformation callback Java/JVM suspend fun createPost(token: Token, item: Item): Post { … } Object createPost(Token token, Item item, Continuation<Post> cont) { … }
  • 14. Continuation suspend fun createPost(token: Token, item: Item): Post { … } Object createPost(Token token, Item item, Continuation<Post> cont) { … } interface Continuation<in T> { val context: CoroutineContext fun resume(value: T) fun resumeWithException(exception: Throwable) }
  • 15. Continuation suspend fun createPost(token: Token, item: Item): Post { … } Object createPost(Token token, Item item, Continuation<Post> cont) { … } interface Continuation<in T> { val context: CoroutineContext fun resume(value: T) fun resumeWithException(exception: Throwable) }
  • 16. Continuation suspend fun createPost(token: Token, item: Item): Post { … } Object createPost(Token token, Item item, Continuation<Post> cont) { … } interface Continuation<in T> { val context: CoroutineContext fun resume(value: T) fun resumeWithException(exception: Throwable) }
  • 17. Continuation suspend fun createPost(token: Token, item: Item): Post { … } Object createPost(Token token, Item item, Continuation<Post> cont) { … } interface Continuation<in T> { val context: CoroutineContext fun resume(value: T) fun resumeWithException(exception: Throwable) }
  • 18. 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: CoroutineContext fun resume(value: T) fun resumeWithException(exception: Throwable) }
  • 20. Direct code suspend fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) }
  • 21. Continuations suspend fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) } Initial continuation
  • 22. Continuations suspend fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) } Continuation
  • 23. Continuations suspend fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) } Continuation Convert to CPS?
  • 24. Callbacks? fun postItem(item: Item) { requestToken { token -> createPost(token, item) { post -> processPost(post) } } }
  • 25. Labels suspend fun postItem(item: Item) { // LABEL 0 val token = requestToken() // LABEL 1 val post = createPost(token, item) // LABEL 2 processPost(post) }
  • 26. Labels suspend fun postItem(item: Item) { switch (label) { case 0: val token = requestToken() case 1: val post = createPost(token, item) case 2: processPost(post) } }
  • 27. 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
  • 28. 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
  • 29. fun postItem(item: Item, cont: Continuation) { val sm = … switch (sm.label) { case 0: sm.item = item sm.label = 1 requestToken(sm) case 1: createPost(token, item, sm) case 2: processPost(post) } } Save state
  • 30. fun postItem(item: Item, cont: Continuation) { val sm = object : CoroutineImpl { … } switch (sm.label) { case 0: sm.item = item sm.label = 1 requestToken(sm) case 1: createPost(token, item, sm) case 2: processPost(post) } } Callback State Machine as Continuation
  • 31. fun postItem(item: Item, cont: Continuation) { val sm = object : CoroutineImpl { fun resume(…) { postItem(null, this) } } switch (sm.label) { case 0: sm.item = item sm.label = 1 requestToken(sm) case 1: createPost(token, item, sm) … } Callback
  • 32. 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 = item sm.label = 1 requestToken(sm) case 1: createPost(token, item, sm) … } Callback
  • 33. fun postItem(item: Item, cont: Continuation) { val sm = … switch (sm.label) { case 0: sm.item = item sm.label = 1 requestToken(sm) case 1: val item = sm.item val token = sm.result as Token sm.label = 2 createPost(token, item, sm) … } Restore state
  • 34. fun postItem(item: Item, cont: Continuation) { val sm = … switch (sm.label) { case 0: sm.item = item sm.label = 1 requestToken(sm) case 1: val item = sm.item val token = sm.result as Token sm.label = 2 createPost(token, item, sm) … } Continue
  • 35. 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) }
  • 36. 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
  • 37. State Machine vs Callbacks suspend 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
  • 38. 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
  • 40. Zoo of futures on JVM
  • 41. interface Service { fun createPost(token: Token, item: Item): Call<Post> }
  • 42. interface Service { fun createPost(token: Token, item: Item): Call<Post> } suspend fun createPost(token: Token, item: Item): Post = serviceInstance.createPost(token, item).await()
  • 43. suspend fun <T> Call<T>.await(): T { … }
  • 44. Callbacks everywhere suspend 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 } }) }
  • 45. 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) } }) }
  • 46. suspend fun <T> suspendCoroutine(block: (Continuation<T>) -> Unit): T
  • 47. suspend fun <T> suspendCoroutine(block: (Continuation<T>) -> Unit): T Regular function Inspired by call/cc from Scheme
  • 48. Install callback 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) } }) }
  • 49. Install callback 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) } }) }
  • 50. Analyze response 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) } }) }
  • 51. Analyze response 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) } }) } That’s all
  • 54. What thread it resumes on? suspend fun postItem(item: Item) { val token = requestToken() val post = createPost(token, item) processPost(post) } Continuation It depends!
  • 55. What thread it resumes on? fun postItem(item: Item) { launch(UI) { val token = requestToken() val post = createPost(token, item) processPost(post) } } Continuation
  • 56. Continuation Interceptor interface ContinuationInterceptor : CoroutineContext.Element { companion object Key : CoroutineContext.Key<ContinuationInterceptor> fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> }
  • 57. Continuation Interceptor interface ContinuationInterceptor : CoroutineContext.Element { companion object Key : CoroutineContext.Key<ContinuationInterceptor> fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> }
  • 58. Continuation Interceptor interface ContinuationInterceptor : CoroutineContext.Element { companion object Key : CoroutineContext.Key<ContinuationInterceptor> fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> }
  • 59. 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
  • 61. fun <T> future( context: CoroutineContext = DefaultDispatcher, block: suspend () -> T ): CompletableFuture<T> Coroutine builder
  • 62. fun <T> future( context: CoroutineContext = DefaultDispatcher, block: suspend () -> T ): CompletableFuture<T> A regular function
  • 63. fun <T> future( context: CoroutineContext = DefaultDispatcher, block: suspend () -> T ): CompletableFuture<T>
  • 64. fun <T> future( context: CoroutineContext = DefaultDispatcher, block: suspend () -> T ): CompletableFuture<T> suspending lambda
  • 65. fun <T> future( context: CoroutineContext = DefaultDispatcher, block: suspend () -> T ): CompletableFuture<T> { val future = CompletableFuture<T>() block.startCoroutine(…) return future }
  • 66. fun <T> future( context: CoroutineContext = DefaultDispatcher, block: suspend () -> T ): CompletableFuture<T> { val future = CompletableFuture<T>() block.startCoroutine(…) return future }
  • 67. fun <T> future( context: CoroutineContext = DefaultDispatcher, block: suspend () -> T ): CompletableFuture<T> { val future = CompletableFuture<T>() block.startCoroutine(completion = object : Continuation<T> { … }) return future }
  • 68. 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 }
  • 69. 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!
  • 71. Launch coroutine builder fun launch( context: CoroutineContext = DefaultDispatcher, block: suspend () -> Unit ): Job { … }
  • 72. Launching coroutine val job = launch { … }
  • 73. val job = launch { … } job.join()
  • 74. val job = launch { … } job.join() job.cancel()
  • 75. Job interface Job : CoroutineContext.Element { companion object Key : CoroutineContext.Key<Job> … }
  • 76. Using coroutine context launch { val job = coroutineContext[Job]!! … }
  • 77. Using coroutine context launch { val job = coroutineContext[Job]!! val interceptor = coroutineContext[CoroutineInterceptor]!! … }
  • 82. Cooperative cancellation launch { while (true) { delay(…) … } }
  • 83. Cancellable suspension suspend fun <T> Call<T>.await(): T = suspendCancellableCoroutine { cont -> enqueue(…) }
  • 84. Cancellable continuation suspend fun <T> Call<T>.await(): T = suspendCancellableCoroutine { cont: CancellableContinuation<T> -> enqueue(…) }
  • 85. Completion handler suspend fun <T> Call<T>.await(): T = suspendCancellableCoroutine { cont: CancellableContinuation<T> -> enqueue(…) cont.invokeOnCompletion { [email protected]() } }
  • 86. Completion handler suspend fun <T> Call<T>.await(): T = suspendCancellableCoroutine { cont: CancellableContinuation<T> -> enqueue(…) cont.invokeOnCompletion { [email protected]() } }
  • 90. Example fun 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) } } }
  • 91. Main coroutine fun 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) } } }
  • 92. Channel fun 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) } } }
  • 93. Launch fun 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
  • 94. Coroutine body fun 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!
  • 95. Send fun 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) } } }
  • 96. Close fun 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) } } }
  • 97. Receive for loop fun 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) } } }
  • 98. Demo
  • 99. Actors The other way to look at CSP
  • 100. The choice Named channels Named coroutines Actor == named coroutine & inbox channel
  • 101. Example fun 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() } }
  • 102. Actor coroutine builder fun 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() } }
  • 103. Actor body fun 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!
  • 104. Interacting with an actor fun 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() } }
  • 106. Guide to kotlinx.coroutines by 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
  • 107. #kotlinconf17 relizarov elizarov at JetBrains Roman Elizarov Thank you Any questions?