feat: replace fiber run() with transfer() and suspend() for scheduler-friendly semantics

Remove the run() method from fibers and introduce transfer() for non-stack-based fiber switching, along with suspend() to pause the interpreter and return control to the host application. Update Timer.sleep() to use runNextScheduled_() instead of Fiber.yield(), and add Timer.schedule() for creating independently scheduled fibers. Refactor the core fiber runtime to support transfer semantics, including proper error handling for aborted fibers and self-transfer edge cases. Fix a missing quote in test.py's runtime error validation string.
This commit is contained in:
Bob Nystrom
2015-08-31 05:15:37 +00:00
parent ca8e97521a
commit 7e111d19da
49 changed files with 469 additions and 350 deletions
+10 -2
View File
@@ -19,6 +19,14 @@ fiber is run. Does not immediately start running the fiber.
The currently executing fiber.
### Fiber.**suspend**()
Pauses the current fiber, and stops the interpreter. Control returns to the
host application.
To resume execution, the host application will need to invoke the interpreter
again. If there is still a reference to the suspended fiber, it can be resumed.
### Fiber.**yield**()
Pauses the current fiber and transfers control to the parent fiber. "Parent"
@@ -119,10 +127,10 @@ Invokes the fiber or resumes the fiber if it is in a paused state and sets
Whether the fiber's main function has completed and the fiber can no longer be
run. This returns `false` if the fiber is currently running or has yielded.
### **run**()
### **transfer**()
**TODO**
### **run**(value)
### **transfer**(value)
**TODO**
+8 -13
View File
@@ -158,17 +158,12 @@ Fibers have one more trick up their sleeves. When you execute a fiber using
lets you build up a chain of fiber calls that will eventually unwind back to
the main fiber when all of the called ones yield or finish.
This works fine for most uses, but sometimes you want something a little more
freeform. For example, you may be creating a [state
machine](http://en.wikipedia.org/wiki/Finite-state_machine) where each state is
a fiber. When you switch from one state to the next, you *don't* want to build
an implicit stack of fibers to return to. There is no "returning" in this case.
You just want to *transfer* to the next fiber and forget about the previous one
entirely. (This is analogous to [tail call
elimination](http://en.wikipedia.org/wiki/Tail_call) for regular function
calls.)
This is almost always what you want. But if you're doing something really low
level, like writing your own scheduler to manage a pool of fibers, you may not
want to treat them explicitly like a stack.
To enable this, fibers also have a `run()` method. This begins executing that
fiber, and "forgets" the previous one. If the running fiber yields or ends, it
will transfer control back to the last *called* one. (If there are no called
fibers, it will end execution.)
For rare cases like that, fibers also have a `transfer()` method. This switches
execution immediately to the transferred fiber. The previous one is suspended,
leaving it in whatever state it was in. You can resume the previous fiber by
transferring back to it, or even calling it. If you don't, execution stops when
the last transferred fiber returns.