Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:126670 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 B737F1A00FB for ; Sun, 9 Mar 2025 09:04:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1741510913; bh=GVKJnjnfycf4EUt9J/G+ZWdWrDH7+p6vwflNczSeiEU=; h=Date:From:To:Cc:In-Reply-To:References:Subject:From; b=bzB4+Cj8L1kmqtgkRclT+cutN2WlAMI64JmIxvSLsQgclBGW5ZIcTCVpNkDDo/gSj xt/I0RnqJUebUlDWDNLwA+bRNgS6yjMNJIcefDs1vEO27gEaBszmbQPlmCDcFaFK+/ M+XoNFulBWt6itzuppLdpDtopSN5+MgJRpDmbqNhoDGiw7ZtrMjZG6irZGFtUfCNz1 pom+Tm9Tlfg+o6dOewwsU8bP+XTeaplerH9w9LZbSFQtPhgy8Qv7zSPRFeEcpziAUN QE9hpW8xDJP6gkaHbYtYOCHxgqxZCZYAkyXA7GbocN6m6IKntyYtxf0w4vhlw/vvHX Wi+1KLoyNop4g== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id E595F180037 for ; Sun, 9 Mar 2025 09:01:51 +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.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,HTML_MESSAGE, RCVD_IN_DNSWL_LOW,SPF_HELO_PASS,SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from fout-a2-smtp.messagingengine.com (fout-a2-smtp.messagingengine.com [103.168.172.145]) (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 ; Sun, 9 Mar 2025 09:01:51 +0000 (UTC) Received: from phl-compute-11.internal (phl-compute-11.phl.internal [10.202.2.51]) by mailfout.phl.internal (Postfix) with ESMTP id 9C4991382837; Sun, 9 Mar 2025 05:04:25 -0400 (EDT) Received: from phl-imap-09 ([10.202.2.99]) by phl-compute-11.internal (MEProxy); Sun, 09 Mar 2025 05:04:25 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bottled.codes; h=cc:cc:content-type:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm2; t=1741511065; x= 1741597465; bh=oAZj+0tWGvD7XSa7GvQex62omVB7i4ZYA1adNq+2Ar0=; b=l tcCLmJuSTKNWIslv95KtM9azCk8yRq747rw7j/mBjQuJb1xO0dqeb91C7rSGehIK XZHDgK7UbTPPi9Iql+hevuv8EHPmQ+XUznHQkzT6uPmIcTish/TaLvz+lt7nwjPa wa6Ogf8J5f7X4ziN+fHZepPp/VVsC+ErBHrqaMXBA0BVM5fDfR6Gi4iLdkUB87QC LeH0cxPGsbJSEdCPHVRIqLsyJjG/JdTEWJemT6Zifet1d4wRdkPURpBCSBNSslBb RonEl9LQOqV5ftormspy/GzKnHEamSV7i0e2gwhS2nVtXgnh03U7c4djka6NbcpY MK+ES/K7QhG723/nZa7pw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t= 1741511065; x=1741597465; bh=oAZj+0tWGvD7XSa7GvQex62omVB7i4ZYA1a dNq+2Ar0=; b=ASzzNKjUUqzIjrgb2C6Nz+lulC8EuP7sFli5pxU80IE6oB9yphI PeBBLc0LVqTKoI8pwtqBE/A3W+McOUqtHVehqnPOxvBcM8UrTJP9y0za4HvaMYZj bX73ks6n+W+6ETxTXZ5X9Pbi8aQqb4Wl9nlzc/NoZwNp16v5T28qdw2dLMvtlNXv vbRkLboMRr++gk57lCtrHUDtvdx8V6UOn/99JjaPHtMY3wQ0qoRsBUl9otCcQm0J eTAdj4vwCRQ+cijSBc3z1AVj2ixe1jI/Z8tiBUOD/nVk6G3oBdprgQjHKRQQyuSa 5ZyBFpvfEtt5RN8n7b+GHrGtSO+4AAb4o4Q== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgdduudeitdegucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucesvcftvggtihhpih gvnhhtshculddquddttddmnecujfgurhepofggfffhvfevkfgjfhfutgesrgdtreerredt jeenucfhrhhomhepfdftohgsucfnrghnuggvrhhsfdcuoehrohgssegsohhtthhlvggurd gtohguvghsqeenucggtffrrghtthgvrhhnpeeiueethedvvdefjefhgfeiheelheehtdfh feekjefflefgvedvkeduteejjedttdenucevlhhushhtvghrufhiiigvpedtnecurfgrrh grmhepmhgrihhlfhhrohhmpehrohgssegsohhtthhlvggurdgtohguvghspdhnsggprhgt phhtthhopeegpdhmohguvgepshhmthhpohhuthdprhgtphhtthhopegurghnihhilhdrgh gvnhhtihhlihesghhmrghilhdrtghomhdprhgtphhtthhopegurhgvrghlvggtshesghhm rghilhdrtghomhdprhgtphhtthhopegvughmohhnugdrhhhtsehgmhgrihhlrdgtohhmpd hrtghpthhtohepihhnthgvrhhnrghlsheslhhishhtshdrphhhphdrnhgvth X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id 42C16780068; Sun, 9 Mar 2025 05:04:25 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 Date: Sun, 09 Mar 2025 10:04:04 +0100 To: "Edmond Dantes" , =?UTF-8?Q?Alexandru_P=C4=83tr=C4=83nescu?= Cc: "Daniil Gentili" , "PHP internals" Message-ID: <3384e8b2-5fee-420f-b8cc-e54bba0884db@app.fastmail.com> In-Reply-To: References: <9964db8c-0ffe-43d5-8246-47fc76b07180@app.fastmail.com> <78a03dd0-fd4a-4f4a-ad8a-37e5704f06fc@app.fastmail.com> <08c8ad0b-e8f4-46e3-99f0-b80748d40b89@app.fastmail.com> <07973EAE-2D83-47A8-8FA0-84286C77C02B@rwec.co.uk> <48d66433-3ae9-4895-8361-7c81a0a3670d@app.fastmail.com> <8599eb8b-d4a3-4cb8-899a-25b134e0d64d@gmail.com> <74c4c726-63aa-44e0-84c9-840e13a65a4f@gmail.com> <9b7ab30f-5ed6-400d-b941-1291e9185286@app.fastmail.com> <9a2e81e8-3534-455b-879a-5a45c85b3ba7@gmail.com> Subject: Re: [PHP-DEV] PHP True Async RFC Content-Type: multipart/alternative; boundary=3aa9d4110d904bf1b2d12e7dae9f9934 From: rob@bottled.codes ("Rob Landers") --3aa9d4110d904bf1b2d12e7dae9f9934 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Sun, Mar 9, 2025, at 09:05, Edmond Dantes wrote: > Good day, Alex. >=20 > > > > Can you please share a bit more details on how the Scheduler is imp= lemented, to make sure that I understand why this contradiction exists? = Also with some examples, if possible. > > >=20 > ```php > $fiber1 =3D new Fiber(function () { > echo "Fiber 1 starts\n"; >=20 > $fiber2 =3D new Fiber(function () use (&$fiber1) { > echo "Fiber 2 starts\n"; >=20 > Fiber::suspend(); // Suspend the inner fiber > echo "Fiber 2 resumes\n"; >=20 > }); >=20 > }); > ``` >=20 >=20 > Yes, of course, let's try to look at this in more detail. > Here is the classic code demonstrating how `Fiber` works. `Fiber1` cre= ates `Fiber2`. When `Fiber2` yields control, execution returns to `Fiber= 1`. >=20 > Now, let's try to do the same thing with `Fiber3`. Inside `Fiber2`, we= create `Fiber3`. Everything will work perfectly=E2=80=94`Fiber3` will r= eturn control to `Fiber2`, and `Fiber2` will return it to `Fiber1`=E2=80= =94this forms a hierarchy. >=20 >=20 > Now, imagine that we want to turn `Fiber1` into a *Scheduler* while fo= llowing these rules. > To achieve this, we need to ensure that all `Fiber` instances are crea= ted from the *Scheduler*, so that control can always be properly returne= d. >=20 > ```php >=20 >=20 > class Scheduler { > private array $queue =3D []; >=20 > public function add(callable $task) { > $fiber =3D new Fiber($task); > $this->queue[] =3D $fiber; > } >=20 > public function run() { > while (!empty($this->queue)) { > $fiber =3D array_shift($this->queue); >=20 > if ($fiber->isSuspended()) { > $fiber->resume($this); > } > } > } >=20 > public function yield() { > $fiber =3D Fiber::getCurrent(); > if ($fiber) { > $this->queue[] =3D $fiber; > Fiber::suspend(); > } > } > } >=20 > $scheduler =3D new Scheduler(); >=20 > $scheduler->add(function (Scheduler $scheduler) { > echo "Task 1 - Step 1\n"; > $scheduler->yield(); > echo "Task 1 - Step 2\n"; > }); >=20 > $scheduler->add(function (Scheduler $scheduler) { > echo "Task 2 - Step 1\n"; > $scheduler->yield(); > echo "Task 2 - Step 2\n"; > }); >=20 > $scheduler->run(); >=20 > ``` >=20 > So, to successfully switch between Fibers: >=20 > 1. A Fiber must return control to the *Scheduler*. > 2. The *Scheduler* selects the next Fiber from the queue and switches= to it. > 3. That Fiber then returns control back to the *Scheduler* again. >=20 >=20 > This algorithm has one drawback: *it requires two context switches ins= tead of one*. We could switch *FiberX* to *FiberY* directly.=20 >=20 > Breaking the contract not only disrupts the code in this RFC but also = affects Revolt's functionality. However, in the case of Revolt, you can = say: *"If you use this library, follow the library's contracts and do no= t use Fiber directly."* >=20 >=20 >=20 > But PHP is not just a library, it's a language that must remain consis= tent and cohesive. >=20 >=20 > > > > Reading the RFC initially, I though that the Scheduler is using fib= ers for everything that runs.=20 > > >=20 > Exactly. =20 >=20 >=20 > > > > You mean that when one of the fibers started by the Scheduler is st= arting other fibers they would usually await for them to finish, and tha= t is a blocking operating that blocks also the Scheduler? > > >=20 > When a *Fiber* from the *Scheduler* decides to create another *Fiber* = and then tries to call blocking functions inside it, control can no long= er return to the *Scheduler* from those functions. >=20 > Of course, it would be possible to track the state and disable the con= currency mode flag when the user manually creates a *Fiber*. But=E2=80=A6= this wouldn't lead to anything good. Not only would it complicate the c= ode, but it would also result in a mess with different behavior inside a= nd outside of *Fiber*. >=20 >=20 >=20 > This is even worse than calling *startScheduler*. >=20 > The hierarchical switching rule is a *design flaw* that happened becau= se a *low-level component* was introduced into the language as part of t= he implementation of a *higher-level component*. However, the high-level= component is in *User-land*, while the low-level component is in *PHP c= ore*. >=20 > It's the same as implementing `$this` in OOP but requiring it to be ex= plicitly passed in every method. This would lead to inconsistent behavio= r. >=20 >=20 >=20 > So, this situation needs to be resolved one way or another. =20 >=20 > -- >=20 > Ed >=20 Hi Ed, If I remember correctly, the original implementation of Fibers were buil= t in such a way that extensions could create their own fiber types that = were distinct from fibers but reused the context switch code. From the original RFC: > An extension may still optionally provide their own custom fiber imple= mentation, but an internal API would allow the extension to use the fibe= r implementation provided by PHP. Maybe, we could create a different version of fibers ("managed fibers", = maybe?) distinct from the current implementation, with the idea to depre= cate them in PHP 10? Then, at least, the scheduler could always be runni= ng. If you are using existing code that uses fibers, you can't use the n= ew fibers but it will "just work" if you aren't using the new fibers (si= nce the scheduler will never pick up those fibers). Something to think about. =E2=80=94 Rob --3aa9d4110d904bf1b2d12e7dae9f9934 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable
On Sun, Mar 9, = 2025, at 09:05, Edmond Dantes wrote:
Good day, Alex.

>
> =0A=0ACan you please s= hare a bit more details on how the Scheduler is implemented, to make sur= e that I understand why this contradiction exists? Also with some exampl= es, if possible.
>

```php<= br>
$fiber1 =3D new Fiber(function () {
    echo "Fiber 1 starts\n";

&= nbsp;   $fiber2 =3D new Fiber(function () use (&$fiber1) {
<= /div>
        echo "Fiber 2 starts\n";
=

        Fiber::suspend(); // Sus= pend the inner fiber
        echo "Fib= er 2 resumes\n";

    });

});
```

Yes, of course, let's try to look at this in more deta= il.
Here is the classic code demonstrating how Fibe= r works. Fiber1 creates Fiber2. When Fiber2 yields control, execution returns to Fiber1.

Now, let's try to do the same thing with Fi= ber3. Inside Fiber2, we create Fiber3. = Everything will work perfectly=E2=80=94Fiber3 will return c= ontrol to Fiber2, and Fiber2 will return it to= Fiber1=E2=80=94this forms a hierarchy.

= Now, imagine that we want to turn Fiber1 into a Schedule= r while following these rules.
To achieve this, we ne= ed to ensure that all Fiber instances are created from the = Scheduler, so that control can always be properly returned.

```php

class Scheduler {
=     private array $queue =3D [];

=     public function add(callable $task) {
 =       $fiber =3D new Fiber($task);
  =       $this->queue[] =3D $fiber;
  =   }

    public function run(= ) {
        while (!empty($this->qu= eue)) {
            $fiber =3D= array_shift($this->queue);

   = ;         if ($fiber->isSuspended()) {
<= div>                $fiber->r= esume($this);
            }<= br>
        }
    = }

    public function yield() {
        $fiber =3D Fiber::getCurrent();=
        if ($fiber) {
&= nbsp;           $this->queue[] =3D $fiber;
            Fiber::suspend();=
        }
   = }
}

$scheduler =3D new Sched= uler();

$scheduler->add(function (Schedu= ler $scheduler) {
    echo "Task 1 - Step 1\n";<= br>
    $scheduler->yield();
 =   echo "Task 1 - Step 2\n";
});

$scheduler->add(function (Scheduler $scheduler) {
<= div>    echo "Task 2 - Step 1\n";
    = $scheduler->yield();
    echo "Task 2 - Step = 2\n";
});

$scheduler->run(= );

```

So, to successfully switch between F= ibers:

  1. A Fiber must return control to the Scheduler.
  2. The Scheduler selects the next Fiber from the queu= e and switches to it.
  3. That Fiber then returns control back t= o the Scheduler again.


This algorithm = has one drawback: it requires two context switches instead of one= . We could switch FiberX to FiberY directly. 

=

Breaking the contract not only disrupts the code in this RFC but also= affects Revolt's functionality. However, in the case of Revolt, you can= say: "If you use this library, follow the library's contracts and do= not use Fiber directly."


But PHP is not just a= library, it's a language that must remain consistent and cohesive.
<= /p>

>
> =0A=0AReading the RFC initia= lly, I though that the Scheduler is using fibers for everything that run= s. 
>

Exactly.  
<= /p>

>
> =0A=0AYou mean that when one= of the fibers started by the Scheduler is starting other fibers they wo= uld usually await for them to finish, and that is a blocking operating t= hat blocks also the Scheduler?
>

Whe= n a Fiber from the Scheduler decides to create another = Fiber and then tries to call blocking functions inside it, control c= an no longer return to the Scheduler from those functions.

Of course, it would be possible to track the state and disable the c= oncurrency mode flag when the user manually creates a Fiber. But=E2= =80=A6 this wouldn't lead to anything good. Not only would it complicate= the code, but it would also result in a mess with different behavior in= side and outside of Fiber.


This is even wors= e than calling startScheduler.

The hierarchical switchi= ng rule is a design flaw that happened because a low-level com= ponent was introduced into the language as part of the implementatio= n of a higher-level component. However, the high-level component = is in User-land, while the low-level component is in PHP core<= /b>.

It's the same as implementing $this in OOP b= ut requiring it to be explicitly passed in every method. This would lead= to inconsistent behavior.


So, this situation needs= to be resolved one way or another.  

--

E= d


Hi Ed,

If I remember correctly, the original implementation o= f Fibers were built in such a way that extensions could create their own= fiber types that were distinct from fibers but reused the context switc= h code.

From the original RFC:

An extension may still optionally provide their= own custom fiber implementation, but an internal API would al= low the extension to use the fiber implementation provided by PHP.

Maybe, we could create a dif= ferent version of fibers ("managed fibers", maybe?) distinct from the cu= rrent implementation, with the idea to deprecate them in PHP 10? Then, a= t least, the scheduler could always be running. If you are using existin= g code that uses fibers, you can't use the new fibers but it will "just = work" if you aren't using the new fibers (since the scheduler will never= pick up those fibers).

Something to think = about.

=E2=80=94 Rob
--3aa9d4110d904bf1b2d12e7dae9f9934--