PHP True Async

php.internals

Edmond Dantes

1 year ago
Good day, everyone. I would like to request permission to create an RFC dedicated to the asynchronous module for PHP. Although the source code development is still in progress, a significant part of it has already been written, and the initial tests are working successfully. Therefore, I could start the documentation process if you consider this topic relevant and interesting for the community. A brief overview of what this RFC will cover: 1. *True* asynchronous support for PHP core functions without additional "tricks." 2. *Scheduler* and *Reactor* components, providing a C-API for PHP extensions to enable non-blocking I/O in plugins or core code. 3. *User-Land API* that mirrors the C-API for PHP developers. 4. Basic primitives for concurrent programming, such as *Channel* and *Iterator*. 5. *Built-in* integration with the *LibUV* library. The library is based on *Fiber*, extending and enhancing it to provide PHP developers with a full set of tools for concurrent applications. Have a great day! Edmond

Jakub Zelenka

1 year ago
Hi On Fri, Feb 14, 2025 at 12:24 PM Edmond Dantes <edmond.ht@gmail.com> wrote:
> Good day, everyone. > > I would like to request permission to create an RFC dedicated to the > asynchronous module for PHP. >
Someone will surely grant you karma soon. RFC makes sense.
> Although the source code development is still in progress, a significant > part of it has already been written, and the initial tests are working > successfully. Therefore, I could start the documentation process if you > consider this topic relevant and interesting for the community. > A brief overview of what this RFC will cover: > > 1. *True* asynchronous support for PHP core functions without > additional "tricks." > 2. *Scheduler* and *Reactor* components, providing a C-API for PHP > extensions to enable non-blocking I/O in plugins or core code. > 3. *User-Land API* that mirrors the C-API for PHP developers. > 4. Basic primitives for concurrent programming, such as *Channel* and > *Iterator*. > 5. *Built-in* integration with the *LibUV* library. > > The library is based on *Fiber*, extending and enhancing it to provide > PHP developers with a full set of tools for concurrent applications. >
Looks interesting. I understand that's not ready for review yet but quickly checked the code anyway. Is there some particular reason why this is not an extension? Think it might actually make sense to split it to two extensions - one for the core main stuff (that could be enabled by default) and then libuv optional part - similar to pdo for example. I haven't checked too deeply the code though so I might be missing things. Feel free to create a draft PR even if it's not ready. It might help to get some initial pre-reviews... :) Regards Jakub

Jakub Zelenka

1 year ago
> > >> A brief overview of what this RFC will cover: >> >> 1. *True* asynchronous support for PHP core functions without >> additional "tricks." >> 2. *Scheduler* and *Reactor* components, providing a C-API for PHP >> extensions to enable non-blocking I/O in plugins or core code. >> 3. *User-Land API* that mirrors the C-API for PHP developers. >> 4. Basic primitives for concurrent programming, such as *Channel* and >> *Iterator*. >> 5. *Built-in* integration with the *LibUV* library. >> >> The library is based on *Fiber*, extending and enhancing it to provide >> PHP developers with a full set of tools for concurrent applications. >> > > Looks interesting. I understand that's not ready for review yet but > quickly checked the code anyway. Is there some particular reason why this > is not an extension? Think it might actually make sense to split it to two > extensions - one for the core main stuff (that could be enabled by default) > and then libuv optional part - similar to pdo for example. I haven't > checked too deeply the code though so I might be missing things. Feel free > to create a draft PR even if it's not ready. It might help to get some > initial pre-reviews... :) > >
In terms of RFC, it would be good to also compare it with alternatives. From a quick look it slightly reminded me swow ( https://github.com/swow/swow ) but I didn't really look that deeply into it and I also don't know that much about swow so might be wrong.

Christoph Becker

1 year ago
On 14.02.2025 at 12:22, Edmond Dantes wrote:
> I would like to request permission to create an RFC dedicated to the > asynchronous module for PHP.
Have you already registered a new Wiki user? If so, what's your username? Otherwise head over to <https://wiki.php.net/?do=register>. Christoph

Bilge

1 year ago
On 14/02/2025 11:22, Edmond Dantes wrote:
> > Good day, everyone. > > I would like to request permission to create an RFC dedicated to the > asynchronous module for PHP. > > Although the source code development is still in progress, a > significant part of it has already been written, and the initial tests > are working successfully. Therefore, I could start the documentation > process if you consider this topic relevant and interesting for the > community. > > > A brief overview of what this RFC will cover: > > 1. *True* asynchronous support for PHP core functions without > additional "tricks." > 2. *Scheduler* and *Reactor* components, providing a C-API for PHP > extensions to enable non-blocking I/O in plugins or core code. > 3. *User-Land API* that mirrors the C-API for PHP developers. > 4. Basic primitives for concurrent programming, such as *Channel* and > *Iterator*. > 5. *Built-in* integration with the *LibUV* library. > > The library is based on *Fiber*, extending and enhancing it to provide > PHP developers with a full set of tools for concurrent applications. > > Have a great day! > > Edmond >
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. Cheers, Bilge

Edmond Dantes

1 year ago
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 implementation is simpler — not so much in terms of code size but in terms of reducing сonfusion. And we achieve a consistent state when asynchronous code runs within a Fiber, rather than having a situation where we are in a Fiber here but not there. For example, consider how the Fiber switching mechanism works in PHP. If FiberB is launched from FiberA, FiberB can only return to FiberA. Consequently, if the Scheduler component is placed inside a Fiber, the code must track the "direction" of switching. Running the Scheduler in the "zero" Fiber results in the simplest possible switching logic. It also defines a clear point where exceptions can be thrown. One might argue that no other language does it this way. But then, no other language has Fiber as a low-level tool (except C). From this perspective, explicit activation of the Scheduler aligns with Fiber as a low-level construct. Can this be avoided? Yes, but such alternative approaches often rely on *implicit behavior*. And implicit behavior tends to break things. Of course, we could start the code inside a Fiber to initialize the Scheduler and hide the implementation, but again, that's not always necessary or useful for everyone. Ed.

Edmond Dantes

1 year ago
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 not break *backward compatibility* in any way. Calling sleep(10) at the beginning of <?php will cause the process to go into a wait state. *True Async* changes the behavior of functions only if they are running inside a Fiber with an active Scheduler. This is yet another consequence of this architectural decision, and some people might not like it. However, at this point, I don’t see a better solution that would be optimally balanced between legacy support, explicit behavior, and implementation simplicity. Have a nice day! Ed.

Bilge

1 year ago
On 17/02/2025 18:53, Edmond Dantes wrote:
> > 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 not break *backward > compatibility* in any way. Calling |sleep(10)| at the beginning of > |<?php| will cause the process to go into a wait state. > > *True Async* changes the behavior of functions only if they are > running inside a Fiber with an active Scheduler. > > This is yet another consequence of this architectural decision, and > some people might not like it. However, at this point, I don’t see a > better solution that would be optimally balanced between legacy > support, explicit behavior, and implementation simplicity. > > Have a nice day! > > Ed. >
There should be no perceptible difference between a blocking sleep(10) and an async sleep(10), so what backwards compatibility are you referring to? Bilge

Edmond Dantes

1 year ago
> 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/ suspend is called. However, when the Scheduler is activated, this behavior changes. Now, calling sleep() inside a Fiber will lead to a context switch. If the activation of the Scheduler is implicit, previously written code may not work as the developer expects. <?php $fiber = new Fiber(function (): void { echo "Start fiber\n"; sleep(1); Fiber::suspend("Paused"); echo "Resume fiber\n"; }); $result = $fiber->start(); echo "Fiber suspended with: $result\n"; sleep(10); $fiber->resume(); echo "Fiber finished\n";

Rob Landers

1 year ago
On Mon, Feb 17, 2025, at 20:31, Edmond Dantes wrote:
> > 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`/`suspend` is called. > > However, when the `Scheduler` is activated, this behavior changes. Now, calling `sleep()` inside a `Fiber` will lead to a context switch. > > If the activation of the `Scheduler` is implicit, previously written code may not work as the developer expects. > > > <?php > > $fiber = new Fiber(function (): void { > echo "Start fiber\n"; > sleep(1); > Fiber::suspend("Paused"); > echo "Resume fiber\n"; > }); > > $result = $fiber->start(); > > echo "Fiber suspended with: $result\n"; > > sleep(10); > > $fiber->resume(); > > echo "Fiber finished\n";
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). — Rob

Edmond Dantes

1 year ago
> > 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 is an unpleasant issue in the context of language design. Essentially, they break the integrity of the implementation by creating a "backdoor" for user-land. So... various ways can be devised to disguise this fact, but they will all have drawbacks. Of course, you can create the Scheduler before starting the script and run the script inside a Fiber. But I'm sure there will be someone who will say they don't like this approach. Ed.