Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:126550 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 57F761A00BC for ; Mon, 3 Mar 2025 12:21:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1741004352; bh=dIkCUtHBswS3aMCyfghjwrt9QPS0e2mgfSD25norwJE=; h=From:Subject:Date:References:To:In-Reply-To:From; b=MWGWdHBUpUl/ud2TMi15etbBq7ZvlViwObRrmk2bZDZ5FFK955byg5d/ZG6Hw2gq8 4HjaI2bFOXcY5l3wsYWArMmxSzYA7Tsh7azs42tcZ5e9VPEXkbuv/1aSSWkDBPUvrN X1m+3ybAA2NICwf2S8eYMeVlYy/W6DhvN7MKt5hlcT1SoW2Zbhi3e5LhxopxOubBjb TKtYvfzimbEvgNz2m2Kvn15mc44p3x+adV+vWYY9vJ9wopjcD18fvRoHbvSOEVQXhB /mgBypFE/+rDmzMyJNBRiMEEvLfzbUhLkheIM+5LSB3kYNU3sjHg8oFM0KdGC2ssb5 u8vdJ79PvHXQw== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 24C7D18007F for ; Mon, 3 Mar 2025 12:19:11 +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=-0.2 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_20, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS, HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL, SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from sender4-of-o52.zoho.com (sender4-of-o52.zoho.com [136.143.188.52]) (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, 3 Mar 2025 12:19:10 +0000 (UTC) ARC-Seal: i=1; a=rsa-sha256; t=1741004504; cv=none; d=zohomail.com; s=zohoarc; b=SXGaXFk+BhQ8490qTSHlqVf3ITrcoMF8YxIdTIO6wNibbvsXbdkN2Iy2giwnCfn4GBOThF1O8trHI9Hq0/3VAypSrePn/KlEOFqOsBLB4X+OkzjER38YlsHBTufcfJhXJLqJ9w8TnASlXtOVc1p0oT978s4omR9doWgnTfUIYSo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1741004504; h=Content-Type:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To:Cc; bh=BWmbMVAogSQesVq1Yh2sKbeb5S8hjWb8tROc30tSs8Q=; b=JjpcbYYFDKXqc9WoapfRKrPtOs1nDabGYZBZFNeIhyTg/BfqDVp2UgW46DxPvqAyLVmL8Sy6rzNcjjQB7w/+b4wR9oZ6wLRDF31QW4y0XwVz1VmJeN3OtBN9lbP7eW3B5Om2ODFbZ7XAkD+0y+FapVJmOYwbRYJlRHqVdjeB7ic= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=daniil.it; spf=pass smtp.mailfrom=daniil@daniil.it; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1741004504; s=daniil; d=daniil.it; i=daniil@daniil.it; h=From:From:Content-Type:Mime-Version:Subject:Subject:Date:Date:References:To:To:In-Reply-To:Message-Id:Message-Id:Reply-To:Cc; bh=BWmbMVAogSQesVq1Yh2sKbeb5S8hjWb8tROc30tSs8Q=; b=esQeC0w7iLYFFmoYhrtxA+EiVAe7/fII+L9FPFJzzP7EsxVh96osm4hnb8WJIwdt NVugZhKEEdFhnGyzakAQTtte/AwrnDcVk5IqY0Tgee+zKipZ8IPc9YU2MVVxEPR4I+A JZQkR7q2rZPFRxqMGUbBcWI8rfBkrAsJJiV7XgeQ= Received: by mx.zohomail.com with SMTPS id 1741004501216603.6566003328803; Mon, 3 Mar 2025 04:21:41 -0800 (PST) Content-Type: multipart/alternative; boundary="Apple-Mail=_E07F9C84-6D90-4104-A8D2-FECAD39B3031" Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3826.400.131.1.6\)) Subject: Re: [PHP-DEV] PHP True Async RFC Date: Mon, 3 Mar 2025 13:21:27 +0100 References: To: internals@lists.php.net In-Reply-To: Message-ID: X-Mailer: Apple Mail (2.3826.400.131.1.6) X-ZohoMailClient: External From: daniil@daniil.it (Daniil Gentili) --Apple-Mail=_E07F9C84-6D90-4104-A8D2-FECAD39B3031 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 > On 3 Mar 2025, at 13:05, Edmond Dantes wrote: >=20 >> As a heavy use of both amphp and go, cancellations (contexts in go) = are absolutely needed, as a fiber may spawn further background fibers in = order to execute some operation, just cancelling that specific fiber = will not cancel the spawned fibers, unless a bunch of boilerplate = try-catch blocks are added to propagate CancellationExceptions.=20 >=20 > I didn't mean that Cancellation isn't needed at all. I meant that = canceling a Fiber is sufficient in most scenarios and leads to clean, = understandable code. >=20 > Other languages have child coroutines (Swoole supports them too), but = I'm not sure if that's the right approach. >=20 > I like context.WithCancel from Go, but it can essentially be = implemented directly in PHP land since all the necessary tools are = available. >=20 Note, this is precisely the problem, implement cancellation propagation = to child fibers in userland PHP requires writing a bunch of boilerplate = try-catch blocks to propagate CancellationExceptions to child = FutureHandle::cancel()s (spawning multiple fibers to execute subtasks = concurrently during an async method call is pretty common, and the = current implicit cancellation mode requires writing a bunch of try-catch = blocks to propagate cancellation, instead of just passing a cancellation = object, or a flag to inherit the cancellation of the current fiber when = spawning a new one). >> A nicer API should use only explicit cancellation objects, as this = pattern of preemptive implicit cancellations=20 >=20 > The exception mechanism is the standard way to alter the execution = flow in PHP. If a programmer writes code with lock and unlock outside of = a try-finally block but calls functions between these methods, they are = potentially creating a bad solution=E2=80=94at the very least because = someone else might later introduce an exception in one of those = functions. This is a classic case for languages with exceptions. >=20 Note the explicit use case I listed is that of an unlock() in a finally = block that *requires spawning a new fiber* in order to execute the = actual unlock() RPC call: this is explicitly in contrast with the RFC, = which specifies that=20 >ATTENTION: A programmer must never attempt to create a new fiber while = handling a CancellationException, as this behavior may trigger an = exception during Graceful Shutdown mode. While this is *somewhat* understandable in the context of graceful = shutdown, it still means that unlocking in a finally block (the only way = of properly handling cancellations with the current model) isn=E2=80=99t = always possible.. > So far, I haven't found a better way to ensure the logical consistency = and integrity of the execution flow. Maybe someone has a suggestion? >=20 >> The main reason given in the RFC >=20 > The main reason is that PHP has been around for many years and = didn=E2=80=99t just appear yesterday.=20 >=20 > If you have an idea on how to start the Scheduler implicitly, let's = implement it. So far, I have a few ideas: >=20 > Using an option in php.ini (downside: if PHP is used for multiple = projects). > Using a CLI option =E2=80=93 so far, I like this the most.=20 I would really prefer it to be always enabled, no fallback at all, = because as I said, it will make absolutely no difference to legacy, = non-async projects that do not use fibers, but it will avoid a split = ecosystem scenario. >> A thing I would love to see, on the other hand, is for Context to = become a=20 >> =E2=80=9Cprovider=E2=80=9D =20 >=20 > It's hard for me to evaluate this idea. Intuitively, it doesn't seem = ideal. In general, I'm not very fond of $_GET/$_POST. But on the other = hand, why not? This needs some consideration.=20 >=20 >> allow to very easily to turn i.e. php-fpm into a fully asynchronous = application server, >> where each request is started in the same thread (or in N threads in = an M-N M>N >> execution model) but its global state is entirely isolated between = fibers. > I haven=E2=80=99t thought about this possibility. But wouldn=E2=80=99t = this break the FCGI contract? =20 I see no reason why it should break the contract, if implemented by = isolating the global state of each fiber, it can be treated as a mere = implementation detail of the (eventually new) SAPI. Regards, Daniil Gentili =E2=80=94 Daniil Gentili - Senior software engineer=20 Portfolio: https://daniil.it Telegram: https://t.me/danogentili= --Apple-Mail=_E07F9C84-6D90-4104-A8D2-FECAD39B3031 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=utf-8

On 3 Mar 2025, at 13:05, Edmond Dantes = <edmond.ht@gmail.com> wrote:

As a heavy use of both amphp = and go, cancellations (contexts in go) are absolutely needed, as a fiber = may spawn further background fibers in order to execute some operation, = just cancelling that specific fiber will not cancel the spawned fibers, = unless a bunch of boilerplate try-catch blocks are added to propagate = CancellationExceptions. 

I didn't = mean that Cancellation isn't needed at all. I meant that canceling a = Fiber is sufficient in most scenarios and leads to clean, = understandable code.

Other languages have child coroutines (Swoole = supports them too), but I'm not sure if that's the right = approach.

I like context.WithCancel from Go, but it = can essentially be implemented directly in PHP land since all the = necessary tools are = available.


Note, this is precisely the problem, implement cancellation = propagation to child fibers in userland PHP requires writing a bunch of = boilerplate try-catch blocks to propagate CancellationExceptions to = child FutureHandle::cancel()s (spawning multiple fibers to execute = subtasks concurrently during an async method call is pretty common, and = the current implicit cancellation mode requires writing a bunch of = try-catch blocks to propagate cancellation, instead of just passing a = cancellation object, or a flag to inherit the cancellation of the = current fiber when spawning a new one).

A nicer API should use only explicit = cancellation objects, as this pattern of preemptive implicit = cancellations 

The exception = mechanism is the standard way to alter the execution flow in PHP. If a = programmer writes code with lock and unlock = outside of a try-finally block but calls functions between = these methods, they are potentially creating a bad solution=E2=80=94at = the very least because someone else might later introduce an exception = in one of those functions. This is a classic case for languages with = exceptions.


Note the = explicit use case I listed is that of an unlock() in a finally block = that *requires spawning a new fiber* in order to execute the actual = unlock() RPC call: this is explicitly in contrast with the RFC, which = specifies that 
>ATTENTION: A = programmer must never attempt to create a new = fiber while handling a CancellationException, as this = behavior may trigger an exception during Graceful = Shutdown mode.

While this is = *somewhat* understandable in the context of graceful shutdown, it still = means that unlocking in a finally block (the only way of properly = handling cancellations with the current model) isn=E2=80=99t always = possible..

So far, I haven't found a better way to ensure the = logical consistency and integrity of the execution flow. Maybe someone = has a suggestion?

The main reason given in the =
RFC

The main reason is that PHP has been = around for many years and didn=E2=80=99t just appear = yesterday. 

If you have an idea on how to start the Scheduler = implicitly, let's implement it. So far, I have a few ideas:

  1.   Using an option in = php.ini (downside: if PHP is used for multiple = projects).
  2. Using a CLI option =E2=80=93 so far, I like this the = most. 
I would = really prefer it to be always enabled, no fallback at all, because as I = said, it will make absolutely no difference to legacy, non-async = projects that do not use fibers, but it will avoid a split ecosystem = scenario.


A thing I would love to see, on the other hand, is = for Context to become a 
=E2=80=9Cprovider=E2=80=9D&nbs= p;  

It's hard for me to evaluate = this idea. Intuitively, it doesn't seem ideal. In general, I'm not very = fond of $_GET/$_POST. But on the other hand, = why not? This needs some  = consideration. 

allow to very easily to turn i.e. php-fpm into a fully =
asynchronous application server,
where each request is started in the same thread (or in N threads in an =
M-N M>N
execution model) but its global state is entirely isolated between =
fibers.
I haven=E2=80=99t thought about this possibility. But wouldn=E2=80=99t = this break the FCGI contract? =   

I see no reason why it should break the contract, if implemented = by isolating the global state of each fiber, it can be treated as a mere = implementation detail of the (eventually new) = SAPI.

Regards,
Daniil = Gentili

=E2=80=94

Daniil Gentili - Senior software = engineer 

Portfolio: https://daniil.it
Telegram: https://t.me/danogentili
= --Apple-Mail=_E07F9C84-6D90-4104-A8D2-FECAD39B3031--