Fiber Extension
Fiber implementation for PHP using native C fibers.
Warning
Please upgrade to PHP 8.1 instead of using this extension. There are subtle differences, which can't be fixed in the extension.
Requirements
- PHP 8.0 only (PHP 8.1+ includes Fibers, so this extension is unnecessary)
Installation
Installation of the extension is similar to other PHP extensions.
git clone https://github.com/amphp/ext-fiber
cd ext-fiber
phpize
./configure
make
make test
make install
API
Fibers are made by creating an instance of the Fiber
class.
final class Fiber
{
/**
* @param callable $callback Function to invoke when starting the fiber.
*/
public function __construct(callable $callback) {}
/**
* Starts execution of the fiber. Returns when the fiber suspends or terminates.
*
* @param mixed ...$args Arguments passed to fiber function.
*
* @return mixed Value from the first suspension point or NULL if the fiber returns.
*
* @throw FiberError If the fiber has already been started.
* @throw Throwable If the fiber callable throws an uncaught exception.
*/
public function start(mixed ...$args): mixed {}
/**
* Resumes the fiber, returning the given value from {@see Fiber::suspend()}.
* Returns when the fiber suspends or terminates.
*
* @param mixed $value
*
* @return mixed Value from the next suspension point or NULL if the fiber returns.
*
* @throw FiberError If the fiber has not started, is running, or has terminated.
* @throw Throwable If the fiber callable throws an uncaught exception.
*/
public function resume(mixed $value = null): mixed {}
/**
* Throws the given exception into the fiber from {@see Fiber::suspend()}.
* Returns when the fiber suspends or terminates.
*
* @param Throwable $exception
*
* @return mixed Value from the next suspension point or NULL if the fiber returns.
*
* @throw FiberError If the fiber has not started, is running, or has terminated.
* @throw Throwable If the fiber callable throws an uncaught exception.
*/
public function throw(Throwable $exception): mixed {}
/**
* @return bool True if the fiber has been started.
*/
public function isStarted(): bool {}
/**
* @return bool True if the fiber is suspended.
*/
public function isSuspended(): bool {}
/**
* @return bool True if the fiber is currently running.
*/
public function isRunning(): bool {}
/**
* @return bool True if the fiber has completed execution (returned or threw).
*/
public function isTerminated(): bool {}
/**
* @return mixed Return value of the fiber callback. NULL is returned if the fiber does not have a return statement.
*
* @throws FiberError If the fiber has not terminated or the fiber threw an exception.
*/
public function getReturn(): mixed {}
/**
* @return self|null Returns the currently executing fiber instance or NULL if in {main}.
*/
public static function getCurrent(): ?self {}
/**
* Suspend execution of the fiber. The fiber may be resumed with {@see Fiber::resume()} or {@see Fiber::throw()}.
*
* Cannot be called from {main}.
*
* @param mixed $value Value to return from {@see Fiber::resume()} or {@see Fiber::throw()}.
*
* @return mixed Value provided to {@see Fiber::resume()}.
*
* @throws FiberError Thrown if not within a fiber (i.e., if called from {main}).
* @throws Throwable Exception provided to {@see Fiber::throw()}.
*/
public static function suspend(mixed $value = null): mixed {}
}
A Fiber
object is created using new Fiber(callable $callback)
with any callable. The callable need not call Fiber::suspend()
directly, it may be in a deeply nested call, far down the call stack (or perhaps never call Fiber::suspend()
at all). The returned Fiber
may be started using Fiber->start(mixed ...$args)
with a variadic argument list that is provided as arguments to the callable used when creating the Fiber
.
Fiber::suspend()
suspends execution of the current fiber and returns execution to the call to Fiber->start()
, Fiber->resume()
, or Fiber->throw()
. The value passed to Fiber::suspend()
is used as the return value of these methods. If the fiber throws an exception, the exception is thrown from the call to these methods. Fiber::suspend()
will throw an instance of FiberError
if called outside a fiber (i.e., if called from {main}
).
A suspended fiber may be resumed in one of two ways:
- returning a value from
Fiber::suspend()
usingFiber->resume()
- throwing an exception from
Fiber::suspend()
usingFiber->throw()
Fiber->getReturn()
returns the value returned from a terminated fiber (NULL
is returned if the fiber did not return a value). This function will throw an instance of FiberError
if the fiber has not completed execution or threw an exception.
Fiber::getCurrent()
returns the currently executing Fiber
instance or NULL
if called from {main}
. This allows a fiber to store a reference to itself elsewhere, such as within an event loop callback or an array of awaiting fibers.
ReflectionFiber
ReflectionFiber
is used to inspect executing fibers. A ReflectionFiber
object can be created from any Fiber
object, even if it has not been started or if it has been finished. This reflection class is similar to ReflectionGenerator
.
final class ReflectionFiber
{
/**
* @param Fiber $fiber Any Fiber object, including those that are not started or have
* terminated.
*/
public function __construct(Fiber $fiber) {}
/**
* @return Fiber The reflected Fiber object.
*/
public function getFiber(): Fiber {}
/**
* @return string Current file of fiber execution.
*
* @throws Error If the fiber has not been started or has terminated.
*/
public function getExecutingFile(): string {}
/**
* @return int Current line of fiber execution.
*
* @throws Error If the fiber has not been started or has terminated.
*/
public function getExecutingLine(): int {}
/**
* @param int $options Same flags as {@see debug_backtrace()}.
*
* @return array Fiber backtrace, similar to {@see debug_backtrace()}
* and {@see ReflectionGenerator::getTrace()}.
*
* @throws Error If the fiber has not been started or has terminated.
*/
public function getTrace(int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT): array {}
/**
* @return callable Callable used to create the fiber.
*
* @throws Error If the fiber has been terminated.
*/
public function getCallable(): callable {}
}
Unfinished Fibers
Fibers that are not finished (do not complete execution) are destroyed similarly to unfinished generators, executing any pending finally
blocks. Fiber::suspend()
may not be invoked in a force-closed fiber, just as yield
cannot be used in a force-closed generator. Fibers are destroyed when there are no references to the Fiber
object.
Fiber Stacks
Each fiber is allocated a separate C stack and VM stack on the heap. The C stack is allocated using mmap
if available, meaning physical memory is used only on demand (if it needs to be allocated to a stack value) on most platforms. Each fiber stack is allocated a maximum of 8M of memory by default, settable with an ini setting fiber.stack_size
. Note that this memory is used for the C stack and is not related to the memory available to PHP code. VM stacks for each fiber are allocated in a similar way to generators and use a similar amount of memory and CPU. VM stacks are able to grow dynamically, so only a single VM page (4K) is initially allocated.
PHP 8.1+
This extension only works with PHP 8.0. The Fiber RFC was accepted and will be a part of PHP 8.1, so this extension is not necessary and incompatible with PHP 8.1 and above.