Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:126427 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 1E6D51A00BC for ; Mon, 17 Feb 2025 09:18:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1739783731; bh=bkeha4S8csE+LI3+V9HPGS/42kQKoSoGsG1j/Ye9Zuo=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=WDAEAfhWFQaPGHlZbxd3GgroaWmDe8Pe2j5GgJDC/7aEU9D3NI9CV6tdS+WA9LGdd y366ydbtJWYe4q8bxN/rNMjMo+6dipGDRmiy/Pgt9f28ft8nB/A1tuEjvf72QQGY8j IBCKjDqGOwEesUqa/ggGIGuRTBnbMcA3t8OHCoJCfjZVTkH22bqbPseqezg4XA5Fte Azi5HMWfyHYjAT4NRhgyW/zMA8RwhuXoN/H49ynK6LSlhCseYTLjxXayXdLTD8ezua 80XHZ8IshjJPADwQ4YmNbI8vNh9lZamBEysHAaE2zaJ0FlHXcRZS846n9I6MSmXVCF ZKOewe8ORXNOQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 5B1E4180059 for ; Mon, 17 Feb 2025 09:15:30 +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.0 required=5.0 tests=BAYES_40,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE, SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-yb1-f175.google.com (mail-yb1-f175.google.com [209.85.219.175]) (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, 17 Feb 2025 09:15:30 +0000 (UTC) Received: by mail-yb1-f175.google.com with SMTP id 3f1490d57ef6-e5df8468d6eso16538276.0 for ; Mon, 17 Feb 2025 01:18:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1739783891; x=1740388691; darn=lists.php.net; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=Xk9SRMs73juClBpiOeAUmFTHUtOOGS9F+N69mdAjlpw=; b=WZg8rwSYX1EkrL5Qf+hSRbQFf/DRpcCyXmEx52MtkSSDGGArH8iO4rlBE9maB3Uh7K tkiCsrE5ROE4hLCut3Ot1cHJVE4ZmnYMBFNFy4K0wJC+qboHGOvZIdJzsfpTArgSJjBG pbl3ekbkNYzkIgzl7wwBspCa5Qdx2rVh+szwbsrpPwNMeGDKrC39h4D9HKl1XPmhHAE6 znJdK1yJ0APMzGJz0HcDvV/5SKv6NeTPG+mZ6J6fIXdiLLj3NJOtQH0ZPNC61Fnn7gys 6r4SRMnYZ+1FcULi1MGLLRvCaAiuoxWyPZrxixFA0BkUI3SpJFZ8aOMRehNWdQbO/xvz vIrg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1739783891; x=1740388691; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=Xk9SRMs73juClBpiOeAUmFTHUtOOGS9F+N69mdAjlpw=; b=Z8K1t8BRtdINflDrzMjm+tSwZUvLqF+Be27ibP4xMJDvj4gnJZjWL+/NpsQtivaBu3 vRKhKBcnDNYEcpSngn3evOOxKMGkEAPOvaqiXLYmpoCpGiS8Els6/3NXN+t/ap0iKKRn IP0GW/b4Hj6Lk6+G78ktmq0WWa14bKNUmLhMRdQl+ywNXpMphXLfhMJ05H6MA/qI1mkm InNUJtmumHkXkU4hqztiCt6fIHQgRzKl4DYyETt9uY3DJECfeosT6sZn0zdZV73JbVJO MruZknMDfonxJt8gO4jBwHLbO4ycIuKfZNmXUWT7iQ3b4OBBaeuH6FBHelupNe6eFUxU dZxg== X-Gm-Message-State: AOJu0YyecDcAOjBFKS4GEN1qvxYFEma+zy3ibNiIxILrPiGGO+ehRXGh bJVi2hAPmlWftF+GF7WiMY7gbhMnX0EELvV1wm8GR4KLM2YO6dVuNANfbYkFoeKcqipfzoouwr/ HSTSNr3ZXblSWZcG4nkm44p7uVcKJi8NcZQU= X-Gm-Gg: ASbGncufUEMFOfxfEqYZQ2o4NWOI3TAlYgnlNWSUlRuD5qzeCu6eB0bnBto2G0hg2AU EhOxf9u5T5CAz/Z8Ooo+bTgOo1s3IaS8Z1D5WpnEuRYRzsOEETkkQ6DUdYmg8zkg2KoyqvvQE X-Google-Smtp-Source: AGHT+IHnNGiP31TxHgcxPqylAHiUTLfGxzPJAdqCyLQofGB0pJkl2MX2OMKJBpItPMtvTQijEfL6riFP7l43QuyqzRY= X-Received: by 2002:a05:6902:72d:b0:e5d:bdbd:acc9 with SMTP id 3f1490d57ef6-e5dc9010334mr7071674276.6.1739783890671; Mon, 17 Feb 2025 01:18:10 -0800 (PST) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 References: In-Reply-To: Date: Mon, 17 Feb 2025 11:18:00 +0200 X-Gm-Features: AWEUYZm53nePfRLrIYT5VzCMNt5jl_490PIVGJCivMODwvx_Me8VWX5-GVP6jB4 Message-ID: Subject: Re: [PHP-DEV] PHP True Async To: Rob Landers Cc: internals@lists.php.net Content-Type: multipart/alternative; boundary="000000000000ca20b0062e53001b" From: edmond.ht@gmail.com (Edmond Dantes) --000000000000ca20b0062e53001b Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hello, Rob! It is probably best to have channels be non-buffered by default (like in > Go); buffered channels can hide architectural issues in async code. > Great point! If a channel has a default capacity greater than 1, it leads to implicit behavior that the user must be aware of. If something results in implicit behavior, it's a bad practice. I'll fix it! > Why do we need "finishing" producing and consuming? Won't "closing" the channel be enough, or does closing a channel prevent consumers from reading pending items? Because additional methods allow us to express explicit semantics while also performing extra checks. Although, in essence, it's the same as the close() method. I'm not 100% sure whether this implementation is the right choice because it has more elements compared to Go. However, the close() method has an ambiguity regarding whether we want to terminate the Consumer or the Producer. And ambiguity leads to errors. > Personally, it should be an exception (or at least a warning) if a channel gets GC'd with pending data. Yes, we need to think about what would be better=E2=80=94an exception or a = warning. The GC considers whether the channel was explicitly closed or not. However, if someone tries to close consumption while there is still data, a warning is mandatory because this is, once again, *explicit* semantics. If a programmer wants to close a channel with data, they must first call discardData() and only then close it. So yes, explicit operations are much better. > Looking at the public api, it seems that it is very hard to use correctly. It would probably be a good idea to pair it down and come up with an api/ideomatic usage that makes it hard to use incorrectly. What exactly is difficult to use? > Can we name this Async\Closure or something similar? Just to keep it inline with the current \Closure It's hard for me to judge this. On one hand, the name *Callback* clearly reflects what this object does. > Speaking of, you probably meant to use \Closure as the $callback property instead of mixed? Unfortunately, PHP does not support the *callable* type for properties. > - The constructor for a Future is public, but it looks like it would be weird to use. I'm currently working on this module and decided to base it on the implementation in AMPHP. So, the Future class will just be a wrapper around FutureState. And yes, its constructor can remain public, allowing Future to be used as DeferredFuture. > It would be nice to see this simply use Futures as much as possible. For example, returning a Future instead of FiberHandles. Yes, this will be implemented a bit later, after the *Future* module is fully ready. Most likely, this week. > Is onSignal OS signals or application signals? (I didn't look at the C code) If the latter, it is probably better to use something other than integer. If the former, this is usually handled by extensions (such as pcntl) and comes with a ton of caveats. It might be better to let those extensions handle that. Yes, these are OS signals, but they are essentially cross-platform. Can int be replaced with something else, like an enum? Of course. > - Exposing $reserved -- which is accessible via reflection -- is probably a bad idea. Yes, I=E2=80=99ve thought about this. It really doesn=E2=80=99t seem like t= he best idea. I might end up making the Notifier object final, which would eliminate this issue. > Should this be implementing Stringable interface? Also, this will automatically cast to a string if accidentally passed to a function that takes a string -- unless you are using strict mode. https://3v4l.org/3AqcW = This is probably not desired. Yes, all Notifier classes have a string representation for analysis purposes. Do you think it would be better to use a separate function instead of __toString to avoid implicit behavior? If it helps prevent unintended behavior, then you=E2=80=99re probably right. > Instead of Terminate(), should it be Close(), to be consistent with the rest of the library? Yes, maybe close() is better. > - It would be nice to get a list of callbacks on the Notifier as well. I 100% agree. I'll add this method now before I forget. > It would be nice to be able to get a future from the Walker instead of polling "isFinished". Of course. > IMHO, it would be better to implement this around Futures (see amphp's extensive library around this). Here are some examples from my own code: Sorry, but for some reason, I don't see the code. But just in case, I can say in advance that Walker was essentially made thanks to *AMPHP*=E2=80=94I borrowed part of the implementation from there :) > timeouts: race two futures, one which times out and the other which performs a long-running task. If the timeout wins, then I cancel the long-running task and proceed as an error. If the long-running task wins, I cancel the timeout. Why do you need Walker for this? > - wait for all to complete: there are a couple of variations on this theme. But sometimes we want to wait for all to complete, errors or not; sometimes we want to stop on the first error; and sometimes we want to just get the first success (see timeouts). This looks more like AwaitAll() / AwaitAny(). These functions will also be available once I finish Future. > $result =3D match (Async\select($channel)) { > Async\EOF =3D> "channel closed", > default =3D> $channel->receive(), >} Why? foreach ($channel as $data) { // ... } just use it :) > Also, keep in mind this is pretty valid That's exactly how it's implemented now. The channel does not treat NULL as the end of transmission=E2=80=94the programmer must close it explicitly,= or the *GC* will do it. Thanks for the great comments! They will help make this product better. --000000000000ca20b0062e53001b Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hello, Rob!

=C2=A0It is probably best to have channels be non-buffered by d= efault (like in Go); buffered channels can hide architectural issues in asy= nc code.
Great point! If a channel has a default capacity greater than 1, it leads t= o implicit behavior that the user must be aware of. If something results in= implicit behavior, it's a bad practice. I'll fix it!=C2=A0

> Why do we need "finishing" producing and c= onsuming? Won't "closing" the channel be enough, or does clos= ing a channel prevent consumers from reading pending items?

Because= additional methods allow us to express explicit semantics while also perfo= rming extra checks. Although, in essence, it's the same as the cl= ose() method.

I'm not 100% sure whether this implementat= ion is the right choice because it has more elements compared to Go. Howeve= r, the close() method has an ambiguity regarding whether we wa= nt to terminate the Consumer or the Producer. And ambiguity leads to errors= .=C2=A0=C2=A0

> Personally, it should be an exc= eption (or at least a warning) if a channel gets GC'd with pending data= .

Yes, we need to think about what would be bet= ter=E2=80=94an exception or a warning.

The GC considers whether the c= hannel was explicitly closed or not.
However, if someone tries to close = consumption while there is still data, a warning is mandatory because this = is, once again, explicit semantics. If a programmer wants = to close a channel with data, they must first call discardData() and only then close it.

So yes, explicit operations are much bette= r.

> Looking at the public api, it seems that it is very hard to u= se correctly. It would probably be a good idea to pair it down and come up = with an api/ideomatic usage that makes it hard to use incorrectly.

= =C2=A0 What exactly is difficult to use?=C2=A0=C2=A0

> Can we name= this Async\Closure or something similar? Just to keep it inline with the c= urrent \Closure

=C2=A0 It's hard for me to judge this. On one han= d, the name Callback clearly reflects what this object doe= s.

=C2=A0 > Speaking of, you probably meant to use \Closure as the= $callback property instead of mixed?

=C2=A0 Unfortunately, PHP does = not support the callable type for properties.

=C2=A0= > - The constructor for a Future is public, but it looks like it would = be weird to use.

I'm currently working on this module and decided= to base it on the implementation in AMPHP. So, the Future cla= ss will just be a wrapper around FutureState. And yes, its con= structor can remain public, allowing Future to be used as DeferredFuture.

=C2=A0> It would be nice to see this simp= ly use Futures as much as possible. For example, returning a Future instead= of FiberHandles.

=C2=A0 Yes, this will be implemented a bit later, a= fter the Future module is fully ready. Most likely, this w= eek.

=C2=A0 >=C2=A0 Is onSignal OS signals or application signals? (I didn't look at the C = code) If the latter, it is probably better to use something other than inte= ger. If the former, this is usually handled by extensions (such as pcntl) a= nd comes with a ton of caveats. It might be better to let those extensions = handle that.

Yes, these are OS signals, but they are essentially cross-platform. = Can int be replaced with something else, like an enum? Of course.

> - Exposing $reserved -- which is accessible via= reflection -- is probably a bad idea.

Yes, I=E2=80=99ve thought abou= t this. It really doesn=E2=80=99t seem like the best idea.

I might en= d up making the Notifier object final, which would eliminate t= his issue.

> Should this be implementing Stringable interface? Als= o, this will automatically cast to a string if accidentally passed to a fun= ction that takes a string -- unless you are using strict mode.=C2=A0https://3v4l.org/3AqcW=C2= =A0This is probably not desired.

Yes, all Notifier class= es have a string representation for analysis purposes.

Do you think i= t would be better to use a separate function instead of __toString to avoid implicit behavior? If it helps prevent unintended behavior, t= hen you=E2=80=99re probably right.

> Instead of Terminate(), shoul= d it be Close(), to be consistent with the rest of the library?

=C2= =A0 Yes, maybe close() is better.

> - It would be nic= e to get a list of callbacks on the Notifier as well.

I 100% agree. I'll add this method now before I forget.=C2=A0

=C2= =A0> It would be nice to be able to get a future from the Walker instead= of polling "isFinished".
Of course.

>=C2=A0 IMHO, it= would be better to implement this around Futures (see amphp's extensiv= e library around this). Here are some examples from my own code:

=C2= =A0 Sorry, but for some reason, I don't see the code. But just in case,= I can say in advance that Walker was essentially made thanks = to AMPHP=E2=80=94I borrowed part of the implementation fro= m there :)

=C2=A0 >=C2=A0 timeouts: race two futures, one which times out and the other which perform= s a long-running task. If the timeout wins, then I cancel the long-running = task and proceed as an error. If the long-running task wins, I cancel the t= imeout.

=C2=A0 Why do you need Walker for this?

= =C2=A0 >=C2=A0 - wait for all to complete: there are a couple of variations on this theme.= But sometimes we want to wait for all to complete, errors or not; sometime= s we want to stop on the first error; and sometimes we want to just get the= first success (see timeouts).
=C2=A0 This looks more like AwaitAl= l() / AwaitAny(). These functions will also be availabl= e once I finish Future.

> $result =3D match (Async\se= lect($channel)) {

>=C2=A0 Async\EOF =3D> "channel closed= ",
=C2=A0> default =3D> $channel->receive(),
>}

Why?

foreach ($chan= nel as $data) {
=C2=A0 =C2=A0 // ...
}

j= ust use it :)

> Also, keep in mind this is pret= ty valid
=C2=A0 That's exactly how it's implemented now. = The channel does not treat NULL as the end of transmission=E2= =80=94the programmer must close it explicitly, or the GC w= ill do it.

Thanks for the great comments!=C2=A0=20 They will help make this product better. =C2=A0=C2=A0
--000000000000ca20b0062e53001b--