rfc:fiber

PHP RFC: Fiber

Introduction

Fibers are primitives for implementing light weight cooperative concurrency in PHP. Basically they are a means of creating Closure that can be paused and resumed. The scheduling of fiber must be done by the programmer and not the VM.

As opposed to Generator (a stackless light weight concurrency implementation), each fiber comes with a stack. This enables the fiber to be paused from deeply nested function calls within the fiber block.

When a fiber is created it will not run automatically. Rather it must be explicitly asked to run using the Fiber::resume method. The code running inside the fiber can give up control by calling Fiber::yield in which case it yields control back to caller (the caller of the Fiber::resume).

Proposal

Why not make it as a Extension?

Implementation

final class Fiber {
  public const STATUS_SUSPENDED = 1;
  public const STATUS_RUNNING   = 2;
  public const STATUS_FINISHED  = 3;
  public const STATUS_DEAD      = 4;
 
  public function __construct(callable $callable = null, int stack_size = null) {}
 
  public static function yield($arg1) {}
  public function resume($arg1...) {}
  public function throw(Throwable $e) {}
 
  /** @throws \UnexpectedValueException */
  public function __wakeup(): void {}
 
  /** @throws \Error */
  private function __clone() {}
}
function sub1()
{
	return Fiber::yield(1);
}
$fiber = new Fiber(function ($a, $b) {
	$c = Fiber::yield($a + $b);
 
	$d = sub1();
	return $d.$c;
});
 
echo $fiber->resume(1, 2);     // echo 3
echo $fiber->resume("world");  // echo 1
echo $fiber->resume("hello "); // echo "hello world"

Backward Incompatible Changes

“Fiber” are now globally defined classes, which might collide with user defined classes of the same name in the global namespace. However, the risk of the introduction of them is considered to be very low, since the global namespace should not be used by PHP users.

Proposed PHP Version(s)

7.3

RFC Impact

To SAPIs

None

To Existing Extensions

None

To Opcache

None

New Constants

None

php.ini Defaults

- fiber.stack_size default stack size for A fiber

Open Issues

What happens if there are internal calls on the call stack?Say something like array_map(function() { await; }, [1, 2, 3]); inside a fiber. Internal calls (using the C stack rather than the VM stack) are usually the problem with this kind of endeavor.

Fiber does not support yielding during the internal call. Calling Fiber::yield in a internal call will cause core dump. However, avoiding yield in the internal still make sense and useful

How do you determine when a fiber has returned? Looking at the source, it appears Fiber::status() must be used, comparing against constants. Separate methods similar to Generator would be better.

Offering methods like Fiber::alive, Fiber::running makes no difference to check the Fiber::status() return value. This is just a style issue. And as a language feature, Fiber only offer the essential API and let other works to the user land.

What about throwing exceptions into a fiber?

The Fiber::throw(Exception $exception) has been implemented.

Using Fiber::resume() to initialize the fiber and resume feels awkward. Separate methods again would be better here, perhaps Fiber::init(…$args) and Fiber::resume($send).

Both Ruby's Fiber and Lua's coroutine using one resume API to init and resume their coroutine. There is no need to offer a dedicate init API.

What happens if the sub1() function in the RFC is invoked outside of a fiber?

You will get a Fatal Error like

Fatal error: Uncaught Error: Cannot call Fiber::yield out of Fiber

I think a keyword here would be beneficial, even if it has a minor BC impact. Fibers could then be written like generators. `await` or `emit` as a keyword perhaps? This would be a less verbose API, feel less magical (a static method call that actually pauses execution feels out of place), and would allow Fibers to be returned from methods, named functions, etc with less boilerplate.

Introducing new keywords like await/emit does not offer any essential benefit but cause BC impact.

Both Ruby's Fiber and Lua's coroutine use method to pause and resume their coroutine. There is no need to introduce new keyword.

Unaffected PHP Functionality

None

Future Scope

This sections details areas where the feature might be improved in future, but that are not currently proposed in this RFC.

Proposed Voting Choices

Simple 50%+1 majority vote.

Patches and Tests

coming soon. Current developing at https://github.com/fiberphp/fiber-ext

Implementation

After the project is implemented, this section should contain

  1. the version(s) it was merged to
  2. a link to the git commit(s)
  3. a link to the PHP manual entry for the feature
  4. a link to the language specification section (if any)

References

Rejected Features

Keep this updated with features that were discussed on the mail lists.

rfc/fiber.txt · Last modified: 2018/02/10 15:47 by lvht