Skip to content
This repository was archived by the owner on Jun 16, 2025. It is now read-only.

Commit ddad941

Browse files
committed
Refactor TaskGroup constructor to remove 'bounded' parameter; update documentation for clarity on Scope and TaskGroup usage
1 parent bb2d770 commit ddad941

File tree

2 files changed

+43
-44
lines changed

2 files changed

+43
-44
lines changed

basic.md

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1645,7 +1645,6 @@ function generateReport(): void
16451645
} catch (Exception $e) {
16461646
echo "Failed to generate report: ", $e->getMessage(), "\n";
16471647
} finally {
1648-
$taskGroup->dispose();
16491648
$scope->disposeSafely();
16501649
}
16511650
}
@@ -1900,7 +1899,7 @@ final class ProcessPool
19001899
$this->poolScope = new Scope();
19011900
$this->watcherScope = new Scope();
19021901
$this->jobsScope = new Scope();
1903-
$this->taskGroup = new TaskGroup(captureResults: false);
1902+
$this->taskGroup = new TaskGroup($this->poolScope, captureResults: false);
19041903
}
19051904

19061905
public function __destruct()
@@ -2066,21 +2065,51 @@ that all coroutines launched within it should also be terminated.
20662065

20672066
#### Combining TaskGroup and Scope
20682067

2069-
`TaskGroup` is convenient to use in combination with `Scope`, if
2070-
the `$scope` object belongs exclusively to the `TaskGroup`.
2071-
In that case, when the `TaskGroup` goes out of scope,
2072-
the `Scope` object and all associated tasks will be destroyed together.
2068+
Combining `Scope` and `TaskGroup` helps implement the pattern of primary and secondary tasks,
2069+
where tasks in the `TaskGroup` are treated as explicit, primary tasks that must be monitored for completion.
2070+
All other tasks that enter the `Scope` while the primary tasks are running are considered secondary.
2071+
2072+
The following code demonstrates this idea:
20732073

20742074
```php
2075-
$scope = new Async\Scope();
2076-
$taskGroup = new Async\TaskGroup(scope: $scope, captureResults: false); // <- Scope reference equals two.
2075+
use Async\Scope;
2076+
use Async\TaskGroup;
2077+
2078+
function targetTask(int $i): void
2079+
{
2080+
spawn {
2081+
// subtask
2082+
};
2083+
}
2084+
2085+
$scope = new Scope();
2086+
$taskGroup = new TaskGroup(scope: $scope, captureResults: true);
2087+
2088+
for($i = 0; $i < 10; $i++) {
2089+
$taskGroup->add(spawn with $scope targetTask($i));
2090+
}
2091+
2092+
try {
2093+
$results = await $taskGroup;
2094+
} finally {
2095+
$scope->dispose();
2096+
}
2097+
```
2098+
2099+
Coroutines that can be created by `targetTask` will be placed into `$scope`.
2100+
However, only the tasks that are explicitly added to the `TaskGroup` will be awaited.
2101+
When `$scope` is cancelled, the `TaskGroup` will be disposed of along with it.
2102+
2103+
```php
2104+
$scope = new Async\Scope(); // <- $scope reference equals one.
2105+
$taskGroup = new Async\TaskGroup(scope: $scope, captureResults: false); // <- Scope reference equals one.
20772106
$taskGroup->add(spawn with $scope {
20782107
Async\delay(5000);
20792108
echo "This line will be executed\n";
20802109
});
2081-
unset($scope); // <- $scope reference equals one.
20822110
sleep(1);
2083-
$taskGroup->dispose(); // <- $scope reference equals zero.
2111+
$scope->dispose(); // <- $scope reference equals zero.
2112+
// $taskGroup->dispose() will be called before the $scope disposal.
20842113
```
20852114

20862115
**Expected output:**
@@ -2089,42 +2118,12 @@ $taskGroup->dispose(); // <- $scope reference equals zero.
20892118
```
20902119

20912120
There are no warnings about **zombie coroutines** in the output
2092-
because the task was canceled using `$taskGroup->dispose()`, after which the `scope` was also destroyed.
2121+
because the task was canceled using `$taskGroup->dispose()`.
20932122

20942123
However, if the `Scope` contains other coroutines that were created outside the `TaskGroup`,
20952124
they will follow the general rules. In the case of the `Scope::disposeSafely()` strategy,
20962125
a warning will be issued if unfinished tasks are detected, as they would become **zombie coroutines**.
20972126

2098-
If you are certain that all tasks should be canceled, use the `bounded: true` option in the `TaskGroup` constructor.
2099-
In this case, the `Scope` will be terminated using the `Scope::dispose()` strategy.
2100-
2101-
```php
2102-
use Async\TaskGroup;
2103-
use Async\Scope;
2104-
use function Async\delay;
2105-
2106-
$scope = new Scope();
2107-
$taskGroup = new TaskGroup(scope: , captureResults: false, bounded: true);
2108-
2109-
spawn with $scope { // <- it's a zombie coroutine, but it will be canceled because of the bounded task group
2110-
delay(5000);
2111-
echo "This line will be executed1\n";
2112-
};
2113-
2114-
$taskGroup->spawn(function() {
2115-
delay(5000);
2116-
echo "This line will be executed2\n";
2117-
});
2118-
2119-
sleep(1);
2120-
$scope->dispose();
2121-
```
2122-
2123-
**Expected output:**
2124-
2125-
```
2126-
```
2127-
21282127
#### TaskGroup Race
21292128

21302129
The `TaskGroup` class allows you to wait for the first task to complete using the `race()` method.
@@ -2134,7 +2133,8 @@ use Async\TaskGroup;
21342133

21352134
function fetchFirstSuccessful(string ...$apiHosts): string
21362135
{
2137-
$taskGroup = new Async\TaskGroup(scope: new Async\Scope(), captureResults: false, bounded: true);
2136+
$scope = new Async\Scope();
2137+
$taskGroup = new Async\TaskGroup(scope: $scope, captureResults: false);
21382138

21392139
foreach ($apiHosts as $host) {
21402140
$taskGroup->spawn(function() use ($host) {

examples/Async/TaskGroup.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ final class TaskGroup implements Awaitable
88
{
99
public function __construct(
1010
private ?Scope $scope = null,
11-
private bool $ignoreErrors = false,
12-
private bool $bounded = false,
11+
private bool $ignoreErrors = false
1312
) {}
1413

1514
public function race(bool $ignoreErrors = false): Awaitable {}

0 commit comments

Comments
 (0)