Corou%nes	
  in	
  Kotlin	
  
Andrey.Breslav@JetBrains.com	
  
This	
  talk	
  could	
  have	
  been	
  named…	
  
•  async/await/yield	
  
•  fibers	
  
•  [stackless]	
  con%nua%ons	
  
Suspendable	
  
Computa%ons	
  
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?	
  
“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	
  Thread	
  
Latency!	
  
•  Blocking	
  bad.	
  Very	
  bad.	
  
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(…)	
  
“Callback	
  Hell”	
  
Combining	
  Futures	
  
•  CompletableFuture	
  
  .supplyAsync	
  {	
  loadImage(url)	
  }	
  
  .thenAccept(myUI::setImage)	
  
•  so	
  veeery	
  func%onal	
  J	
  
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)	
  
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>	
  
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	
  
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	
  
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}”);	
  
}	
  
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	
  
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")!
}	
  
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)!
}!
}!
!
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	
  
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"!
}!
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())!
}!
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	
  
How	
  Suspension	
  Works	
  
fun moreWork() = async {!
println("Work started")!
val str = await(work())!
println("Work completed: $str")!
}!
!
!
!
!
!
!
!
!
!
.!
controller.await(!
work(), !
current_continuation!
)!
return!
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	
  
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())	
  
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)!
}!
...!
}	
  
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!
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	
  
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!
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	
  
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	
  
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	
  
asyncUI	
  {	
  	
  
val	
  image	
  =	
  await(loadImage(url))	
  
myUI.setImage(image)	
  
}	
  
IOExcep%on	
  
CannotAwaitExcep%on	
  
WindowDisposedExcep%on	
  
Excep%on	
  Handling	
  
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	
  
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)	
  
  }	
  
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)	
  
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	
  
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>	
  
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