Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:126684 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id 113BD1A00BC for ; Mon, 10 Mar 2025 04:44:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1741581705; bh=hIYp9FIbcBjLPpxD7D+SFNvezoOS0DGgrcEDffarkuw=; h=Date:From:To:In-Reply-To:References:Subject:From; b=TlFBMjk8b2HYWe4VyuQraKvLBtvliHU9hyREmarb66e79DxSD0gTXnklh0yJL83bG DUTvVg1MAe52yoo8MPaGZITXsC4CkQIcHIP2Darv3BMKZmr2scvJ7KBXmy09R/1A2G Gi8yeU31W9N3WRbkagv5Nz4NiETbimFDZLWOC6z6KJJcNSL9WG1h7jNVeJ9IHqjXGU fzM46aguyqFJ1UfEM7IQ1HInvk6DyeJpyD3k5rG2e/Dbzi96OC3J3gAljgdxDMLGtK bxfxUWqxsWPoxViLi9l1Ts/EJJi3auB16WzkiPcyQ8Vppgn/YImQLzRCanGiUboXXA bSflwJmTrBiPQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 87676180066 for ; Mon, 10 Mar 2025 04:41:44 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-4.6 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H2,SPF_HELO_PASS,SPF_NONE autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from fhigh-b1-smtp.messagingengine.com (fhigh-b1-smtp.messagingengine.com [202.12.124.152]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Mon, 10 Mar 2025 04:41:44 +0000 (UTC) Received: from phl-compute-04.internal (phl-compute-04.phl.internal [10.202.2.44]) by mailfhigh.stl.internal (Postfix) with ESMTP id 40D222540137 for ; Mon, 10 Mar 2025 00:44:18 -0400 (EDT) Received: from phl-imap-06 ([10.202.2.83]) by phl-compute-04.internal (MEProxy); Mon, 10 Mar 2025 00:44:18 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= garfieldtech.com; h=cc:content-transfer-encoding:content-type :content-type:date:date:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to; s=fm1; t=1741581858; x=1741668258; bh=cq9QmdzGBu23SvvKgEbKw mXZkcD1IZE2iJ6MnNXk1w8=; b=h2izLv4VyjJaZGA2yLzKGgBYcSz1piOgE/g4F +wWVWVGmnsGL26qTBXa9WcJ7vdNs7ntrxIZuHpG9MgO7L+j9gVB7D31joOllmCkA A3hFLZsjg/rRV9KKIT8F0oVixBcvYPzNb4PzhW7oQBWRSpE8g0qMrFUIi2Jqf2Xn gs33pCNPkrzwq72VVVe0yp9gMx5o0lu9s1w3oqmeNd7GAwbBLtyMysByRwRhtRRU tDwaInBeJnmnZ7qbbR4XHaSV1Cg3hwUMmjUI6lUYouA8WuJlGhB3lcYofuI4DVfx I/EZxZtUyjgSQGKoh1eUTo/us2O7s69n/dw2+BXdj/wijyNHQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding:content-type :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm1; t=1741581858; x=1741668258; bh=c q9QmdzGBu23SvvKgEbKwmXZkcD1IZE2iJ6MnNXk1w8=; b=e36TPdbEDjq9dinsD TnpnZQNTiHqJw40736NxOD9duMi17k9vXb7d/M10spZT4u83eztaYAqKzuE4DXe9 fb1vF3AAOHa6lZJUa5LXfinHUYxeqOgNciYx1/upXrpY+yVTFsgwNYhE1e9vIhJR r2ZoDx/ewxy+B+xoPviOnVFgGhi7Y6w6+Fxc3lF87r8uB3hvQ8aCjV0Aux/wnxZa vHDufspAM15cvhr2fduM/649qZHhagJmQqiFSkHVw8szxkZ+TbNWjqjw7mwL+JSQ VvSG9bYWUN+sWF5RjKYHfRpcPN3Wej77SI/2ToGzuuejPN8yy89TuDiMeTmo/iVV CUnOw== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgdduudekgeduucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucesvcftvggtihhpih gvnhhtshculddquddttddmnecujfgurhepofggfffhvffkjghfufgtgfesthhqredtredt jeenucfhrhhomhepfdfnrghrrhihucfirghrfhhivghlugdfuceolhgrrhhrhiesghgrrh hfihgvlhguthgvtghhrdgtohhmqeenucggtffrrghtthgvrhhnpeehjeefvefgfeduteff ffdvheeiudekieefleevvdduiefgkeehvdevheffvdegteenucffohhmrghinhepghhith hhuhgsrdgtohhmnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhf rhhomheplhgrrhhrhiesghgrrhhfihgvlhguthgvtghhrdgtohhmpdhnsggprhgtphhtth hopedupdhmohguvgepshhmthhpohhuthdprhgtphhtthhopehinhhtvghrnhgrlhhssehl ihhsthhsrdhphhhprdhnvght X-ME-Proxy: Feedback-ID: i8414410d:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id D69B129C0072; Mon, 10 Mar 2025 00:44:17 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 Date: Sun, 09 Mar 2025 23:43:51 -0500 To: "php internals" Message-ID: <2b7dfacf-fd9f-4692-8b92-b2f22591d32a@app.fastmail.com> In-Reply-To: References: <08c8ad0b-e8f4-46e3-99f0-b80748d40b89@app.fastmail.com> <07973EAE-2D83-47A8-8FA0-84286C77C02B@rwec.co.uk> <48d66433-3ae9-4895-8361-7c81a0a3670d@app.fastmail.com> <8599eb8b-d4a3-4cb8-899a-25b134e0d64d@gmail.com> <74c4c726-63aa-44e0-84c9-840e13a65a4f@gmail.com> <77DC5F50-531D-49E8-8BE2-504A19CB5FFD@rwec.co.uk> <676e36e4-0b84-4d8c-b3db-2998831cd79d@gmail.com> <510B8EF1-9D07-41A8-9EA0-7D99CF7BFC91@rwec.co.uk> <4690cff0-7a48-4fe0-8310-688be253f976@gmail.com> <4871b2e4-353e-424f-92b9-7e2bb753bafa@gmail.com> Subject: Re: [PHP-DEV] PHP True Async RFC Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable From: larry@garfieldtech.com ("Larry Garfield") On Sun, Mar 9, 2025, at 11:56 AM, Edmond Dantes wrote: > *Let me summarize the current state for today:* > > 1. I am abandoning `startScheduler` and the idea of preserving=20 > backward compatibility with `await_all` or anything else in that=20 > category. The scheduler will be initialized implicitly, and this does=20 > not concern user-land. Consequently, the `spawn function()` code will=20 > work everywhere and always. > > 2. I will not base the implementation on `Fiber` (perhaps only on the=20 > low-level part). Instead of `Fiber`, there will be a separate class.=20 > There will be no changes to `Fiber` at all. This decision follows the=20 > principle of Win32 COM/DCOM: old interfaces should never be changed. I= f=20 > an old interface needs modification, it should be given a new name.=20 > This should have been done from the start. > > 3. I am abandoning low-level objects in PHP-land (FiberHandle,=20 > SocketHandle etc). Over time, no one has voted for them, which means=20 > they are unnecessary. There might be a low-level interface for=20 > compatibility with Revolt. > > 4. It might be worth restricting microtasks in PHP-land and keeping=20 > them only for C code. This would simplify the interface, but we need t= o=20 > ensure that it doesn=E2=80=99t cause any issues. =20 > > > The remaining question on the agenda: deciding which model to choose =E2= =80=94=20 > *parent-child* or the *Go-style model*. As noted, I am in broad agreement with the previously linked article on = "playpens" (even if I hate that name), that the "go style model" is too = analogous to goto statements. Basically, this is asking "so do we use gotos or for loops?" For which = the answer is, I hope obviously, for loops. Offering both, frankly, undermines the whole point of having structured,= predictable concurrency. The entire goal of that is to be able to know= if there's some stray fiber running off in the background somewhere sti= ll doing who knows what, manipulating shared data, keeping references to= objects, and other nefarious things. With a nursery, you don't have th= at problem... *but only if you remove goto*. A language with both a for= loop and an arbitrary goto statement gets basically no systemic benefit= from having the for loop, because neither developers nor compilers get = any guarantees of what will or won't happen. Especially when, as demonstrated, the "this can run in the background an= d I don't care about the result" use case can be solved more elegantly w= ith nested blocks and channels, and in a way that, in practice, would pr= obably get subsumed into DI Containers eventually so most devs don't hav= e to worry about it. Of interesting note along similar lines would be Rust, and... PHP.=20 Rust's whole thing is memory safety. The language simply will not let y= ou write memory-unsafe code, even if it means the code is a bit more ver= bose as a result. In exchange for the borrow checker, you get enough me= mory guarantees to write extremely safe parallel code. However, the des= igners acknowledge that occasionally you do need to turn off the checker= and do something manually... in very edge-y cases in very small blocks = set off with the keyword "unsafe". Viz, "I know what I'm doing is stupi= d, but trust me." The discouragement of doing so is built into the lang= uage, and tooling, and culture. PHP... has a goto operator. It was added late, kind of as a joke, but i= t's there. However, it is not a full goto. It can only jump within the= current function, and only "up" control structures. It's basically a n= amed break. While it only rarely has value, it's not al that harmful un= less you do something really dumb with it. And then it's only harmful w= ithin the scope of the function that uses it. And, very very rarely, th= ere's some micro-optimization to be had. (cf, this classic: https://git= hub.com/igorw/retry/issues/3). But PHP has survived quite well for 30 y= ears without an arbitrary goto statement. So if we start from a playpen-like, structured concurrency assumption, w= hich (as demonstrated) gives us much more robust code that is easier to = follow and still covers nearly all use cases, there's two questions to a= nswer: 1. Is there still a need for an "unsafe {}" block or in-function goto eq= uivalent? 2. If so, what would that look like? I am not convinced of 1 yet, honestly. But if it really is needed, we s= hould be targeting the least-uncontrolled option possible to allow for t= hose edge cases. A quick-n-easy "I'mma violate the structured concurren= cy guarantees, k?" undermines the entire purpose of structured concurren= cy. > During our discussion, everything seems to be converging on the idea=20 > that the changes introduced by the RFC into `Fiber` would be better=20 > moved to a separate class. This would reduce confusion between the old=20 > and new solutions. That way, developers wouldn't wonder why `Fiber` an= d=20 > coroutines behave differently=E2=80=94they are simply different classe= s. > The new *Coroutine* class could have a different interface with new=20 > logic. This sounds like an excellent solution. > > The interface could look like this: > > =E2=80=A2 *`suspend`* (or another clear name) =E2=80=93 a method that= explicitly hands=20 > over execution to the *Scheduler*. > =E2=80=A2 *`defer`* =E2=80=93 a handler that is called when the corou= tine completes. > =E2=80=A2 *`cancel`* =E2=80=93 a method to cancel the coroutine. > =E2=80=A2 *`context`* =E2=80=93 a property that stores the execution = context. > =E2=80=A2 *`parent`* (public property or `getParent()` method) =E2=80= =93 returns the=20 > parent coroutine. > (*Just an example for now.*) > > The *Scheduler* would be activated automatically when a coroutine is=20 > created. If the `index.php` script reaches the end, the interpreter=20 > would wait for the *Scheduler* to finish its work under the hood. > > Do you like this approach? That API is essentially what I was calling "AsyncContext" before. I am = flexible on the name, as long as it is descriptive and gives the user th= e right mental model. :-) (I'm not sure if Coroutine would be the right= name either, since in what I was describing it's the spawn command that= starts a coroutine; the overall async scope is the container for severa= l coroutines.) But perhaps that is a sufficient "escape hatch"? Spitballing again: async $nursery { // Formerly AsyncContext // Runs at the end of this nursery scope $nursery->defer($fn); // This creates and starts a coroutine, in this scope. $future =3D $nursery->spawn($fn); // A short-hand for "spawn this coroutine, in whatever the nearest asy= nc nursery scope is. // aka, an alias for the above line, but doesn't require passing $nurs= ery around. $future spawn $fn; // If you want. $future->cancel(); // See below. $nursery->spawn(stuff(...)); } // This blocks until escape() finishes, too, because it was bound to t= his scope. function stuff() { async $inner { // This is bound to the $inner scope; $inner cannot end // until this is complete. This is by design. spawn $inner; // This spawns a new coroutine on the parent scope, if any. // If there isn't one, $inner->parent is null so it falls back // to the current scope. // One could technically climb the entire tree to the top-most // scope and spawn a coroutine there. It would be a bit annoying = to do, // but, as noted, that's a good thing, because you shouldn't be do= ing that 99.9% of the time! // Channels are better 99.9% of the time. ($inner->parent ?? $inner)->spawn(escape(...)); } } I'm not sure I fully like the above. I don't know if it makes the guara= ntees too weak still. But it does offer a limited, partial escape hatch= , so may be an acceptable compromise. It would be valuable to take this idea (or whatever we end up with) to e= xperts in other languages with better async models than JS, and maybe a = few academics, to let them poke obvious-to-them holes in it. Edmund, does that make any sense to you? --Larry Garfield