@@ -78,28 +78,6 @@ extern transfer_t jump_fcontext(fcontext_t to, void *vp);
78
78
79
79
#define ZEND_FIBER_DEFAULT_PAGE_SIZE 4096
80
80
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
-
103
81
static size_t zend_fiber_get_page_size (void )
104
82
{
105
83
static size_t page_size = 0 ;
@@ -172,6 +150,11 @@ static bool zend_fiber_stack_allocate(zend_fiber_stack *stack, size_t size)
172
150
stack -> valgrind = VALGRIND_STACK_REGISTER (base , base + stack -> size );
173
151
#endif
174
152
153
+ #ifdef __SANITIZE_ADDRESS__
154
+ stack -> prior_pointer = stack -> pointer ;
155
+ stack -> prior_size = stack -> size ;
156
+ #endif
157
+
175
158
return true;
176
159
}
177
160
@@ -200,24 +183,19 @@ static void zend_fiber_stack_free(zend_fiber_stack *stack)
200
183
201
184
static ZEND_NORETURN void zend_fiber_trampoline (transfer_t transfer )
202
185
{
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 ;
212
188
213
- context -> self = NULL ;
189
+ from -> handle = transfer . context ;
214
190
215
191
#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 );
217
193
#endif
218
194
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 ));
220
197
198
+ /* Abort here because we are in an inconsistent program state. */
221
199
abort ();
222
200
}
223
201
@@ -230,10 +208,10 @@ ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, zend_fiber_co
230
208
// Stack grows down, calculate the top of the stack. make_fcontext then shifts pointer to lower 16-byte boundary.
231
209
void * stack = (void * ) ((uintptr_t ) context -> stack .pointer + context -> stack .size );
232
210
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
+
235
214
context -> function = coroutine ;
236
- context -> caller = NULL ;
237
215
238
216
return true;
239
217
}
@@ -245,81 +223,59 @@ ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context)
245
223
246
224
ZEND_API void zend_fiber_switch_context (zend_fiber_context * to )
247
225
{
248
- ZEND_ASSERT ( to && to -> self && to -> stack . pointer && "Invalid fiber context" );
226
+ zend_fiber_context * from = EG ( current_fiber );
249
227
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" );
256
231
257
232
#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 );
259
238
#endif
260
239
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 ;
267
241
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 ;
272
244
273
- transfer_t transfer = jump_fcontext ( current -> caller , NULL ) ;
245
+ EG ( current_fiber ) = from ;
274
246
275
247
#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 );
277
249
#endif
278
-
279
- current -> caller = transfer .context ;
280
250
}
281
251
282
252
static void zend_fiber_suspend_from (zend_fiber * fiber )
283
253
{
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 ;
290
255
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" );
292
257
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 );
296
261
}
297
262
298
263
static void zend_fiber_switch_to (zend_fiber * fiber )
299
264
{
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 ;
309
267
310
- zend_observer_fiber_switch_notify (previous , fiber );
268
+ zend_observer_fiber_switch_notify (EG ( current_fiber ), context );
311
269
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 ) );
313
271
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 );
315
275
316
- zend_fiber_switch_context ( & fiber -> context ) ;
276
+ fiber -> caller = NULL ;
317
277
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 ));
323
279
324
280
if (UNEXPECTED (fiber -> flags & ZEND_FIBER_FLAG_BAILOUT )) {
325
281
// 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)
339
295
return page ;
340
296
}
341
297
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 )
343
299
{
344
- zend_fiber * fiber = EG (current_fiber );
345
- ZEND_ASSERT (fiber );
300
+ zend_fiber * fiber = zend_fiber_from_context (context );
346
301
347
302
zend_long error_reporting = INI_INT ("error_reporting" );
348
303
if (!error_reporting && !INI_STR ("error_reporting" )) {
@@ -396,6 +351,8 @@ static void ZEND_STACK_ALIGNED zend_fiber_execute(zend_fiber_context *context)
396
351
zend_vm_stack_destroy ();
397
352
fiber -> execute_data = NULL ;
398
353
fiber -> stack_bottom = NULL ;
354
+
355
+ return zend_fiber_get_context (fiber -> caller );
399
356
}
400
357
401
358
static zend_object * zend_fiber_object_create (zend_class_entry * ce )
@@ -454,7 +411,7 @@ static void zend_fiber_object_free(zend_object *object)
454
411
455
412
zval_ptr_dtor (& fiber -> value );
456
413
457
- zend_fiber_destroy_context (& fiber -> context );
414
+ zend_fiber_destroy_context (zend_fiber_get_context ( fiber ) );
458
415
459
416
zend_object_std_dtor (& fiber -> std );
460
417
}
@@ -494,7 +451,7 @@ ZEND_API void zend_fiber_start(zend_fiber *fiber, zval *params, uint32_t param_c
494
451
fiber -> fci .param_count = param_count ;
495
452
fiber -> fci .named_params = named_params ;
496
453
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 ))) {
498
455
RETURN_THROWS ();
499
456
}
500
457
@@ -524,13 +481,13 @@ ZEND_METHOD(Fiber, start)
524
481
525
482
ZEND_API void zend_fiber_suspend (zval * value , zval * return_value )
526
483
{
527
- zend_fiber * fiber = EG (current_fiber );
528
-
529
- if (UNEXPECTED (!fiber )) {
484
+ if (UNEXPECTED (EG (current_fiber ) == EG (main_fiber ))) {
530
485
zend_throw_error (zend_ce_fiber_error , "Cannot suspend outside of a fiber" );
531
486
RETURN_THROWS ();
532
487
}
533
488
489
+ zend_fiber * fiber = zend_fiber_from_context (EG (current_fiber ));
490
+
534
491
if (UNEXPECTED (fiber -> flags & ZEND_FIBER_FLAG_DESTROYED )) {
535
492
zend_throw_error (zend_ce_fiber_error , "Cannot suspend in a force-closed fiber" );
536
493
RETURN_THROWS ();
@@ -734,17 +691,13 @@ ZEND_METHOD(Fiber, getReturn)
734
691
735
692
ZEND_METHOD (Fiber , this )
736
693
{
737
- zend_fiber * fiber ;
738
-
739
694
ZEND_PARSE_PARAMETERS_NONE ();
740
695
741
- fiber = EG (current_fiber );
742
-
743
- if (!fiber ) {
696
+ if (EG (current_fiber ) == EG (main_fiber )) {
744
697
RETURN_NULL ();
745
698
}
746
699
747
- RETURN_OBJ_COPY (& fiber -> std );
700
+ RETURN_OBJ_COPY (& zend_fiber_from_context ( EG ( current_fiber )) -> std );
748
701
}
749
702
750
703
ZEND_METHOD (FiberError , __construct )
@@ -775,5 +728,15 @@ void zend_register_fiber_ce(void)
775
728
776
729
void zend_fiber_init (void )
777
730
{
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 ));
779
742
}
0 commit comments