@@ -72,6 +72,11 @@ struct _zend_fiber_stack {
72
72
const void * asan_pointer ;
73
73
size_t asan_size ;
74
74
#endif
75
+
76
+ #ifdef ZEND_FIBER_UCONTEXT
77
+ /* Embedded ucontext to avoid unnecessary memory allocations. */
78
+ ucontext_t ucontext ;
79
+ #endif
75
80
};
76
81
77
82
/* Zend VM state that needs to be captured / restored during fiber context switch. */
@@ -113,6 +118,10 @@ static zend_always_inline void zend_fiber_restore_vm_state(zend_fiber_vm_state *
113
118
EG (active_fiber ) = state -> active_fiber ;
114
119
}
115
120
121
+ #ifdef ZEND_FIBER_UCONTEXT
122
+ # include <ucontext.h>
123
+ ZEND_TLS zend_fiber_transfer * transfer_data ;
124
+ #else
116
125
/* boost_context_data is our customized definition of struct transfer_t as
117
126
* provided by boost.context in fcontext.hpp:
118
127
*
@@ -130,7 +139,8 @@ typedef struct {
130
139
131
140
/* These functions are defined in assembler files provided by boost.context (located in "Zend/asm"). */
132
141
extern void * make_fcontext (void * sp , size_t size , void (* fn )(boost_context_data ));
133
- extern boost_context_data jump_fcontext (void * to , zend_fiber_transfer * data );
142
+ extern boost_context_data jump_fcontext (void * to , zend_fiber_transfer * transfer );
143
+ #endif
134
144
135
145
ZEND_API zend_class_entry * zend_ce_fiber ;
136
146
static zend_class_entry * zend_ce_fiber_error ;
@@ -244,20 +254,29 @@ static void zend_fiber_stack_free(zend_fiber_stack *stack)
244
254
245
255
efree (stack );
246
256
}
247
-
257
+ #ifdef ZEND_FIBER_UCONTEXT
258
+ static ZEND_NORETURN void zend_fiber_trampoline (void )
259
+ #else
248
260
static ZEND_NORETURN void zend_fiber_trampoline (boost_context_data data )
261
+ #endif
249
262
{
250
- zend_fiber_context * from = data .transfer -> context ;
263
+ /* Initialize transfer struct with a copy of passed data. */
264
+ #ifdef ZEND_FIBER_UCONTEXT
265
+ zend_fiber_transfer transfer = * transfer_data ;
266
+ #else
267
+ zend_fiber_transfer transfer = * data .transfer ;
268
+ #endif
269
+
270
+ zend_fiber_context * from = transfer .context ;
251
271
252
272
#ifdef __SANITIZE_ADDRESS__
253
273
__sanitizer_finish_switch_fiber (NULL , & from -> stack -> asan_pointer , & from -> stack -> asan_size );
254
274
#endif
255
275
256
- /* Get a hold of the context that resumed us and update its handle to allow for symmetric coroutines. */
276
+ #ifndef ZEND_FIBER_UCONTEXT
277
+ /* Get the context that resumed us and update its handle to allow for symmetric coroutines. */
257
278
from -> handle = data .handle ;
258
-
259
- /* Initialize transfer struct with a copy of passed data. */
260
- zend_fiber_transfer transfer = * data .transfer ;
279
+ #endif
261
280
262
281
/* Ensure that previous fiber will be cleaned up (needed by symmetric coroutines). */
263
282
if (from -> status == ZEND_FIBER_STATUS_DEAD ) {
@@ -300,11 +319,26 @@ ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, void *kind, z
300
319
return false;
301
320
}
302
321
322
+ #ifdef ZEND_FIBER_UCONTEXT
323
+ ucontext_t * handle = & context -> stack -> ucontext ;
324
+
325
+ getcontext (handle );
326
+
327
+ handle -> uc_stack .ss_size = context -> stack -> size ;
328
+ handle -> uc_stack .ss_sp = context -> stack -> pointer ;
329
+ handle -> uc_stack .ss_flags = 0 ;
330
+ handle -> uc_link = NULL ;
331
+
332
+ makecontext (handle , (void (* )(void )) zend_fiber_trampoline , 0 );
333
+
334
+ context -> handle = handle ;
335
+ #else
303
336
// Stack grows down, calculate the top of the stack. make_fcontext then shifts pointer to lower 16-byte boundary.
304
337
void * stack = (void * ) ((uintptr_t ) context -> stack -> pointer + context -> stack -> size );
305
338
306
339
context -> handle = make_fcontext (stack , context -> stack -> size , zend_fiber_trampoline );
307
340
ZEND_ASSERT (context -> handle != NULL && "make_fcontext() never returns NULL" );
341
+ #endif
308
342
309
343
context -> kind = kind ;
310
344
context -> function = coroutine ;
@@ -363,14 +397,26 @@ ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer)
363
397
to -> stack -> asan_size );
364
398
#endif
365
399
400
+ #ifdef ZEND_FIBER_UCONTEXT
401
+ transfer_data = transfer ;
402
+
403
+ swapcontext (from -> handle , to -> handle );
404
+
405
+ /* Copy transfer struct because it might live on the other fiber's stack that will eventually be destroyed. */
406
+ * transfer = * transfer_data ;
407
+ #else
366
408
boost_context_data data = jump_fcontext (to -> handle , transfer );
367
409
368
410
/* Copy transfer struct because it might live on the other fiber's stack that will eventually be destroyed. */
369
411
* transfer = * data .transfer ;
412
+ #endif
370
413
371
- /* Get a hold of the context that resumed us and update its handle to allow for symmetric coroutines. */
372
414
to = transfer -> context ;
415
+
416
+ #ifndef ZEND_FIBER_UCONTEXT
417
+ /* Get the context that resumed us and update its handle to allow for symmetric coroutines. */
373
418
to -> handle = data .handle ;
419
+ #endif
374
420
375
421
#ifdef __SANITIZE_ADDRESS__
376
422
__sanitizer_finish_switch_fiber (fake_stack , & to -> stack -> asan_pointer , & to -> stack -> asan_size );
@@ -839,9 +885,13 @@ void zend_fiber_init(void)
839
885
{
840
886
zend_fiber_context * context = ecalloc (1 , sizeof (zend_fiber_context ));
841
887
842
- #ifdef __SANITIZE_ADDRESS__
843
- // Main fiber context stack is only accessed if ASan is enabled.
888
+ #if defined( __SANITIZE_ADDRESS__ ) || defined( ZEND_FIBER_UCONTEXT )
889
+ // Main fiber stack is only needed if ASan or ucontext is enabled.
844
890
context -> stack = emalloc (sizeof (zend_fiber_stack ));
891
+
892
+ #ifdef ZEND_FIBER_UCONTEXT
893
+ context -> handle = & context -> stack -> ucontext ;
894
+ #endif
845
895
#endif
846
896
847
897
context -> status = ZEND_FIBER_STATUS_RUNNING ;
@@ -855,9 +905,10 @@ void zend_fiber_init(void)
855
905
856
906
void zend_fiber_shutdown (void )
857
907
{
858
- #ifdef __SANITIZE_ADDRESS__
908
+ #if defined( __SANITIZE_ADDRESS__ ) || defined( ZEND_FIBER_UCONTEXT )
859
909
efree (EG (main_fiber_context )-> stack );
860
910
#endif
911
+
861
912
efree (EG (main_fiber_context ));
862
913
863
914
zend_fiber_switch_block ();
0 commit comments