Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:126657 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 64F231A00BC for ; Sat, 8 Mar 2025 18:49:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1741459589; bh=/B7D16BKTE+1F5fBV4ey9j6XQJ7/xkOBoUaZzFAExCc=; h=Date:From:To:In-Reply-To:References:Subject:From; b=WbYn/TCoi+WQ3nAJXLv4KSoiR/CpebIaD6sOHFOVFRNTopaw1PnQLMq8lEpluLrmO +9H1MffWf/vJ/rgLOqIPS29aac7iK1mZS/9Fv28vK20bGsCyLOYJ3IuR0jTsnfE4ew LnPkMTCmQUhvsLD4AgXleb/qIYhZL201S/1+4bwwFlOwaGSE38ojxiI5JUqUlz1z4j eJHJv/DRy9PpalwU+Js76BtJr6PRK1gYA57MXHHWt8ddqwXqk3Ro/vSCnVzhLZkJm+ 12JDkmFsbGP+D/hkRmECjb5mNLbQ8zI1A6OGPM8uPmPt0LcItKK73BOSMwKlc6BTpC aV7gcLR1wIj5w== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 56E26180088 for ; Sat, 8 Mar 2025 18:46:28 +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=-2.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,RCVD_IN_DNSWL_LOW, SPF_HELO_PASS,SPF_NONE autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from fout-b4-smtp.messagingengine.com (fout-b4-smtp.messagingengine.com [202.12.124.147]) (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 ; Sat, 8 Mar 2025 18:46:28 +0000 (UTC) Received: from phl-compute-04.internal (phl-compute-04.phl.internal [10.202.2.44]) by mailfout.stl.internal (Postfix) with ESMTP id A6DB21140137 for ; Sat, 8 Mar 2025 13:49:02 -0500 (EST) Received: from phl-imap-06 ([10.202.2.83]) by phl-compute-04.internal (MEProxy); Sat, 08 Mar 2025 13:49:02 -0500 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=1741459742; x=1741546142; bh=pzZlbKQhAntck7EbTGnmN GE/NoZM9YGg929AJaiINxI=; b=NskmYgYs+U0X+sd5etZAi7QkmAJU6jm+ylpyF N5gdzCbgjCim3IT9F+3Baz0bo2KmrkJEX+hx/cUmeCDAnN8mYP7lJqb7fmFPyJfa AvaYcdBAtuJ5MGQDROdBwlf8o0fQgqHDx1MQYn1a0uMv0Fp4RsX3MyEMCNHCAOMP s3xs2ExKb0PUNjBUrhuBVgXvxqnIGYWHDZ/cqRhJihRe+WQQIAeOzJky+UuTsmqP ZkChtSq9iQH++PMljxZVAhexAH/Y69yBW0C3KX6zbT+3iv672Vjhf5ub9wlIoOKA 9VUhHEM3/sH5/nW5xjYg2OgZnOIyjXOErV1T50sVgR1YkLS1g== 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=1741459742; x=1741546142; bh=p zZlbKQhAntck7EbTGnmNGE/NoZM9YGg929AJaiINxI=; b=GN/RsoipsNMUjJiuq FUTQK+7UfbJnTOf26lz+whNZfATcSEE+p+7SLRyg411/IKZh+Dc56nADPmo/uBKZ C2H13tDYwKoeoOlZFrXh4HSU9x17JPhELyXSMSzmLRpNFDKNCdsEp6koUg3za06a pvL0bzjFcO5bLQo3CQnPXbrc/Czipc9RXD3LRDi2GGMq05zrDJmAi/oTBqjb5ewE wfBiFkOR6DniL4Qhifqivwaz1p9dEK2OQG6lLoIKUVhw5Bft8/EvG3A+ITBlXF7X A+Pz2Wohsg46Oz3KWta9a+uJbX19O7lUbLjUcOHNvzK3qob0upG2aDR3qcRUtgxZ IbfJA== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgdduudegfeduucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucesvcftvggtihhpih gvnhhtshculddquddttddmnecujfgurhepofggfffhvffkjghfufgtgfesthejredtredt tdenucfhrhhomhepfdfnrghrrhihucfirghrfhhivghlugdfuceolhgrrhhrhiesghgrrh hfihgvlhguthgvtghhrdgtohhmqeenucggtffrrghtthgvrhhnpedugedvlefgueegheef jeetffduveeltefhfeegjeffffelgedttdevkeegkedugfenucevlhhushhtvghrufhiii gvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehlrghrrhihsehgrghrfhhivghlught vggthhdrtghomhdpnhgspghrtghpthhtohepuddpmhhouggvpehsmhhtphhouhhtpdhrtg hpthhtohepihhnthgvrhhnrghlsheslhhishhtshdrphhhphdrnhgvth X-ME-Proxy: Feedback-ID: i8414410d:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id 3C43729C0072; Sat, 8 Mar 2025 13:49:02 -0500 (EST) 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: Sat, 08 Mar 2025 12:48:39 -0600 To: "php internals" Message-ID: <1b6a5b3b-be9b-4e46-9cc6-b8b7f57b8494@app.fastmail.com> In-Reply-To: References: Subject: Re: [PHP-DEV] Re: PHP True Async RFC Content-Type: text/plain Content-Transfer-Encoding: 7bit From: larry@garfieldtech.com ("Larry Garfield") On Sat, Mar 8, 2025, at 1:05 AM, Edmond Dantes wrote: > 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. Yes. > ### The `async` operator > The `async` (or *spawn*?) operator can be used as a shorthand for > spawning a coroutine: This is incorrect. "Create an async bounded context playpen" (what I called "async" in my example) and "start a fiber/thread/task" (what I called "spawn") are two *separate* operations, and must remain so. create space for async stuff { start async task a(); start async task b(); } However those get spelled, they're necessarily separate things. If any creation of a new async task also creates a new async context, then we no longer have the ability to run multiple tasks in parallel in the same context. Which is, as I understand it, kinda the point. I also don't believe that an async bounded context necessarily needs to be a function, as doing so introduces a lot of extra complexity for the user when they need to manually "use" things. (Though perhaps sometimes we can have a shorthand for that; that comes later.) I am also still very much against allowing tasks to "detach". If a thread is allowed to escape its bounded context, then I can no longer rely on that context being bounded. It removes the very guarantee that we're trying to provide. There are better ways to handle "throwing off a long-running background task." (See below.) Edmond, correct me if I'm wrong here, but in practice, the *only* places that it makes sense to switch fibers are: 1. At an otherwise-blocking IO call. 2. In a very long running CPU task, where the task is easily broken up into logical pieces so that we can interleave it with shorter tasks in the same process. This is only really necessary when running a shared single process for multiple requests. And in this proposal, IO operations auto-switch between blocking and thread-sharing as appropriate. To be more concrete, let's consider specific use cases that should be addressed: 1. Multiplexing IO, within an otherwise sync context like PHP-FPM I predict that, in the near term, this will be the most common usage pattern. (Long term, who knows.) This one is easily solvable; it's basically par_map() and variations therein. // Creates a context in which async is allowed to happen. IO operations auto async $ctx = new AsyncContext() { $val1 = spawn task1(); $val2 = spawn task2(); // Do stuff with those values. } // We are absolutely certain nothing started in that block is still running. (I'm still unclear if $val1 and $val2 should be values or a Future object. Possibly the latter.) 4. Shared-process async server This is the ReactPHP/Swoole space. This... honestly gets kind of easy. Wrap the entire application in an async {} block. Boom. All IO is now async. inputChannel(); spawn handler($logChannel); } function handler($logger) { async { while (true) { $request = spawn listen_for_request(); spawn handle_request($request, $logChannel); } // An exception could get us to here. } } function handle_request($request, $logChannel) { $logChannel->send($request->url()); // Do other complex stuff with the request. } This is probably not the ideal way to structure it in practice, but it should get the point across. The background logger fiber already exists in the parent async playpen. That's OK! We can send messages to it via a channel. It can keep running after the inner async block ends. The logger fiber doesn't need to be attached, because it was already attached to a parent playpen anyway! This means passing either a channel-enabled logger instance around (probably better for BC; this should be easy to do behind PSR-3) or the sending channel itself. I'm sure someone will object that is too much work. However, it is no more, or less, work than passing a PSR-3 logger to services today. And in practice "your DI container handles that, stop worrying" is a common and effective answer. An async-aware DI Container could have an Async-aware PSR-3 logger it passes to various services like any other boring PSR-3 instance. That logger forwards the message across a channel to a waiting parent-playpen-bound fiber, where it just enters the rotation of other fibers getting run. Services don't need to be modified at all. We don't need to have dangling fibers. And for smaller, more contained cases, eh, Go has shown that "just pass the channel around and move on with life" can be an effective approach. The only caveat is you can't pass a channel-based logger to a scope that will be called outside of an async playpen... But that would be the case anyway, so it's not really an issue. There's still the context question, as well as whether spawn is a method on a context object or a keyword, but I think this gets us to 80% of what the original RFC tries to provide, with 20% of the mental overhead. --Larry Garfield