phpfibers

Fibers simple example


I'm trying to make some simple fibers work, but doesn't seem to go as expected. What am I missing here? Have never worked with fibers before

The output I expected:

iteration: 0
Fiber 1 is working!
Fiber 2 is working!
Fiber 3 is working (0)!
(1 sec sleep)

iteration: 1    
Fiber 1 is working!
Fiber 2 is working!
Fiber 3 is working (1)!
(1 sec sleep)

iteration: 2
Fiber 1 is working!
Fiber 2 is working!
Fiber 3 completed!
(1 sec sleep)

iteration: 3
Fiber 1 is working!
Fiber 2 is working!
(1 sec sleep)

...

code

$fibers = [];

//  add fiber 1
fiber_await(new \Fiber(function(){
    while(true){
        echo "Fiber 1 is working!\n";
        
        \Fiber::suspend();
    }
}));

//  add fiber 2
fiber_await(new \Fiber(function(){
    while(true){
        echo "Fiber 2 is working!\n";
        
        \Fiber::suspend();
    }
}));

//  add fiber 3
fiber_await(new \Fiber(function(){
    for($i=0; $i<2; $i++){
        echo "Fiber 3 is working ($i)!\n";
        
        \Fiber::suspend();
    }
    echo "Fiber 3 completed!\n";
}));

$j = 0;
foreach($fibers as $i => &$pair){
    echo "iteration: $j\n";
    $parent = $pair[0];
    
    if($parent->isTerminated()){
        unset($fibers[$i]);
    }
    elseif($parent->isSuspended()){
        $parent->resume();
    }
    
    sleep(1);
    $j++;
}

function fiber_await(\Fiber $child): mixed{
    $fibers[] = [\Fiber::getCurrent(), $child];
    $child->start();
    while(!$child->isTerminated()){
        $child->resume();
        
        \Fiber::suspend();
    }
    
    return $child->getReturn();
}

output

Fiber 1 is working!
Fiber 1 is working!

Fatal error: Uncaught FiberError: Cannot suspend outside of a fiber in Standard input code:46
Stack trace:
#0 Standard input code(46): Fiber::suspend()
#1 Standard input code(12): fiber_await(Object(Fiber))
#2 {main}
  thrown in Standard input code on line 46

Solution

  • There are a couple of general (not fiber-specific) logic errors here:

    The first fiber-specific problem is that you're calling $child->start(); which echoes "Fiber 1 is working!" and suspends the fiber; then you immediately call $child->resume(); which echoes "Fiber 1 is working!" again, and suspends it again.

    After that, you call \Fiber::suspend(), but the fiber has already suspended itself, and you're back in top-level code. So you get an error about trying to suspend when no fiber is currently running. That whole loop makes no sense: you don't want to immediately exhaust the fiber at this stage, just store it for later.

    For the same reason, \Fiber::getCurrent() will always return null, because you're not nesting the fibers - they're all siblings, with the top-level stack as their parent. As such, if you fixed the other problems, all the references to $parent in the foreach loop will give errors. You actually want to check and resume the child, which is $pair[1]; or, since you don't need $parent right now, just use $fibers[] = $child;