Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Corou%nes	
  in	
  Kotlin	
  
Andrey.Breslav@JetBrains.com	
  
This	
  talk	
  could	
  have	
  been	
  named…	
  
•  async/await/yield	
  
•  fibers	
  
•  [stackless]	
  con%nua%ons	
 ...
Outline	
  
•  Mo%va%on/Examples	
  
•  Solu%ons	
  in	
  other	
  languages	
  
•  Kotlin’s	
  Solu%on	
  
–  Client	
  c...
“Legal”	
  
•  All	
  I’m	
  saying	
  is	
  no	
  more	
  final	
  than	
  Valhalla	
  J	
  
 
val	
  image	
  =	
  loadImage(url)	
  
myUI.setImage(image)	
  
	
  
Mo%va%on	
  
Time-­‐consuming	
  
opera%on	
  
UI	...
Latency!	
  
•  Blocking	
  bad.	
  Very	
  bad.	
  
asyncUI	
  {	
  
val	
  image	
  =	
  await(loadImage(url))	
  
myUI.setImage(image)	
  
}	
  
Suspendable	
  Computa%on	
...
“Callback	
  Hell”	
  
Combining	
  Futures	
  
•  CompletableFuture	
  
  .supplyAsync	
  {	
  loadImage(url)	
  }	
  
  .thenAccept(myUI::setIm...
asyncUI	
  {	
  
val	
  image	
  =	
  loadImageAsync(url)	
  
myUI.setImage(image)	
  
}	
  
Flexibility	
  
Asynchronous	...
asyncUI	
  {	
  
val	
  image	
  =	
  await(loadImage(url))	
  
myUI.setImage(image)	
  
}	
  
Run%me	
  Support	
  
Con%n...
Summary:	
  Goals	
  
•  Asynchronous	
  programing	
  (and	
  more)	
  
–  without	
  explicit	
  callbacks	
  
–  withou...
Flavors	
  of	
  Corou%nes	
  
Stackless	
   Stackful	
  
Language	
  restric;ons	
   Use	
  in	
  special	
  contexts	
  ...
The	
  C#	
  Way	
  
async	
  Task<String>	
  work()	
  {	
  
Thread.sleep(200);	
  
return	
  “done”;	
  
}	
  
	
  
asyn...
Example:	
  async/await	
  (I)	
  
fun work(): CompletableFuture<String> = async {!
Thread.sleep(200)!
"done"!
}!
!
fun mo...
Example:	
  async/await	
  (I)	
  
fun work() = async {!
Thread.sleep(200)!
"done"!
}!
!
fun moreWork() = async {!
println...
await()	
  
fun moreWork() = async {!
println("Work started")!
val str = await(work())!
println("Work completed: $str")!
}...
async()	
  
fun moreWork() = async {!
println("Work started")!
val str = await(work())!
println("Work completed: $str")!
}...
Controller	
  
@AllowSuspendExtensions!
class FutureController<T> {!
internal val future = CompletableFuture<T>()!
!
suspe...
Extensibility	
  
suspend fun <V> FutureController<*>.await(!
lf: ListenableFuture<V>, c: Continuation<V>!
) {!
Futures.ad...
Summary:	
  Corou%ne	
  Libraries	
  
•  fun	
  async(coroutine	
  c:	
  …)	
  
–  func%on	
  with	
  a	
  coroutine param...
How	
  Suspension	
  Works	
  
fun moreWork() = async {!
println("Work started")!
val str = await(work())!
println("Work c...
Yield	
  (The	
  C#	
  Way)	
  
IEnumerable<int>	
  Fibonacci()	
  {	
  
	
  	
  	
  	
  var	
  cur	
  =	
  1;	
  
	
  	
 ...
Example:	
  Lazy	
  Fibonacci	
  
val fibonacci: Sequence<Int> = generate {!
var cur = 1!
var next = 1!
!
yield(1)!
!
whil...
fun <T> generate(!
coroutine c: GeneratorController<T>.() -> Continuation<Unit>!
): Sequence<T> =!
object : Sequence<T> {!...
Compiling	
  to	
  State	
  Machines	
  
generate {!
var cur = 1!
var next = 1!
!
yield(1)!
!
while (true) {!
yield(next)!...
val fibonacci = generate {!
var cur = 1!
var next = 1!
!
yield(1)!
!
while (true) {!
yield(next)!
!
val tmp = cur + next!
...
Compiling	
  Corou%nes	
  (II)	
  
•  Fields:	
  
–  GeneratorController	
  controller	
  
–  int	
  label	
  
	
  
	
  
•...
Compiling	
  Corou%nes	
  (III)	
  
–  L0:	
  
  var	
  cur	
  =	
  1	
  
  var	
  next	
  =	
  1	
  
  this.cur	
  =	
  c...
Compiling	
  Corou%nes	
  (IV)	
  
•  void	
  doResume(Object	
  param,	
  Throwable	
  e)	
  
–  L1:	
  
  cur	
  =	
  th...
Summary:	
  Compiling	
  Corou%nes	
  
•  Note:	
  generate()/yield()	
  can	
  be	
  expressed	
  	
  
–  flexibility:	
  ...
asyncUI	
  {	
  	
  
val	
  image	
  =	
  await(loadImage(url))	
  
myUI.setImage(image)	
  
}	
  
IOExcep%on	
  
CannotAw...
Throwing	
  and	
  Catching	
  
•  Who	
  can	
  throw	
  
–  Synchronous	
  code	
  (inside	
  a	
  corou%ne)	
  
–  Asyn...
Controller.handleExcep%on(e)	
  
void	
  doResume(Object	
  param,	
  Throwable	
  e)	
  
	
  
tableswitch	
  (label)	
  
...
Rou%ng	
  Asynchronous	
  Excep%ons	
  
•  void	
  doResume(Object	
  param,	
  Throwable	
  e)	
  
	
  	
  	
  ...	
  
– ...
Example:	
  Excep%on	
  Handling	
  
asyncUI	
  {	
  
	
  	
  	
  	
  val	
  image	
  =	
  try	
  {	
  
	
  	
  	
  	
  	
...
Summary:	
  Excep%on	
  Handling	
  
•  Uniform	
  treatment	
  of	
  all	
  excep%ons	
  
–  both	
  sync	
  and	
  async...
Appendix.	
  Serializable	
  Corou%nes?	
  
serializableAsync(getDB())	
  {	
  
	
  	
  	
  	
  val	
  newUser	
  =	
  sho...
Upcoming SlideShare
Loading in …5
×

JVMLS 2016. Coroutines in Kotlin

1,921 views

Published on

This talk is about asynchronous programming in Kotlin and how it's done with the new feature coming in 1.1 — Coroutines.

Published in: Technology
  • Be the first to comment

JVMLS 2016. Coroutines in Kotlin

  1. 1. Corou%nes  in  Kotlin   Andrey.Breslav@JetBrains.com  
  2. 2. This  talk  could  have  been  named…   •  async/await/yield   •  fibers   •  [stackless]  con%nua%ons   Suspendable   Computa%ons  
  3. 3. Outline   •  Mo%va%on/Examples   •  Solu%ons  in  other  languages   •  Kotlin’s  Solu%on   –  Client  code   –  Library  code   •  Compiling  Corou%nes   •  Excep%on  Handling   •  Appendix.  Serializable  Corou%nes?  
  4. 4. “Legal”   •  All  I’m  saying  is  no  more  final  than  Valhalla  J  
  5. 5.   val  image  =  loadImage(url)   myUI.setImage(image)     Mo%va%on   Time-­‐consuming   opera%on   UI  Thread  
  6. 6. Latency!   •  Blocking  bad.  Very  bad.  
  7. 7. asyncUI  {   val  image  =  await(loadImage(url))   myUI.setImage(image)   }   Suspendable  Computa%on   Suspending   call   Con%nua%on   Time-­‐consuming   opera%on   await(…)   loadImage(url)   UI  Thread   Worker  Thread   setImage(…)  
  8. 8. “Callback  Hell”  
  9. 9. Combining  Futures   •  CompletableFuture     .supplyAsync  {  loadImage(url)  }     .thenAccept(myUI::setImage)   •  so  veeery  func%onal  J  
  10. 10. asyncUI  {   val  image  =  loadImageAsync(url)   myUI.setImage(image)   }   Flexibility   Asynchronous   opera%on   Con%nua%on   interface  Continuation<P>  {          fun  resume(data:  P)          fun  resumeWithException(e:  Throwable)   }   <Image>   Library  Func%on   (corou%ne  builder)  
  11. 11. asyncUI  {   val  image  =  await(loadImage(url))   myUI.setImage(image)   }   Run%me  Support   Con%nua%on   interface  Continuation<P>  {          fun  resume(data:  P)          fun  resumeWithException(e:  Throwable)   }   <Image>  
  12. 12. Summary:  Goals   •  Asynchronous  programing  (and  more)   –  without  explicit  callbacks   –  without  explicit  Future  combinators   •  Maximum  flexibility  for  library  designers   –  with  minimal  run%me  support   –  and  no  macros  J  
  13. 13. Flavors  of  Corou%nes   Stackless   Stackful   Language  restric;ons   Use  in  special  contexts  L   Use  anywhere  J   Implemented  in   C#,  Scala,  Kotlin,  …   Quasar,  Javaflow,  …   Code  transforma;on   Local  (compiler  magic)  J   All  over  the  place  L   Run;me  support   Lidle  J   Substan%al  L  
  14. 14. The  C#  Way   async  Task<String>  work()  {   Thread.sleep(200);   return  “done”;   }     async  Task  moreWork()  {   Console.WriteLine(“Work  started”);   var  str  =  await  work();   Console.WriteLine($“Work  completed:  {str}”);   }  
  15. 15. Example:  async/await  (I)   fun work(): CompletableFuture<String> = async {! Thread.sleep(200)! "done"! }! ! fun moreWork() = async {! println("Work started")! val str = await(work())! println("Work completed: $str")! }   type  is  op%onal  
  16. 16. Example:  async/await  (I)   fun work() = async {! Thread.sleep(200)! "done"! }! ! fun moreWork() = async {! println("Work started")! val str = await(work())! println("Work completed: $str")! }  
  17. 17. await()   fun moreWork() = async {! println("Work started")! val str = await(work())! println("Work completed: $str")! }! ! suspend fun <V> await(f: CompletableFuture<V>, c: Continuation<V>) {! f.whenComplete { value, throwable ->! if (throwable == null)! c.resume(value)! else! c.resumeWithException(throwable)! }! }! !
  18. 18. async()   fun moreWork() = async {! println("Work started")! val str = await(work())! println("Work completed: $str")! }! ! fun <T> async(! coroutine c: FutureController<T>.() -> Continuation<Unit>! ): CompletableFuture<T> {! val controller = FutureController<T>()! c(controller).resume(Unit)! return controller.future! }! implicit  receiver   λ  has  no  params  
  19. 19. Controller   @AllowSuspendExtensions! class FutureController<T> {! internal val future = CompletableFuture<T>()! ! suspend fun <V> await(f: CompletableFuture<V>, c: Continuation<V>) {! f.whenComplete { value, throwable ->! if (throwable == null)! c.resume(value)! else! c.resumeWithException(throwable)! }! }! ! operator fun handleResult(value: T, c: Continuation<Nothing>) {! future.complete(value)! }! ! operator fun handleException(t: Throwable, c: Continuation<Nothing>) {! future.completeExceptionally(t)! }! }! fun work() = async {! Thread.sleep(200)! "done"! }!
  20. 20. Extensibility   suspend fun <V> FutureController<*>.await(! lf: ListenableFuture<V>, c: Continuation<V>! ) {! Futures.addCallback(lf, object : FutureCallback<V> { ! override fun onSuccess(value: V) {! c.resume(value)! }! override fun onFailure(e: Throwable) {! c.resumeWithException(throwable)! }! })! }! ! // Example! async {! val res1 = await(getCompletableFuture()) ! val res2 = await(getListeableFuture())! }!
  21. 21. Summary:  Corou%ne  Libraries   •  fun  async(coroutine  c:  …)   –  func%on  with  a  coroutine parameter     •  suspend  fun  await(…,  c:  Continuation<…>)   –  func%on  marked  suspend ! –  con%nua%on  is  implicitly  passed  in  at  the  call  site     •  class  Controller   –  declares  suspend func%ons   •  may  allow  suspend extensions   –  declares  return/excep%on  handlers  
  22. 22. How  Suspension  Works   fun moreWork() = async {! println("Work started")! val str = await(work())! println("Work completed: $str")! }! ! ! ! ! ! ! ! ! ! .! controller.await(! work(), ! current_continuation! )! return!
  23. 23. Yield  (The  C#  Way)   IEnumerable<int>  Fibonacci()  {          var  cur  =  1;          var  next  =  1;            yield  return  1;            while  (true)  {                  yield  return  next;                    var  tmp  =  cur  +  next;                  cur  =  next;                  next  =  tmp;          }   }   Infinite  (lazy)  sequence  of     Fibonacci  numbers  
  24. 24. Example:  Lazy  Fibonacci   val fibonacci: Sequence<Int> = generate {! var cur = 1! var next = 1! ! yield(1)! ! while (true) {! yield(next)! ! val tmp = cur + next! cur = next! next = tmp! }! }! ! assertEquals("1, 1, 2, 3, 5", fibonacci.take(5).joinToString())  
  25. 25. fun <T> generate(! coroutine c: GeneratorController<T>.() -> Continuation<Unit>! ): Sequence<T> =! object : Sequence<T> {! override fun iterator(): Iterator<T> {! val iterator = GeneratorController<T>()! iterator.setNextStep(c(iterator))! return iterator! }! }! ! class GeneratorController<T> : AbstractIterator<T>() {! ...! suspend fun yield(value: T, c: Continuation<Unit>) {! setNext(value)! setNextStep(c)! }! ...! }  
  26. 26. Compiling  to  State  Machines   generate {! var cur = 1! var next = 1! ! yield(1)! ! while (true) {! yield(next)! ! val tmp = cur + next! cur = next! next = tmp! }! }   L0   L1   L2   var cur = 1! var next = 1   true   val tmp = cur + next! cur = next! next = tmp!
  27. 27. val fibonacci = generate {! var cur = 1! var next = 1! ! yield(1)! ! while (true) {! yield(next)! ! val tmp = cur + next! cur = next! next = tmp! }! }   Compiling  Corou%nes  (I)   class fibonacci$1 implements Function1, ! Continuation {!          volatile  GeneratorController  controller          volatile  int  label  =  -­‐2            volatile  int  cur          volatile  int  next              public  Continuation<Unit>  invoke(                                                GeneratorController  controller)            public  void  resume(Object  param)          public  void  resumeWithException(Throwable  e)          private  void  doResume(Object  param,  Throwable  e)       }   for  shared  local  variables  
  28. 28. Compiling  Corou%nes  (II)   •  Fields:   –  GeneratorController  controller   –  int  label       •  void  doResume(Object  param,  Throwable  e)   –  tableswitch  (label)     case  0:  L0     case  1:  L1     case  2:  L2   –  L0:     ...     label  =  1     controller.yield(1,  /*  continuation  =  */  this)     return   –  L1:     ...     label  =  2     controller.yield(next,  /*  continuation  =  */  this)     return   –  L2:     ...   L0   L1   L2   var cur = 1! var next = 1   true   val tmp = cur + next! cur = next! next = tmp!
  29. 29. Compiling  Corou%nes  (III)   –  L0:     var  cur  =  1     var  next  =  1     this.cur  =  cur     this.next  =  next     this.label  =  1     this.controller.yield(1,  this)     return   L0   L1   L2   var cur = 1! var next = 1  
  30. 30. Compiling  Corou%nes  (IV)   •  void  doResume(Object  param,  Throwable  e)   –  L1:     cur  =  this.cur     next  =  this.next     if  (e  !=  null)  throw  e     //  while  (true)  {     this.cur  =  cur     this.next  =  next     this.label  =  2     this.controller.yield(next,  this)     return   L0   L1   L2   true  
  31. 31. Summary:  Compiling  Corou%nes   •  Note:  generate()/yield()  can  be  expressed     –  flexibility:  þ •  Corou%ne  body  is  compiled  to  a  state  machine   •  Only  one  instance  is  allocated  at  run%me  
  32. 32. asyncUI  {     val  image  =  await(loadImage(url))   myUI.setImage(image)   }   IOExcep%on   CannotAwaitExcep%on   WindowDisposedExcep%on   Excep%on  Handling  
  33. 33. Throwing  and  Catching   •  Who  can  throw   –  Synchronous  code  (inside  a  corou%ne)   –  Asynchronous  opera%ons  (called  from  corou%ne)   –  Library  code  (that  manages  the  corouitne)   •  Who  can  catch   –  The  corou%ne  itself  (user  code)   –  Library  code  
  34. 34. Controller.handleExcep%on(e)   void  doResume(Object  param,  Throwable  e)     tableswitch  (label)   case  0:  L0   case  1:  L1   case  2:  L2   try  {     L0:     ...     label  =  1     controller.await(...,  /*  continuation  =  */  this)     return     L1:     ...     label  =  2     controller.await(...,  /*  continuation  =  */  this)     return     L2:     ...     }  catch  (Throwable  e)  {     controller.handleException(e)     }  
  35. 35. Rou%ng  Asynchronous  Excep%ons   •  void  doResume(Object  param,  Throwable  e)        ...   –  L1:     //  fields  -­‐>  locals     if  (e  !=  null)  throw  e     ...     //  locals  -­‐>  fields     this.label  =  2     this.controller.yield(next,  this)     return   suspend fun await(f, c) {! f.whenComplete { value, e ->! if (throwable == null)! c.resume(value)! else! c.resumeWithException(e)  
  36. 36. Example:  Excep%on  Handling   asyncUI  {          val  image  =  try  {                await(                        loadImage(url)                )          }  catch(e:  Exception)  {                  log(e)                  throw  e          }            myUI.setImage(image)   }   Operation  order:   •  loadImage(url)   •  -­‐>  tmp_future   •  -­‐>  actual_work()   •  await(tmp_future)   •  <suspend>   •  actual_work()  completes   •  <resume>   •  myUI.setImage(image)   actual_work(url)   worker     thread  
  37. 37. Summary:  Excep%on  Handling   •  Uniform  treatment  of  all  excep%ons   –  both  sync  and  async   •  Default  handler:  controller.handleException(e)   •  Not  covered  in  this  talk   –  Suspending  in  finally  blocks   –  Calling  finally  blocks  through  Continuation<T>  
  38. 38. Appendix.  Serializable  Corou%nes?   serializableAsync(getDB())  {          val  newUser  =  showRegistrationForm()          sendConfirmationEmail(newUser)          if  (awaitEmailAddressConfirmation(newUser))  {                  //  registration  confirmed  in  time                  confirmRegistration(getDB(),  newUser)                  showWelcomeScreen(newUser)          }  else  {                  //  registration  not  confirmed                  cancelRegistration(getDB(),  newUser)          }   }   Suspending     Calls   Data  to  be  serialized:   •  label  (state  of  the  SM)   •  newUser  

×