-
Notifications
You must be signed in to change notification settings - Fork 7.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve fiber interoperability #7128
Conversation
@kooldev Any feedback on this before I request another review? I did attempt to implement this using a pointer to the previous context and following the list of previous pointers back to the fiber context, but it is possible for context switches to create a circle that no longer includes the context linked to the |
I think it is fine to have special handling for $fiber = new Fiber(function () {
$test = async(function () {
AsyncTask::yield(); // Yields back to $fiber
// 123 is returned to $fiber->start(), multiplied by 2 and sent back as 246.
// Afterwards 246 is the coroutine result and sent into await().
return Fiber::suspend(123);
});
AsyncTask::yield(); // Starts $test coroutine
var_dump($result = await($test)); // int(246)
return $result / 2; // Becomes the return value of $fiber
});
$fiber->resume(2 * $fiber->start());
var_dump($fiber->getReturn()); // int(123) This seems to be correct behavior but it is really hard to keep track of control flow... $defer = new Deferred();
$t = async(function (Awaitable $a) {
$fiber = new Fiber(function () {
return Fiber::suspend(123); // Sends 123 as return value of $fiber->start()
});
// Receives 123 from $fiber and multiplies it with 2.
// Result is sent into $fiber and becomes the fiber return value.
$fiber->resume($fiber->start() * await($a));
return $fiber->getReturn(); // Return value is 246
}, [ $defer->getAwaitable() ]);
AsyncTask::yield(); // Starts coroutine $t
$defer->resolve(2); // Resolves awaitable $a with the value 2
var_dump(await($t)); // int(246) @trowski Is this the kind of behavior you are expecting? |
@kooldev Yes, this exactly the behavior I was expecting. Mixing different schedulers will probably be terrible due to the schedulers conflicting. It's like trying to use multiple event loops – one will probably block the other, but at least things won't crash. Note that above I was using the term "scheduler" somewhat broadly. A simple push/pull I wanted to be sure a scheduler + async/await could coexist with Do I anticipate anyone will actually use fibers within code designed for async/await like you did above? No, not really… the control flow is awful. 🤣 |
16f46f1
to
3071650
Compare
Adds another global,
EG(active_fiber)
to track the instance ofFiber
that is active, regardless of the currently runningzend_fiber_context
. This allows alternative implementations to not interfere with the operation ofFiber
as well as allowingFiber
instances to contain such instances (and vice-versa), switching between fiber contexts as determined by each implementation.Consider a
TestFiber
that functions identically toFiber
(this is just for example purposes, a custom implementation would likely exist to schedule fibers differently thanFiber
). The above example switches back to{main}
whenFiber::suspend()
is called within theTestFiber
, as theFiber
instance was started from{main}
, not within the fiber running forTestFiber
.A custom fiber implementation is then able to schedule fibers using their own mechanism without a call to
Fiber::suspend()
potentially breaking scheduling or invalidating the custom fiber context.If in the future, PHP includes an internal scheduler (event-loop) to create and schedule fibers, this PR will allow
Fiber
to mix with such a scheduler. Custom user space schedulers ofFiber
instances will continue to work, even if interrupted by the built-in scheduler.