<<

Section 6: Implementing

In Section 5, we worked on the usage of coroutines. Today, we will be reviewing the basic concepts relating to how coroutines can be implemented.

1 def f() { 2 yield(1) 3 yield(2) 4 null 5 } 6 7 def g(unused) { 8 f() 9 } 10 11 def co = (g) 12 print(resume(co, null)) 13 print(resume(co, null)) 14 print(resume(co, null))

Part 1. What does the above program print?

1 2 null

Part 2. Which piece of information about the coroutine co does the interpreter need to remember to continue execution when the second resume is invoked?

To continue execution within f, the interpreter needs to remember: ● the coroutine call stack that contains frames for the calls to g and to f ● the next instruction to execute in each stack frame

Part 3. What happens if we invoke resume a fourth time? Think about how the interpreter should implement the intended behavior.

Invoking resume for a fourth time will result in an error. The interpreter can track this by distinguishing whether the coroutine has finished its execution or not.

Part 4. Describe a to maintain the state of the coroutine throughout program execution.

For each coroutine, we must store: ● A coroutine state (running, suspended, or dead) ● A call stack ● For each frame in the call stack: ○ The ○ The bytecode array into which the PC indexes ○ The environment

Part 5. Now consider the following changes to the program above.

1 def f() { 2 yield(1) 3 print "between yields" 4 yield(2) 5 null 6 } 7 8 def g(unused) { 9 f() 10 } 11 12 def co = coroutine(g) 13 print "created coroutine" 14 resume(co, null) 15 print "between resumes" 16 resume(co, null) 17 resume(co, null) 18 print "end"

Describe the state of coroutines maintained by the interpreter at each print statement. Your answer must include the stack frames at each point, but need not include the environments.

We describe below the state of the coroutine at each print statement. For each stack frame, we maintain a bytecode array corresponding to the translation of the function body, which we do not repeat below.

1. print “created coroutine” ○ the suspended coroutine state. ○ the call stack for the coroutine, which contains: ■ the stack frame for the call to g: ● the program counter at the beginning of the function body. 2. print “between resumes” ○ the suspended coroutine state. ○ the call stack for the coroutine, which contains: ■ the stack frame for the call to g: ● the program counter after the call to f. ■ the stack frame for the call to f: ● the program counter after the first yield instruction 3. print “between yields” ○ the running coroutine state. ○ the call stack for the coroutine, which contains: ■ the stack frame for the call to g: ● the program counter after the call to f. ■ the stack frame for the call to f: ● the program counter after the print statement in f 4. print “end” ○ the dead coroutine state. ○ the call stack for the coroutine is now empty.