Skip to content

Commit da94baf

Browse files
committed
Add proper handling to observe functions in fibers
The current_observed_frame is carried along the fiber context and thus always correct now. Signed-off-by: Bob Weinand <bobwei9@hotmail.com>
1 parent dc5475c commit da94baf

File tree

6 files changed

+210
-2
lines changed

6 files changed

+210
-2
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 & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ zend_llist zend_observer_fiber_destroy;
3939

4040
int zend_observer_fcall_op_array_extension;
4141

42-
ZEND_TLS zend_execute_data *first_observed_frame;
4342
ZEND_TLS zend_execute_data *current_observed_frame;
4443

4544
// Call during minit/startup ONLY
@@ -95,7 +94,6 @@ ZEND_API void zend_observer_post_startup(void)
9594

9695
ZEND_API void zend_observer_activate(void)
9796
{
98-
first_observed_frame = NULL;
9997
current_observed_frame = NULL;
10098
}
10199

@@ -321,6 +319,8 @@ ZEND_API void ZEND_FASTCALL zend_observer_fiber_init_notify(zend_fiber_context *
321319
zend_llist_element *element;
322320
zend_observer_fiber_init_handler callback;
323321

322+
initializing->top_observed_frame = NULL;
323+
324324
for (element = zend_observer_fiber_init.head; element; element = element->next) {
325325
callback = *(zend_observer_fiber_init_handler *) element->data;
326326
callback(initializing);
@@ -332,10 +332,17 @@ ZEND_API void ZEND_FASTCALL zend_observer_fiber_switch_notify(zend_fiber_context
332332
zend_llist_element *element;
333333
zend_observer_fiber_switch_handler callback;
334334

335+
if (from->status == ZEND_FIBER_STATUS_DEAD) {
336+
zend_observer_fcall_end_all(); // fiber is either finished (call will do nothing) or has bailed out
337+
}
338+
335339
for (element = zend_observer_fiber_switch.head; element; element = element->next) {
336340
callback = *(zend_observer_fiber_switch_handler *) element->data;
337341
callback(from, to);
338342
}
343+
344+
from->top_observed_frame = current_observed_frame;
345+
current_observed_frame = to->top_observed_frame;
339346
}
340347

341348
ZEND_API void ZEND_FASTCALL zend_observer_fiber_destroy_notify(zend_fiber_context *destroying)

ext/zend_test/test.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,7 @@ PHP_MSHUTDOWN_FUNCTION(zend_test)
756756
PHP_RINIT_FUNCTION(zend_test)
757757
{
758758
zend_hash_init(&ZT_G(global_weakmap), 8, NULL, ZVAL_PTR_DTOR, 0);
759+
ZT_G(observer_nesting_depth) = 0;
759760
return SUCCESS;
760761
}
761762

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)