corounes in kotlin · outline • mo%vaon/examples • solu%ons in other languages • kotlin’s...

39
Corou%nes in Kotlin [email protected]

Upload: others

Post on 27-Jan-2020

5 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

Corou%nesinKotlin

[email protected]

Page 2: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

Thistalkcouldhavebeennamed…

•  async/await/yield

•  fibers

•  [stackless]con%nua%ons

SuspendableComputa%ons

Page 3: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

Outline•  Mo%va%on/Examples•  Solu%onsinotherlanguages•  Kotlin’sSolu%on–  Clientcode–  Librarycode

•  CompilingCorou%nes•  Excep%onHandling•  Appendix.SerializableCorou%nes?

Page 4: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

“Legal”

•  AllI’msayingisnomorefinalthanValhallaJ

Page 5: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

valimage=loadImage(url)myUI.setImage(image)

Mo%va%onTime-consumingopera%on

UIThread

Page 6: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

Latency!

•  Blockingbad.Verybad.

Page 7: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

asyncUI{valimage=await(loadImage(url))myUI.setImage(image)

}

SuspendableComputa%onSuspendingcall

Con%nua%on

Time-consumingopera%on

await(…) loadImage(url)

UIThread

WorkerThreadsetImage(…)

Page 8: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

“CallbackHell”

Page 9: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

CombiningFutures•  CompletableFuture

  .supplyAsync{loadImage(url)}   .thenAccept(myUI::setImage)

•  soveeeryfunc%onalJ

Page 10: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

asyncUI{valimage=loadImageAsync(url)myUI.setImage(image)

}

FlexibilityAsynchronousopera%on

Con%nua%on

interfaceContinuation<P>{funresume(data:P)funresumeWithException(e:Throwable)}

<Image>

LibraryFunc%on(corou%nebuilder)

Page 11: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

asyncUI{valimage=await(loadImage(url))myUI.setImage(image)

}

Run%meSupport

Con%nua%on

interfaceContinuation<P>{funresume(data:P)funresumeWithException(e:Throwable)}

<Image>

Page 12: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

Summary:Goals•  Asynchronousprograming(andmore)– withoutexplicitcallbacks– withoutexplicitFuturecombinators

•  Maximumflexibilityforlibrarydesigners– withminimalrun%mesupport–  andnomacrosJ

Page 13: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

FlavorsofCorou%nes

Stackless Stackful

Languagerestric;ons UseinspecialcontextsL UseanywhereJ

Implementedin C#,Scala,Kotlin,… Quasar,Javaflow,…

Codetransforma;on Local(compilermagic)J AllovertheplaceL

Run;mesupport LidleJ Substan%alL

Page 14: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

TheC#WayasyncTask<String>work(){

Thread.sleep(200);return“done”;

}asyncTaskmoreWork(){

Console.WriteLine(“Workstarted”);varstr=awaitwork();Console.WriteLine($“Workcompleted:{str}”);

}

Page 15: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

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") !}

typeisop%onal

Page 16: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

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") !}

Page 17: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

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) ! } !} !!!!!!!

Page 18: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

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!} !

implicitreceiver λhasnoparams

Page 19: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

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" !} !

Page 20: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

Extensibilitysuspend 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()) !} !

Page 21: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

Summary:Corou%neLibraries•  funasync(coroutinec:…)

–  func%onwithacoroutine parameter

•  suspendfunawait(…,c:Continuation<…>)–  func%onmarkedsuspend !–  con%nua%onisimplicitlypassedinatthecallsite

•  classController

–  declaressuspend func%ons•  mayallowsuspend extensions

–  declaresreturn/excep%onhandlers

Page 22: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

HowSuspensionWorksfun moreWork() = async { ! println("Work started") ! val str = await(work()) ! println("Work completed: $str") !} !!!!!!!!!!. !

controller.await( ! work(), ! current_continuation!) !return !

Page 23: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

Yield(TheC#Way)IEnumerable<int>Fibonacci(){varcur=1;varnext=1;yieldreturn1;while(true){yieldreturnnext;vartmp=cur+next;cur=next;next=tmp;}}

Infinite(lazy)sequenceofFibonaccinumbers

Page 24: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

Example:LazyFibonaccival 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())

Page 25: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

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) ! } ! ... !}

Page 26: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

CompilingtoStateMachinesgenerate { ! 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!

Page 27: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

val fibonacci = generate { ! var cur = 1 ! var next = 1 !! yield(1) !! while (true) { ! yield(next) !! val tmp = cur + next ! cur = next ! next = tmp! } !}

CompilingCorou%nes(I)class fibonacci$1 implements Function1, ! Continuation { !volatileGeneratorControllercontrollervolatileintlabel=-2volatileintcurvolatileintnextpublicContinuation<Unit>invoke(GeneratorControllercontroller)publicvoidresume(Objectparam)publicvoidresumeWithException(Throwablee)privatevoiddoResume(Objectparam,Throwablee) }

forsharedlocalvariables

Page 28: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

CompilingCorou%nes(II)•  Fields:

–  GeneratorControllercontroller–  intlabel

•  voiddoResume(Objectparam,Throwablee)–  tableswitch(label)

  case0:L0   case1:L1   case2: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!

Page 29: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

CompilingCorou%nes(III)–  L0:

  varcur=1   varnext=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

Page 30: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

CompilingCorou%nes(IV)•  voiddoResume(Objectparam,Throwablee)

–  L1:   cur=this.cur   next=this.next

  if(e!=null)throwe

  //while(true){

  this.cur=cur   this.next=next

  this.label=2   this.controller.yield(next,this)   return

L0

L1

L2

true

Page 31: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

Summary:CompilingCorou%nes

•  Note:generate()/yield()canbeexpressed–  flexibility:þ

•  Corou%nebodyiscompiledtoastatemachine•  Onlyoneinstanceisallocatedatrun%me

Page 32: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

asyncUI{valimage=await(loadImage(url))myUI.setImage(image)

}

IOExcep%on

CannotAwaitExcep%on

WindowDisposedExcep%on

Excep%onHandling

Page 33: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

ThrowingandCatching•  Whocanthrow–  Synchronouscode(insideacorou%ne)– Asynchronousopera%ons(calledfromcorou%ne)–  Librarycode(thatmanagesthecorouitne)

•  Whocancatch–  Thecorou%neitself(usercode)–  Librarycode

Page 34: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

Controller.handleExcep%on(e)voiddoResume(Objectparam,Throwablee)

tableswitch(label)case0:L0case1:L1case2:L2

try{   L0:

  ...   label=1   controller.await(...,/*continuation=*/this)   return

  L1:   ...   label=2   controller.await(...,/*continuation=*/this)   return

  L2:   ...

  }catch(Throwablee){   controller.handleException(e)

  }

Page 35: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

Rou%ngAsynchronousExcep%ons•  voiddoResume(Objectparam,Throwablee)...

–  L1:   //fields->locals

  if(e!=null)throwe

  ...

  //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)

Page 36: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

Example:Excep%onHandlingasyncUI{valimage=try{await(loadImage(url))}catch(e:Exception){log(e)throwe}myUI.setImage(image)}

Operationorder:•  loadImage(url)

•  ->tmp_future•  ->actual_work()

•  await(tmp_future)•  <suspend>•  actual_work()completes•  <resume>•  myUI.setImage(image)

actual_work(url)workerthread

Page 37: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

Summary:Excep%onHandling•  Uniformtreatmentofallexcep%ons–  bothsyncandasync

•  Defaulthandler:controller.handleException(e)

•  Notcoveredinthistalk–  Suspendinginfinallyblocks–  CallingfinallyblocksthroughContinuation<T>

Page 38: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

Appendix.SerializableCorou%nes?serializableAsync(getDB()){valnewUser=showRegistrationForm()sendConfirmationEmail(newUser)if(awaitEmailAddressConfirmation(newUser)){//registrationconfirmedintimeconfirmRegistration(getDB(),newUser)showWelcomeScreen(newUser)}else{//registrationnotconfirmedcancelRegistration(getDB(),newUser)}}

SuspendingCalls

Datatobeserialized:•  label(stateoftheSM)•  newUser

Page 39: Corounes in Kotlin · Outline • Mo%vaon/Examples • Solu%ons in other languages • Kotlin’s Soluon – Client code – Library code • Compiling Corounes

References•  LanguageDesignProposal(KEEP)– hdps://github.com/Kotlin/kotlin-corou%nes– GiveyourfeedbackinGitHubIssues

•  Examplelibraries– hdps://github.com/Kotlin/kotlinx.corou%nes