Search code examples
multithreadingdyieldcoroutinefibers

What will happen if not call yield in fiber?


As I understand from docs yield() function is passing control to another fiber. What will happen if we will not call yield in fiber in D? Will it's mean that thread will hang?

Or I am wrong understand that fibers works inside threads and they work inside process? And process can have or threads or fibers?


Solution

  • To answer this question, it is important to understand how fibers work. When you call Fiber.call() method, current execution context (CPU register state) gets dumped into memory and execution context for that fiber object is loaded instead. When you call Fiber.yield() current execution context is dumped too, but control is passed to whoever called current fiber last time. It can be both another fiber or plain thread context - it doesn't need to know as any execution context is completely defined by dumped data, it doesn't even need to be fiber aware.

    When fiber function ends, it simply returns control to last caller similar to as if there was an yield call in the very end. Main difference is that when fiber function ends, matching fiber object gets into "terminated" state and on yielding it always remains in "running" state (even if there is no more to run technically). That is important because it is only defined behavior to reset and recycle fibers in terminated state.

    Common mistake is to treat fibers as some sort of tasks and expect inherent scheduling semantics. It is not the case - fiber itself is simply a context switching primitive to implement any smart task system on top, there is never any scheduling on its own.

    Some code example to show relevant semantics:

    void main ( )
    {
        import core.thread, std.stdio;
    
        Fiber fiber1, fiber2;
    
        fiber1 = new Fiber({        
            fiber2.call();
            Fiber.yield(); 
        });
    
        fiber2 = new Fiber({
            Fiber.yield(); 
        });
    
        fiber1.call(); // switches to fiber1, which switches to fiber2
                       // which yield back to fiber1 and finally fiber1 yield back to main
        assert (fiber1.state == Fiber.State.HOLD && fiber2.state == Fiber.State.HOLD);
        fiber2.call(); // switches to fiber2 which reaches end of function
                       // and switches back to main upon exit
        assert (fiber1.state == Fiber.State.HOLD && fiber2.state == Fiber.State.TERM);
        fiber1.call(); // switches to fiber1 which also reaches end of function
                       // and switches back to main upon exist
        assert (fiber1.state == Fiber.State.TERM && fiber2.state == Fiber.State.TERM);
    }