Hi,

On Tue, Dec 9, 2025 at 12:48 PM Derick Rethans <[email protected]> wrote:

> On Thu, 30 Oct 2025, Jakub Zelenka wrote:
>
> > I would like to introduce a new polling API RFC that is part of my
> > stream evolution work:
> >
> > https://wiki.php.net/rfc/poll_api
>
> Under "Event Constants", I realised that we can't use enums for this
> due to them needing to be OR'ed, but it would be nice if at some point
> in the future we had a way of doing: PollEvent::Read | PollEvent::Write;
>
>
I introduce \Io\Poll\Event enum. It doesn't allow ORing but all functions
now accept array of those events which is hopefully good enough and cover
this. If we ever introduced something like enum sets supporting OR
operator, the functions could accept it in addition to the array.


> The enum PollBackend (not sure if that needs to be a backed enum), but
> this can not stand:
>
>         case EventPorts = "eventport";
>
> All the others have the case name and value the same, but this one
> misses the 's'.
>

It's no longer a backed enum so this is sorted.


>
>     /**
>      * Remove this watcher from the poll context
>      *
>      * After removal, the watcher becomes inactive and cannot be reused.
>      */
>     public function remove(): void {}
>
> Wouldn't this leave the PollWatcher object as a zombie: ie, it exists,
> but you can't do anything with it. Would it be possible to move this API
> somewhere else, so that the memory manager can just destruct it and
> release the object?
>

The PollWatcher was introduced on request from Bob so the new objects are
not created after each polling. This should be more efficient but to be
able to remove it, it needs to be done explicitly because the reference is
held so we need to somehow inform context that it can be removed. I think
we could introduce API to re-use it in the future but for now leaving clean
up to GC is the way.

Btw. there isActive() method that still works after removal and it returns
false. The modification method will throw exception if used.

If you have some suggestion for better API, I will be happy to consider it.


>
>      * @param int $maxEvents Maximum number of events to return (-1 for
> unlimited)
>      * @return array Array of PollWatcher instances that have triggered
> events
>      * @throws PollException If the wait operation fails
>      */
>     public function wait(int $timeout = -1, int $maxEvents = -1): array {}
>
> I am not sure if I like this returning an array. Would it perhaps be
> better to always return 1 (or 0 in case of non-blocking) events, which
> allows typing the return value as ?PollWatcher?
>

Returning just a single event is not ideal for performance if there are
many events so not sure it should be the preferred usage of the API.


>
> Alternative, perhaps this can be split up into two methods, wait() and
> waitMultiple() to be able to handle both approaches?
>

I would see it more like introducing waitSingle but it's basically just

public function waitSingle(int $timeout = -1): ?PollWatcher {
    return $this->wait($timeout, 1)[0] ?? null;
}

so not sure if it's that useful. But if you think, it's worth it, I can add
it..?


>
> Under "Future Scope", you have (for example) TimerHandle, but as that
> will (have to) extend PollHandle, it makes little sense to have a
> getFileDescriptor() method on PollHandle.


The timer and signal are actually file descriptors on Linux (timerfd and
signalfd) but you are right that this is not always the case. This is
however internal and that abstract class offers a different API for
internal extending that is more flexible. It means it won't
call getFileDescriptor() for internal classes but use direct C API.
That getFileDescriptor() is really meant for user classes that
extend PollHandle so they do something rather than extending class with
empty interface.


> Perhaps there should be
> another (abstract) class, so that you can have:
>
> - PollHandle
>   - FileDescriptorPollHandle
>     - StreamPollHandle
>     - SocketPollHandle
>     - CurlPollHandle
>   - TimerPollHandle
>   - SignalPollHandle
>
>
So in this case I think I would need to come up with some different way how
to allow polling without file descriptor for user classes otherwise user
space class could extend PollHandle and it would just do nothing which I
don't think is the right thing. I thought about it and I just don't have
any idea what that could be. I think that non fd variants don't make much
sense for user space or at least I don't see any reasonable use case.

The only other concern I have is that some polling backends allow for
> different events to be watched, which makes it harder to write portable
> code.
>

I tried to make it as portable as possible but edge triggering is just not
possible to simulate and I think it's worth exposing it even though it
doesn't work on Windows and Solaris. I added at least flag in the backend
so it's quite simple to check.

Kind regards,

Jakub

Reply via email to