Hi Rob, Edmond,

> On Nov 15, 2025, at 06:37, Rob Landers <[email protected]> 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

Reply via email to