@@ -78,28 +78,6 @@ extern transfer_t jump_fcontext(fcontext_t to, void *vp);
7878
7979#define ZEND_FIBER_DEFAULT_PAGE_SIZE 4096
8080
81- #define ZEND_FIBER_BACKUP_EG (stack , stack_page_size , execute_data , error_reporting , trace_num , bailout ) do { \
82- stack = EG(vm_stack); \
83- stack->top = EG(vm_stack_top); \
84- stack->end = EG(vm_stack_end); \
85- stack_page_size = EG(vm_stack_page_size); \
86- execute_data = EG(current_execute_data); \
87- error_reporting = EG(error_reporting); \
88- trace_num = EG(jit_trace_num); \
89- bailout = EG(bailout); \
90- } while (0)
91-
92- #define ZEND_FIBER_RESTORE_EG (stack , stack_page_size , execute_data , error_reporting , trace_num , bailout ) do { \
93- EG(vm_stack) = stack; \
94- EG(vm_stack_top) = stack->top; \
95- EG(vm_stack_end) = stack->end; \
96- EG(vm_stack_page_size) = stack_page_size; \
97- EG(current_execute_data) = execute_data; \
98- EG(error_reporting) = error_reporting; \
99- EG(jit_trace_num) = trace_num; \
100- EG(bailout) = bailout; \
101- } while (0)
102-
10381static size_t zend_fiber_get_page_size (void )
10482{
10583 static size_t page_size = 0 ;
@@ -172,6 +150,11 @@ static bool zend_fiber_stack_allocate(zend_fiber_stack *stack, size_t size)
172150 stack -> valgrind = VALGRIND_STACK_REGISTER (base , base + stack -> size );
173151#endif
174152
153+ #ifdef __SANITIZE_ADDRESS__
154+ stack -> prior_pointer = stack -> pointer ;
155+ stack -> prior_size = stack -> size ;
156+ #endif
157+
175158 return true;
176159}
177160
@@ -200,24 +183,19 @@ static void zend_fiber_stack_free(zend_fiber_stack *stack)
200183
201184static ZEND_NORETURN void zend_fiber_trampoline (transfer_t transfer )
202185{
203- zend_fiber_context * context = transfer .data ;
204-
205- #ifdef __SANITIZE_ADDRESS__
206- __sanitizer_finish_switch_fiber (NULL , & context -> stack .prior_pointer , & context -> stack .prior_size );
207- #endif
208-
209- context -> caller = transfer .context ;
210-
211- context -> function (context );
186+ zend_fiber_context * context = EG (current_fiber );
187+ zend_fiber_context * from = transfer .data ;
212188
213- context -> self = NULL ;
189+ from -> handle = transfer . context ;
214190
215191#ifdef __SANITIZE_ADDRESS__
216- __sanitizer_start_switch_fiber (NULL , context -> stack .prior_pointer , context -> stack .prior_size );
192+ __sanitizer_finish_switch_fiber (NULL , & from -> stack .prior_pointer , & from -> stack .prior_size );
217193#endif
218194
219- jump_fcontext (context -> caller , NULL );
195+ /* Final context switch, the fiber must not be resumed afterwards! */
196+ zend_fiber_switch_context (context -> function (context ));
220197
198+ /* Abort here because we are in an inconsistent program state. */
221199 abort ();
222200}
223201
@@ -230,10 +208,10 @@ ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, zend_fiber_co
230208 // Stack grows down, calculate the top of the stack. make_fcontext then shifts pointer to lower 16-byte boundary.
231209 void * stack = (void * ) ((uintptr_t ) context -> stack .pointer + context -> stack .size );
232210
233- context -> self = make_fcontext (stack , context -> stack .size , zend_fiber_trampoline );
234- ZEND_ASSERT (context -> self != NULL && "make_fcontext() never returns NULL" );
211+ context -> handle = make_fcontext (stack , context -> stack .size , zend_fiber_trampoline );
212+ ZEND_ASSERT (context -> handle != NULL && "make_fcontext() never returns NULL" );
213+
235214 context -> function = coroutine ;
236- context -> caller = NULL ;
237215
238216 return true;
239217}
@@ -245,81 +223,59 @@ ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context)
245223
246224ZEND_API void zend_fiber_switch_context (zend_fiber_context * to )
247225{
248- ZEND_ASSERT ( to && to -> self && to -> stack . pointer && "Invalid fiber context" );
226+ zend_fiber_context * from = EG ( current_fiber );
249227
250- #ifdef __SANITIZE_ADDRESS__
251- void * fake_stack ;
252- __sanitizer_start_switch_fiber (& fake_stack , to -> stack .pointer , to -> stack .size );
253- #endif
254-
255- transfer_t transfer = jump_fcontext (to -> self , to );
228+ ZEND_ASSERT (to && to -> handle && "Invalid fiber context" );
229+ ZEND_ASSERT (from && "From fiber context must be present" );
230+ ZEND_ASSERT (to != from && "Cannot switch into the running fiber context" );
256231
257232#ifdef __SANITIZE_ADDRESS__
258- __sanitizer_finish_switch_fiber (fake_stack , & to -> stack .prior_pointer , & to -> stack .prior_size );
233+ void * fake_stack = NULL ;
234+ __sanitizer_start_switch_fiber (
235+ from -> status != ZEND_FIBER_STATUS_DEAD ? & fake_stack : NULL ,
236+ to -> stack .prior_pointer ,
237+ to -> stack .prior_size );
259238#endif
260239
261- to -> self = transfer .context ;
262- }
263-
264- ZEND_API void zend_fiber_suspend_context (zend_fiber_context * current )
265- {
266- ZEND_ASSERT (current && current -> caller && current -> stack .pointer && "Invalid fiber context" );
240+ EG (current_fiber ) = to ;
267241
268- #ifdef __SANITIZE_ADDRESS__
269- void * fake_stack ;
270- __sanitizer_start_switch_fiber (& fake_stack , current -> stack .prior_pointer , current -> stack .prior_size );
271- #endif
242+ transfer_t transfer = jump_fcontext (to -> handle , from );
243+ ((zend_fiber_context * ) transfer .data )-> handle = transfer .context ;
272244
273- transfer_t transfer = jump_fcontext ( current -> caller , NULL ) ;
245+ EG ( current_fiber ) = from ;
274246
275247#ifdef __SANITIZE_ADDRESS__
276- __sanitizer_finish_switch_fiber (fake_stack , & current -> stack .prior_pointer , & current -> stack .prior_size );
248+ __sanitizer_finish_switch_fiber (fake_stack , & to -> stack .prior_pointer , & to -> stack .prior_size );
277249#endif
278-
279- current -> caller = transfer .context ;
280250}
281251
282252static void zend_fiber_suspend_from (zend_fiber * fiber )
283253{
284- zend_vm_stack stack ;
285- size_t stack_page_size ;
286- zend_execute_data * execute_data ;
287- int error_reporting ;
288- uint32_t jit_trace_num ;
289- JMP_BUF * bailout ;
254+ zend_fiber_vm_state state ;
290255
291- ZEND_FIBER_BACKUP_EG ( stack , stack_page_size , execute_data , error_reporting , jit_trace_num , bailout );
256+ ZEND_ASSERT ( fiber -> caller && "Fiber has no caller" );
292257
293- zend_fiber_suspend_context ( & fiber -> context );
294-
295- ZEND_FIBER_RESTORE_EG ( stack , stack_page_size , execute_data , error_reporting , jit_trace_num , bailout );
258+ zend_fiber_capture_vm_state ( & state );
259+ zend_fiber_switch_context ( zend_fiber_get_context ( fiber -> caller ));
260+ zend_fiber_restore_vm_state ( & state );
296261}
297262
298263static void zend_fiber_switch_to (zend_fiber * fiber )
299264{
300- zend_fiber * previous ;
301- zend_vm_stack stack ;
302- size_t stack_page_size ;
303- zend_execute_data * execute_data ;
304- int error_reporting ;
305- uint32_t jit_trace_num ;
306- JMP_BUF * bailout ;
307-
308- previous = EG (current_fiber );
265+ zend_fiber_context * context = zend_fiber_get_context (fiber );
266+ zend_fiber_vm_state state ;
309267
310- zend_observer_fiber_switch_notify (previous , fiber );
268+ zend_observer_fiber_switch_notify (EG ( current_fiber ), context );
311269
312- ZEND_FIBER_BACKUP_EG ( stack , stack_page_size , execute_data , error_reporting , jit_trace_num , bailout );
270+ fiber -> caller = zend_fiber_from_context ( EG ( current_fiber ) );
313271
314- EG (current_fiber ) = fiber ;
272+ zend_fiber_capture_vm_state (& state );
273+ zend_fiber_switch_context (context );
274+ zend_fiber_restore_vm_state (& state );
315275
316- zend_fiber_switch_context ( & fiber -> context ) ;
276+ fiber -> caller = NULL ;
317277
318- EG (current_fiber ) = previous ;
319-
320- ZEND_FIBER_RESTORE_EG (stack , stack_page_size , execute_data , error_reporting , jit_trace_num , bailout );
321-
322- zend_observer_fiber_switch_notify (fiber , previous );
278+ zend_observer_fiber_switch_notify (context , EG (current_fiber ));
323279
324280 if (UNEXPECTED (fiber -> flags & ZEND_FIBER_FLAG_BAILOUT )) {
325281 // zend_bailout() was called in the fiber, so call it again in the previous fiber or {main}.
@@ -339,10 +295,9 @@ static zend_always_inline zend_vm_stack zend_fiber_vm_stack_alloc(size_t size)
339295 return page ;
340296}
341297
342- static void ZEND_STACK_ALIGNED zend_fiber_execute (zend_fiber_context * context )
298+ static ZEND_STACK_ALIGNED zend_fiber_context * zend_fiber_execute (zend_fiber_context * context )
343299{
344- zend_fiber * fiber = EG (current_fiber );
345- ZEND_ASSERT (fiber );
300+ zend_fiber * fiber = zend_fiber_from_context (context );
346301
347302 zend_long error_reporting = INI_INT ("error_reporting" );
348303 if (!error_reporting && !INI_STR ("error_reporting" )) {
@@ -396,6 +351,8 @@ static void ZEND_STACK_ALIGNED zend_fiber_execute(zend_fiber_context *context)
396351 zend_vm_stack_destroy ();
397352 fiber -> execute_data = NULL ;
398353 fiber -> stack_bottom = NULL ;
354+
355+ return zend_fiber_get_context (fiber -> caller );
399356}
400357
401358static zend_object * zend_fiber_object_create (zend_class_entry * ce )
@@ -454,7 +411,7 @@ static void zend_fiber_object_free(zend_object *object)
454411
455412 zval_ptr_dtor (& fiber -> value );
456413
457- zend_fiber_destroy_context (& fiber -> context );
414+ zend_fiber_destroy_context (zend_fiber_get_context ( fiber ) );
458415
459416 zend_object_std_dtor (& fiber -> std );
460417}
@@ -494,7 +451,7 @@ ZEND_API void zend_fiber_start(zend_fiber *fiber, zval *params, uint32_t param_c
494451 fiber -> fci .param_count = param_count ;
495452 fiber -> fci .named_params = named_params ;
496453
497- if (!zend_fiber_init_context (& fiber -> context , zend_fiber_execute , EG (fiber_stack_size ))) {
454+ if (!zend_fiber_init_context (zend_fiber_get_context ( fiber ) , zend_fiber_execute , EG (fiber_stack_size ))) {
498455 RETURN_THROWS ();
499456 }
500457
@@ -524,13 +481,13 @@ ZEND_METHOD(Fiber, start)
524481
525482ZEND_API void zend_fiber_suspend (zval * value , zval * return_value )
526483{
527- zend_fiber * fiber = EG (current_fiber );
528-
529- if (UNEXPECTED (!fiber )) {
484+ if (UNEXPECTED (EG (current_fiber ) == EG (main_fiber ))) {
530485 zend_throw_error (zend_ce_fiber_error , "Cannot suspend outside of a fiber" );
531486 RETURN_THROWS ();
532487 }
533488
489+ zend_fiber * fiber = zend_fiber_from_context (EG (current_fiber ));
490+
534491 if (UNEXPECTED (fiber -> flags & ZEND_FIBER_FLAG_DESTROYED )) {
535492 zend_throw_error (zend_ce_fiber_error , "Cannot suspend in a force-closed fiber" );
536493 RETURN_THROWS ();
@@ -734,17 +691,13 @@ ZEND_METHOD(Fiber, getReturn)
734691
735692ZEND_METHOD (Fiber , this )
736693{
737- zend_fiber * fiber ;
738-
739694 ZEND_PARSE_PARAMETERS_NONE ();
740695
741- fiber = EG (current_fiber );
742-
743- if (!fiber ) {
696+ if (EG (current_fiber ) == EG (main_fiber )) {
744697 RETURN_NULL ();
745698 }
746699
747- RETURN_OBJ_COPY (& fiber -> std );
700+ RETURN_OBJ_COPY (& zend_fiber_from_context ( EG ( current_fiber )) -> std );
748701}
749702
750703ZEND_METHOD (FiberError , __construct )
@@ -775,5 +728,15 @@ void zend_register_fiber_ce(void)
775728
776729void zend_fiber_init (void )
777730{
778- EG (current_fiber ) = NULL ;
731+ zend_fiber_context * context = ecalloc (1 , sizeof (zend_fiber_context ));
732+
733+ context -> status = ZEND_FIBER_STATUS_RUNNING ;
734+
735+ EG (main_fiber ) = context ;
736+ EG (current_fiber ) = context ;
737+ }
738+
739+ void zend_fiber_shutdown (void )
740+ {
741+ efree (EG (main_fiber ));
779742}
0 commit comments