Skip to content

Commit c95a0df

Browse files
committed
Add proper handling to observe functions in fibers
Signed-off-by: Bob Weinand <[email protected]>
1 parent d3abca4 commit c95a0df

File tree

5 files changed

+209
-0
lines changed

5 files changed

+209
-0
lines changed

Zend/zend_fibers.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ struct _zend_fiber_context {
8888
/* Fiber status. */
8989
zend_fiber_status status;
9090

91+
/* Observer state */
92+
zend_execute_data *top_observed_frame;
93+
9194
/* Reserved for extensions */
9295
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
9396
};

Zend/zend_observer.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,8 @@ ZEND_API void ZEND_FASTCALL zend_observer_fiber_init_notify(zend_fiber_context *
321321
zend_llist_element *element;
322322
zend_observer_fiber_init_handler callback;
323323

324+
initializing->top_observed_frame = NULL;
325+
324326
for (element = zend_observer_fiber_init.head; element; element = element->next) {
325327
callback = *(zend_observer_fiber_init_handler *) element->data;
326328
callback(initializing);
@@ -332,10 +334,17 @@ ZEND_API void ZEND_FASTCALL zend_observer_fiber_switch_notify(zend_fiber_context
332334
zend_llist_element *element;
333335
zend_observer_fiber_switch_handler callback;
334336

337+
if (EG(current_execute_data) == NULL) {
338+
zend_observer_fcall_end_all(); // fiber is either finished (call will do nothing) or has bailed out
339+
}
340+
335341
for (element = zend_observer_fiber_switch.head; element; element = element->next) {
336342
callback = *(zend_observer_fiber_switch_handler *) element->data;
337343
callback(from, to);
338344
}
345+
346+
from->top_observed_frame = current_observed_frame;
347+
current_observed_frame = to->top_observed_frame;
339348
}
340349

341350
ZEND_API void ZEND_FASTCALL zend_observer_fiber_destroy_notify(zend_fiber_context *destroying)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
--TEST--
2+
Observer: Basic function observing in fibers
3+
--EXTENSIONS--
4+
zend_test
5+
--INI--
6+
zend_test.observer.enabled=1
7+
zend_test.observer.observe_all=1
8+
zend_test.observer.fiber_init=1
9+
zend_test.observer.fiber_switch=1
10+
zend_test.observer.fiber_destroy=1
11+
--FILE--
12+
<?php
13+
14+
$fiber = new Fiber(function (): void {
15+
var_dump(1);
16+
Fiber::suspend();
17+
var_dump(2);
18+
});
19+
20+
$fiber->start();
21+
$fiber->resume();
22+
23+
?>
24+
--EXPECTF--
25+
<!-- init '%s' -->
26+
<file '%s'>
27+
<!-- init Fiber::__construct() -->
28+
<Fiber::__construct>
29+
</Fiber::__construct>
30+
<!-- init Fiber::start() -->
31+
<Fiber::start>
32+
<!-- alloc: %s -->
33+
<!-- switching from fiber %s to %s -->
34+
<init '%s'>
35+
<!-- init {closure}() -->
36+
<{closure}>
37+
<!-- init var_dump() -->
38+
<var_dump>
39+
int(1)
40+
</var_dump>
41+
<!-- init Fiber::suspend() -->
42+
<Fiber::suspend>
43+
<!-- switching from fiber %s to %s -->
44+
<suspend '%s'>
45+
</Fiber::start>
46+
<!-- init Fiber::resume() -->
47+
<Fiber::resume>
48+
<!-- switching from fiber %s to %s -->
49+
<resume '%s'>
50+
</Fiber::suspend>
51+
<var_dump>
52+
int(2)
53+
</var_dump>
54+
</{closure}>
55+
<!-- switching from fiber %s to %s -->
56+
<returned '%s'>
57+
<!-- destroy: %s -->
58+
</Fiber::resume>
59+
</file '%s'>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
--TEST--
2+
Observer: Function observing in fibers with unfinished fiber
3+
--EXTENSIONS--
4+
zend_test
5+
--INI--
6+
zend_test.observer.enabled=1
7+
zend_test.observer.observe_all=1
8+
zend_test.observer.fiber_init=1
9+
zend_test.observer.fiber_switch=1
10+
zend_test.observer.fiber_destroy=1
11+
--FILE--
12+
<?php
13+
14+
$fiber = new Fiber(function (): void {
15+
var_dump(1);
16+
Fiber::suspend();
17+
var_dump(2);
18+
});
19+
20+
$fiber->start();
21+
22+
?>
23+
--EXPECTF--
24+
<!-- init '%s' -->
25+
<file '%s'>
26+
<!-- init Fiber::__construct() -->
27+
<Fiber::__construct>
28+
</Fiber::__construct>
29+
<!-- init Fiber::start() -->
30+
<Fiber::start>
31+
<!-- alloc: %s -->
32+
<!-- switching from fiber %s to %s -->
33+
<init '%s'>
34+
<!-- init {closure}() -->
35+
<{closure}>
36+
<!-- init var_dump() -->
37+
<var_dump>
38+
int(1)
39+
</var_dump>
40+
<!-- init Fiber::suspend() -->
41+
<Fiber::suspend>
42+
<!-- switching from fiber %s to %s -->
43+
<suspend '%s'>
44+
</Fiber::start>
45+
</file '%s'>
46+
<!-- switching from fiber %s to %s -->
47+
<destroying '%s'>
48+
<!-- Exception: GracefulExit -->
49+
</Fiber::suspend>
50+
<!-- Exception: GracefulExit -->
51+
</{closure}>
52+
<!-- switching from fiber %s to %s -->
53+
<destroyed '%s'>
54+
<!-- destroy: %s -->
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
--TEST--
2+
Observer: Function observing in fibers with bailout in fiber
3+
--EXTENSIONS--
4+
zend_test
5+
--INI--
6+
zend_test.observer.enabled=1
7+
zend_test.observer.observe_all=1
8+
zend_test.observer.fiber_init=1
9+
zend_test.observer.fiber_switch=1
10+
zend_test.observer.fiber_destroy=1
11+
memory_limit=100M
12+
--FILE--
13+
<?php
14+
15+
$notBailedOutFiber = new Fiber(function (): void {
16+
var_dump(1);
17+
Fiber::suspend();
18+
});
19+
20+
$notBailedOutFiber->start();
21+
22+
$fiber = new Fiber(function (): void {
23+
var_dump(2);
24+
Fiber::suspend();
25+
str_repeat('A', 200_000_000);
26+
});
27+
28+
$fiber->start();
29+
$fiber->resume();
30+
31+
?>
32+
--EXPECTF--
33+
<!-- init '%s' -->
34+
<file '%s'>
35+
<!-- init Fiber::__construct() -->
36+
<Fiber::__construct>
37+
</Fiber::__construct>
38+
<!-- init Fiber::start() -->
39+
<Fiber::start>
40+
<!-- alloc: %s -->
41+
<!-- switching from fiber %s to %s -->
42+
<init '%s'>
43+
<!-- init {closure}() -->
44+
<{closure}>
45+
<!-- init var_dump() -->
46+
<var_dump>
47+
int(1)
48+
</var_dump>
49+
<!-- init Fiber::suspend() -->
50+
<Fiber::suspend>
51+
<!-- switching from fiber %s to %s -->
52+
<suspend '%s'>
53+
</Fiber::start>
54+
<Fiber::__construct>
55+
</Fiber::__construct>
56+
<Fiber::start>
57+
<!-- alloc: %s -->
58+
<!-- switching from fiber %s to %s -->
59+
<init '%s'>
60+
<!-- init {closure}() -->
61+
<{closure}>
62+
<var_dump>
63+
int(2)
64+
</var_dump>
65+
<Fiber::suspend>
66+
<!-- switching from fiber %s to %s -->
67+
<suspend '%s'>
68+
</Fiber::start>
69+
<!-- init Fiber::resume() -->
70+
<Fiber::resume>
71+
<!-- switching from fiber %s to %s -->
72+
<resume '%s'>
73+
</Fiber::suspend>
74+
<!-- init str_repeat() -->
75+
<str_repeat>
76+
77+
Fatal error: Allowed memory size of 104857600 bytes exhausted %s on line %d
78+
</str_repeat>
79+
</{closure}>
80+
<!-- switching from fiber %s to %s -->
81+
<returned '%s'>
82+
<!-- destroy: %s -->
83+
</Fiber::resume>
84+
</file '%s'>

0 commit comments

Comments
 (0)