Hello!
> Why is it so difficult to introduce asynchronous or coroutine features in PHP?
It’s indeed quite a complex component, and other programming languages
have spent a lot of effort and resources to implement it. And Java,
for example, is still working on it to this day.
Go has a serious adva
Good day everyone.
Today I’d like to present an updated RFC related to True Async.
This RFC proposes changes only to PHP core and does not affect any
user-facing features.
https://wiki.php.net/rfc/true_async_engine_api
The main goal of the RFC is to introduce a backend for asynchronous
function
Hello, Carlos!
Thank you for your support.
I feel like I should add a bit of jam to this story and share some news.
When I set the two-week limit for the vote, Roman Pronskiy reached out
to me almost immediately and offered direct contact with PHP CORE.
I received maximum support and valuable adv
Hello, everyone.
While the code is being developed and changes for the RFC are in
progress, I’d like to share a real-world situation.
This is one of those cases that once again proves why software design
cannot happen without writing real code — why architecture is
impossible without practice.
`
Hello, Michael.
Thank you very much for your work. It's very interesting.
Let me look at this idea from an architectural perspective.
Did I understand correctly from the text that:
* Modules control which entities are public and which are not?
* During import, we specify which entities we want
Hello Bob.
> So you effectively will have to manage a weakmap of next-stack-frame to
> exception objects.
Yes, that was exactly my first idea — to use a WeakMap from
exceptions. Now it seems that copying part of the frame information
will use less memory. But that's not certain :)
> Of note is
Hello, Rob!
> For what it is worth, the stack trace is not generated in the constructor (at
> least from the perspective of the developer).
This is a design feature. In PHP, call frames for PHP functions are
stored in a separate stack. However, they are still stored following
the stack (LIFO) pr
Good day, everyone.
In the neighboring thread Concept: Lightweight error channels, the
topic of exception performance was discussed.
The main issue is the immediate backtrace generation in the
constructor, which can cause up to a 50% performance loss compared to
return (as I understood from the d
Hello.
> Suppressing? You mean fataling. Suppressing implies ignore, which is the
> exact opposite of what I am proposing.
Yes, it seems I didn’t explain my thought clearly.
We have the following code:
```php
function some(): bool
{
return false;
}
function target(): void
{
if(some()
> I have already demonstrated how it can be solved without generics.
Multiple response channels from a function already exist: Normal returns
and exceptions. Exceptions as currently designed are just very poorly
suited to the problem space I am describing.
If another error channel is introduced,
>
> > 3) Implement a new LightException (or similar name) with no args in
> getTrace().
>
> + or 4) Deferred backtrace mechanism:
>
> 1. Does not compute the backtrace when the exception is created.
> 2. The backtrace is fully computed only if the exception is caught using a
> catch block or by a
> 3) Implement a new LightException (or similar name) with no args in
getTrace().
+ or 4) Deferred backtrace mechanism:
1. Does not compute the backtrace when the exception is created.
2. The backtrace is fully computed only if the exception is caught using a
catch block or by a global handler w
Hello all.
> Yes. And even if it can be made faster (as it looks like Niels is doing,
which is great), it will never be as fast as an empty constructor and a
return. That's the level I'm proposing.
If the backtrace is generated only when needed, rather than at the moment
the exception is create
The HHVM project suggests an interesting Lazy backtrace generation
mechanism based on stack unwinding.
1.
An exception is generated.
2.
The stack enters an unwinding mode. In this mode, PHP gradually collects
information into the backtrace.
3.
Each frame exit contributes a
Hello, Rob.
> I was quite surprised at how *impossible* it is.
```c
static zend_object *zend_default_exception_new(zend_class_entry
*class_type) /* {{{ */
{
if (EG(current_execute_data)) {
zend_fetch_debug_backtrace(
```
It's not that this is 100% impossible. PHP is a language with an
Good afternoon, Larry.
Looking at the comparison table, it seems that the two most important
differences are:
1.
Backtrace consumes a lot of resources.
2.
There is an explicit contract for exceptions thrown by a function.
3.
I didn't fully understand the point about the excep
Good day, Deleu!
> What happens if the coroutine didn't finish execution? does
`disposeSafely()` means that it will wait until completion to safely clear
it up or does it mean it will attempt to dispose and throw an exception if
it fails to do so?
Cooperative cancellation implies that a coroutine
Hello, Rowan!
> I don't have time to dig into the details of this draft at the moment,
but will say that a vote in two weeks is not at all realistic.
I just used the Flow from the Wiki. :)
> My only other comment at a very quick glance is that I see the Context
section is still included, and stil
Hello, Vincent.
> The `spawn` keyword might disqualify this rfc, i talked to alot of php
devs about this rfc and they all had a similar complaint about the keyword,
maybe a keyword choice poll might help when the rfc voting goes live.
To have any kind of vote, we need multiple options. The keywor
> The link in Patches and Tests is currently giving a 404 Not Found.
> Can you update the link to your proof of concept implementation?
Thank you for pointing that out. I’ve corrected the link.
https://github.com/EdmondDantes/php-src/tree/async/ext/async
Good day, everyone. I hope you're doing well.
I’d like to introduce the RFC for the True Async component.
https://wiki.php.net/rfc/true_async
This time the work took longer because I was exploring different
architectural options and paid more attention to how it works in other
languages.
I was
Continuing the discussion from [[PHP-DEV] PHP True Async RFC - Stage 2](
https://discourse.thephp.foundation/t/php-dev-php-true-async-rfc-stage-2/1573/16
):
[quote="Crell, post:16, topic:1573"]
// Creates an async scope, in which you can create coroutines.
[/quote]
Yes, I understand what this is
>
> "Cheating" in the sense that you wrote out a "general syntax",
>
I got it.
> That's not the problem; the problem is that the following are all
equivalent expressions:
> (((foo(
In principle, this is not a problem because the expression in parentheses
preceded by `spawn` is unambiguously
Oops, I made a mistake in the logic of `Scope` and coroutines.
According to the RFC, the following code behaves differently:
```php
currentScope()->spawn ... // This coroutine belongs to the Scope
spawn ... // This one is a child coroutine
```
I was sure that I had checked all the major edge cas
Good day, everyone.
Just a ping email — I haven’t disappeared, development is ongoing.
Since the task has a high level of interdependency, I have to cautiously
try different combinations. The second-to-last version, in trying to
satisfy all requirements, turned out too complex to be taken serious
Hello everyone,
It's a nice Sunday evening, and I'd like to share some updates and thoughts
from this week — kind of like a digest :)
1. Big thanks to Rowan Tommins for the syntax suggestions, ideas, and
feedback. I decided to try using the `spawn block` syntax, and in practice,
it turned out to
>
> You're cheating again - you've put an extra pair of brackets around one
> expression and not the other, and assumed they'll work differently, but
that's
> not the grammar you proposed.
>
Why am I cheating?
> spawn (getClosure());
This is an honest statement, provided that the second parenthese
Hello.
>
> So as an outline, I would recommend:
>
Yes, your suggestion is interesting. I'll think about that section next
week.
However, I can say that I’ve already dropped the “philosophy” section. I
decided to move it into a separate article that will be available as an
artifact.
Such articles
>
> You already explicitly await all fibers spawned in the generateReport
function, you get all the data you need, any extra spawned fibers should
not interest you for the purpose of the logic of generateReport.
>
In this specific code, it only awaits the tasks it has launched itself.
So, if anot
>
> Again, that's the *how*, not the *why*. We only need to "get rid of" the
parentheses if there's some reason to type them in the first place.
>
I’ve already understood that. You mean it’s not the reason, but one of the
possible solutions.
But it’s one of the solutions that, from my point of vie
Good day, everyone.
As I write more code examples, I'm starting to get annoyed by the verbosity
of the `spawn in $scope` construct—especially in situations where all
spawns need to happen within the same context.
At the same time, in 80% of cases, it turns out that explicitly defining
`$scope` is
>
> Nitpick to make sure we're talking about the same thing: What does
"Separate execution context" mean here? Because a keyword whose
description includes "and" is always a yellow flag at least.
>
At the language abstraction level, we can say that spawn performs a single
operation: it creates a
>
> This example highlights one of the concerns I have with fibers and this
approach in general. That example will still execute synchronously, taking
file_get_contents() * 3, even though it is in a coroutine function.
>
Is that really a problem? If a programmer wrote the code `$x = 1 / 0`, then
> In terms of the grammar, it is a special case of #1
yes.
> This example maybe helps explain why this might be surprising:
> spawn $action;
Aha, so if I can write `spawn closure`, why can't I do the same with a
variable?
Yes, this creates an inconsistency.
that's to be expected since the parent
>
> await $some limit 5s;
>
Yes, limit is also a good keyword.
And some else examples with "until":
**CancellationExp**:
- A variable of the `Awaitable` interface
```php
$cancellation = new Future();
$result = await $coroutine until $cancellation;
```
- A function that returns an Awai
Hello everyone,
I’d like to ask for your help regarding the syntax.
**Goal:** I want to get rid of the `BoundedScope` object while still
providing a convenient built-in tool for controlling wait time.
To achieve this, we could extend the `await` expression so that it allows
explicitly specifying
This is simply a wonderful explanation. I will be able to go through each
point.
But before that, let's recall what spawn essentially is.
Spawn is an operation that creates a separate execution context and then
calls a function within it.
To perform this, spawn requires two things:
1. **callable**
>
> But even though we're talking in circles about why,
> your latest examples do avoid the particular problem I was trying to
describe.
>
I thought the problem was that the syntax wouldn't work. Is there any other
issue?
>
> Yes, that would probably be a bad choice as well. Which is why I've
rep
>
> When even the official language documentation is telling you in ALL CAPS
to not use something, you automatically know it’s a major footgun which has
already been abused by newbies.
>
This is a compelling example of why you should not use the await
currentScope() construct. Thank you.
>
>
> Please, don't use word operator in this context. It's a keyword,
> statement or language construct, but not operator. It's important
> especially when you write an RFC.
>
Thank you so much for paying attention to this!
>
> Generally, RFCs are for changes in the language itself, not for API
contracts in C. That can generally be handled in PRs, if I understand
correctly.
>
I thought this was handled by PHP INTERNAL.
So I have no idea how it actually works.
>
> or other weird shenanigans? I think it would be bet
Continuing the discussion from [[PHP-DEV] PHP True Async RFC - Stage 2](
https://discourse.thephp.foundation/t/php-dev-php-true-async-rfc-stage-2/1573/24
):
[quote="Rowan_Tommins_IMSoP, post:24, topic:1573"]
I'm still confused why you started talking about how to implement "defer".
Are you saying
>
> spawning in a scope a "second-class citizen" if `spawn foo($bar);`
>
Reminds me of *"post-purchase rationalization"* or the *"IKEA effect".*
when effort has already been invested into something, and then suddenly,
there's a more convenient way. And that convenient way seems to devalue the
fi
Hello, Larry.
>
> First off, it desperately needs an "executive summary" section up at the
top.
> There's a *lot* going on, and having a big-picture overview would help a
ton. (For
> examples, see property hooks[1] and pattern matching[2].)
>
I looked at the examples you provided, but I still don
# BoundedScope
I tried to refine the `BoundedScope` class to its logical completeness,
considering your feedback.
However, I no longer like it because it now resembles an advanced
`ComposeFuture` or `BoundedFuture` (I'm not even sure which one).
There is no doubt that such functionality is needed
Hello.
In this email, I will focus only on the syntax because it is a separate and
rather complex topic.
First, the RFC does not clearly describe the syntax, which needs to be
fixed.
Second, you are right that methods and operators cause confusion.
However, I really liked the `$scope->spawn()` co
Hello, Vincent.
>
> Personally, i love the formal RFC for it's low level accessibility and
this new RFC isn't that bad.
>
If you mean classes like SocketHandle and so on, then the low-level API can
be available as a separate extension.
>
> The `spawn` keyword maybe the right keyword to use but
> What is this?
I mean structured concurrency:
https://wiki.php.net/rfc/true_async#structured_concurrency
> Spawning a child thread means you don't care about if it will ever
finish.
In the context of this RFC, the parent limits the execution time of child
coroutines. Does this mean that the verb *spawn* is not the best choice?
Just in case, I'll state this explicitly.
The current RFC does not remove features from the previous version; rather,
it represents its high-level part, with structural concurrency added. It
has been reduced in size, making it easier to discuss.
>From an implementation perspective, it seems that a
Hello.
>
> Just one quick question for now; why is `suspend()` a function and not a
statement?
>
Yes, suspend() is a function from the Async namespace.
I couldn't find any strong reasons to define it as an operator:
```php
suspend();
// vs
suspend;
```
For example, the spawn operator makes the c
Good day, everyone. I hope you're doing well.
https://wiki.php.net/rfc/true_async
Here is a new version of the RFC dedicated to asynchrony.
Key differences from the previous version:
* The RFC is not based on Fiber; it introduces a separate class
representation for the asynchronous context.
* A
Hello!
In modern PHP, the garbage collector (GC) is not a mandatory component,
provided that the code is written without circular dependencies. As soon as
an object loses its last reference (i.e., its reference count reaches
zero), PHP immediately frees the resources. In this sense, you can think
>function par_map(iterable $it, callable $c) {
> $result = [];
> async {
>foreach ($it as $val) {
> $result[] = $c($val);
>}
> }
>return $result;
>}
If the assumption is that each call can be asynchronous and all elements
need to be processed, the only proper tool is a concurrent i
>
> Sure:
>
Yeah, this is a Watcher, a periodic function that is called to clean up or
check something. Yes, it’s a very specific pattern. And of course, the
Watcher belongs to the service. If the service is destroyed, the Watcher
should also be stopped.
In the context of this RFC, it's better t
>
> As noted, I am in broad agreement with the previously linked article on
"playpens" (even if I hate that name), that the "go style model" is too
analogous to goto statements.
>
The syntax and logic you describe are very close to Kotlin's implementation.
I would say that Kotlin is probably the
> Edmond,
>
> If you want to make async PHP with multiple processes you have to check
> variables semaphored to make it work.
>
>
Hello, Iliya.
Thank you for your feedback. I'm not sure if I fully understood the entire
context. But.
At the moment, I have no intention of adding multitasking
>
> I think the same thing applies to scheduling coroutines: we want the
Scheduler to take over the "null fiber",
>
Yes, you have quite accurately described a possible implementation.
When a programmer loads the initial index.php, its code is already running
inside a coroutine.
We can call it the
>
> I can give you several examples where such logic is used in Amphp
libraries, and it will break if they are invoked within an async block.
>
Got it, it looks like I misunderstood the post due to my focus. So,
essentially, you're talking not so much about wait_all itself, but rather
about the p
>
> Maybe, we could create a different version of fibers ("managed fibers",
maybe?) distinct from the current implementation, with the idea to
deprecate them in PHP 10?
> Then, at least, the scheduler could always be running. If you are using
existing code that
> uses fibers, you can't use the new
>
> Have a different method `Fiber::suspendToScheduler(Resume $resume)` that
would return the control to the Scheduler.
>
That's exactly how it works. The RFC includes the method Async\wait()
(Fiber::await() is nice), which hands control over to the Scheduler.
At the PHP core level, there is an eq
>
> The wait_all block is EXPLICITLY DESIGNED to meddle with the internals of
async libraries,
>
How exactly does it interfere with the implementation of asynchronous
libraries?
Especially considering that these libraries operate at the User-land level?
It’s a contract. No more. No less.
>
> Lib
Good day, Alex.
>
> Can you please share a bit more details on how the Scheduler is
implemented, to make sure that I understand why this contradiction exists?
Also with some examples, if possible.
>
```php
$fiber1 = new Fiber(function () {
echo "Fiber 1 starts\n";
$fiber2 = new Fiber(fu
>
> This is incorrect. "Create an async bounded context playpen" (what I
called "async" in my example)
> and "start a fiber/thread/task" (what I called "spawn") are two
*separate* operations, and > must remain so.
>
>
So, you use *async* to denote the context and *spawn* to create a coroutine.
Re
>
> Crippling async PHP with async blocks just because some libraries aren't
ready for concurrency now, means crippling the future of async php.
>
How can calling a single function have such a destructive impact on the
future of PHP?
Yes, you have to write 10-20 more characters than usual one tim
>
> This also seems like a very bad idea: there is no reason for the
language hide concurrency behind an INI or even worse a compilation flag.
>
This is not because someone wants it that way. This situation is solely due
to the fact that the Scheduler contradicts of Fiber.
- The Scheduler exp
>
> Colored functions completely preclude a possible future thread-based
implementation of concurrency.
>
I can assure you that colored functions are neither part of this RFC nor *any
future ones* from me. And it's not because it's my decision it's rather the
language itself and the existing code
>
> Let's assume we want to support this scenario; we could:
>
Thank you, that's an accurate summary. I would focus on two options:
1. Creating child coroutines by default, but allowing unbound ones to
exist.
2. Explicitly creating child coroutines.
And in the RFC, I would leave the ch
>
>for($i = 0; $i < 10; $i++) $results[] = async\async(fn($f) =>
file_get_contents($f),
> $file[$i]);
> // convert $results into futures somehow -- though actually doesn't look
like it is
> possible.
> $results = async\awaitAll($results);
>
Future can be obtained via getFuture(), according to the
>
> Neither of these is a bad use case, and they're not mutually exclusive,
but they do lead to different priorities.
> I freely admit my bias is towards Type 1, while it sounds like Edmond is
coming from a Type 2 perspective.
>
Exactly. A coroutine-based server is what I work with, so this aspect
Hello all.
A few thoughts aloud about the emerging picture.
### Entry point into the asynchronous context
Most likely, it should be implemented as a separate function (I haven't
come up with a good name yet), with a unique name to ensure its behavior
does not overlap with other operators. It has
>
> This sounds like you are not using DI meant for fibers/multiple requests
at the same time.
>
Spiral already supports DI containers based on *Scope *(like "per request"
injections). Symfony, if I’m not mistaken, does too.
Spiral introduces a restriction to ensure correct handling of Scope
depe
>
> A SAPI is written in C (or at least, using
> the C api's) and thus can do just about anything. If it wanted to, it
could swap out
> the global state when switching fibers.
>
Probably, it's possible. However, if I'm not mistaken, $_GET and $_POST are
implemented as regular PHP arrays, so if the
>
> See, what you call "paternalistic" I say is "basic good usability."
> Affordances are part of the design of everything. Good design means
making doing the
>
If we worry about "intuitive usability", we should ban caching, finite
state machines, and of course, concurrency.
Parallelism? Not just
>
> As far as I know, all current SAPIs follow one of two patterns:
>
It seems that my example, although taken from real life, is more of an
anti-pattern. Let's take a real example that is not an anti-pattern.
There is a B2B CRM built on services. Services are classes instantiated in
memory only
Hello, Daniil.
> Essentially, the only thing that’s needed for backwards-compatibility in
most cases is an API that can be used to register onWritable,
> onReadable callbacks for streams and a way to register delayed (delay)
tasks, to completely remove the need to invoke stream_select.
Thank you
> In a syntax-and-semantics approach, we only need to describe the things
people actually need.
There is no doubt that syntax provides the programmer with a clear tool for
expressing intent.
> In the same way, do we actually need to design what an "async context"
looks like to the user?
Its imp
> One key question, if we disallow explicitly creating Fibers inside an
async block,
> can a Fiber be created outside of it and not block async, or would that
also be excluded? Viz, this is illegal:
>
Creating a `Fiber` outside of an asynchronous block is allowed; this
ensures backward compatibi
> Defining new syntax would encourage us to define a minimum top-level
> behaviour, such as "inside an async{} block, these things are possible,
> and these things are guaranteed to be true"
True. This is precisely the main reason not to change the syntax. The
issue is not even about how many ch
>
> Thinking aloud, perhaps we could cause `new Fiber` to create an
automatic async block?
>
The main issue with Fibers is their switching logic:
If you create Fiber A and call another Fiber B inside it, Fiber B can only
return to the Fiber that created it, not just anywhere. However, the
Schedul
> You might want to look to parallel extension as it's already dealing
> with that and mostly works - of course combination with coroutines will
certainly complicate it but the point is that memory is not shared.
Do you mean this extension: https://www.php.net/manual/en/book.parallel.php?
Yes, I
Hello, Eugene!
What I think it could be.
async function baz(): int {
$foo = foo();
$bar = bar();
return $foo + $bar;
}
// value is returned just like from any other ordinary function
$val = baz();
If we have code like $x + $y, and in one block it follows rule 1 while in
an
Hello, Jakub.
>
> I thought about this quite a bit and I think we should first try to
clarify the primary design that we want to go for.
> What I mean is whether we would like to ever support a true concurrency
(threads) in it.
> If we think it would be worth it (even thought it wouldn't be initia
Good day, Larry.
> First off, as others have said, thank you for a thorough and detailed
proposal.
Thanks!
> * A series of free-standing functions.
> * That only work if the scheduler is active.
> * The scheduler being active is a run-once global flag.
> * So code that uses those functions is onl
Hi, Nicolas.
Hi Edmond,
The target DX is that when outside any event loop, we're still able to
leverage fibers to provide concurrency, for requests only, and when inside
an event loop, requests run concurrently to any other things that the loop
monitors.
Is that something that could be a
>
> Note the explicit use case I listed is that of an unlock() in a finally
> block that *requires spawning a new fiber* in order to execute the actual
> unlock() RPC call: this is explicitly in contrast with the RFC, which
> specifies that
> >*ATTENTION*: A programmer must *never* attempt to creat
Lock/Unlock issue
It seems that this is actually about a database query that puts the Fiber
into a waiting state specifically, query("UNLOCK").
In that case, everything should work correctly.
Although there are some dangerous edge cases. The database might be under
high load, causing the q
I like context.WithCancel from Go, but it can essentially be implemented
directly in PHP land since all the necessary tools are available.
Note, this is precisely the problem, implement cancellation propagation to
child fibers in userland PHP requires writing a bunch of boilerplate try-catch
>
> As a heavy use of both amphp and go, cancellations (contexts in go) are
> absolutely needed, as a fiber may spawn further background fibers in order
> to execute some operation, just cancelling that specific fiber will not
> cancel the spawned fibers, unless a bunch of boilerplate try-catch blo
>
> There should be a defined ordering (or at least, some guarantees).
The execution order, which is *part of the contract*, is as follows:
1. Microtasks are executed first.
2. Then I/O events and OS signals are processed.
3. Then timer events are executed.
4. Only after that are fibe
> but it would be interesting to replace the scheduler with something that
> utilized go-routines for true multi-threading. Whether that works or not,
> is a whole different can of worms.
>
> — Rob
>
If the question is whether it is possible to interact with a PHP thread
from another thread by send
>
> First: PHP having native async support would be a huge step forward for
> the language. It's really exciting to see how this proposal develops.
>
> Thank you for the kind words, it was awesome to read.
I wonder if there are ways it can be split into smaller pieces, so that
> we don't overl
>
> FYI: once you introduce a draft RFC for discussion, the RFC should change
> status to "under discussion" per (4):
>
>
It's done. Thank you.
Ed.
Good day, everyone. I hope you're doing well.
I’d like to introduce a draft version of the RFC for the True Async
component.
https://wiki.php.net/rfc/true_async
I believe this version is not perfect and requires analysis. And I strongly
believe that things like this shouldn't be developed in iso
>
> I think what bilge was trying to point out is that there should be
> absolutely no change on existing software with or without the scheduler
> running (for software not using fibers).
>
I thought the same. But where would you hide Fibers? They are part of the
language.
The existence of Fibers
> There should be no perceptible difference between a blocking sleep(10)
and an async sleep(10), so what backwards compatibility are you referring
to?
For example, the behavior of the code below will not change. The code will
execute sequentially, and context switching will only occur when resume
I forgot to mention this. There is an important *limitation* in how this
solution behaves in the context of PHP.
Calls to PHP functions that normally block execution in the zero-Fiber do
not change their behavior. This solution has both advantages and
disadvantages. The advantage is that it does n
Hello Bilge!
> Is it really necessary to have all these `Async\launchScheduler();`
calls? Why can't the scheduler always be running, the same as it is in
JavaScript or any other async language? Even (userland) Revolt does not
require the event loop to be manually started.
*Short answer:* This imp
Hello, Rob!
It is probably best to have channels be non-buffered by default (like in
> Go); buffered channels can hide architectural issues in async code.
>
Great point! If a channel has a default capacity greater than 1, it leads
to implicit behavior that the user must be aware of. If something
> I'm actually curious why you chose libuv?
I relied on the usual set of selection criteria: relevance, support, and
documentation.
Hope there won’t be any serious issues with this component. But if there
are, we can implement a Plan B: copy the necessary Win32 code from libUV,
adapt it for PHP, a
1 - 100 of 103 matches
Mail list logo