Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.
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...
Nächste SlideShare
Wird geladen in …5
×

JVMLS 2016. Coroutines in Kotlin

2.385 Aufrufe

Veröffentlicht am

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

Veröffentlicht in: Technologie
  • Als Erste(r) kommentieren

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  

×