Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Coroutines: Kotlin Versus DCSF18

bolot
November 20, 2018

Coroutines: Kotlin Versus DCSF18

Many languages implement coroutines. In addition to the usual suspects like C#, Python and JavaScript, they are available in dozens of languages. Some languages use async/await syntax. Almost all coroutines are built on top of futures/promises. Kotlin implements many patterns in the standard library.

A bit of history: Melvin Conway invented coroutines in 1958. Tony Hoare may be known for introducing the null reference, but he also created quick sort and wrote the paper that inspired Go's goroutines and channels.

Many of the languages share motivations for introducing coroutines. In this talk, we'll go over:
* Goroutines and channels in Go.
* Evolution and generalization of generators/yield in Python.
* Comparison of promises and coroutines in JavaScript.
* Code transformation and optimization in C++.

References

* https://en.wikipedia.org/wiki/Coroutine
* https://en.wikipedia.org/wiki/Tony_Hoare
* https://en.wikipedia.org/wiki/Melvin_Conway
* Melvin E. Conway, Design of a separable transition-diagram compiler, Communications of the ACM, Volume 6 Issue 7, July 1963, Pages 396-408 https://dl.acm.org/citation.cfm?id=366704
* C. A. R. Hoare, Communicating sequential processes, Communications of the ACM, Volume 21 Issue 8, Aug. 1978, Pages 666-677 https://dl.acm.org/citation.cfm?doid=359576.359585
* https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/
* http://www.lua.org/pil/9.1.html
* https://wow.curseforge.com/projects/auctioneer
* http://www.golangpatterns.info/concurrency/coroutines
* https://blog.golang.org/concurrency-is-not-parallelism
* https://tour.golang.org/concurrency/1
* How the heck does async/await work in Python 3.5? Brett Cannon, Feb 11, 2016 https://snarky.ca/how-the-heck-does-async-await-work-in-python-3-5/
* David Beazley, Keynote at PyCon Brasil 2015 (Screencast) https://www.youtube.com/watch?v=lYe8W04ERnY
* 6 Reasons Why JavaScript’s Async/Await Blows Promises Away (Tutorial), Mostafa Gaafar, Senior Software Engineer https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9
* CppCon 2015: Gor Nishanov “C++ Coroutines - a negative overhead abstraction" https://www.youtube.com/watch?v=_fu0gx-xseY
* CppCon 2016: Gor Nishanov “C++ Coroutines: Under the covers" https://www.youtube.com/watch?v=8C8NnE1Dg4A
* CppCon 2017: Gor Nishanov “Naked coroutines live (with networking)” https://www.youtube.com/watch?v=UL3TtTgt3oU
* CppCon 2017: Toby Allsopp “Coroutines: what can't they do?” https://www.youtube.com/watch?v=mlP1MKP8d_Q
* Coroutine Types (C++), By Titus Winters, https://abseil.io/blog/20180713-coroutine-types
* http://www.llvm.org/docs/Coroutines.html

bolot

November 20, 2018
Tweet

More Decks by bolot

Other Decks in Programming

Transcript

  1. Kotlin 1.3 • No longer “experimental” • buildSequence and buildIterator

    moved to the kotlin.sequences package • Simplified Continuation<T>, only one method resumeWith(result:SuccessOrFailure<T>) • KT-16908 Support callable references to suspending functions • KT-18559 Serializability of all coroutine-related classes
  2. Callback hell fun loadImage(url: String, callback: (image: Image) -> Unit)

    fun combineImages(image1: Image, image2: Image, callback: (result: Image) -> Unit) fun displayImage(image: Image) loadImage(url1) { image1 -> loadImage(url2) { image2 -> combineImages(image1, image2) { result -> displayImage(result) } } }
  3. Coroutines suspend fun loadImage(url: String): Image suspend fun combineImages(image1: Image,

    image2: Image): Image suspend fun displayImage(image: Image): Unit val image1 = loadImage(url1) val image2 = loadImage(url2) val result = combineImages(image1, image2) displayImage(result)
  4. Sequence: Iterator class RangeClassic(begin: Int, private val end: Int ):

    Iterator<Int> { private var current = begin override fun hasNext() = current <= end override fun next() = current++ } for (x in RangeClassic(7, 11)) println(x)
  5. Sequence: Coroutine fun rangeCoroutine(begin: Int, end: Int) = buildSequence {

    for (i in begin..end) yield(i) } for (x in rangeCoroutine(7, 11)) println(x)
  6. Channels fun CoroutineScope.rangeProducer(begin: Int, end: Int) = produce { for(x

    in begin..end) send(x) } fun sandbox() = runBlocking { for (x in rangeProducer(7, 11)) println(x) }
  7. Deferred interface UsersService { @GET("users/{id}") fun user(@Path("id") userId: String): Deferred<Response<List<User>>>

    @POST("users/{id}") fun update(@Path("id") userId: String, @Body update: String): Deferred<Response<Unit>> }
  8. Languages with Coroutines • C# • Dart • Go •

    JavaScript • Kotlin • Python • Swift
  9. Languages with Coroutines • Aikido • AngelScript • Assembly •

    BCPL • BETA • BLISS • C# • C++ • ChucK • CLU • D • Dart • Dynamic C • Erlang • F# • Factor • Game Monkey Script • GDScript • Go • Haskell • Icon • Io • Java • JavaScript • Julia • Kotlin • Limbo • Lua • Lucid • µC++ • MiniD • Modula-2 • Nemerle • Pascal • Perl • PHP • Picolisp • Prolog • Python • Ruby • Sather • Scala • Scheme • Self • Simula 67 • Squirrel • Stackless Python • Super- Collider • Tcl • urbiscript • Vala https://en.wikipedia.org/wiki/Coroutine
  10. Coroutines vs subroutines Subroutines Coroutines Call Allocate frame, pass parameters

    Allocate frame, pass parameters Return Free frame, return result Free frame, return eventual result Suspend x Yes Resume x Yes https://www.youtube.com/watch?v=UL3TtTgt3oU CppCon 2017: Gor Nishanov “Naked coroutines live (with networking)”
  11. Sir Charles Antony Richard Hoare • Inventor of null reference

    https://en.wikipedia.org/wiki/Tony_Hoare
  12. Sir Charles Antony Richard Hoare • Inventor of null reference

    • Turing award recipient https://en.wikipedia.org/wiki/Tony_Hoare
  13. Sir Charles Antony Richard Hoare • Inventor of null reference

    • Turing award recipient • Inventor of quick sort https://en.wikipedia.org/wiki/Tony_Hoare
  14. Sir Charles Antony Richard Hoare • Inventor of null reference

    • Turing award recipient • Inventor of quick sort • Communicating Sequential Processes https://en.wikipedia.org/wiki/Tony_Hoare
  15. Melvin Conway • Conway's law: • "Organizations which design systems

    are constrained to produce designs which are copies of the communication structures of these organizations." https://en.wikipedia.org/wiki/Melvin_Conway
  16. Melvin Conway • Conway's law: • "Organizations which design systems

    are constrained to produce designs which are copies of the communication structures of these organizations." • Coroutines https://en.wikipedia.org/wiki/Melvin_Conway
  17. COBOL Compiler https://dl.acm.org/citation.cfm?id=366704 Melvin E. Conway, Design of a separable

    transition-diagram compiler Communications of the ACM Volume 6 Issue 7, July 1963 Pages 396-408
  18. C#

  19. C# async Task<int> AccessTheWebAsync() { HttpClient client = new HttpClient();

    Task<string> getStringTask = client.GetStringAsync("https:// msdn.microsoft.com"); DoIndependentWork(); string urlContents = await getStringTask; return urlContents.Length; } int urlContents = await AccessTheWebAsync();
  20. C# async Task<int> AccessTheWebAsync() { HttpClient client = new HttpClient();

    Task<string> getStringTask = client.GetStringAsync("https:// msdn.microsoft.com"); DoIndependentWork(); string urlContents = await getStringTask; return urlContents.Length; } int urlContents = AccessTheWebAsync().Result;
  21. Lua

  22. Lua co = coroutine.create(function () print("hi") end) print(co) --> thread:

    0x8071d98 print(coroutine.status(co)) --> suspended coroutine.resume(co) --> hi print(coroutine.status(co)) --> dead
  23. Lua co = coroutine.create(function () for i=1,10 do print("co", i)

    coroutine.yield() end end) coroutine.resume(co) --> co 1 print(coroutine.status(co)) --> suspended coroutine.resume(co) --> co 2 ... coroutine.resume(co) --> co 10 coroutine.resume(co) -- prints nothing
  24. WoW function lib.PerformSearch(searcher) if (not coSearch) or (coroutine.status(coSearch) == "dead")

    then coSearch = coroutine.create(PerformSearch) local status, result = coroutine.resume(coSearch, searcher) if not status and result then error("Error in search coroutine: " ..result .."\n\n{{{Coroutine Stack:}}}\n" ..debugstack(coSearch)); end else debugPrint("coroutine already running: " ..coroutine.status(coSearch)) end end
  25. Go

  26. Concurrency is not parallelism • Concurrency is the composition of

    independently executing processes • Parallelism is the simultaneous execution of (possibly related) computations • goroutines imply parallelism; coroutines in general do not • goroutines communicate via channels; coroutines communicate via yield and resume operations https://blog.golang.org/concurrency-is-not-parallelism
  27. Goroutines func say(s string) { for i := 0; i

    < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main() { go say("world") say("hello") } https://tour.golang.org/concurrency/1
  28. Channels func sum(s []int, c chan int) { sum :=

    0 for _, v := range s { sum += v } c <- sum // send sum to c } func main() { s := []int{7, 2, 8, -9, 4, 0} c := make(chan int) go sum(s[:len(s)/2], c) go sum(s[len(s)/2:], c) x, y := <-c, <-c // receive from c fmt.Println(x, y, x+y) } https://tour.golang.org/concurrency/2
  29. Channels func sum(s []int, c chan int) { sum :=

    0 for _, v := range s { sum += v } c <- sum // send sum to c } func main() { s := []int{7, 2, 8, -9, 4, 0} c := make(chan int) go sum(s[:len(s)/2], c) go sum(s[len(s)/2:], c) x, y := <-c, <-c // receive from c fmt.Println(x, y, x+y) } https://tour.golang.org/concurrency/2
  30. Channels func sum(s []int, c chan int) { sum :=

    0 for _, v := range s { sum += v } c <- sum // send sum to c } func main() { s := []int{7, 2, 8, -9, 4, 0} c := make(chan int) go sum(s[:len(s)/2], c) go sum(s[len(s)/2:], c) x, y := <-c, <-c // receive from c fmt.Println(x, y, x+y) } https://tour.golang.org/concurrency/2
  31. Buffered Channels func main() { ch := make(chan int, 4)

    ch <- 1 ch <- 2 for _, v := range []int{1, 2} { ch <- v } fmt.Println(<-ch) fmt.Println(<-ch) } https://tour.golang.org/concurrency/3
  32. Buffered Channels func main() { ch := make(chan int, 4)

    ch <- 1 ch <- 2 for _, v := range []int{1, 2} { ch <- v } fmt.Println(<-ch) fmt.Println(<-ch) } https://tour.golang.org/concurrency/3
  33. Range and close func fibonacci(n int, c chan int) {

    x, y := 0, 1 for i := 0; i < n; i++ { c <- x x, y = y, x+y } close(c) } func main() { c := make(chan int, 10) go fibonacci(cap(c), c) for i := range c { fmt.Println(i) } } https://tour.golang.org/concurrency/4
  34. Python • Python 2.2: yield -- pause execution • Python

    2.5: send -- send values back to paused generators • Python 3.3: yield from -- generator chaining • Python 3.4: asyncio -- event loop • Python 3.5: async/await https://snarky.ca/how-the-heck-does-async-await-work-in-python-3-5/
  35. Yield/send @coroutine def answer(): result = yield 'the ultimate answer'

    print('The ultimate answer is', result) f = answer() f # --> <generator object answer at 0xdeadbeef> f.send(None) # --> 'the ultimate answer' f.send(42) # --> The result is 42 https://www.youtube.com/watch?v=lYe8W04ERnY David Beazley, Keynote at PyCon Brasil 2015 (Screencast)
  36. Asyncio (Python 3.4) import asyncio @asyncio.coroutine def countdown(number, n): while

    n > 0: print('T-minus', n, '({})'.format(number)) yield from asyncio.sleep(1) n -= 1 loop = asyncio.get_event_loop() tasks = [ asyncio.ensure_future(countdown("A", 2)), asyncio.ensure_future(countdown("B", 3))] loop.run_until_complete(asyncio.wait(tasks)) loop.close() https://snarky.ca/how-the-heck-does-async-await-work-in-python-3-5/
  37. Async/await Python 3.5 @asyncio.coroutine def py34_coro(): yield from stuff() async

    def py35_coro(): await stuff() >>> dis.dis(py34_coro) 0 LOAD_GLOBAL 0 (stuff) 3 CALL_FUNCTION 0 (0 positional, 0 keyword pair) 6 GET_YIELD_FROM_ITER 7 LOAD_CONST 0 (None) 10 YIELD_FROM 11 POP_TOP 12 LOAD_CONST 0 (None) 15 RETURN_VALUE >>> dis.dis(py35_coro) 0 LOAD_GLOBAL 0 (stuff) 3 CALL_FUNCTION 0 (0 positional, 0 keyword pair) 6 GET_AWAITABLE 7 LOAD_CONST 0 (None) 10 YIELD_FROM 11 POP_TOP 12 LOAD_CONST 0 (None) 15 RETURN_VALUE https://snarky.ca/how-the-heck-does-async-await-work-in-python-3-5/
  38. Async/await Python 3.5 @asyncio.coroutine def py34_coro(): yield from stuff() async

    def py35_coro(): await stuff() >>> dis.dis(py34_coro) 0 LOAD_GLOBAL 0 (stuff) 3 CALL_FUNCTION 0 (0 positional, 0 keyword pair) 6 GET_YIELD_FROM_ITER 7 LOAD_CONST 0 (None) 10 YIELD_FROM 11 POP_TOP 12 LOAD_CONST 0 (None) 15 RETURN_VALUE >>> dis.dis(py35_coro) 0 LOAD_GLOBAL 0 (stuff) 3 CALL_FUNCTION 0 (0 positional, 0 keyword pair) 6 GET_AWAITABLE 7 LOAD_CONST 0 (None) 10 YIELD_FROM 11 POP_TOP 12 LOAD_CONST 0 (None) 15 RETURN_VALUE https://snarky.ca/how-the-heck-does-async-await-work-in-python-3-5/
  39. JavaScript Async/await 101 • Async/await is a new way to

    write asynchronous code. Previous options for asynchronous code are callbacks and promises. • Async/await is actually built on top of promises. It cannot be used with plain callbacks or node callbacks. • Async/await is, like promises, non blocking. • Async/await makes asynchronous code look and behave a little more like synchronous code. This is where all its power lies. https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9
  40. Promise vs async/await const makeRequest = () => getJSON() .then(data

    => { console.log( data ) return "done" }) makeRequest() https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9
  41. Promise vs async/await const makeRequest = async () => {

    console.log(await getJSON()) return "done" } makeRequest() https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9
  42. Error handling const makeRequest = () => { try {

    getJSON() .then(result => { // this parse may fail const data = JSON.parse( result ) console.log(data) }) .catch((err) => { console.log(err) }) } catch (err) { console.log(err) } } https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9
  43. Error handling const makeRequest = async () => { try

    { // this parse may fail const data = JSON.parse( await getJSON() ) console.log(data) } catch (err) { console.log(err) } } https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9
  44. Conditionals const makeRequest = () => { return getJSON() .then(data

    => { if (data.needsAnotherRequest) { return makeAnotherRequest(data) .then(moreData => { console.log(moreData) return moreData }) } else { console.log(data) return data } }) } https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9
  45. Conditionals const makeRequest = async () => { const data

    = await getJSON() if (data.needsAnotherRequest) { const moreData = await makeAnotherRequest(data); console.log(moreData) return moreData } else { console.log(data) return data } } https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9
  46. Intermediate values const makeRequest = () => { return promise1()

    .then(value1 => { // do something return promise2(value1) .then(value2 => { // do something return promise3(value1, value2) }) }) } https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9
  47. Intermediate values const makeRequest = async () => { const

    value1 = await promise1() const value2 = await promise2(value1) return promise3(value1, value2) } https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9
  48. C++

  49. C++ implementations • C++ coroutines TS (Technical Specification), a standard

    for C++ language extensions for a stackless subset of coroutine-like behaviour, is under development. Visual C++ and Clang already support major portions in the std::experimental namespace. coroutines Technical Specification • Boost.Coroutine - created by Oliver Kowalke, is the official released portable coroutine library of boost since version 1.53. The library relies on Boost.Context and supports ARM, MIPS, PowerPC, SPARC and X86 on POSIX, Mac OS X and Windows. • Boost.Coroutine2 - also created by Oliver Kowalke, is a modernized portable coroutine library since boost version 1.59. It takes advantage of C++11 features, but removes the support for symmetric coroutines. • Mordor - In 2010, Mozy open sourced a C++ library implementing coroutines, with an emphasis on using them to abstract asynchronous I/O into a more familiar sequential model.[36] • CO2 - stackless coroutine based on C++ preprocessor tricks, providing await/yield emulation. • ScummVM - The ScummVM project implements a light-weight version of coroutines based on Simon Tatham's article. • tonbit::coroutine - C++11 single .h asymmetric coroutine implementation via ucontext / fiber • Coroutines landed in Clang in May 2017, with libc++ implementation ongoing.[37] • elle by Docker https://en.wikipedia.org/wiki/Coroutine
  50. Design Principles • Scalable (to billions of concurrent coroutines) •

    Efficient (resume and suspend operations comparable in cost to a function call overhead) • Seamless integration with existing facilities with no overhead • Open ended coroutine machinery allowing library designers to develop coroutine libraries exposing various high level semantics, such as generators, tasks, async streamers and more • Usable in environments where exceptions are forbidden or not available https://www.youtube.com/watch?v=UL3TtTgt3oU CppCon 2017: Gor Nishanov “Naked coroutines live (with networking)”
  51. Synchronous int tcp_reader(int total) { char buf[4 * 1024]; auto

    conn = Tcp::Connect("127.0.0.1", 1337); for (;;) { auto bytesRead = conn.Read(buf, sizeof(buf)); total -= bytesRead; if (total <= 0 || bytesRead == 0) return total; } } https://www.youtube.com/watch?v=_fu0gx-xseY CppCon 2015: Gor Nishanov “C++ Coroutines - a negative overhead abstraction"
  52. Future/promise + .then future<int> tcp_reader(int64_t total) { struct State {

    char buf[4 * 1024]; int64_t total; Tcp::Connection conn; explicit State(int64_t total) : total(total) {} }; auto state = make_shared<State>(total); return Tcp::Connect("127.0.0.1", 1337).then( [state](future<Tcp::Connection> conn) { state->conn = std::move(conn.get()); return do_while([state]()->future<bool> { if (state->total <= 0) return make_ready_future(false); return state->conn.read(state->buf, sizeof(state->buf)).then( [state](future<int> nBytesFut) { auto nBytes = nBytesFut.get(); if (nBytes == 0) return make_ready_future(false); state->total -= nBytes; return make_ready_future(true); }); }) }).then([state](future<void>){ return make_ready_future(state->total) }); } future<void> do_while(function<future<bool>()> body) { return body().then([=](future<bool> notDone) { return notDone.get() ? do_while(body) : make_ready_future(); }); } https://www.youtube.com/watch?v=_fu0gx-xseY CppCon 2015: Gor Nishanov “C++ Coroutines - a negative overhead abstraction"
  53. Synchronous auto tcp_reader(int total) -> int { char buf[4 *

    1024]; auto conn = Tcp::Connect("127.0.0.1", 1337); for (;;) { auto bytesRead = conn.Read(buf, sizeof(buf)); total -= bytesRead; if (total <= 0 || bytesRead == 0) return total; } } https://www.youtube.com/watch?v=_fu0gx-xseY CppCon 2015: Gor Nishanov “C++ Coroutines - a negative overhead abstraction"
  54. Coroutines auto tcp_reader(int total) -> future<int> { char buf[4 *

    1024]; auto conn = co_await Tcp::Connect("127.0.0.1", 1337); for (;;) { auto bytesRead = co_await conn.Read(buf, sizeof(buf)); total -= bytesRead; if (total <= 0 || bytesRead == 0) return total; } } https://www.youtube.com/watch?v=_fu0gx-xseY CppCon 2015: Gor Nishanov “C++ Coroutines - a negative overhead abstraction"
  55. Performance MB/s Executable size Handcrafted Coroutines Handcrafted Coroutines Original 380

    495 30 25 Sync Completion 485 1028 30 25 Prealloc handler 690 1028 28 25 https://www.youtube.com/watch?v=_fu0gx-xseY CppCon 2015: Gor Nishanov “C++ Coroutines - a negative overhead abstraction"
  56. Generalized Functions • Plain old function • Monadic: await -

    suspend • Task: await • Generator: yield • Async Generator: await + yield
  57. Coroutine state • Stores promise, parameter copies, local variables and

    any information needed to resume the coroutine at the correct point • Dynamically allocated (can be elided in certain circumstances) • Created when the coroutine is called • Destroyed when control flows off the end of the transformed coroutine body https://www.youtube.com/watch?v=mlP1MKP8d_Q CppCon 2017: Toby Allsopp “Coroutines: what can't they do?”
  58. LLVM: Structure Intrinsics • @llvm.coro.size • @llvm.coro.begin • @llvm.coro.free •

    @llvm.coro.alloc • @llvm.coro.noop • @llvm.coro.frame • @llvm.coro.id • @llvm.coro.end • @llvm.coro.suspend • @llvm.coro.save • @llvm.coro.param
  59. C void *f(int n) { for(;;) { print(n++); <suspend> //

    returns a coroutine handle on first suspend } }
  60. C define i8* @f(i32 %n) { entry: %id = call

    token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) %size = call i32 @llvm.coro.size.i32() %alloc = call i8* @malloc(i32 %size) %hdl = call noalias i8* @llvm.coro.begin(token %id, i8* %alloc) br label %loop loop: %n.val = phi i32 [ %n, %entry ], [ %inc, %loop ] %inc = add nsw i32 %n.val, 1 call void @print(i32 %n.val) %0 = call i8 @llvm.coro.suspend(token none, i1 false) switch i8 %0, label %suspend [i8 0, label %loop i8 1, label %cleanup] cleanup: %mem = call i8* @llvm.coro.free(token %id, i8* %hdl) call void @free(i8* %mem) br label %suspend suspend: %unused = call i1 @llvm.coro.end(i8* %hdl, i1 false) ret i8* %hdl }
  61. C void* f(int n) { void* hdl = CORO_BEGIN(malloc); for

    (int i = n;; ++i) { CORO_SUSPEND(hdl); print(i); CORO_SUSPEND(hdl); print(-i); } CORO_END(hdl, free); } https://www.youtube.com/watch?v=8C8NnE1Dg4A CppCon 2016: Gor Nishanov “C++ Coroutines: Under the covers" int main() { void* coro = f(1); for (int i = 0; i < 4; ++i) { CORO_RESUME(coro); } CORO_DESTROY(coro); }
  62. C void* f(int n) { void* hdl = CORO_BEGIN(malloc); for

    (int i = n;; ++i) { CORO_SUSPEND(hdl); print(i); CORO_SUSPEND(hdl); print(-i); } CORO_END(hdl, free); } https://www.youtube.com/watch?v=8C8NnE1Dg4A CppCon 2016: Gor Nishanov “C++ Coroutines: Under the covers" int main() { void* coro = f(1); for (int i = 0; i < 4; ++i) { CORO_RESUME(coro); } CORO_DESTROY(coro); } define i32 @main() { call void @print(i32 1) call void @print(i32 -1) call void @print(i32 2) call void @print(i32 -2) ret i32 0 }
  63. Kotlin Python JavaScript Go C++ 17 C# Function modifier keyword

    suspend async async — — async Call-site keyword — await await go await await Future function return type — Promises future<T> Task<T> Keywords suspend async, await, yield async, await go, make chan co_await, co_yield, co_return async, await