On Sat, Nov 15, 2025, at 13:16, Edmond Dantes wrote:
> Hello all.
> 
> Some of these questions sound familiar. Let’s try to sort them out.
> 
> > If I have a tight loop processing data in memory (no I/O), will it 
> > monopolise the coroutine scheduler? Do I need to manually insert suspend() 
> > calls? How do I know when and where?
> 
> A coroutine must yield control on its own. If it keeps it through an
> endless loop, then it will be the only one running.
> 
> > The RFC suggests that existing PHP functions won’t automatically be 
> > non-blocking. So which will? Is there a way to identify suspension points 
> > at the language/API level?
> 
> The RFC does not say that. PHP I/O functions automatically become
> non-blocking relative to the whole process. In other words, an I/O
> function calls suspend() on its own when needed. The programmer writes
> code exactly as before, under the illusion that operations are
> executed one after another.
> 
> > Performance implications: Without knowing where suspensions occur, how do 
> > developers avoid either:
> 
> In most cases, this is not the developer’s concern. Situations where
> performance is critical should be handled with dedicated tools.
> A PHP developer should not have to drop down to the C level. Properly
> designed abstractions must provide the required performance.
> 
> I can already anticipate the question: but a developer could write
> something like “for i < 10000 suspend” or something similar.
> The answer is this: a developer must know how to use abstractions. As
> always. Everywhere. In any area of programming.
> It’s just as important as respecting proper layering in code.
> 
> Provided that PHP does not try to play the role of a C-level language
> (there have already been such attempts, and they keep resurfacing),
> and does not try to play a web server or a database system.
> For most web scenarios, the current approach is more than sufficient.
> This has been proven by Swoole, which has been on the market for many
> years. Therefore, performance questions are outside the scope of this
> RFC.
> 
> As for the concurrency model, let me remind you that Go has true
> multitasking. Goroutines in Go, although they have a “synthetic”
> stack. But you already know all this well.
> This RFC and its implementation describe coroutines in a single
> thread. That is very far from what Go provides.
> 
> There is no preemptive multitasking in this RFC because it is
> completely unrelated.
> This RFC and its implementation provide cooperative concurrency in a
> single thread, where coroutine code yields control on its own.
> Why was this model chosen? The simple answer is: because it is the
> only one that can realistically be implemented within a finite
> timeframe.
> 
> Any more questions?
> 
> -- Ed

Hey Ed,

I feel like we’re talking past each other a bit. I’m not questioning the choice 
of a cooperative scheduler, that’s totally fine, I’m trying to understand what 
the actual suspension points are. Every other language/runtime with cooperative 
concurrency spells this out, because without it developers can’t reason about 
performance or why a deadlock is happening.

Based on the conversation so far, I’d imagine the list to look something like:

- network i/o (streams/sockets/dns/curl/etc)
- sleeps
- subprocess waits? `proc_open` and friends?
- extensions with a reactor implementation
- awaiting `FutureLike`
- `suspend()`

If that’s the intended model, it’d help to have that spelled out directly; it 
makes it immediately clear which functions can or will suspend and prevents 
surprises.

I also think the RFC needs at least minimal wording about scheduler guarantees, 
even if the details are implementation-specific. For example, is the scheduler 
run-to-suspend? FIFO or round-robin wakeup? And non-preemptive behaviour only 
appears here in the thread. It isn’t mentioned in the RFC itself. That’s 
important for people writing long, CPU-bound loops, since nothing will 
interrupt them unless they explicitly yield.

Lastly, cancellation during a syscall is still unclear. If a coroutine is 
cancelled while something like `fwrite()` or a DB write is in progress, what 
should happen? Does `fwrite()` still return the number of bytes written? Does 
it throw? For write-operations in particular, this affects whether applications 
can maintain a consistent state.

Clarifying these points would really help people understand how to reason about 
concurrency with this API.

— Rob

Reply via email to