<?php
class FiberPool
{
/** @var Fiber[] */
private array $fibers = [];
private int $max = 10;
private array $results = [];
public function __construct(
private \Generator $generator,
) {
}
public function wait(): void
{
while ($this->fibers || $this->generator->valid()) {
// Fill up the pending fibers
while (count($this->fibers) < $this->max && $this->generator->valid()) {
$this->startNextItem();
}
foreach ($this->fibers as $key => $fiber) {
// TBD: We could try/catch this and set the exception to the results array.
if ($fiber->isSuspended()) {
$fiber->resume();
} else if (!$fiber->isStarted()) {
$fiber->start();
}
if ($fiber->isTerminated()) {
$this->results[$key] = $fiber->getReturn();
unset($this->fibers[$key]);
$this->startNextItem();
}
}
// If we're in a fiber loop ourselves then suspend up the chain once per loop.
if (Fiber::getCurrent()) {
Fiber::suspend();
}
}
}
public function getResults(): array
{
return $this->results;
}
private function startNextItem(): void
{
if ($this->generator->valid()) {
$callable = $this->generator->current();
$this->fibers[$this->generator->key()] = new Fiber($callable);
$this->generator->next();
}
}
}
$consistentSuspend = function () {
static $seed = [0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0];
return array_pop($seed);
};
$example = new FiberPool((function () use ($consistentSuspend) {
$output = array_combine(range('A', 'Z'), range(1,26));
foreach ($output as $key => $item) {
echo 'generating ', $item, "\n";
yield $key => function () use ($consistentSuspend, $item) {
echo 'starting ', $item, "\n";
while ($consistentSuspend()) {
echo 'suspending ', $item, "\n";
Fiber::suspend();
}
echo 'returning ', $item, "\n";
return $item;
};
}
})());
$example->wait();
print_r($example->getResults());
preferences:
29.02 ms | 407 KiB | 5 Q