Skip to content

Commit 0e62cd6

Browse files
committed
#9: * fixes for Scope tests
1 parent 41bf8fc commit 0e62cd6

18 files changed

+231
-307
lines changed

scheduler.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,8 @@ void async_scheduler_main_coroutine_suspend(void)
719719
if (UNEXPECTED(EG(exception) != NULL)) { \
720720
if(ZEND_ASYNC_GRACEFUL_SHUTDOWN) { \
721721
finally_shutdown(); \
722+
switch_to_scheduler(transfer); \
723+
zend_exception_restore(); \
722724
return; \
723725
} \
724726
start_graceful_shutdown(); \

scope.c

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,18 @@ METHOD(isClosed)
445445
RETURN_BOOL(ZEND_ASYNC_SCOPE_IS_CLOSED(&scope_object->scope->scope));
446446
}
447447

448+
METHOD(isCancelled)
449+
{
450+
ZEND_PARSE_PARAMETERS_NONE();
451+
452+
async_scope_object_t *scope_object = THIS_SCOPE;
453+
if (UNEXPECTED(scope_object->scope == NULL)) {
454+
RETURN_BOOL(scope_object->is_cancelled);
455+
}
456+
457+
RETURN_BOOL(ZEND_ASYNC_SCOPE_IS_CANCELLED(&scope_object->scope->scope));
458+
}
459+
448460
METHOD(setExceptionHandler)
449461
{
450462
zend_fcall_info fci;
@@ -889,6 +901,10 @@ static bool scope_catch_or_cancel(
889901

890902
ZEND_ASYNC_SCOPE_SET_CANCELLED(&async_scope->scope);
891903

904+
if (((async_scope_object_t *)async_scope->scope.scope_object) != NULL) {
905+
((async_scope_object_t *)async_scope->scope.scope_object)->is_cancelled = true;
906+
}
907+
892908
// If an unexpected exception occurs during the function's execution, we combine them into one.
893909
if (EG(exception)) {
894910
exception = zend_exception_merge(exception, true, transfer_error);
@@ -1123,7 +1139,8 @@ static void scope_dispose(zend_async_event_t *scope_event)
11231139
FREE_HASHTABLE(scope->finally_handlers);
11241140
scope->finally_handlers = NULL;
11251141
}
1126-
1142+
1143+
zend_async_callbacks_free(&scope->scope.event);
11271144
async_scope_free_coroutines(scope);
11281145
zend_async_scope_free_children(&scope->scope);
11291146
efree(scope);
@@ -1148,6 +1165,7 @@ zend_async_scope_t * async_new_scope(zend_async_scope_t * parent_scope, const bo
11481165
}
11491166

11501167
scope_object->scope = scope;
1168+
scope_object->is_cancelled = false;
11511169
scope->scope.scope_object = &scope_object->std;
11521170
}
11531171

scope.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ typedef struct _async_scope_object_s {
6060
struct {
6161
char _padding[sizeof(zend_object) - sizeof(zval)];
6262
async_scope_t *scope;
63+
bool is_cancelled; /* Indicates if the scope is cancelled */
6364
};
6465
};
6566
} async_scope_object_t;

scope.stub.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ public function afterCoroutineEnqueue(Coroutine $coroutine, Scope $scope): void;
3535
*/
3636
final class Scope implements ScopeProvider
3737
{
38-
//public readonly Context $context;
39-
4038
/**
4139
* Creates a new Scope that inherits from the specified one. If the parameter is not provided,
4240
* the Scope inherits from the current one.
@@ -65,6 +63,8 @@ public function isFinished(): bool {}
6563

6664
public function isClosed(): bool {}
6765

66+
public function isCancelled(): bool {}
67+
6868
/**
6969
* Sets an error handler that is called when an exception is passed to the Scope from one of its child coroutines.
7070
*/

scope_arginfo.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: 6106b3351f8094535fee7310e1d096ed06fb813d */
2+
* Stub hash: 655728a28912cc420f1fe0ae483291cff8153fdc */
33

44
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Async_ScopeProvider_provideScope, 0, 0, Async\\Scope, 1)
55
ZEND_END_ARG_INFO()
@@ -49,6 +49,8 @@ ZEND_END_ARG_INFO()
4949

5050
#define arginfo_class_Async_Scope_isClosed arginfo_class_Async_Scope_isFinished
5151

52+
#define arginfo_class_Async_Scope_isCancelled arginfo_class_Async_Scope_isFinished
53+
5254
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Async_Scope_setExceptionHandler, 0, 1, IS_VOID, 0)
5355
ZEND_ARG_TYPE_INFO(0, exceptionHandler, IS_CALLABLE, 0)
5456
ZEND_END_ARG_INFO()
@@ -81,6 +83,7 @@ ZEND_METHOD(Async_Scope, awaitCompletion);
8183
ZEND_METHOD(Async_Scope, awaitAfterCancellation);
8284
ZEND_METHOD(Async_Scope, isFinished);
8385
ZEND_METHOD(Async_Scope, isClosed);
86+
ZEND_METHOD(Async_Scope, isCancelled);
8487
ZEND_METHOD(Async_Scope, setExceptionHandler);
8588
ZEND_METHOD(Async_Scope, setChildScopeExceptionHandler);
8689
ZEND_METHOD(Async_Scope, onFinally);
@@ -111,6 +114,7 @@ static const zend_function_entry class_Async_Scope_methods[] = {
111114
ZEND_ME(Async_Scope, awaitAfterCancellation, arginfo_class_Async_Scope_awaitAfterCancellation, ZEND_ACC_PUBLIC)
112115
ZEND_ME(Async_Scope, isFinished, arginfo_class_Async_Scope_isFinished, ZEND_ACC_PUBLIC)
113116
ZEND_ME(Async_Scope, isClosed, arginfo_class_Async_Scope_isClosed, ZEND_ACC_PUBLIC)
117+
ZEND_ME(Async_Scope, isCancelled, arginfo_class_Async_Scope_isCancelled, ZEND_ACC_PUBLIC)
114118
ZEND_ME(Async_Scope, setExceptionHandler, arginfo_class_Async_Scope_setExceptionHandler, ZEND_ACC_PUBLIC)
115119
ZEND_ME(Async_Scope, setChildScopeExceptionHandler, arginfo_class_Async_Scope_setChildScopeExceptionHandler, ZEND_ACC_PUBLIC)
116120
ZEND_ME(Async_Scope, onFinally, arginfo_class_Async_Scope_onFinally, ZEND_ACC_PUBLIC)

tests/scope/022-scope_awaitCompletion_basic.phpt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Scope: awaitCompletion() - basic usage
66
use function Async\spawn;
77
use function Async\timeout;
88
use Async\Scope;
9+
use function Async\await;
910

1011
echo "start\n";
1112

@@ -32,7 +33,7 @@ $external = spawn(function() use ($scope) {
3233
});
3334

3435
echo "awaiting external\n";
35-
$external->getResult();
36+
await($external);
3637

3738
echo "verifying results\n";
3839
echo "coroutine1 result: " . $coroutine1->getResult() . "\n";
@@ -45,10 +46,10 @@ echo "end\n";
4546
--EXPECT--
4647
start
4748
spawned coroutines
48-
external waiting for scope completion
49+
awaiting external
4950
coroutine1 running
5051
coroutine2 running
51-
awaiting external
52+
external waiting for scope completion
5253
scope completed
5354
verifying results
5455
coroutine1 result: result1

tests/scope/023-scope_awaitCompletion_timeout.phpt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Scope: awaitCompletion() - timeout handling
66
use function Async\spawn;
77
use function Async\suspend;
88
use function Async\timeout;
9+
use function Async\await;
910
use Async\Scope;
1011

1112
echo "start\n";
@@ -35,7 +36,7 @@ $external = spawn(function() use ($scope) {
3536
}
3637
});
3738

38-
$external->getResult();
39+
await($external);
3940

4041
// Cancel the long running coroutine to clean up
4142
$long_running->cancel();
@@ -48,8 +49,9 @@ echo "end\n";
4849
--EXPECT--
4950
start
5051
spawned long running coroutine
51-
external waiting with timeout
5252
long running coroutine started
53+
external waiting with timeout
54+
long running coroutine finished
5355
caught timeout as expected
5456
scope finished: true
5557
end

tests/scope/024-scope_awaitAfterCancellation_basic.phpt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Scope: awaitAfterCancellation() - basic usage
66
use function Async\spawn;
77
use function Async\suspend;
88
use function Async\timeout;
9+
use function Async\await;
910
use Async\Scope;
1011

1112
echo "start\n";
@@ -31,6 +32,8 @@ $coroutine2 = $scope->spawn(function() {
3132

3233
echo "spawned coroutines\n";
3334

35+
suspend(); // Let coroutines start
36+
3437
// Cancel the scope
3538
$scope->cancel();
3639
echo "scope cancelled\n";
@@ -42,7 +45,7 @@ $external = spawn(function() use ($scope) {
4245
echo "awaitAfterCancellation completed\n";
4346
});
4447

45-
$external->getResult();
48+
await($external);
4649

4750
echo "scope finished: " . ($scope->isFinished() ? "true" : "false") . "\n";
4851
echo "scope closed: " . ($scope->isClosed() ? "true" : "false") . "\n";

tests/scope/025-scope_awaitAfterCancellation_error_handler.phpt

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Scope: awaitAfterCancellation() - with error handler
66
use function Async\spawn;
77
use function Async\suspend;
88
use function Async\timeout;
9+
use function Async\await;
910
use Async\Scope;
1011

1112
echo "start\n";
@@ -15,8 +16,14 @@ $scope = Scope::inherit();
1516

1617
$error_coroutine = $scope->spawn(function() {
1718
echo "error coroutine started\n";
18-
suspend();
19-
throw new \RuntimeException("Coroutine error");
19+
20+
try {
21+
suspend(); // Suspend to simulate work
22+
} catch (\CancellationException $e) {
23+
echo "coroutine cancelled\n";
24+
suspend();
25+
throw new \RuntimeException("Coroutine error after cancellation");
26+
}
2027
});
2128

2229
$normal_coroutine = $scope->spawn(function() {
@@ -29,34 +36,28 @@ $normal_coroutine = $scope->spawn(function() {
2936

3037
echo "spawned coroutines\n";
3138

32-
// Cancel the scope
33-
$scope->cancel();
34-
echo "scope cancelled\n";
35-
3639
// Await after cancellation with error handler
3740
$external = spawn(function() use ($scope) {
3841
echo "external waiting with error handler\n";
3942

40-
$errors_handled = [];
41-
43+
// Cancel the scope
44+
$scope->cancel();
45+
echo "scope cancel\n";
46+
suspend(); // Let cancellation propagate
47+
48+
echo "awaitAfterCancellation with handler started\n";
49+
4250
$scope->awaitAfterCancellation(
43-
function($errors) use (&$errors_handled) {
44-
echo "error handler called\n";
45-
echo "errors count: " . count($errors) . "\n";
46-
foreach ($errors as $error) {
47-
echo "error: " . $error->getMessage() . "\n";
48-
$errors_handled[] = $error->getMessage();
49-
}
51+
function($error) {
52+
echo "error handler called: {$error->getMessage()}\n";
5053
},
51-
timeout(1000)
54+
timeout(10)
5255
);
5356

5457
echo "awaitAfterCancellation with handler completed\n";
55-
return $errors_handled;
5658
});
5759

58-
$handled_errors = $external->getResult();
59-
echo "handled errors count: " . count($handled_errors) . "\n";
60+
await($external);
6061

6162
echo "scope finished: " . ($scope->isFinished() ? "true" : "false") . "\n";
6263

@@ -68,12 +69,11 @@ start
6869
spawned coroutines
6970
error coroutine started
7071
normal coroutine started
71-
scope cancelled
7272
external waiting with error handler
73-
error handler called
74-
errors count: %d
75-
error: %s
73+
scope cancel
74+
coroutine cancelled
75+
awaitAfterCancellation with handler started
76+
error handler called: Coroutine error after cancellation
7677
awaitAfterCancellation with handler completed
77-
handled errors count: %d
7878
scope finished: true
7979
end

tests/scope/026-scope_cancel_with_active_coroutines.phpt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ suspend();
4646
echo "cancelling scope\n";
4747
$scope->cancel(new \Async\CancellationException("Custom cancellation message"));
4848

49+
// Let cancellation propagate
50+
suspend();
51+
4952
echo "verifying cancellation\n";
5053
echo "scope finished: " . ($scope->isFinished() ? "true" : "false") . "\n";
5154
echo "scope closed: " . ($scope->isClosed() ? "true" : "false") . "\n";
@@ -60,7 +63,7 @@ try {
6063
return "should_not_work";
6164
});
6265
echo "ERROR: Should not be able to spawn in closed scope\n";
63-
} catch (Error $e) {
66+
} catch (Async\AsyncException $e) {
6467
echo "caught expected error: " . $e->getMessage() . "\n";
6568
}
6669

0 commit comments

Comments
 (0)