Skip to content

Commit 5dad0ed

Browse files
committed
add tests for fiber cancellation scenarios and coroutine behavior
1 parent f449138 commit 5dad0ed

6 files changed

+260
-0
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
--TEST--
2+
Cancel fiber's coroutine via getCoroutine()->cancel()
3+
--FILE--
4+
<?php
5+
6+
use function Async\spawn;
7+
use function Async\await;
8+
use Async\CancellationError;
9+
10+
$c = spawn(function() {
11+
$fiber = new Fiber(function() {
12+
echo "Fiber started\n";
13+
Fiber::suspend("suspended");
14+
echo "Fiber resumed\n";
15+
return "done";
16+
});
17+
18+
$fiber->start();
19+
echo "Fiber suspended\n";
20+
21+
// Get fiber's coroutine and cancel it
22+
$coro = $fiber->getCoroutine();
23+
$coro->cancel(new CancellationError("cancelled"));
24+
echo "Coroutine cancelled\n";
25+
26+
// Try to resume fiber
27+
try {
28+
$fiber->resume();
29+
echo "Fiber completed\n";
30+
} catch (CancellationError $e) {
31+
echo "CancellationError: " . $e->getMessage() . "\n";
32+
} catch (FiberError $e) {
33+
echo "FiberError: " . $e->getMessage() . "\n";
34+
}
35+
});
36+
37+
await($c);
38+
echo "OK\n";
39+
?>
40+
--EXPECTF--
41+
Fiber started
42+
Fiber suspended
43+
Coroutine cancelled
44+
%a
45+
OK
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--TEST--
2+
Cancel fiber's coroutine while fiber is suspended
3+
--FILE--
4+
<?php
5+
6+
use function Async\spawn;
7+
use function Async\suspend;
8+
use function Async\await;
9+
use Async\CancellationError;
10+
11+
$c = spawn(function() {
12+
$fiber = new Fiber(function() {
13+
echo "Fiber: before suspend\n";
14+
Fiber::suspend();
15+
echo "Fiber: after suspend\n";
16+
return "result";
17+
});
18+
19+
$fiber->start();
20+
21+
// Fiber is suspended, cancel its coroutine
22+
$coro = $fiber->getCoroutine();
23+
echo "Cancelling coroutine\n";
24+
$coro->cancel(new CancellationError("test"));
25+
26+
// Give scheduler a chance
27+
suspend();
28+
29+
// Try resume
30+
try {
31+
$fiber->resume();
32+
} catch (Throwable $e) {
33+
echo "Caught: " . $e->getMessage() . "\n";
34+
}
35+
});
36+
37+
await($c);
38+
echo "OK\n";
39+
?>
40+
--EXPECTF--
41+
Fiber: before suspend
42+
Cancelling coroutine
43+
Caught: Cannot resume a fiber that is not suspended
44+
OK
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
--TEST--
2+
Double cancel - parent coroutine and fiber's coroutine
3+
--FILE--
4+
<?php
5+
6+
use function Async\spawn;
7+
use function Async\await;
8+
use Async\CancellationError;
9+
10+
$parent = spawn(function() {
11+
$fiber = new Fiber(function() {
12+
Fiber::suspend();
13+
return "done";
14+
});
15+
16+
$fiber->start();
17+
18+
// Cancel fiber's coroutine
19+
$fiberCoro = $fiber->getCoroutine();
20+
$fiberCoro->cancel(new CancellationError("fiber cancel"));
21+
echo "Fiber coroutine cancelled\n";
22+
23+
// Try resume
24+
try {
25+
$fiber->resume();
26+
} catch (Throwable $e) {
27+
echo "Fiber caught: " . $e->getMessage() . "\n";
28+
}
29+
30+
return "parent done";
31+
});
32+
33+
// Also cancel parent
34+
$parent->cancel(new CancellationError("parent cancel"));
35+
echo "Parent cancelled\n";
36+
37+
try {
38+
await($parent);
39+
} catch (CancellationError $e) {
40+
echo "Parent caught: " . $e->getMessage() . "\n";
41+
}
42+
43+
echo "OK\n";
44+
?>
45+
--EXPECTF--
46+
%a
47+
OK
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
Get fiber's coroutine after fiber termination
3+
--FILE--
4+
<?php
5+
6+
use function Async\spawn;
7+
use function Async\await;
8+
9+
$c = spawn(function() {
10+
$fiber = new Fiber(function() {
11+
return "done";
12+
});
13+
14+
$result = $fiber->start();
15+
echo "Fiber completed: {$result}\n";
16+
17+
// Get coroutine after fiber is terminated
18+
$coro = $fiber->getCoroutine();
19+
20+
if ($coro !== null) {
21+
echo "Has coroutine: yes\n";
22+
echo "Is finished: " . ($coro->isFinished() ? "yes" : "no") . "\n";
23+
} else {
24+
echo "Has coroutine: no\n";
25+
}
26+
27+
return "ok";
28+
});
29+
30+
await($c);
31+
echo "OK\n";
32+
?>
33+
--EXPECTF--
34+
Fiber completed: done
35+
Has coroutine: yes
36+
Is finished: yes
37+
OK
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
--TEST--
2+
Cancel fiber's coroutine from nested spawn
3+
--FILE--
4+
<?php
5+
6+
use function Async\spawn;
7+
use function Async\suspend;
8+
use function Async\await;
9+
use Async\CancellationError;
10+
11+
$outer = spawn(function() {
12+
$fiber = new Fiber(function() {
13+
echo "Fiber running\n";
14+
Fiber::suspend();
15+
echo "This should not print\n";
16+
return "done";
17+
});
18+
19+
$fiber->start();
20+
$fiberCoro = $fiber->getCoroutine();
21+
22+
// Nested coroutine cancels fiber's coroutine
23+
$inner = spawn(function() use ($fiberCoro) {
24+
echo "Inner: cancelling fiber coroutine\n";
25+
$fiberCoro->cancel(new CancellationError("nested cancel"));
26+
});
27+
28+
await($inner);
29+
suspend();
30+
31+
// Try to use fiber
32+
try {
33+
$fiber->resume();
34+
} catch (Throwable $e) {
35+
echo "Caught: " . $e->getMessage() . "\n";
36+
}
37+
38+
return "outer done";
39+
});
40+
41+
$result = await($outer);
42+
echo "Result: {$result}\n";
43+
echo "OK\n";
44+
?>
45+
--EXPECTF--
46+
Fiber running
47+
Inner: cancelling fiber coroutine
48+
%a
49+
OK
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
Fiber throws exception while coroutine is being cancelled
3+
--FILE--
4+
<?php
5+
6+
use function Async\spawn;
7+
use function Async\await;
8+
use Async\CancellationError;
9+
10+
$c = spawn(function() {
11+
$fiber = new Fiber(function() {
12+
Fiber::suspend();
13+
throw new Exception("fiber exception");
14+
});
15+
16+
$fiber->start();
17+
$coro = $fiber->getCoroutine();
18+
19+
// Cancel coroutine
20+
$coro->cancel(new CancellationError("cancel"));
21+
22+
// Resume - what happens? Exception or CancellationError?
23+
try {
24+
$fiber->resume();
25+
echo "No exception\n";
26+
} catch (CancellationError $e) {
27+
echo "CancellationError: " . $e->getMessage() . "\n";
28+
} catch (Exception $e) {
29+
echo "Exception: " . $e->getMessage() . "\n";
30+
}
31+
});
32+
33+
await($c);
34+
echo "OK\n";
35+
?>
36+
--EXPECTF--
37+
%a
38+
OK

0 commit comments

Comments
 (0)