Skip to content

Implement Fibers #6875

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

Closed
wants to merge 43 commits into from
Closed

Implement Fibers #6875

wants to merge 43 commits into from

Conversation

trowski
Copy link
Member

@trowski trowski commented Apr 16, 2021

Implementation of the Fibers RFC.

@trowski trowski added the RFC label Apr 16, 2021
@trowski trowski force-pushed the fibers branch 9 times, most recently from 2d09d0d to 69e1cad Compare April 19, 2021 00:50
@nikic
Copy link
Member

nikic commented Apr 19, 2021

<?php
new class() {
    function __destruct() {
        $fiber = new Fiber(static function() {
            Fiber::suspend();
        });
        $fiber->start();
        throw new Exception;
    }
};

Zend/zend_exceptions.c:994: void zend_throw_fiber_exit(void): Assertion `!(executor_globals.exception)' failed.

Fatal error: Uncaught Exception: test in %sfailing-fiber.php:%d
Stack trace:
#0 [internal function]: {closure}()
#1 {main}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, this trace is not super clear to me. Does {main} here refer to the top of the fiber, and the caller of resume() is not present at all?

I feel like at the very least there should be an indication that we're inside a fiber here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way we could change [internal function] to something like [fiber] instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One suggestion I have is swapping {main} to {fiber} in the exception backtrace: trowski@8265319

I attempted to save a pointer the call stack invoking start, but a fiber is able to long outlast the stack that may have started it. It's also undesirable to swap the stack trace on each resume, as this will drop the backtrace context in the fiber, which leads to the terrible backtraces we often had with Generators in Amp.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One suggestion I have is swapping {main} to {fiber} in the exception backtrace: trowski/php-src@8265319

Adding a bool for this seems really ad-hoc ... would it be valuable for something if the exception contains a reference to the containing Fiber object outright?

I attempted to save a pointer the call stack invoking start, but a fiber is able to long outlast the stack that may have started it. It's also undesirable to swap the stack trace on each resume, as this will drop the backtrace context in the fiber, which leads to the terrible backtraces we often had with Generators in Amp.

Not sure I follow. To be clear, what I would expect is the backtrace at the point of resume, not at point of start. For generators, this is implemented with a dummy frame that gets resolved when the backtrace is actually fetched. Why are the resulting backtraces "terrible"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nikic Including resume() doesn't really make sense, because you want the complete fiber stack there, where resume() would be somewhere in between. But having {main} there is really weird, I guess it should be {fiber}.


public function isRunning(): bool {}

public function isTerminated(): bool {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need these methods on ReflectionFiber if they're already available on Fiber?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RFC did include them, but you can just as easily do $reflection->getFiber()->isSuspended(). I'm fine with removing them, we can always add them back.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we instead skip ReflectionFiber entirely and add the methods to Fiber?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That might make sense. The current split is basically just "these are only used for debugging"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, basically.

@trowski
Copy link
Member Author

trowski commented Apr 19, 2021

<?php
new class() {
    function __destruct() {
        $fiber = new Fiber(static function() {
            Fiber::suspend();
        });
        $fiber->start();
        throw new Exception;
    }
};

Zend/zend_exceptions.c:994: void zend_throw_fiber_exit(void): Assertion `!(executor_globals.exception)' failed.

Fixed in f062904.

@nikic
Copy link
Member

nikic commented Apr 20, 2021

The observer functionality should be tested via zend_test.

@trowski
Copy link
Member Author

trowski commented Apr 21, 2021

The observer functionality should be tested via zend_test.

I moved the observer functionality to zend_observer and added tests in zend_test.

@trowski trowski force-pushed the fibers branch 3 times, most recently from e38740f to f96610f Compare April 21, 2021 22:23
@trowski
Copy link
Member Author

trowski commented Apr 23, 2021

This issue still exists, just in different variations, e.g. this one causes use-after-free

Can you double check this is fixed now? I'm not seeing it anymore, but I want to be sure you're not either.

@nikic
Copy link
Member

nikic commented Apr 23, 2021

This issue still exists, just in different variations, e.g. this one causes use-after-free

Can you double check this is fixed now? I'm not seeing it anymore, but I want to be sure you're not either.

Yeah, this works fine now.

@nikic
Copy link
Member

nikic commented Apr 23, 2021

This will need a small rebase due to some conflicting error handling changes I've made.

@trowski
Copy link
Member Author

trowski commented Apr 25, 2021

This is using boost context 1.76. However, I'm only bundling the files that can be resolved by configure. I added the s390x files, looks to work fine.

@trowski trowski closed this in c276c16 Apr 26, 2021
@trowski trowski deleted the fibers branch May 6, 2021 15:57
@bwoebi
Copy link
Member

bwoebi commented Sep 13, 2021

While reviewing the observers handling, I've noticed that first_observed_frame and current_observed_frame are unfortunately global, as well as zend_observer_fcall_end_all only affecting a single fiber, instead of them all.

In PHP 8.0 there's a guarantee that all begun observer callback also will have the end callback called. With fibers this seems to not be true in a bailout case at least.

@nikic
Copy link
Member

nikic commented Sep 13, 2021

Can we kill zend_observer_fcall_end_all() and let the extension deal with it?

@bwoebi
Copy link
Member

bwoebi commented Sep 13, 2021

I'm pretty unsure about this. I guess an extension can keep track of all currently begun observers, prepend a custom internal callback to BG(user_shutdown_function_names) and then cleanup itself. This most probably is the least error prone way. This first_observed_frame and current_observed_frame seems really susceptible for being invalid...
But obviously only an option for PHP 8.1. Let me also ask @morrisonlevi whether he has some input here.

@morrisonlevi
Copy link
Contributor

Can we kill zend_observer_fcall_end_all() and let the extension deal with it?

Not sure I understand. It is important to the design of observers that each extension doesn't have to handle weird cases where they have to end things manually. Are you asking to move this out of the engine and into every extension? If so, I must object. These edge cases change over time, as the new fibers case show. We didn't even make it one minor version without a new edge case!

@nikic
Copy link
Member

nikic commented Sep 13, 2021

I would expect this to be a lot easier to handle for an extension, because it knows which open internal state it has that needs to be closed. It doesn't need to integrate with any of the actual observer infrastructure.

I grudgingly accepted this hack back then on the premise that it at least won't hurt, but that clearly hasn't held up. If there is no robust implementation of this functionality (and no, @bwoebi's recent commit just adds one more hack on top of the hack) then I'm not willing to provide it at all.

@SammyK
Copy link
Contributor

SammyK commented Sep 13, 2021

If there is no robust implementation of this functionality

Zooming out, the only robust solution I see is to sunset zend_bailout across the board.

@bwoebi
Copy link
Member

bwoebi commented Sep 13, 2021

Yep, my fix is just for an actual issue people are facing, I'm definitely not happy with this as a long term solution.
On the topic of moving this to the extension, it should be pretty easy at least for my extension to work around this.

@SammyK I really don't know how doable sunsetting zend_bailout is ... I mean, any OOM will trigger a bailout for example, but shutdown sequence shall still happen.

@SammyK
Copy link
Contributor

SammyK commented Sep 13, 2021

OOM will trigger a bailout

What is blocking OOM from doing a clean shutdown similar to unwind exit?

@morrisonlevi
Copy link
Contributor

For PHP 8.1 I think we should try to play whack-a-mole with the issues and keep zend_observer_fcall_end_all. For v8.2, we should make a list of all zend_bailout calls and decide whether 1) we consider that a legitimate usage or if it should be migrated to a different error handling mechanism and 2) if the ones that remain are sufficiently edge-case that we can accept not calling observers' .end for these. What does everyone think about that?

@SammyK
Copy link
Contributor

SammyK commented Sep 13, 2021

For v8.2, we should make a list of all zend_bailout calls and decide

I like this. FWIW I started working on a big "sunsetting zend_bailout" doc earlier this year but lost steam on it. Maybe when winter hits I'll get bored and start working on it again. 😄

@bwoebi
Copy link
Member

bwoebi commented Sep 13, 2021

What is blocking OOM from doing a clean shutdown similar to unwind exit?

OOMs can happen in midst of any function basically, and all our code assumes that emalloc() will return a valid chunk of memory. So basically everything would horribly break if we were to return an invalid pointer (incl. NULL).

@SammyK
Copy link
Contributor

SammyK commented Sep 13, 2021

all our code assumes that emalloc() will return a valid chunk of memory

Bah. Good point. We can revisit this issue while trying to find a path to sunset zend_bailout a bit later.

jrfnl added a commit to PHPCompatibility/PHPCompatibility that referenced this pull request Mar 9, 2022
> Support for Fibers has been added.

I've gone through all PRs I could find related to this and these were the only classes I could find to account for.

Includes unit tests.

Refs:
* https://www.php.net/manual/en/migration81.new-features.php#migration81.new-features.core.fibers
* https://wiki.php.net/rfc/fibers
* https://www.php.net/manual/en/language.fibers.php
* php/php-src#6875
* php/php-src@c276c16
jrfnl added a commit to PHPCompatibility/PHPCompatibility that referenced this pull request Mar 9, 2022
> Support for Fibers has been added.

I've gone through all PRs I could find related to this and this was the only in directive I could find to account for.

Includes unit tests.

Refs:
* https://www.php.net/manual/en/migration81.new-features.php#migration81.new-features.core.fibers
* https://wiki.php.net/rfc/fibers
* https://www.php.net/manual/en/language.fibers.php
* php/php-src#6875
* php/php-src@c276c16
jrfnl added a commit to jrfnl/doc-en that referenced this pull request Mar 11, 2022
> Added `CURLStringFile`, which can be used to post a file from a string rather than a file

Refs:
* https://github.com/php/php-src/blob/f67986a9218f4889d9352a87c29337a5b6eaa4bd/UPGRADING#L231-L235
* php/php-src#6456
* php/php-src@e727919

As part of the Fibers RFC/PR, the Reflection extension also received a new class.

* https://wiki.php.net/rfc/fibers
* php/php-src#6875
* php/php-src@c276c16

Note: I've not listed the `Fiber` and `FiberError` classes as those could be considered covered via the mention of Fibers on the "New Features" page, though happy to update the commit if so desired.
cmb69 pushed a commit to php/doc-en that referenced this pull request Mar 11, 2022
> Added `CURLStringFile`, which can be used to post a file from a string rather than a file

Refs:
* https://github.com/php/php-src/blob/f67986a9218f4889d9352a87c29337a5b6eaa4bd/UPGRADING#L231-L235
* php/php-src#6456
* php/php-src@e727919

As part of the Fibers RFC/PR, the Reflection extension also received a new class.

* https://wiki.php.net/rfc/fibers
* php/php-src#6875
* php/php-src@c276c16

Note: I've not listed the `Fiber` and `FiberError` classes as those could be considered covered via the mention of Fibers on the "New Features" page, though happy to update the commit if so desired.

Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>

Closes GH-1447.
tiffany-taylor pushed a commit to tiffany-taylor/doc-en that referenced this pull request Jan 16, 2023
> Added `CURLStringFile`, which can be used to post a file from a string rather than a file

Refs:
* https://github.com/php/php-src/blob/f67986a9218f4889d9352a87c29337a5b6eaa4bd/UPGRADING#L231-L235
* php/php-src#6456
* php/php-src@e727919

As part of the Fibers RFC/PR, the Reflection extension also received a new class.

* https://wiki.php.net/rfc/fibers
* php/php-src#6875
* php/php-src@c276c16

Note: I've not listed the `Fiber` and `FiberError` classes as those could be considered covered via the mention of Fibers on the "New Features" page, though happy to update the commit if so desired.

Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>

Closes phpGH-1447.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants