-
Notifications
You must be signed in to change notification settings - Fork 7.9k
True async api stable #19142
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
base: master
Are you sure you want to change the base?
True async api stable #19142
Conversation
Initial version of the asynchronous API for PHP. Includes only the API itself and the necessary core changes required for the API to function.
…ized into a method for retrieving any ClassEntry. Now, using this API function, you can obtain the required class entry by a type descriptor type.
Added API functions for coroutine context management: - zend_async_context_set_t: set value by string or object key - zend_async_context_get_t: get value by string or object key - zend_async_context_has_t: check if key exists - zend_async_context_delete_t: delete key-value pair Includes convenience macros for easier usage: ZEND_ASYNC_CONTEXT_SET_STR/OBJ, GET_STR/OBJ, HAS_STR/OBJ, DELETE_STR/OBJ
Added complete Context API implementation: - Updated zend_async_scheduler_register signature to accept context functions - Added context function pointers and registration in zend_async_API.c - Context API supports both string and object keys - Includes convenience macros for easy usage This completes the core API infrastructure for coroutine context management.
- Remove separate context_set, context_get, context_has, context_delete functions - Add single zend_async_new_context_fn function to create context instances - Move context implementation to ext/async module using zend_async_context_t structure - Update scheduler registration to include new_context_fn parameter - Add context field to zend_async_globals_t and zend_coroutine_s - Simplify Context API macros to ZEND_ASYNC_NEW_CONTEXT and ZEND_ASYNC_CURRENT_CONTEXT
% removal of the current Scope from the global structure
… always needs to be explicitly known. Macros like ZEND_ASYNC_CURRENT_SCOPE were updated. A new macro ZEND_ASYNC_MAIN_SCOPE was added for the main Scope.
…main coroutine can correctly return control.
* add macro START_REACTOR_OR_RETURN for reactor autostart
… is now passed to the main coroutine’s finalize method instead of being displayed on screen. * Fixed an issue with correctly passing the exception into the coroutine.
… captures the exception, marking it as handled in another coroutine.
…with the thread pool has been updated. An API for CoroutineGroup has been introduced.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just an initial review without really reviewing the logic - will need to schedule some bigger chunk of time for that later...
Sorry for not asking this question on the list, but what's the motivation for landing this in 8.5? This seems unnecessary if there isn't an intent to use it until 8.6/9.0. |
Good day. The main goal of these changes is to make PHP 8.5 asynchronous today (for as long as this version remains supported). The idea is not to wait for 8.6 or 9.0, but to make it asynchronous now. And this is possible because the implementation is split into two parts. Users of PHP 8.5 will be able to use coroutines and non-blocking I/O through an additional extension. Yes, this extension will remain experimental for some time. This means that, for a while, developers won’t be able to rely on it with 100% certainty. However, once the functions are officially finalized, PHP 8.5 will gain most of the official — and even new —functionality without changing its codebase. In other words, the main value is the ability to support full-fledged asynchrony starting from this version, while continuing to develop additional functionality that doesn’t depend on PHP itself. |
|
||
require "zend_constants.stub.php"; | ||
|
||
class CancellationException implements Throwable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this not just inherit from Exception
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO the naming is off. CancellationException
should either be an instance of Exception
or should become a Cancellation
- new root Throwable
next to Exception
and Error
.
This is especially dangerous if catching Exception
will not catch the CancellationException
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello.
The CancellationException
is intended for situations where execution must be stopped unconditionally.
Such an exception may only be caught for the purpose of rethrowing it.
Therefore, code that attempts to catch Exception must never catch CancellationException.
Incorrect handling of CancellationException is a known issue, and this is a simple way to mitigate it.
The ideal solution would be to introduce a special additional syntax block, similar to finally, but triggered only for CancellationException
.
Such a solution would be logically perfect and clean, but it would require far more changes.
Meanwhile, CancellationException
is a typical solution to this problem and is widely used in other languages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not having a problem with its function, just the naming is confusing. I agree that the type of error represented by the CancellationException
should not be caught by a } catch (\Exception $e)
block. The problem is that, because of the naming clash, reading the code will suggest that it will get caught, due to its name ending with *Exception
. Developers will have to remember this special case and I'm wondering if there is a better way of naming it so that we can avoid introducing this memoization requirement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with what was exposed here. CancellationException
indeed gives the impression that it should be treated as a regular Exception
. If this is an exception that is not meant to be treated (if I understood correctly by Edmond's comment) it reminds me more of something closer to a panic()
in Golang, for example, which basically shuts the entire application execution due to a very exceptional circunstance (even though you still can recover()
from it).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would changing it to CancellationError
make more sense? Or is *Error
too much and we'd need something inbetween?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By author's description it would need to be something in between. What is confusing to me is that he says "The CancellationException
is intended for situations where execution must be stopped unconditionally. Such an exception may only be caught for the purpose of rethrowing it" and what it translates to me is that it is something that should be, in some sense, "throwable" (like an Exception/Error) but not totally "treateable" (such a Fatal Error or a panic()
in golang which is recoverable in some situations, but not all of them).
However, at the same time, the CancellationException
class implements Throwable
which, theoretically, allows a try...catch
block to capture it with a catch(Throwable $e)
, right? Which makes it "treateable". So, in order to name it properly, I think we should understand what the author expects from this implementation:
- Should "CancellationException" be treatable/catchable in a
try...catch
block or not? - If not, definitelly it must be something else cause anything that is throwable can be captured in a
try...catch
block. Fatal Error? A panic equivalent new category of error in PHP? Don't know... but definitely not a "Throwable" implementation. - If it's not a big deal to capture and handle this exception and there's only some weird edge cases where it would be bad to handle it (or it should be handled in a very particular way) I think it should inherit from
Exception
and the proper behaviour be document to let the developers know what needs to be done when handling this particular type.
Another thing is that I'm truly curious why handle this CancellationException
as a normal Exception
would be a problem. From the outside, I cannot imagine a reason for not being a regular exception in the language.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can use CancelledError, like in Python. However, I’m not aware of any research showing that one clear name makes code better than another equally clear name.
Although modern languages don’t have an explicit division between catchable and uncatchable exceptions, such a distinction exists implicitly by convention.
From a semantic standpoint, operation cancellation has all the characteristics of an exception: it interrupts code execution, carries a stack trace, and so on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The thing is that the name CancellationException
suggests that it inherits from the root Exception
class, which is not true. This is far from being clear, at least for me :)
Coming from Java, I tend to lean to their understanding of the division between Exceptions
and Errors
(both originating from Throwable
).
Exception
is generally a kind of error you can recover from. Error
, on the other hand, is generally not recoverable. You can intercept both of them by catching a Throwable
, but in case of an Error
there is usually very little we can do, except logging it and (if it's even possible at this point) notifying external systems about it, and then rethrowing it. Usually at this point JVM is cooked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand your logic.
But if we follow this path, then we might as well not implement this class here at all, and I can remove it from the PR.
Instead, we inherit CancellationError
from the Error
class.
What do you think?
TrueAsync engine API
The TrueAsync engine API defines a pluggable interface that lets extensions register different async backends while the core supplies standardized primitives.
Key Components
PR for https://wiki.php.net/rfc/true_async_engine_api