xref: /ext-fiber/README.md (revision 304e1c14)
1# Fiber Extension
2
3Fiber implementation for PHP using native C fibers.
4
5> **Warning**
6> Please upgrade to PHP 8.1 instead of using this extension. There are subtle differences, which can't be fixed in the extension.
7
8### Requirements
9
10- PHP 8.0 *only* (PHP 8.1+ includes Fibers, so this extension is unnecessary)
11
12## Installation
13
14Installation of the extension is similar to other PHP extensions.
15
16``` bash
17git clone https://github.com/amphp/ext-fiber
18cd ext-fiber
19phpize
20./configure
21make
22make test
23make install
24```
25
26## API
27
28Fibers are made by creating an instance of the `Fiber` class.
29
30``` php
31final class Fiber
32{
33    /**
34     * @param callable $callback Function to invoke when starting the fiber.
35     */
36    public function __construct(callable $callback) {}
37
38    /**
39     * Starts execution of the fiber. Returns when the fiber suspends or terminates.
40     *
41     * @param mixed ...$args Arguments passed to fiber function.
42     *
43     * @return mixed Value from the first suspension point or NULL if the fiber returns.
44     *
45     * @throw FiberError If the fiber has already been started.
46     * @throw Throwable If the fiber callable throws an uncaught exception.
47     */
48    public function start(mixed ...$args): mixed {}
49
50    /**
51     * Resumes the fiber, returning the given value from {@see Fiber::suspend()}.
52     * Returns when the fiber suspends or terminates.
53     *
54     * @param mixed $value
55     *
56     * @return mixed Value from the next suspension point or NULL if the fiber returns.
57     *
58     * @throw FiberError If the fiber has not started, is running, or has terminated.
59     * @throw Throwable If the fiber callable throws an uncaught exception.
60     */
61    public function resume(mixed $value = null): mixed {}
62
63    /**
64     * Throws the given exception into the fiber from {@see Fiber::suspend()}.
65     * Returns when the fiber suspends or terminates.
66     *
67     * @param Throwable $exception
68     *
69     * @return mixed Value from the next suspension point or NULL if the fiber returns.
70     *
71     * @throw FiberError If the fiber has not started, is running, or has terminated.
72     * @throw Throwable If the fiber callable throws an uncaught exception.
73     */
74    public function throw(Throwable $exception): mixed {}
75
76    /**
77     * @return bool True if the fiber has been started.
78     */
79    public function isStarted(): bool {}
80
81    /**
82     * @return bool True if the fiber is suspended.
83     */
84    public function isSuspended(): bool {}
85
86    /**
87     * @return bool True if the fiber is currently running.
88     */
89    public function isRunning(): bool {}
90
91    /**
92     * @return bool True if the fiber has completed execution (returned or threw).
93     */
94    public function isTerminated(): bool {}
95
96    /**
97     * @return mixed Return value of the fiber callback. NULL is returned if the fiber does not have a return statement.
98     *
99     * @throws FiberError If the fiber has not terminated or the fiber threw an exception.
100     */
101    public function getReturn(): mixed {}
102
103    /**
104     * @return self|null Returns the currently executing fiber instance or NULL if in {main}.
105     */
106    public static function getCurrent(): ?self {}
107
108    /**
109     * Suspend execution of the fiber. The fiber may be resumed with {@see Fiber::resume()} or {@see Fiber::throw()}.
110     *
111     * Cannot be called from {main}.
112     *
113     * @param mixed $value Value to return from {@see Fiber::resume()} or {@see Fiber::throw()}.
114     *
115     * @return mixed Value provided to {@see Fiber::resume()}.
116     *
117     * @throws FiberError Thrown if not within a fiber (i.e., if called from {main}).
118     * @throws Throwable Exception provided to {@see Fiber::throw()}.
119     */
120    public static function suspend(mixed $value = null): mixed {}
121}
122```
123
124A `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`.
125
126`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}`).
127
128A suspended fiber may be resumed in one of two ways:
129
130* returning a value from `Fiber::suspend()` using `Fiber->resume()`
131* throwing an exception from `Fiber::suspend()` using `Fiber->throw()`
132
133`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.
134
135`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.
136
137---
138
139### ReflectionFiber
140
141`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`.
142
143``` php
144final class ReflectionFiber
145{
146    /**
147     * @param Fiber $fiber Any Fiber object, including those that are not started or have
148     *                     terminated.
149     */
150    public function __construct(Fiber $fiber) {}
151
152    /**
153     * @return Fiber The reflected Fiber object.
154     */
155    public function getFiber(): Fiber {}
156
157    /**
158     * @return string Current file of fiber execution.
159     *
160     * @throws Error If the fiber has not been started or has terminated.
161     */
162    public function getExecutingFile(): string {}
163
164    /**
165     * @return int Current line of fiber execution.
166     *
167     * @throws Error If the fiber has not been started or has terminated.
168     */
169    public function getExecutingLine(): int {}
170
171    /**
172     * @param int $options Same flags as {@see debug_backtrace()}.
173     *
174     * @return array Fiber backtrace, similar to {@see debug_backtrace()}
175     *               and {@see ReflectionGenerator::getTrace()}.
176     *
177     * @throws Error If the fiber has not been started or has terminated.
178     */
179    public function getTrace(int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT): array {}
180
181    /**
182     * @return callable Callable used to create the fiber.
183     *
184     * @throws Error If the fiber has been terminated.
185     */
186    public function getCallable(): callable {}
187}
188```
189
190#### Unfinished Fibers
191
192Fibers 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.
193
194#### Fiber Stacks
195
196Each 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.
197
198## PHP 8.1+
199
200This extension *only* works with PHP 8.0. The [Fiber RFC](https://wiki.php.net/rfc/fibers) 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.
201