Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:126802 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 318531A00BC for ; Mon, 17 Mar 2025 11:02:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1742209196; bh=63BeRODsPF6DAoSVX7PYpzoYbTNgm+WyNnSaiDZ1wPM=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=GSFi9ggFwY7wu8MBEaSj7muGJipStS4OXANBks2tL70RA4FZbO1hBvZ0phPi+fpy7 cQ44vFzhxTMhGq/4l9Is5IiNvbeIiXu0wMGyRg9UMl5nWS0C4gGJPOr7VE+o4PcAV/ E65SNRPvHfcG4hIV0+04P9dwJILPKkx/FrhbRXpe1KaCGYdJzBxfuI+kuK4FbbKIVF Kzcr/BkVLzHZaP2slcS3XAyHwt1cMMhD0wbpAY9DD836WBOeBBFYd3MdLwqWrjwZ3F OSoQD8Ju7XfP6Il2viS4548J6W97p7S9Idr+z+sOG7W/3Dtgu4u3qU3szAmK75ywQz WBALG8lLaTpDA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 3557618005B for ; Mon, 17 Mar 2025 10:59:55 +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=0.6 required=5.0 tests=BAYES_20,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, HTML_MESSAGE,MANY_SPAN_IN_TEXT,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-f182.google.com (mail-yb1-f182.google.com [209.85.219.182]) (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 ; Mon, 17 Mar 2025 10:59:55 +0000 (UTC) Received: by mail-yb1-f182.google.com with SMTP id 3f1490d57ef6-e455bf1f4d3so3164916276.2 for ; Mon, 17 Mar 2025 04:02:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1742209346; x=1742814146; 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=FLLBNy08Tq2lAvT7t9MJmbr+MLhpIi7aHtP65b7N3F4=; b=CFiZOuGixU5V7qeQVUzKTYROTp0CaQ84hESWvgrlpTYy9WkNPqe2uuyebyKH5ihjm+ sefDTdDmIeA3t0Vw4sROgFyspFp0dPMHraCXGNTMfBUC/z4B9F2wU4REp1/YizsoGAV/ VaYy/9sX2MFOX0Hr40N9ajJNuvDlBf1tnJ4JfHN75SdFvChQCUHRDeia1zD8RbQF8/IR s5P7r2r+PpsbKNCJvErmfeySsBrZBbLAIKHuUFJjdo37aj+TN+/a/L/UsGvQi4E+MZPH GdGVqvrexjyEA11M+vKGS7Lhpbw0soo3uWeJiAW1xa67kqxgYYYel68tk+TdO6U0Jhy7 n9Rg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742209346; x=1742814146; 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=FLLBNy08Tq2lAvT7t9MJmbr+MLhpIi7aHtP65b7N3F4=; b=Uf0osSnBvKZV7BwwBSpBQ0etkZg7adVviUxlxtjYLENC7jE2sw7H3pdghQ4xiDbf9A s5EtLCyzP6aBv0TdVh8o9CFuRAvrBPtuVA/L8NFDhLl/P4Maf8yAkQHVNS7ER3tBkHSK ylSZ1SUQlp7PPUMTylMXS0WZ7/7wuzy1gEU3eogiC4eC/4hNedxZVoYDF4ZoABslu9IK 4QqDNxwxCrnjc+houIdesGiYWObZK2mVWAt+cTDemVNBt1UibNzPX+KX2xFm1mmSyse9 p53CrPIcAHYZwiJGUu5z1D31R38i4Edxi6eNInusiLvAjHY59sfbYlX+gnqcDDmme8xJ Mi6g== X-Gm-Message-State: AOJu0YzECHOkqwcbrt2KzIr4B1g/STNeGz40FiFFvo9Y8SEKYDY/wKq8 dsIOwo61T4VOjk1cB+s9rq9S5KRXfCv/2cssMvWKMGuwpicDJ/EwAK2thbQ8cQgfpNQZihTBQzN xeopg2giZVQY2I2x8yVJLv0N9+hkUICCfhhc= X-Gm-Gg: ASbGnctnFliXeFIRQTGFpJeefVgVDr/h/f2ekAiE+Zb3INHQDJTftvwjy+FM5KDBmWv /wBRmqxEGuYXN8n/IBrzxs/h5ujesj4SzSdNazw2A5xH77a+hkJWukAgyhi7U0qRmS+WV5abHOL eHr0Ta9kLdEqGfHVKI1hOWGuWncQ== X-Google-Smtp-Source: AGHT+IGvR90TrZd1XBojB4j19BRuohbg1MBQeyZiAiosq1fqtlKgZKd2ZKlOOUAirQ4QNZU5ZvKaB1KCDvHA3MbABa8= X-Received: by 2002:a05:6902:1206:b0:e5d:ad43:9b36 with SMTP id 3f1490d57ef6-e63f65274bemr15729554276.20.1742209346058; Mon, 17 Mar 2025 04:02:26 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 References: <72bd5401-53a9-409f-ad45-687333401961@rwec.co.uk> In-Reply-To: <72bd5401-53a9-409f-ad45-687333401961@rwec.co.uk> Date: Mon, 17 Mar 2025 13:02:14 +0200 X-Gm-Features: AQ5f1Jrfb5bBERKCZ2VuumVfokCSnrFPiovZgxAvKsNcfwUeGVAfvSyc8uksgzw Message-ID: Subject: Re: [PHP-DEV] PHP True Async RFC - Stage 2 To: "Rowan Tommins [IMSoP]" Cc: internals@lists.php.net Content-Type: multipart/alternative; boundary="0000000000003246a5063087b9e2" From: edmond.ht@gmail.com (Edmond Dantes) --0000000000003246a5063087b9e2 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable # BoundedScope I tried to refine the `BoundedScope` class to its logical completeness, considering your feedback. However, I no longer like it because it now resembles an advanced `ComposeFuture` or `BoundedFuture` (I'm not even sure which one). There is no doubt that such functionality is needed, but I have concerns about the design. It seems better to implement `BoundedFuture` separately (placing it in a dedicated RFC) and incorporate this logic there, while `BoundedScope` might not be necessary at all. Essentially, the code using `BoundedScope` could be replaced with: ```php $scope =3D new Scope(); $future =3D BoundedFuture(); try { await $future; } finally { $scope->dispose(); } ``` On the other hand, a method like `spawnAndProlong` could be useful if there is a need to implement a pattern where the `Scope` remains alive as long as at least one task is active. But is this case significant enough to keep it? I'm not sure. I need some time to process this. In the meantime, I'll show you the draft I came up with. #### BoundedScope The `BoundedScope` class is designed to create explicit constraints that will be applied to all coroutines spawned within the specified Scope. The `BoundedScope` class implements the following pattern: ```php $scope =3D new Scope(); $constraints =3D new Future(); $scope->spawn(function () use($constraints) { try { await $constraints; } finally { \Async\currentScope()->cancel(); } }); ``` Here, `$constraints` is an object implementing the `Awaitable` interface. Once it completes, the `Scope` will be terminated, and all associated resources will be released. | Method | Description | |----------------------------------------|---------------------------------= ---------------------------------------------------------------------------= -| | `defineTimeout(int $milliseconds)` | Define a specified timeout, automatically canceling coroutines when the time expires. | | `spawnAndBound(callable $coroutine)` | Spawns a coroutine and restricts the lifetime of the entire Scope to match the coroutine=E2=80=99s lifetime. | | `spawnAndProlong(callable $coroutine)` | Spawns a coroutine and extends the lifetime of the entire Scope to match the coroutine=E2=80=99s lifetime. | | `boundedBy(Awaitable $constraint)` | Limits the scope=E2=80=99s lifet= ime based on a **Cancellation token, Future, or another coroutine's lifetime**. | | `prolongedBy(Awaitable $constraint)` | Extends the scope=E2=80=99s lifetime based on a **Cancellation token, Future, or another coroutine's lifetime**. | ```php $scope =3D new BoundedScope(); $scope->defineTimeout(1000); $scope->spawnAndBound(function() { sleep(2); echo "Task 1\n"; }); await $scope; ``` ##### Prolong and Bound triggers The `BoundedScope` class operates with two types of triggers: - **Bound trigger** =E2=80=93 limits execution time by the minimum boundary= . - **Prolong trigger** =E2=80=93 limits execution time by the maximum bounda= ry. For the **Prolong** trigger to execute, all **Prolong** objects must be completed. For the **Bound** trigger to execute, at least one **Bound** object must be completed. The `Scope` will terminate as soon as either the **Prolong** or **Bound** trigger is executed. ##### defineTimeout The `defineTimeout` method sets a global timeout for all coroutines belonging to a `Scope`. The method initializes a single internal timer, which starts when `defineTimeout` is called. When the timer expires, the `Scope::cancel()` method is invoked. The `defineTimeout` method can only be called once; a repeated call will throw an exception. ##### spawnAndBound / spawnAndProlong `spawnAndBound` creates a coroutine and limits its execution time to the current `Scope`. The method can be called multiple times. In this case, the `Scope` will not exist longer than the lifetime of the shortest coroutine. `spawnAndProlong` creates a coroutine and extends the lifetime of the current `Scope` to match the coroutine's lifetime. ##### boundedBy / prolongedBy The `boundedBy` method allows limiting the lifetime of a `Scope` by explici= tly specifying an object that implements the `Awaitable` interface. The `Awaitable` interface is inherited by classes such as `Coroutine` and `Scope`. Additionally, classes like `Future` and `Cancellation`, which are not part of this RFC, can also implement the `Awaitable` interfac= e. --0000000000003246a5063087b9e2 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
#=C2=A0BoundedScope

I tried to refine the `BoundedScope` class to its logical completen= ess, considering your feedback. =C2=A0
However, I no longer like it beca= use it now resembles an advanced `ComposeFuture` or `BoundedFuture` (I'= m not even sure which one). =C2=A0

There is no doubt that such funct= ionality is needed, but I have concerns about the design. =C2=A0
It seem= s better to implement `BoundedFuture` separately (placing it in a dedicated= RFC) and incorporate this logic there, while `BoundedScope` might not be n= ecessary at all. =C2=A0

Essentially, the code using `BoundedScope` c= ould be replaced with:

```php
$scope =3D new Scope();
$future = =3D BoundedFuture();
try {
=C2=A0 =C2=A0 =C2=A0await $future;
} fi= nally {
=C2=A0 =C2=A0 $scope->dispose();
}
```
=

On the other hand, a method like `spawnAndProlong` coul= d be useful if there is a need to implement a pattern where the `Scope` rem= ains alive as long as at least one task is active. =C2=A0

But is thi= s case significant enough to keep it? I'm not sure.=C2=A0=C2=A0

I need some time to process this. =C2=A0
In the me= antime, I'll show you the draft I came up with.=C2=A0=C2=A0

=
#### BoundedScope

The `BoundedScope` class is desi= gned to create explicit constraints
that will be applied to all corouti= nes spawned within the specified Scope.

The `BoundedScope` class implements the following pattern:

```php
$scope =3D new Scope();

$constraints =3D new Future();=

$scope->spawn(function () use($constraints) {

<= /span> try {
await $constraints;
} finally {
= = \Async\currentScope()->cancel();
}
});
```

Here, `$constraints` is an object implementing the `Awaitable` in= terface.
Once it completes, the `Scope` will be terminated, = and all associated resources will be released.


| Method | Description = |
|----------------------------------------|-----------------------------= ---------------------------------------------------------------------------= -----|
| `defineTimeou= t(int $milliseconds)` | Defi= ne a specified timeout, automatically canceling coroutines when the time ex= pires. |
| `spawnAndBound(callable $cor= outine)` | Spawns a coroutine = and restricts the lifetime of the entire Scope to match the coroutine=E2=80= =99s lifetime. |
| `
spawnAndProlong(callable $coroutin= e)` | Spawns a coroutine and ext= ends the lifetime of the entire Scope to match the coroutine=E2=80=99s life= time. |
| `boundedBy(Awaitable $constraint)` | Limits the scope=E2=80=99s lifet= ime based on a **Cancellation to= ken, Future, or another coroutine's lifetime**. |
| `
prolongedBy(Awaitable $constrain= t)` | Extends the scope=E2=80= =99s lifetime based on a **Cance= llation token, Future, or another coroutine's lifetime**. |
<= /span>
```php
$scope =3D new BoundedScope();
$scope->defineTimeout(= 1000);

$scope->spawnAndBound(function() {
sleep(2);<= br> echo "Task 1\n";
});

await $scope;
```

#####
Prolong and Bound triggers
<= br>The `BoundedScope` class operates with two types of triggers= :

- **Bound trigger** =E2=80=93 limits execution time by = the minimum boundary.
- **Pro= long trigger** =E2=80=93 limits = execution time by the maximum boundary.

For the **Prolong** trigger to execute, all **Pro= long** objects must be completed= .
For the **Bound** trigger to execute, at least one **Bound** object must be completed.

The `Scope` will= terminate as soon as either the **Prolong** or **Bound** trigger is executed.

##### = defineTimeout

The `defineTimeout` method sets a gl= obal timeout for all coroutines belonging to a `Scope`.
The = method initializes a single internal timer, which starts when `defineTimeout` is called.
When the timer expires, the `Scope::cancel()` method is invoked.

The `defineTimeout` metho= d can only be called once; a repeated call will throw an exception.

= ##### spawnAndBound / spawnAndPr= olong

`spawnAndBound`
creates a coroutine and limits its = execution time to the current `S= cope`.
The method can be call= ed multiple times. In this case, the `<= /span>Scope` will not exist long= er than
the lifetime of the shortest coroutine.

`spawnAndProlong` creates a coroutine and extends the lifetime of the current `
Scope`
to match the coroutine's lifetime.

##### boundedBy / prolongedBy

The= `boundedBy` method allows limiting the lifetime of a `Scope= ` by explicitly
specifying an object that implements the `Awaitable` interface.

The `= Awaitable` interface is i= nherited by classes such as `Cor= outine` and `Scope`. =
Additionally, classes like `= Future` and `Cancellation`,
which are not part of this RFC, can also implement the `Awaitable` interface.
--0000000000003246a5063087b9e2--