Re: PHP True Async RFC - Stage 2

From: Date: Tue, 18 Mar 2025 07:59:58 +0000
Subject: Re: PHP True Async RFC - Stage 2
References: 1 2  Groups: php.internals 
Request: Send a blank email to internals+get-126820@lists.php.net to get a copy of this message
On 18 March 2025 05:59:07 GMT, Larry Garfield <larry@garfieldtech.com> wrote: >My biggest issue, though, is that I honestly can't tell what the mental model is supposed >to be. The RFC goes into detail about three different async models. Are those standard terms >you're borrowing from elsewhere, or your own creation? If the former, please include >citations. I cannot really tell which one the "playpen" model would fit into. I... think >bottom up, but I'm not sure. Moreover, I then cannot tell which of those models is in use in >the RFC. There's a passing reference to it being bottom up, I think, but it certainly looks >like the No Limit model. There's a section called structured concurrency, but what it >describes doesn't look a thing like the playpen-definition of structured concurrency, which as >noted is my preference. It's not clear why the various positives and negatives are there; >it's just presented as though self-evident. Why does bottom up lead to high memory usage, for >instance? That's not clear to me. So really... I have no idea how to think about any of it. I had a very different reaction to that section. I do agree that some citations and links to prior art would be good - I mentioned in my first email that the "actor model" is mentioned in passing without ever being defined - but in general, I thought this summary was very succinct: > - No limitation. Coroutines are not limited in their lifetime and run as long as needed. > - Top-down limitation: Parent coroutines limit the lifetime of their children > - Bottom-up limitation: Child coroutines extend the execution time of their parents Since you've described playpens as having an implicit "await all", they're bottom-up: the parent lasts as long as its longest child. Top-down would be the same thing, but with an implicit "cancel all" instead. >Broadly speaking, I can think of three usage patterns for async in PHP (speaking, again, as >someone who doesn't have a lot of async experience, so I may be missing some): > >1. Fan-out. This is the "fetch all these URLs at once" type use case, which in most >cases could be wrapped up into a para_map() function. (Which is exactly what Rust does.) >2. Request handlers, for persistent-process servers. Would also apply for a queue worker. >3. Throw it over the wall. This would be the logging example, or sending an email on some >trigger, etc. Importantly, these are cases where there is no result needed from the sub-routine. I agree that using these as key examples would be good. >// Creates an async scope, in which you can create coroutines. >async { The problem with using examples like this is that it's not clear what happens further down the stack - are you not allowed to spawn/start/whatever anything? Does it get started in the "inherited" scope? You've also done exactly what you complained the RFC did and provided a completely artificial example - which of the key use cases you identified is this version of scope trying to solve? I actually think what you're describing is very similar to the RFC, just with different syntax; but your examples are different, so you're talking past each other a bit. >I honestly cannot see a use case at this point for starting coroutines in arbitrary scopes. The way I picture it is mostly about choosing between creating a child within a narrow scope you've just opened, vs creating a sibling in the scope created somewhere up the stack. The "request handler" use case could easily benefit from a "pseudo-global" scope for each request - i e. "tie this to the current request, but not to anything else that's started a scope in between". There were also some concrete examples given in the previous thread of explicitly managing a context/scope/playpen in a library. Rowan Tommins [IMSoP]

« previous php.internals (#126820) next »