Skip to content

Commit cb0da91

Browse files
committed
Fiber ucontext support
1 parent 1607207 commit cb0da91

File tree

3 files changed

+79
-18
lines changed

3 files changed

+79
-18
lines changed

Zend/zend_fibers.c

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ static zend_always_inline void zend_fiber_restore_vm_state(zend_fiber_vm_state *
113113
EG(active_fiber) = state->active_fiber;
114114
}
115115

116+
#ifdef ZEND_FIBER_UCONTEXT
117+
# include <ucontext.h>
118+
ZEND_TLS zend_fiber_transfer *transfer_data;
119+
#else
116120
/* boost_context_data is our customized definition of struct transfer_t as
117121
* provided by boost.context in fcontext.hpp:
118122
*
@@ -130,7 +134,8 @@ typedef struct {
130134

131135
/* These functions are defined in assembler files provided by boost.context (located in "Zend/asm"). */
132136
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);
137+
extern boost_context_data jump_fcontext(void *to, zend_fiber_transfer *transfer);
138+
#endif
134139

135140
ZEND_API zend_class_entry *zend_ce_fiber;
136141
static zend_class_entry *zend_ce_fiber_error;
@@ -244,28 +249,37 @@ static void zend_fiber_stack_free(zend_fiber_stack *stack)
244249

245250
efree(stack);
246251
}
247-
252+
#ifdef ZEND_FIBER_UCONTEXT
253+
static ZEND_NORETURN void zend_fiber_trampoline(void)
254+
#else
248255
static ZEND_NORETURN void zend_fiber_trampoline(boost_context_data data)
256+
#endif
249257
{
250-
zend_fiber_context *from = data.transfer->context;
258+
zend_fiber_context *context = EG(current_fiber_context);
251259

252-
#ifdef __SANITIZE_ADDRESS__
253-
__sanitizer_finish_switch_fiber(NULL, &from->stack->asan_pointer, &from->stack->asan_size);
260+
/* Initialize transfer struct with a copy of passed data. */
261+
#ifdef ZEND_FIBER_UCONTEXT
262+
zend_fiber_transfer transfer = *transfer_data;
263+
#else
264+
zend_fiber_transfer transfer = *data.transfer;
254265
#endif
255266

256-
/* Get a hold of the context that resumed us and update its handle to allow for symmetric coroutines. */
267+
zend_fiber_context *from = transfer.context;
268+
269+
#ifndef ZEND_FIBER_UCONTEXT
270+
/* Get the context that resumed us and update its handle to allow for symmetric coroutines. */
257271
from->handle = data.handle;
272+
#endif
258273

259-
/* Initialize transfer struct with a copy of passed data. */
260-
zend_fiber_transfer transfer = *data.transfer;
274+
#ifdef __SANITIZE_ADDRESS__
275+
__sanitizer_finish_switch_fiber(NULL, &from->stack->asan_pointer, &from->stack->asan_size);
276+
#endif
261277

262278
/* Ensure that previous fiber will be cleaned up (needed by symmetric coroutines). */
263279
if (from->status == ZEND_FIBER_STATUS_DEAD) {
264280
zend_fiber_destroy_context(from);
265281
}
266282

267-
zend_fiber_context *context = EG(current_fiber_context);
268-
269283
context->function(&transfer);
270284
context->status = ZEND_FIBER_STATUS_DEAD;
271285

@@ -300,11 +314,25 @@ ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, void *kind, z
300314
return false;
301315
}
302316

317+
#ifdef ZEND_FIBER_UCONTEXT
318+
ucontext_t *handle = emalloc(sizeof(ucontext_t));
319+
getcontext(handle);
320+
321+
handle->uc_stack.ss_size = context->stack->size;
322+
handle->uc_stack.ss_sp = context->stack->pointer;
323+
handle->uc_stack.ss_flags = 0;
324+
handle->uc_link = NULL;
325+
326+
makecontext(handle, (void (*)(void)) zend_fiber_trampoline, 0);
327+
328+
context->handle = handle;
329+
#else
303330
// Stack grows down, calculate the top of the stack. make_fcontext then shifts pointer to lower 16-byte boundary.
304331
void *stack = (void *) ((uintptr_t) context->stack->pointer + context->stack->size);
305332

306333
context->handle = make_fcontext(stack, context->stack->size, zend_fiber_trampoline);
307334
ZEND_ASSERT(context->handle != NULL && "make_fcontext() never returns NULL");
335+
#endif
308336

309337
context->kind = kind;
310338
context->function = coroutine;
@@ -318,6 +346,10 @@ ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, void *kind, z
318346
ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context)
319347
{
320348
zend_fiber_stack_free(context->stack);
349+
350+
#ifdef ZEND_FIBER_UCONTEXT
351+
efree(context->handle);
352+
#endif
321353
}
322354

323355
ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer)
@@ -363,14 +395,26 @@ ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer)
363395
to->stack->asan_size);
364396
#endif
365397

398+
#ifdef ZEND_FIBER_UCONTEXT
399+
transfer_data = transfer;
400+
401+
swapcontext(from->handle, to->handle);
402+
403+
/* Copy transfer struct because it might live on the other fiber's stack that will eventually be destroyed. */
404+
*transfer = *transfer_data;
405+
#else
366406
boost_context_data data = jump_fcontext(to->handle, transfer);
367407

368408
/* Copy transfer struct because it might live on the other fiber's stack that will eventually be destroyed. */
369409
*transfer = *data.transfer;
410+
#endif
370411

371-
/* Get a hold of the context that resumed us and update its handle to allow for symmetric coroutines. */
372412
to = transfer->context;
413+
414+
#ifndef ZEND_FIBER_UCONTEXT
415+
/* Get the context that resumed us and update its handle to allow for symmetric coroutines. */
373416
to->handle = data.handle;
417+
#endif
374418

375419
#ifdef __SANITIZE_ADDRESS__
376420
__sanitizer_finish_switch_fiber(fake_stack, &to->stack->asan_pointer, &to->stack->asan_size);
@@ -844,6 +888,10 @@ void zend_fiber_init(void)
844888
context->stack = emalloc(sizeof(zend_fiber_stack));
845889
#endif
846890

891+
#ifdef ZEND_FIBER_UCONTEXT
892+
context->handle = emalloc(sizeof(ucontext_t));
893+
#endif
894+
847895
context->status = ZEND_FIBER_STATUS_RUNNING;
848896

849897
EG(main_fiber_context) = context;
@@ -855,9 +903,14 @@ void zend_fiber_init(void)
855903

856904
void zend_fiber_shutdown(void)
857905
{
906+
#ifdef ZEND_FIBER_UCONTEXT
907+
efree(EG(main_fiber_context)->handle);
908+
#endif
909+
858910
#ifdef __SANITIZE_ADDRESS__
859911
efree(EG(main_fiber_context)->stack);
860912
#endif
913+
861914
efree(EG(main_fiber_context));
862915

863916
zend_fiber_switch_block();

Zend/zend_fibers.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ typedef struct _zend_fiber_transfer {
7373
typedef void (*zend_fiber_coroutine)(zend_fiber_transfer *transfer);
7474

7575
struct _zend_fiber_context {
76-
/* Handle to fiber state as needed by boost.context */
76+
/* Pointer to boost.context or ucontext_t data. */
7777
void *handle;
7878

7979
/* Pointer that identifies the fiber type. */

configure.ac

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,12 +1189,14 @@ fi
11891189
dnl Configuring Zend and TSRM.
11901190
dnl ----------------------------------------------------------------------------
11911191

1192+
AC_ARG_ENABLE([fiber-asm],
1193+
[AS_HELP_STRING([--disable-fiber-asm],
1194+
[Disable the use of boost fiber assembly files])],
1195+
[fiber_asm=$enableval], [fiber_asm='yes'])
1196+
11921197
PHP_HELP_SEPARATOR([Zend:])
11931198
PHP_CONFIGURE_PART(Configuring Zend)
11941199

1195-
AC_MSG_CHECKING(for fiber switching context)
1196-
fibers="yes"
1197-
11981200
AS_CASE([$host_cpu],
11991201
[x86_64*|amd64*], [fiber_cpu="x86_64"],
12001202
[x86*|amd*|i?86*|pentium], [fiber_cpu="i386"],
@@ -1231,14 +1233,20 @@ if test "$fiber_os" = 'mac'; then
12311233
elif test "$fiber_asm_file_prefix" != 'unknown'; then
12321234
fiber_asm_file="${fiber_asm_file_prefix}_elf_gas"
12331235
else
1234-
fibers="no"
1236+
fiber_asm="no"
12351237
fi
12361238

1237-
if test "$fibers" = 'yes'; then
1239+
if test "$fiber_asm" = 'yes'; then
1240+
AC_MSG_CHECKING([for fiber switching context])
1241+
AC_DEFINE([ZEND_FIBER_ASM], 1, [ ])
12381242
PHP_ADD_SOURCES(Zend/asm, make_${fiber_asm_file}.S jump_${fiber_asm_file}.S)
12391243
AC_MSG_RESULT([$fiber_asm_file])
12401244
else
1241-
AC_MSG_ERROR([Unable to determine platform!])
1245+
AC_CHECK_HEADER(ucontext.h, [
1246+
AC_DEFINE([ZEND_FIBER_UCONTEXT], 1, [ ])
1247+
], [
1248+
AC_MSG_ERROR([fibers not available on this platform])
1249+
])
12421250
fi
12431251

12441252
LIBZEND_BASIC_CHECKS

0 commit comments

Comments
 (0)