Hello all. A few thoughts aloud about the emerging picture.
### Entry point into the asynchronous context Most likely, it should be implemented as a separate function (I haven't come up with a good name yet), with a unique name to ensure its behavior does not overlap with other operators. It has a unique property: it waits for the full completion of the event loop and the Scheduler. Inside the asynchronous context, `Fiber` is prohibited, and conversely, inside a `Fiber`, the asynchronous context is prohibited. ### The `async` operator The `async` (or *spawn*?) operator can be used as a shorthand for spawning a coroutine: ```php function my($param) {} // Operator used as a function call async my(1); or spawn my(1); // Operator used as a closure async { code }; // Since it's a closure, the `use` statement can be used without restrictions async use($var) { code }; // Returns a coroutine class instance $x = async use($var) { code }; ``` ### The `await` operator The `await` operator can be added to `async`, allowing explicit suspension of execution to wait for a result: ```php $x = await async use($var) { code }; ``` ## Context Manipulations I didn't like functions like `overrideContext`. They allow changing the context multiple times at any point in a function, which can lead to errors that are difficult to debug. This is a really bad approach. It is much better to declare the context at the time of coroutine invocation. With syntax, it might look like this: ```php async in $context use() {} async in $context myFun() async in $context->with("key", value) myFun() or spawn in $context ... ``` ## Thread The syntax spawn in/async in can be used not only in standard cases. ```php $coro = async in new ThreadContext() use($channel) { while() {} }; // This expression is also valid $coro = async in new $threadPool->borrowContext() use($channel) { while() {} }; ``` It is worth noting that $threadPool itself may be provided by an extension and not be a part of PHP. ## Unrelated Coroutines An additional way is needed to create a coroutine that is not bound to a parent. It's worth considering how to make this as clear and convenient as possible. Maybe as keyword: ```php async unbound ... ``` Of course, an `async child` modifier can be used. This is the inverse implementation, but I think it will not be used often. Making `unbound` a separate method is not very appealing at the moment because a programmer might forget to call it. They could forget the word `unbound`, and even more so a whole method. ## Context Operations ```php await $context; // Waits for all coroutines in the context $context.cancel(); // Cancels everything within the context ``` ## Flow I want to thank all the participants in the discussion. Thanks to your ideas, questions, and examples. A week ago, answering this question would have been impossible. If we add exception handling and graceful shutdown, and remove the new syntax by replacing it with an equivalent of 2-3 functions, we will get a fairly cohesive RFC that describes the high-level part without unnecessary details. Channels and even Future can be excluded from this RFC ā everything except the coroutine class and context. Microtasks, of course, will remain. As a result, the RFC will be clean and compact, focusing solely on how coroutines and context work. Channels, Future, and iterators can be moved to a separate RFC dedicated specifically to primitives. Finally, after reviewing the high-level RFCs, we can return to the implementation ā in other words, top-down. Given that the approximate structure of the lower level is already clear, discussing abstractions will remain practical and grounded. Just to clarify, Iām not planning to end the current discussion these are just intermediate thoughts. --- Ed. >