@@ -1331,8 +1331,8 @@ spawn with $scope {
13311331 spawn {
13321332 spawn {
13331333 throw new Exception("Error occurred");
1334- };
1335- };
1334+ };
1335+ };
13361336};
13371337
13381338try {
@@ -1349,7 +1349,7 @@ Error occurred
13491349```
13501350
13511351The ` await $scope ` expression can be used multiple times
1352- because ` $scope ` acts as a trigger that can transition to a completed state multiple times:
1352+ because ` $scope ` acts as a ** trigger** that can transition to a completed state multiple times:
13531353
13541354``` php
13551355$scope = new Scope();
@@ -1485,13 +1485,22 @@ function connectionLimiter(callable $cancelToken): void
14851485
14861486function connectionHandler($socket): void
14871487{
1488- $scope = Scope::inherit();
1489-
1488+ // Note that a parent Scope can stop the execution of all coroutines
1489+ // belonging to a child Scope at any moment.
1490+ $scope = Scope::inherit();
1491+
1492+ //
1493+ // Passing `$scope` via `use` into a single coroutine is equivalent to the logic:
1494+ // the lifetime of `$scope` equals the lifetime of the coroutine.
1495+ // In this way, we create a coroutine-closure that acts as a Point of Responsibility.
1496+ // This code is one example of how to implement Points of Responsibility.
1497+ //
14901498 spawn with $scope use($socket, $scope) {
14911499
14921500 $limiterScope = Scope::inherit(); // child scope for connectionLimiter and connectionChecker
14931501
1494- $cancelToken = fn(string $message) => $scope->cancel(new CancellationException($message));
1502+ // We do not provide direct access to the Scope object in other functions, because this is an antipattern!
1503+ $cancelToken = fn(string $message) => $scope->cancel(new CancellationException($message));
14951504
14961505 // Limiter coroutine
14971506 spawn with $limiterScope connectionLimiter($cancelToken);
@@ -1505,26 +1514,37 @@ function connectionHandler($socket): void
15051514 fwrite($socket, "HTTP/1.1 500 Internal Server Error\r\n\r\n");
15061515 } finally {
15071516 fclose($socket);
1517+ // Explicitly cancel all coroutines in the child scope
15081518 $scope->cancel();
15091519 }
15101520 };
15111521}
15121522
15131523function socketServer(): void
15141524{
1525+ // Main Server $scope
15151526 $scope = new Scope();
15161527
15171528 // Child coroutine that listens for a shutdown signal
1529+ //
1530+ // Note that we are passing `$scope` to another function!
1531+ // This is acceptable here because the code is within a single visual block,
1532+ // and the risk of an error due to oversight is minimal.
15181533 spawn with $scope use($scope) {
15191534 try {
1535+ // Note: The `signal` function is not part of this RFC,
1536+ // but it may be implemented in the standard library in the future.
1537+ // This example shows how such a function could be used.
1538+ // The `signal` function returns a trigger `Awaitable`.
15201539 await Async\signal(SIGINT);
15211540 } finally {
15221541 $scope->cancel(new CancellationException('Server shutdown'));
15231542 }
15241543 }
15251544
15261545 try {
1527- // Main coroutine that listens for incoming connections
1546+ // The main coroutine that listens for incoming connections
1547+ // The server runs as long as this coroutine is running.
15281548 await spawn with $scope {
15291549 while ($socket = stream_socket_accept($serverSocket, 0)) {
15301550 connectionHandler($socket);
@@ -1538,7 +1558,8 @@ function socketServer(): void
15381558 // Graceful exit
15391559 try {
15401560 $scope->cancel();
1541- await $scope until Async\timeout(5);
1561+ // Await for all coroutines to finish but not more than 5 seconds
1562+ await $scope until Async\timeout(5000);
15421563 echo "Server stopped\n";
15431564 } catch (\Throwable $exception) {
15441565 // Force exit
0 commit comments