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

Commit 2f70b92

Browse files
committed
+ Spawn with child expression
1 parent 130492d commit 2f70b92

File tree

2 files changed

+82
-14
lines changed

2 files changed

+82
-14
lines changed

basic.md

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -339,10 +339,10 @@ The `spawn` construct is available in two variations:
339339

340340
```php
341341
// Executing a known function
342-
spawn [with <scope>] <function_call>;
342+
spawn [with [child] <scope>] <function_call>;
343343

344344
// Closure form
345-
spawn [with <scope>] [static] [use(<parameters>)][: <returnType>] {
345+
spawn [with [child] <scope>] [static] [use(<parameters>)][: <returnType>] {
346346
<codeBlock>
347347
};
348348
```
@@ -444,7 +444,7 @@ spawn (fn() => sleep(1))();
444444
Allows creating a coroutine from a closure directly when using `spawn`:
445445

446446
```php
447-
spawn [with <scope>] [static] [use(<parameters>)[: <returnType>]] {
447+
spawn [with [child] <scope>] [static] [use(<parameters>)[: <returnType>]] {
448448
<codeBlock>
449449
};
450450
```
@@ -530,6 +530,70 @@ spawn with $this->scope $this->method();
530530
spawn with $this->getScope() $this->method();
531531
```
532532

533+
#### Spawn with child expression
534+
535+
The `spawn with` expression allows you to create siblings relative to the specified scope.
536+
However, it can be useful to create a coroutine in a child scope to establish a clear hierarchy.
537+
538+
```php
539+
use Async\Scope;
540+
541+
$scope = new Scope();
542+
543+
spawn with $scope wathcher();
544+
545+
spawn with $scope use($scope): void {
546+
foreach ($hosts as $host) {
547+
$child = Scope::inherit($scope);
548+
549+
$coroutine = spawn with $scope {
550+
echo gethostbyname('php.net').PHP_EOL;
551+
};
552+
553+
$coroutine->onComplete(fn() => $child->disposeSafely());
554+
}
555+
};
556+
557+
$scope->awaitAllIgnoringErrors();
558+
```
559+
560+
**Structure:**
561+
562+
```
563+
$scope = new Scope();
564+
├── watcher() ← runs in the $scope
565+
├── foreach($hosts) ← runs in the $scope
566+
├── $child = Scope::inherit($scope)
567+
│ └── subtask1() ← runs in the childScope
568+
├── $child = Scope::inherit($scope)
569+
│ └── subtask2() ← runs in the childScope
570+
├── $child = Scope::inherit($scope)
571+
│ └── subtask3() ← runs in the childScope
572+
```
573+
574+
This structure separates main tasks belonging to the `$scope` and child tasks that are launched in child scopes.
575+
Each child task can be cancelled independently of the others, since it belongs to a separate scope (Supervisor pattern).
576+
577+
The `child` keyword is used to create a child scope from the specified scope.
578+
579+
```php
580+
use Async\Scope;
581+
582+
$scope = new Scope();
583+
584+
spawn with $scope wathcher();
585+
586+
spawn with $scope use($scope): void {
587+
foreach ($hosts as $host) {
588+
spawn with child $scope {
589+
echo gethostbyname('php.net').PHP_EOL;
590+
};
591+
}
592+
};
593+
594+
$scope->awaitAllIgnoringErrors();
595+
```
596+
533597
### Suspension
534598

535599
A coroutine can suspend itself at any time using the `suspend` keyword:
@@ -2580,7 +2644,14 @@ final class Service
25802644
private function run(): void
25812645
{
25822646
while (($socket = $this->service->receive()) !== null) {
2583-
spawn with Scope::inherit($this->scope) $this->handleRequest($socket);
2647+
2648+
$scope = Scope::inherit($this->scope);
2649+
2650+
(spawn with $scope $this->handleRequest($socket))->onCompletion(
2651+
static function () use ($scope) {
2652+
$scope->disposeSafely();
2653+
}
2654+
);
25842655
}
25852656
}
25862657
}

examples/RequestHandler.php

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
final class RequestHandler
99
{
1010
private Scope $scope;
11-
private Scope $connectionScope;
1211

1312
public function __construct()
1413
{
@@ -25,7 +24,7 @@ public function start(): void
2524
spawn with $this->scope use($server) {
2625
try {
2726
while (($client = stream_socket_accept($server)) !== false) {
28-
spawn with $this->connectionScope $this->handleConnection($client);
27+
spawn with child $this->scope $this->handleConnection($client);
2928
}
3029
} finally {
3130
fclose($server);
@@ -36,14 +35,12 @@ public function start(): void
3635

3736
private function handleConnection($client): void
3837
{
39-
async inherit bounded $scope {
40-
try {
41-
$request = fread($client, 1024);
42-
$response = "HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World!";
43-
fwrite($client, $response);
44-
} finally {
45-
fclose($client);
46-
}
38+
try {
39+
$request = fread($client, 1024);
40+
$response = "HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World!";
41+
fwrite($client, $response);
42+
} finally {
43+
fclose($client);
4744
}
4845
}
4946
}

0 commit comments

Comments
 (0)