Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:126667 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 70BD31A00BC for ; Sun, 9 Mar 2025 08:05:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1741507364; bh=8/JKKAZBXbGBDw0Fzgxk5qHZz9uG87ofa1p1SEjwv2Y=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=T4IReymjbRDkrhb4qIRFQEmV3O7ya9TfjhLr+S8Sh7ckS5VVsNTxzBKqrhk/Y1aLD 1mCsJhAn4myrogu6aSY/U5+Xufu5fMxl//t2RF5khzAQNCu7VZG6k/8QChbGU76wKp Bp9woeSmexKw0UWwpyRJdDU8er+HW8kss+Gc43cSBLFItJAIffyXFDfLRYyUN/dJer RTalqZpa1j0bsf3L+I0kTdg3w7OZZSF0OG15BMVoLFnl+4t7htdp6kaZ3++2B9NffX +R9aeI5PcxDv/MA6fN1Ntxhrrsf5CgYe7pns5fmmKj4ctMfVG163IlRusHoRMrwtii gx6hFBr5xSwXA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 69ACE180037 for ; Sun, 9 Mar 2025 08:02:43 +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_20,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-f173.google.com (mail-yb1-f173.google.com [209.85.219.173]) (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 08:02:43 +0000 (UTC) Received: by mail-yb1-f173.google.com with SMTP id 3f1490d57ef6-e636585c375so1330637276.1 for ; Sun, 09 Mar 2025 00:05:17 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1741507517; x=1742112317; 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=GICHGWazdr6cIKogLii56Lwl0GLt4ZJqLAG44T/LvYE=; b=WL+1IW8JslSs0DkDk0fXH1A7VTa41ctuFvm0zpZ31yd5JqaPR7C4qTcKkDjk5+2vX1 IbaYju0jEwXKGcJ7fjoDy0QaF4W68rOiQZ0MPp7yf9YqmS4Ea44EAdNGZb61ddqe+VzY gW5GBwmGs19oHi7tN7gMGFiw6iQxIOMoUalWnBz9bxWgNLDT2ZNrGL9Du5mf79kNSbTX FHgsOHfsA9VLbOGukbGNPiP25iaZvpQzIp06C1jO13ziSr9f4Ob+H8H81NepgxiXD/1k ikJKWbyNeg8rru+5j7z/1q5FsOH/+JWjpzFUejl6dWcsAnz+gQoXCrx+qeIrjJmwiurr uAPw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741507517; x=1742112317; 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=GICHGWazdr6cIKogLii56Lwl0GLt4ZJqLAG44T/LvYE=; b=w5T2yJTnsE+Hsz1GsztxM5qXCc7vlbF6Hg8c3d9YJni5h1MkmsWU4mahiy/cNXJ3oV Eife3EyHdaRa7Dd1rVXdXgP0dR+YR76/8gh2yVTbppPSS+E5QRqTkx6hyTmBKRXJKvN4 9hlNrbb3+N3L7t5q74Tyv7x3HxbN2eHdq1ZskGsfhYqO0WNAewTToDy5EzyA54enD8MY LzdgcMX7Cc11fesoqlF80LAo2SBdeqdSsA5vFpDukztRI/TRYc1oHBGZESHOO4FyMQeB pgcK5HsQfoaZOheMo2DFQ0sckB1f0S4bpg1wvH5ptkEHBZqlHoW/kmGX2kLALmU6e00N YxWQ== X-Forwarded-Encrypted: i=1; AJvYcCVypwuBZC083GglDduaZQWk1kkiqbsfQdlvdhujUKyRkNpDhDmWnVrWuz3y57pz5XDaZXpK9fgOE1U=@lists.php.net X-Gm-Message-State: AOJu0YxHWh71q91ZdbIVzl9pzZvKazmO9cY//zoYJfAv20s8jVr/X2sV 1JlCv+wolAm0/OB2HKGMAIq8GSfn5uAYBvQeHPkX2Tn5yEewmpT+INDNnsGoetlxWMh0CQ7uIpg o5579X6E1AQWWbW6+GsHCeceiAHs= X-Gm-Gg: ASbGncsYQLw3NortQnSBuvREwwdUsm523i7c3+YRywfdKP6ZiuDEkExlQldsoCB8ZJv OFi/yw7wYgbd9kRS0vv6p+Y1D2J/KSLpPkJHAt3W2nfD0i3aG27UV1pKSNGr7+6rdJm84R/8XF+ XAA4Qb7suVMg6Z/f8gXgT0O/MLdw== X-Google-Smtp-Source: AGHT+IHEB6Ceg6FJvbA9pKl/vbWZDF3ZKLp5T3OXGG0+335bjUgG58h3scMf9JjxKNv7YISCOsQxpdJvF3JlHYnnfIY= X-Received: by 2002:a05:6902:e0e:b0:e60:9821:3a with SMTP id 3f1490d57ef6-e635c101784mr12842093276.6.1741507516930; Sun, 09 Mar 2025 00:05:16 -0800 (PST) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 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> In-Reply-To: Date: Sun, 9 Mar 2025 10:05:06 +0200 X-Gm-Features: AQ5f1Jq0rS9eaaD2KXSA334qA5HEs5dMAEKpjra88LPCixGwd977wH9CacKEask Message-ID: Subject: Re: [PHP-DEV] PHP True Async RFC To: =?UTF-8?Q?Alexandru_P=C4=83tr=C4=83nescu?= Cc: Daniil Gentili , PHP internals Content-Type: multipart/alternative; boundary="000000000000eba588062fe450cd" From: edmond.ht@gmail.com (Edmond Dantes) --000000000000eba588062fe450cd Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Good day, Alex. > > Can you please share a bit more details on how the Scheduler is implemented, to make sure that I understand why this contradiction exists? Also with some examples, if possible. > ```php $fiber1 =3D new Fiber(function () { echo "Fiber 1 starts\n"; $fiber2 =3D new Fiber(function () use (&$fiber1) { echo "Fiber 2 starts\n"; Fiber::suspend(); // Suspend the inner fiber echo "Fiber 2 resumes\n"; }); }); ``` Yes, of course, let's try to look at this in more detail. Here is the classic code demonstrating how Fiber works. Fiber1 creates Fiber2. When Fiber2 yields control, execution returns to Fiber1. Now, let's try to do the same thing with Fiber3. Inside Fiber2, we create Fiber3. Everything will work perfectly=E2=80=94Fiber3 will return control t= o Fiber2, and Fiber2 will return it to Fiber1=E2=80=94this forms a hierarchy. Now, imagine that we want to turn Fiber1 into a *Scheduler* while following these rules. To achieve this, we need 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->queue)) { $fiber =3D array_shift($this->queue); if ($fiber->isSuspended()) { $fiber->resume($this); } } } public function yield() { $fiber =3D Fiber::getCurrent(); if ($fiber) { $this->queue[] =3D $fiber; Fiber::suspend(); } } } $scheduler =3D new Scheduler(); $scheduler->add(function (Scheduler $scheduler) { echo "Task 1 - Step 1\n"; $scheduler->yield(); echo "Task 1 - Step 2\n"; }); $scheduler->add(function (Scheduler $scheduler) { echo "Task 2 - Step 1\n"; $scheduler->yield(); echo "Task 2 - Step 2\n"; }); $scheduler->run(); ``` So, to successfully switch between Fibers: 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. 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. > > Reading the RFC initially, I though that the Scheduler is using fibers for everything that runs. > Exactly. > > You mean that when one of the fibers started by the Scheduler is starting other fibers they would usually await for them to finish, and that is a blocking operating that blocks also the Scheduler? > When a *Fiber* from the *Scheduler* decides to create another *Fiber* and then tries to call blocking functions inside it, control can no longer return to the *Scheduler* from those functions. Of course, it would be possible to track the state and disable the concurrency 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 inside and outside of *Fiber*. This is even worse than calling *startScheduler*. The hierarchical switching rule is a *design flaw* that happened because a *low-level component* was introduced into the language as part of the implementation of a *higher-level component*. However, the high-level component is in *User-land*, while the low-level component is in *PHP core*. It's the same as implementing $this in OOP but 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. -- Ed --000000000000eba588062fe450cd Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Good day, Alex.

>
>=C2=A0 Can you please share a bit more details on how the Scheduler is implemented= , to make sure that I understand why this contradiction exists? Also with s= ome examples, if possible.
>

```php
$fiber1 =3D new = Fiber(function () {
=C2=A0 =C2=A0 echo "Fiber 1 starts\n";
=
=C2=A0 =C2=A0 $fiber2 =3D new Fiber(function () use (&$fiber1) {=C2=A0 =C2=A0 =C2=A0 =C2=A0 echo "Fiber 2 starts\n";

=C2= =A0 =C2=A0 =C2=A0 =C2=A0 Fiber::suspend(); // Suspend the inner fiber
= =C2=A0 =C2=A0 =C2=A0 =C2=A0 echo "Fiber 2 resumes\n";

=C2= =A0 =C2=A0 });

});
```

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

Now, let's try to do the same thing with Fiber3. Inside= Fiber2, we create Fiber3. Everything will work p= erfectly=E2=80=94Fiber3 will return control 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 Sch= eduler while following these rules.
To achieve this, we need to ensure that all Fiber instances ar= e created from the Scheduler, so that control can always b= e properly returned.

```php

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

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

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

=C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 if ($fiber->isSuspended()) {
=C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 $fiber->resume($this);
=C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
=C2=A0 =C2=A0 =C2=A0 =C2=A0 }=C2=A0 =C2=A0 }

=C2=A0 =C2=A0 public function yield() {
=C2=A0 = =C2=A0 =C2=A0 =C2=A0 $fiber =3D Fiber::getCurrent();
=C2=A0 =C2=A0 =C2= =A0 =C2=A0 if ($fiber) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 $this= ->queue[] =3D $fiber;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Fiber= ::suspend();
=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
=C2=A0 =C2=A0 }
}
$scheduler =3D new Scheduler();

$scheduler->add(function (Sched= uler $scheduler) {
=C2=A0 =C2=A0 echo "Task 1 - Step 1\n";
= =C2=A0 =C2=A0 $scheduler->yield();
=C2=A0 =C2=A0 echo "Task 1 - = Step 2\n";
});

$scheduler->add(function (Scheduler $sched= uler) {
=C2=A0 =C2=A0 echo "Task 2 - Step 1\n";
=C2=A0 =C2= =A0 $scheduler->yield();
=C2=A0 =C2=A0 echo "Task 2 - Step 2\n&q= uot;;
});

$scheduler->run();

```

So, to succes= sfully switch between Fibers:

  1. A Fiber must return control to the Scheduler.
  2. The Scheduler selects the next Fiber from the queue an= d switches to it.
  3. That Fiber then returns control back to the Scheduler = again.

This algorithm has one drawback: it requires two context swi= tches instead of one. We could switch FiberX to <= strong>FiberY directly.=C2=A0

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.

>
>=C2=A0 Reading the RFC initially, I though that the Scheduler is using fibers for = everything that runs.=C2=A0
>

Exactly.=C2=A0=C2=A0

>>=C2=A0 You mean that when one of the fibers started by the Scheduler is starting o= ther fibers they would usually await for them to finish, and that is a bloc= king operating that blocks also the Scheduler?
>

When a Fiber from the Scheduler decides to create anoth= er Fiber and then tries to call blocking functions inside = it, control can no longer return to the Scheduler from tho= se functions.

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

This is even worse than calling startScheduler.

=

The hierarchical switching rule is a design flaw that h= appened because a low-level component was introduced into = the language as part of the implementation of a higher-level compon= ent. However, the high-level component is in User-land, while the low-level component is in PHP core.

It's the same as implementing $this in OOP but 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.=C2=A0=C2= =A0

--

Ed

--000000000000eba588062fe450cd--