1+ --TEST--
2+ Fiber and spawn operations in destructors - memory management conflicts
3+ --FILE--
4+ <?php
5+
6+ use function Async \spawn ;
7+ use function Async \suspend ;
8+ use function Async \await ;
9+
10+ echo "Test: Fiber and spawn in destructors \n" ;
11+
12+ class FiberSpawner {
13+ private $ name ;
14+
15+ public function __construct ($ name ) {
16+ $ this ->name = $ name ;
17+ echo "Created: {$ this ->name }\n" ;
18+ }
19+
20+ public function __destruct () {
21+ echo "Destructing: {$ this ->name }\n" ;
22+
23+ try {
24+ if ($ this ->name === 'FiberInDestructor ' ) {
25+ // Create Fiber in destructor
26+ $ fiber = new Fiber (function () {
27+ echo "Fiber running in destructor \n" ;
28+ Fiber::suspend ("destructor fiber " );
29+ echo "Fiber resumed in destructor \n" ;
30+ return "destructor done " ;
31+ });
32+
33+ echo "Starting fiber in destructor \n" ;
34+ $ result = $ fiber ->start ();
35+ echo "Fiber suspended with: " . $ result . "\n" ;
36+
37+ $ result = $ fiber ->resume ("resume in destructor " );
38+ echo "Fiber completed with: " . $ result . "\n" ;
39+
40+ } elseif ($ this ->name === 'SpawnInDestructor ' ) {
41+ // Spawn coroutine in destructor
42+ echo "Spawning coroutine in destructor \n" ;
43+ $ coroutine = spawn (function () {
44+ echo "Coroutine running in destructor \n" ;
45+ suspend ();
46+ echo "Coroutine resumed in destructor \n" ;
47+ return "destructor coroutine done " ;
48+ });
49+
50+ echo "Waiting for coroutine in destructor \n" ;
51+ $ result = await ($ coroutine );
52+ echo "Coroutine completed with: " . $ result . "\n" ;
53+ }
54+ } catch (Error $ e ) {
55+ echo "Error in destructor: " . $ e ->getMessage () . "\n" ;
56+ } catch (Exception $ e ) {
57+ echo "Exception in destructor: " . $ e ->getMessage () . "\n" ;
58+ }
59+
60+ echo "Destructor finished: {$ this ->name }\n" ;
61+ }
62+ }
63+
64+ try {
65+ echo "Creating objects that will spawn/fiber in destructors \n" ;
66+
67+ $ obj1 = new FiberSpawner ('FiberInDestructor ' );
68+ $ obj2 = new FiberSpawner ('SpawnInDestructor ' );
69+
70+ echo "Starting some async operations \n" ;
71+ $ mainCoroutine = spawn (function () {
72+ echo "Main coroutine running \n" ;
73+ suspend ();
74+ echo "Main coroutine resumed \n" ;
75+ return "main done " ;
76+ });
77+
78+ // Force destruction by unsetting
79+ echo "Unsetting objects to trigger destructors \n" ;
80+ unset($ obj1 );
81+ unset($ obj2 );
82+
83+ echo "Completing main coroutine \n" ;
84+ $ result = await ($ mainCoroutine );
85+ echo "Main coroutine result: " . $ result . "\n" ;
86+
87+ } catch (Error $ e ) {
88+ echo "Error caught: " . $ e ->getMessage () . "\n" ;
89+ } catch (Exception $ e ) {
90+ echo "Exception caught: " . $ e ->getMessage () . "\n" ;
91+ }
92+
93+ echo "Test completed \n" ;
94+ ?>
95+ --EXPECTF--
96+ Test: Fiber and spawn in destructors
97+ Creating objects that will spawn/fiber in destructors
98+ Created: FiberInDestructor
99+ Created: SpawnInDestructor
100+ Starting some async operations
101+ Unsetting objects to trigger destructors
102+ Destructing: FiberInDestructor
103+ Error in destructor: Cannot create a fiber while an True Async is active
104+ Destructor finished: FiberInDestructor
105+ Destructing: SpawnInDestructor
106+ Spawning coroutine in destructor
107+ Waiting for coroutine in destructor
108+ Main coroutine running
109+ Coroutine running in destructor
110+ Main coroutine resumed
111+ Coroutine resumed in destructor
112+ Coroutine completed with: destructor coroutine done
113+ Destructor finished: SpawnInDestructor
114+ Completing main coroutine
115+ Main coroutine result: main done
116+ Test completed
0 commit comments