Slide 1

Slide 1 text

Coroutines Kotlin Versus... Bolot Kerimbaev twitter.com/bolot

Slide 2

Slide 2 text

Kotlin 1.3 • No longer “experimental” • buildSequence and buildIterator moved to the kotlin.sequences package • Simplified Continuation, only one method resumeWith(result:SuccessOrFailure) • KT-16908 Support callable references to suspending functions • KT-18559 Serializability of all coroutine-related classes

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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)

Slide 5

Slide 5 text

Sequence: Iterator class RangeClassic(begin: Int, private val end: Int ): Iterator { private var current = begin override fun hasNext() = current <= end override fun next() = current++ } for (x in RangeClassic(7, 11)) println(x)

Slide 6

Slide 6 text

Sequence: Coroutine fun rangeCoroutine(begin: Int, end: Int) = buildSequence { for (i in begin..end) yield(i) } for (x in rangeCoroutine(7, 11)) println(x)

Slide 7

Slide 7 text

Channels val channel = Channel() launch { repeat(42) { channel.send(it)} } launch { for (i in channel) print("$i") }

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

Deferred interface UsersService { @GET("users/{id}") fun user(@Path("id") userId: String): Deferred>> @POST("users/{id}") fun update(@Path("id") userId: String, @Body update: String): Deferred> }

Slide 10

Slide 10 text

Languages with Coroutines • C# • Dart • Go • JavaScript • Kotlin • Python • Swift

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

Coroutines vs subroutines return call call resume suspend return

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

Tony Hoare https://en.wikipedia.org/wiki/Tony_Hoare

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

Tony Hoare • Inventor of null reference

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

Melvin Conway https://twitter.com/conways_law

Slide 22

Slide 22 text

Melvin Conway • Conway's law: https://en.wikipedia.org/wiki/Melvin_Conway

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

COBOL

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

Subroutines vs coroutines https://www.youtube.com/watch?v=_fu0gx-xseY CppCon 2015: Gor Nishanov “C++ Coroutines - a negative overhead abstraction"

Slide 28

Slide 28 text

C#

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

Lua

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

Go

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

Python

Slide 45

Slide 45 text

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/

Slide 46

Slide 46 text

Yield def numbers(): n = 0 while(True): yield n n += 1

Slide 47

Slide 47 text

Yield/send @coroutine def answer(): result = yield 'the ultimate answer' print('The ultimate answer is', result) f = answer() f # --> 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)

Slide 48

Slide 48 text

Yield from yield from iterator for x in iterator: yield x

Slide 49

Slide 49 text

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/

Slide 50

Slide 50 text

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/

Slide 51

Slide 51 text

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/

Slide 52

Slide 52 text

JavaScript

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

C++

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

Future overhead https://www.youtube.com/watch?v=UL3TtTgt3oU CppCon 2017: Gor Nishanov “Naked coroutines live (with networking)”

Slide 66

Slide 66 text

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"

Slide 67

Slide 67 text

Future/promise + .then future 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(total); return Tcp::Connect("127.0.0.1", 1337).then( [state](future conn) { state->conn = std::move(conn.get()); return do_while([state]()->future { if (state->total <= 0) return make_ready_future(false); return state->conn.read(state->buf, sizeof(state->buf)).then( [state](future nBytesFut) { auto nBytes = nBytesFut.get(); if (nBytes == 0) return make_ready_future(false); state->total -= nBytes; return make_ready_future(true); }); }) }).then([state](future){ return make_ready_future(state->total) }); } future do_while(function()> body) { return body().then([=](future 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"

Slide 68

Slide 68 text

Hand-crafted state machine https://www.youtube.com/watch?v=_fu0gx-xseY CppCon 2015: Gor Nishanov “C++ Coroutines - a negative overhead abstraction"

Slide 69

Slide 69 text

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"

Slide 70

Slide 70 text

Coroutines auto tcp_reader(int total) -> future { 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"

Slide 71

Slide 71 text

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"

Slide 72

Slide 72 text

Generalized Functions • Plain old function • Monadic: await - suspend • Task: await • Generator: yield • Async Generator: await + yield

Slide 73

Slide 73 text

Coroutine body transformation https://www.youtube.com/watch?v=mlP1MKP8d_Q CppCon 2017: Toby Allsopp “Coroutines: what can't they do?”

Slide 74

Slide 74 text

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?”

Slide 75

Slide 75 text

LLVM

Slide 76

Slide 76 text

LLVM Coroutines • @llvm.coro.begin • @llvm.coro.suspend • @llvm.coro.end • @llvm.coro.destroy • @llvm.coro.resume

Slide 77

Slide 77 text

LLVM: Manipulation Intrinsics • @llvm.coro.destroy • @llvm.coro.resume • @llvm.coro.done • @llvm.coro.promise

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

C void *f(int n) { for(;;) { print(n++); // returns a coroutine handle on first suspend } }

Slide 80

Slide 80 text

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 }

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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 }

Slide 83

Slide 83 text

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 Task Keywords suspend async, await, yield async, await go, make chan co_await, co_yield, co_return async, await

Slide 84

Slide 84 text

Questions? Bolot Kerimbaev twitter.com/bolot