On Fri, Feb 14, 2025, at 15:16, Edmond Dantes wrote:
> Hello, *Jakub.*
> (It seems that simply clicking "Reply" does not work for the conference)
> **
> *> *I understand that's not ready for review yet but quickly
>   You could say that the code is in a state of "architectural stability," so 
> it can be reviewed and criticized. (Unfortunately, only the Windows build is 
> currently working.) 
> In the near future, I plan to start documenting the C-API, where I will 
> describe the implementation details in depth, but feel free to ask any 
> questions about this component.  
> 
> Link: https://github.com/EdmondDantes/php-src/tree/async/async
> 

Just looking through the PHP stubs, and here are some notes:

Channels:
- It is probably best to have channels be non-buffered by default (like in Go); 
buffered channels can hide architectural issues in async code.
- 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? Personally, it should be an exception (or at least a warning) if 
a channel gets GC'd with pending data.
- 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.

Callback:
- Can we name this Async\Closure or something similar? Just to keep it inline 
with the current \Closure
- Speaking of, you probably meant to use \Closure as the $callback property 
instead of mixed?

Futures:
- The constructor for a Future is public, but it looks like it would be weird 
to use.

Module:
- It would be nice to see this simply use Futures as much as possible. For 
example, returning a Future instead of FiberHandles.
- 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.

Notifier:
- Exposing $reserved -- which is accessible via reflection -- is probably a bad 
idea.
- 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.
- Instead of Terminate(), should it be Close(), to be consistent with the rest 
of the library?
- It would be nice to get a list of callbacks on the Notifier as well.

Walker:
- It would be nice to be able to get a future from the Walker instead of 
polling "isFinished".
- 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:

- 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.

- 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).

All in all, this is a good start. It would probably be a good idea to take a 
deeper look at other Channel implementations and borrow heavily from them. The 
current implementation seems to leak a lot of internal information that most 
programs should not use. As a producer, all I care about is that the channel is 
open; not that anyone is listening or not. There may be nobody listening right 
now; but because it is async, it is my job as a programmer to make sure that 
someone is listening, eventually. Same with consumers, they don't need to know 
whether someone is actually producing, just that the channel is open so they 
can consume. It may be a good idea to create a way to use it with match:

$result = match (Async\select($channel)) {
  Async\EOF => "channel closed",
  default => $channel->receive(),
}

Where Async\select() just pauses until there is a value and returns "null" or 
"EOF" or something when it has a value available or becomes closed. Also, keep 
in mind this is pretty valid:

$channel->send($data);
$channel->close();

A consumer should read $data before seeing the channel as closed. I presume 
that this is what "finishProducing()" is for, but it requires synchronization 
to work properly and the entire idea of channels is to not need synchronization 
at all.

— Rob

Reply via email to