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
