Re: RFC proposal: worker mode primitives for SAPIs
| From: | Rowan Tommins | Date: | Sun, 31 Dec 2023 11:59:33 +0000 |
| Subject: | Re: RFC proposal: worker mode primitives for SAPIs | ||
| References: | 1 2 3 4 5 6 7 8 9 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-122077@lists.php.net to get a copy of this message | ||
On 31 December 2023 08:31:16 GMT, "Kévin Dunglas" <kevin@dunglas.fr> wrote:
>This new function is intended for SAPIs. Swoole was given as an example of
>worker mode, but it isn't a SAPI. AFAIK, it doesn't use the SAPI
>infrastructure provided by PHP.
>The scope of my proposal is only to provide a new feature in the SAPI
>infrastructure to build worker modes to handle HTTP requests, not to deal
>with non-SAPI engines.
One of the advantages you suggested of your proposal is that users would have a consistent way to
write worker scripts. To achieve that, you want a *design* that can be adopted by as many
implementations as possible, regardless of how they implement it. Providing helper infrastructure
for that design is a secondary concern - as you admit, the actual code you're proposing to add
is quite short.
>That being said, I don't understand what would prevent Swoole from
>implementing the proposed API
Then one of us is missing something very fundamental. As I understand it, Swoole's model is
similar to that popularised by node.js: a single thread processes multiple incoming requests
concurrently, using asynchronous I/O. For instance, a thread might run the following:
01 Request A received
02 Request A input validated
03 Request A sends async query to DB
04 Request A hands control to event loop while it awaits result
05 Request B received
06 Request B sends async HTTP call to some API
07 Request B awaits result
08 Request A resumed with DB result
09 Request A formats and returns response
10 Request A complete
11 Request B resumed
12 Request B fornats and returns response
Each request has its own call stack, started by a different call to the registered event handler,
but any global state is shared between them - there is no actual threading going on, so no
partitioned memory.
If requests are communicated by setting up superglobals, that will happen at step 01 and again at
step 05. If you try to read from them at step 09, you would see them populated with information
about request B, but you're trying to handle request A.
It would be possible to work around that by placing large warnings to users not to read superglobals
after any async call - basically forcing them to create scoped copies to pass around. But the worse
problem is output: if step 09 and step 12 both just use "echo", how do you track which
output needs to go to which network connection? You can't just set up an output buffer, because
that's global state shared by both call stacks. You have to put *something* into the scope of
the call stack - a callback to write output, an expected return value, etc.
Asynchronous code ends up somewhat resembling functional programming: everything you want to have
side effects on needs to be passed around as parameters and return values, because the only thing
isolated between requests is local variable scope.
Regards,
--
Rowan Tommins
[IMSoP]