Re: PHP True Async RFC Stage 5

From: Date: Sat, 15 Nov 2025 16:20:41 +0000
Subject: Re: PHP True Async RFC Stage 5
References: 1 2 3  Groups: php.internals 
Request: Send a blank email to internals+get-129234@lists.php.net to get a copy of this message
Hi Rob, Edmond,

> On Nov 15, 2025, at 06:37, Rob Landers <rob@bottled.codes> wrote:
> 
> I have concerns about the clarity of when suspension occurs in this RFC.
> 
> The RFC states as a core goal:
> 
> "Code that was originally written and intended to run outside of a Coroutine must work
> EXACTLY THE SAME inside a Coroutine without modifications."
> 
> And:
> 
> "A PHP developer should not have to think about how Coroutine switch and should not need
> to manage their switching—except in special cases where they consciously choose to intervene in
> this logic."
> 
> [...]
> 
> With explicit async/await ("coloured functions"), developers know exactly where
> suspension can occur. This RFC’s implicit model seems convenient, but without clear rules about
> suspension points, I’m unclear how developers can write correct concurrent code or reason about
> performance.
> 
> Could the RFC clarify the rules for when automatic suspension occurs versus when manual
> suspend() calls are required? Is this RFC following Go’s model where suspension timing is an
> implementation detail developers shouldn’t rely on? If so, that should be stated explicitly. Keep
> in mind that Go didn’t start that way and took nearly a decade to get there. Earlier versions of
> Go explicitly stated where suspensions were.
> 
> — Rob

To provide an explicit example for this, code that fits this pattern is going to be problematic:

function writeData() {
	$count = count($this->data);
	for($x = 0; $x < $count; $x++) {
		[$path, $content] = $this->data[$x];
		file_put_contents($path, $content);
	}
	$this->data = [];
}

While there are better ways to write this function, in normal PHP code, there's no problem
here. But if file_put_contents() can block and cause a different coroutine to run, $this->data
can be changed out from under writeData(), which leads to unexpected behavior. (e.g. $this->data
changes length, and now writeData() no longer covers all of it; or it runs past the end of the array
and errors; or doesn't see there's a change and loses it when it clears the data).

Now, yes, the programmer would have to do something to cause there to be two coroutines running in
the first place. But if _this_ code was correct when "originally written and intended to run
outside of a Coroutine", and with no changes is incorrect when run inside a coroutine, one can
only say that it is working "exactly the same" with coroutines by ignoring that it is now
wrong.

Suspension points, whether explicit or hidden, allow for the entire rest of the world to change out
from under the caller. The only way for non-async-aware code to operate safely is for suspension to
be explicit (which, of course, means the code now must be async-aware). There is no way in general
for code written without coroutines or async suspensions in mind to work correctly if it can be
suspended.

-John


Thread (71 messages)

« previous php.internals (#129234) next »