Fun with fibers!

Hi,

I spent a bit of time over new years playing around with ‘fibers’.

This was partly in response to requests for me to look into async/await style functionality, but also touched on a bunch of other stuff I was interested in checking out such as coroutines/generators.

But just what is a fiber? The term is pinched from the win32 API, and they are in effect ‘lightweight’ threads. Like real threads, each fiber has it’s own stack pointer/program counter etc.  However, unlike real threads, fibers don’t run ‘in parallel’. Only one fiber is ever running at a time, and a running fiber must explicitly ‘yield’ to allow another fiber to run.

My first stab at a fiber API was very simple and looked something like:

With this alone, you can do some pretty cool stuff. For example, you could cause a fiber to ‘wait’ for the App.Idle signal, faking a ‘VWait’ of sorts:

(Note: fiber ‘0’ is reserved to mean the ‘GUI’ fiber…this should NEVER block!)

Probably looks  a bit weird, but it’s actually pretty simple. All this does is add a ‘signal listener’ function to the App.Idle signal (which is emitted by the app framework when the app is idle)  which, when called, wakes up the fiber that is VWaiting. Note also that VWait() will not work when called on the GUI fiber, as in this case the SwitchToFiber( 0 ) at the end becomes a NOP.

The messy bit is the switch back to fiber 0 at the end of VWait(). This is fine in a lot of situations, but not all. The simplest example of this is a fiber that starts another fiber that vwaits. In this case, the ‘child’ fiber shouldn’t just switch back to the gui fiber, but back to the ‘parent’ fiber, ie: the fiber that started it.

The problem here is that ‘pure’ fibers only really have 2 states – running or blocked. What’s really needed is a third state – ‘ready’. A ready fiber is not running, but it’s not blocked either – it wants to run again in the future. And it turns out this is actually quite easy to do, via the addition of 2 functions that work a bit like this:

…where ‘readyStack’ is just a global stack of ints.

There are a few subtleties that mean these aren’t just implemented as wrappers around SwitchToFiber (in particular fiber creation/destruction) but that’s about it. Fibers don’t need an explicit ‘state’ var added to them, as their ‘ready’ state is implicit in whether they’re on the ready stack or not.

VWait looks almost the same…

…but can now be safely called from pretty much anywhere  – except, still, the GUI fiber! In which case, SuspendCurrentFiber will attempt to pop an empty stack anyway, so again, do not block the GUI!

But when should you actually use fibers? Well, IMO one of the coolest uses for fibers is to ‘linearize’ game logic. If you check out the ‘defender’ source code in xmas monkey2 demo, you’ll notice a ton of ugly stuff in the ‘OnRender’ method for handling game state changes and dialog timers. This is due to the fact that the app is callback based, and callbacks must return promptly and without blocking for everything to keep going, so you’re stuck with having to update a bunch of state vars incrementally every tick.

However, with fibers you can do the sequential game logic stuff in a much more natural manner. The latest version of defender now has a ‘GameLoop’ method that looks something like:

This code is run on a fiber using CreateFiber( GameLoop ) and as such, it can freely block (eg: vwait) without blocking the GUI thread. The dialogs can therefore ‘wait 10 seconds’, PlayGame() can VWait etc without any problem. All in all, it’s suspiciously like coding in b3d/bmx again!

Another use for fibers is in writing ‘generator’ style code. In fact, it is remarkably easy to build a generic generator class with fibers:

…and a simple example ‘int range’ generator…

Finally, there’s a catch: fibers don’t currently work in emscripten. There are features being added to emscripten that means they may eventually happen, and I have had a demo going (only works on firefox ‘nightly’ though) although it was a bit limited (you could only start a fiber from the main/gui fiber) but realistically I don’t think fibers in emscripten will be seriously viable in the near future.

But one of my ‘conditions’ for providing emscripten support was that I was NOT going to let it hold back the other targets, and this is a very good case in point. So monkey2 will eventually have some sort of fiber support, but probably only for non-emscripten targets. Bummer, but what can ya do…

Bye!
Mark

Liked it? Take a second to support Monkey 2 on Patreon!

2 thoughts to “Fun with fibers!”

Leave a Reply