On Thu, Mar 6, 2025, at 23:26, Rowan Tommins [IMSoP] wrote: > On 06/03/2025 11:31, Edmond Dantes wrote: > > For example, PHP has functions for working with HTTP. One of them > > writes the last received headers into a "global" variable, and another > > function allows retrieving them. This is where a context is needed. > > > OK, let's dig into this case: what is the actual problem, and what does > an async design need to provide so that it can be solved. > > As far as I know, all current SAPIs follow one of two patterns: > > 1) The traditional "shared nothing" approach: each request is launched > in a new process or thread, and all global state is isolated to that > request. > 2) The explicit injection approach: the request and response are > represented as objects, and the user must pass those objects around to > where they are needed. > > Notably, 2 can be emulated on top of 1, but not vice versa, and this is > exactly what a lot of modern applications and frameworks do: they take > the SAPI's global state, and wrap it in injected objects (e.g. PSR-7 > ServerRequestInterface and ServerResponseInterface). > > Code written that way will work fine on a SAPI that spawns a fiber for > each request, so there's no problem for us to solve there. > > At the other extreme are frameworks and applications that access the > global state directly throughout - making heavy use of superglobal, > global, and static variables; directly outputting using echo/print, etc. > Those will break in a fiber-based SAPI, but as far as I can see, there's > nothing the async design can do to fix that. > > In the middle, there are some applications we *might* be able to help: > they rely on global state, but wrap it in global functions or static > methods which could be replaced with some magic from the async > implementation.
I think this might be an invalid assumption. A SAPI is written in C (or at least, using the C api's) and thus can do just about anything. If it wanted to, it could swap out the global state when switching fibers. This isn't impossible, nor all that hard to do. If I were writing this feature in an existing SAPI, this is probably exactly what I would do to maintain maximal compatibility. So, at a minimum, I would guess the engine needs to provide hooks that the SAPI can use to provide request contexts to the global state (such as a "(before|after)FiberSwitch" function or something called around the fiber switch). That being said, I'm unsure if an existing SAPI would send multiple requests to the same thread/process already handling a request. This would be a large undertaking and require those hooks to know from which request output is coming from so it can direct it to the right socket. Remember, fibers are still running in a single thread/process. They are not threading and running concurrently. They are taking turns in the same thread. Sharing memory between fibers is relatively easy and not complicated. Amphp has a fiber-local memory (this context, basically), and I have never had a use for it, even once, in the last five years. If fibers were to allow true concurrency, we would need many more primitives. At the minimum we would need mutexes to prevent race conditions in critical sections. With current fibers, you don't need to worry about that (usually), because there is never more than one fiber running at any given time. That being said, I have had to use amphp mutexes and semaphores to ensure that there is some kind of synchronization -- a real life example is a custom database driver I maintain that needs to ensure exactly one fiber is writing a query to the database at a time (since this is non-blocking). — Rob