Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:129696 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 lists.php.net (Postfix) with ESMTPS id A95F81A00BC for ; Fri, 26 Dec 2025 20:56:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1766782567; bh=Sq7DzNmKBsA1swz3JSfSo/LLeYxCKkvcl2AImz7GplI=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=I6+QrhaeavWeRBWZr5T0LZM4qo6Yw/JUaunkpv6YNUkQyjqRRclf6RgrZcsHYpAU4 7ahlzlsj0Q3r4wzVFFOBo4Ta0kxv1DBXpmyCjodWmryHtpkRI0kIfR0zL8ajp6ba3g bOXIWUbMC59E+qXkTjbVzK84WjWouRs+rr72FBKEAjvAAv1VYTyETevPLuj8q6JXSS +RNUhKehxzH6VCTxHexsRerItfhcvbOWLNrCDxneXx7FtYqp+ayymtn10/I3UwSEkg 5+JP2Q8PSkcWdLqfmhWcE2ti5XyBH1yr7T78ir/SbiwOKptI+nId1bvqfAoLcpIc5k +22dZ6sdBAAig== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 53F7A180050 for ; Fri, 26 Dec 2025 20:56:05 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on php-smtp4.php.net X-Spam-Level: * X-Spam-Status: No, score=1.8 required=5.0 tests=BAYES_50,DMARC_NONE, FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS, HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE, SPF_PASS autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from mail-oi1-f171.google.com (mail-oi1-f171.google.com [209.85.167.171]) (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 ; Fri, 26 Dec 2025 20:56:04 +0000 (UTC) Received: by mail-oi1-f171.google.com with SMTP id 5614622812f47-450823a7776so4762836b6e.2 for ; Fri, 26 Dec 2025 12:55:59 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1766782559; x=1767387359; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=2s6kNDkJDpgM8fYnQOne7gBCWCZYHY86PnDH2XbLB14=; b=S5xFfZjA/p73GoO/YJohkHS8jGkJM73ruj1gvSkYSt/7cYengboYYyl1VryKhG1SEG JAUVW1SKNVRxw2NQw4UI7mcF2BzjtVlvAWPaxeQeLJDlk+4lpQupwKXRFBZ53ZQy1LvJ jPlzTBWCy89awIgltPt6tTD45Xb1hM1VcKvZJBoDQ5Q8WxYJrW6lGG+QGmbwMS3St/rA 4l/0GrBLjartYr9cETHmnm55w4slDOuGccB7DdOKNWzqiCz72eExGAAaS1H0iDjkzsk0 5ejqisxVsPrh/o9bLCoVT2mKHpA8NErqd5ijCxrZ+CnOaN7vhGkkYKX4eAQeUL/+rv4O Lp2g== X-Gm-Message-State: AOJu0Yw0Thf3tyA/8D99Yk/tSpR73Z3RSunjBJduX4fjoDBd+goZDSVz gN3Yy3Q6TqT/NsAE7gfiQSgZ5SNhk44PDvq6cXD3TPsubOn4BjvAXzA8/XspjFPgH8u4CsbDs+J jN+rTygujVYos4gg7LbnAwBCCPGaZ8f4OAf8O X-Gm-Gg: AY/fxX54N0dQh5u5Ni3tSDF+mw82cjeEURL3CcAa39fKzyYk3ylIJZS3Y/hStULhf4e /fWLkSwoczwMeI+Wks+98uls15kdeJpZJkIDGaC6BHlpYdIm70y9LyuxU7fPYGdAg/hggfHVekF Tv0UUzP3NHtUkOprl4IMPBvk+R5GaTmdJl4X0pQkKY8jtFcH57VTGBoyjZFzZphRlO11fbT81pC W24FVSFeDXWeCzisvhV+xeMaFXygWRvkJ8U3LJ/hA8EPF7Pv+57LOi/eghsj9fbVoAsSg== X-Google-Smtp-Source: AGHT+IHwWMwCk8ANGiMGcWEW9G89uFwbSkTq7sRec0tBGyvqtLO+PcuQl2rhRHA2CT31bpHYxa4wiIV4h8KnN+iGKcQ= X-Received: by 2002:a05:6808:c286:b0:450:5af2:2d94 with SMTP id 5614622812f47-457b226d12emr9397402b6e.61.1766782559145; Fri, 26 Dec 2025 12:55:59 -0800 (PST) Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 References: In-Reply-To: Date: Fri, 26 Dec 2025 21:55:47 +0100 X-Gm-Features: AQt7F2r6c8XyXPwmbfwUJNdQle_u9dyHTYE6cpNLQOsoKLMIKCZhgaEvoZJ8lbU Message-ID: Subject: Re: [PHP-DEV] [RFC] Polling API To: Derick Rethans Cc: PHP internals list Content-Type: multipart/alternative; boundary="000000000000d551170646e11ed3" From: bukka@php.net (Jakub Zelenka) --000000000000d551170646e11ed3 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi, On Tue, Dec 9, 2025 at 12:48=E2=80=AFPM Derick Rethans wro= te: > 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 =3D "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 =3D -1, int $maxEvents =3D -1): arr= ay {} > > 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 =3D -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 --000000000000d551170646e11ed3 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi,

On Tue, Dec 9, 2025 at 12:48= =E2=80=AFPM Derick Rethans <derick@php= .net> 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 f= or 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 do= esn't allow ORing but all functions now accept array of those events wh= ich is hopefully good enough and cover this. If we ever introduced somethin= g like enum sets supporting OR operator, the functions could accept it in a= ddition to the array.
=C2=A0
The enum PollBackend (not sure if that needs to be a backed enum), but
this can not stand:

=C2=A0 =C2=A0 =C2=A0 =C2=A0 case EventPorts =3D "eventport";

All the others have the case name and value the same, but this one
misses the 's'.

It's no lon= ger a backed enum so this is sorted.
=C2=A0

=C2=A0 =C2=A0 /**
=C2=A0 =C2=A0 =C2=A0* Remove this watcher from the poll context
=C2=A0 =C2=A0 =C2=A0*
=C2=A0 =C2=A0 =C2=A0* After removal, the watcher becomes inactive and canno= t be reused.
=C2=A0 =C2=A0 =C2=A0*/
=C2=A0 =C2=A0 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 AP= I
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 ea= ch polling. This should be more efficient but to be able to remove it, it n= eeds to be done explicitly because the reference is held so we need to some= how inform context that it can be removed. I think we could introduce API t= o re-use it in the future but for now leaving clean up to GC is the way.=C2= =A0

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

If you have some suggestion for bet= ter API, I will be happy to consider it.
=C2=A0

=C2=A0 =C2=A0 =C2=A0* @param int $maxEvents Maximum number of events to ret= urn (-1 for unlimited)
=C2=A0 =C2=A0 =C2=A0* @return array Array of PollWatcher instances that hav= e triggered events
=C2=A0 =C2=A0 =C2=A0* @throws PollException If the wait operation fails
=C2=A0 =C2=A0 =C2=A0*/
=C2=A0 =C2=A0 public function wait(int $timeout =3D -1, int $maxEvents =3D = -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 ther= e are many events so not sure it should be the preferred usage of the API.= =C2=A0
=C2=A0

Alternative, perhaps this can be split up into two methods, wait() and
waitMultiple() to be able to handle both approaches?
<= br>
I would see it more like introducing waitSingle but it's = basically just

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

so not s= ure if it's that useful. But if you think, it's worth it, I can add= it..?
=C2=A0

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 si= gnalfd) 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 exten= ding that is more flexible. It means it won't call=C2=A0getFileDescript= or() for internal classes but use direct C API. That=C2=A0getFileDescriptor= () is really meant for user classes that extend=C2=A0PollHandle so they do = something rather than extending class with empty interface.
=C2= =A0
Perhaps there sh= ould be
another (abstract) class, so that you can have:

- PollHandle
=C2=A0 - FileDescriptorPollHandle
=C2=A0 =C2=A0 - StreamPollHandle
=C2=A0 =C2=A0 - SocketPollHandle
=C2=A0 =C2=A0 - CurlPollHandle
=C2=A0 - TimerPollHandle
=C2=A0 - SignalPollHandle


So in this case I think I would need t= o come up with some different way how to allow polling without file descrip= tor 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 tho= ught about it and I just don't have any idea what that could be. I thin= k 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 S= olaris. I added at least flag in the backend so it's quite simple to ch= eck.

Kind regards,

Jakub<= /div>

=C2=A0
--000000000000d551170646e11ed3--