Skip to content

Don't overwrite unwind exception #7459

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 1 commit into from

Conversation

nikic
Copy link
Member

@nikic nikic commented Sep 3, 2021

This has been suggested by @twose. When killing a coroutine by throwing an unwind exit into it during an I/O operation, the I/O failure may result in an exception being thrown, which will replace the unwind exit exception and the coroutine will ultimately not exit. This patch avoids this by ignoring the newly thrown exception and keeping the unwind exit exception.

@trowski
Copy link
Member

trowski commented Sep 5, 2021

Would it be feasible to retain the exception in some way so it could be optionally rethrown where the fiber/coroutine is being destroyed?

@twose
Copy link
Member

twose commented Sep 6, 2021

Would it be feasible to retain the exception in some way so it could be optionally rethrown where the fiber/coroutine is being destroyed?

We could just force-resume the coroutine which is in IO operation to cancel it instead of killing it by unwind_exit(), then the IO exception will be thrown, user can catch it normally. So I do not think retain the exception when we kill a coroutine with unwind_exit() make sense, and we have also many other ways to interrupt a coroutine like $coroutine->throw(), at least in Swow it is.
Or you can give a practical use case to help me understand this.

@trowski
Copy link
Member

trowski commented Sep 6, 2021

We could just force-resume the coroutine which is in IO operation to cancel it instead of killing it by unwind_exit()

Assuming you know the current state of the coroutine, sure, but that may not be the case.

External code may not know where a coroutine was suspended, just that it is needs to be destroyed (assuming GC here, correct me if I'm wrong). If another exception is raised during fiber/coroutine destruction, it might be useful to have that information so it can be rethrown/handled/etc.

@twose
Copy link
Member

twose commented Sep 6, 2021

Confused part: It seems that we have banned suspending coroutines in GC?

I am not totally opposed to keeping exception information, because I just need a way to kill a coroutine silently like send SIGKILL to processes.

The intuitive way for me is that after I called exit() the process/thread/coroutine will definetly exit and I always do not care about state of it anymore because I think it's dead/released (or at most some warnings were thrown), of course I know that we cannot guarantee this behaviour in PHP... For me, it would be great if we can guarantee that the exit will always be completed successfully, It's as if I prefer functions with void return type...

And, on the one hand, I cannot find a way to construct this situation for the time being (but it may indeed happen), on the other hand, I don’t know how you want to store these exception information?

@@ -175,6 +175,12 @@ ZEND_API ZEND_COLD void zend_throw_exception_internal(zend_object *exception) /*

if (exception != NULL) {
zend_object *previous = EG(exception);
if (previous && (zend_is_unwind_exit(previous) || zend_is_graceful_exit(previous))) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe this should be limited to unwind_exit only, and leave graceful_exit alone?

Copy link
Member

Choose a reason for hiding this comment

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

So, I may treat unwind_exit() as SIGKILL and graceful_exit() as SIGTERM, it seems reasonable for me in a way... graceful_exit() could be aborted due to other accidents.

Copy link
Member

Choose a reason for hiding this comment

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

@nikic This is limited to internal/extension code, yes? User code would not be able to throw another exception and replace the exception while destroying a 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.

User code can throw an exception in finally. Though that would work independently of this code.

Copy link
Member

@trowski trowski Sep 6, 2021

Choose a reason for hiding this comment

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

Seems I had a test for the user code behavior. Obviously this didn't affect that and since this only concerns throwing an exception internally, never mind about my question of storing the exception.

@nikic I like your suggestion of only preventing replacement of unwind_exit.

@nikic nikic force-pushed the unwind-exit-overwrite branch from 2c57b05 to e16e8c7 Compare September 6, 2021 14:11
@twose
Copy link
Member

twose commented Sep 7, 2021

If it doesn't break anything, is it possible to backport it to all PHP-8.x versions?

@nikic nikic closed this in 85b80c5 Sep 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants