Skip to content

Commit 22897f7

Browse files
committed
Fiber ucontext support
1 parent 1607207 commit 22897f7

File tree

3 files changed

+79
-21
lines changed

3 files changed

+79
-21
lines changed

Zend/zend_fibers.c

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ 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+
#else
116119
/* boost_context_data is our customized definition of struct transfer_t as
117120
* provided by boost.context in fcontext.hpp:
118121
*
@@ -125,12 +128,13 @@ static zend_always_inline void zend_fiber_restore_vm_state(zend_fiber_vm_state *
125128

126129
typedef struct {
127130
void *handle;
128-
zend_fiber_transfer *transfer;
131+
zend_fiber_context *context;
129132
} boost_context_data;
130133

131134
/* These functions are defined in assembler files provided by boost.context (located in "Zend/asm"). */
132135
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);
136+
extern boost_context_data jump_fcontext(void *to, zend_fiber_context *context);
137+
#endif
134138

135139
ZEND_API zend_class_entry *zend_ce_fiber;
136140
static zend_class_entry *zend_ce_fiber_error;
@@ -245,27 +249,21 @@ static void zend_fiber_stack_free(zend_fiber_stack *stack)
245249
efree(stack);
246250
}
247251

248-
static ZEND_NORETURN void zend_fiber_trampoline(boost_context_data data)
252+
static ZEND_NORETURN void zend_fiber_trampoline(zend_fiber_context *context)
249253
{
250-
zend_fiber_context *from = data.transfer->context;
254+
/* Initialize transfer struct with a copy of passed data. */
255+
zend_fiber_transfer transfer = *context->transfer;
256+
zend_fiber_context *from = transfer.context;
251257

252258
#ifdef __SANITIZE_ADDRESS__
253259
__sanitizer_finish_switch_fiber(NULL, &from->stack->asan_pointer, &from->stack->asan_size);
254260
#endif
255261

256-
/* Get a hold of the context that resumed us and update its handle to allow for symmetric coroutines. */
257-
from->handle = data.handle;
258-
259-
/* Initialize transfer struct with a copy of passed data. */
260-
zend_fiber_transfer transfer = *data.transfer;
261-
262262
/* Ensure that previous fiber will be cleaned up (needed by symmetric coroutines). */
263263
if (from->status == ZEND_FIBER_STATUS_DEAD) {
264264
zend_fiber_destroy_context(from);
265265
}
266266

267-
zend_fiber_context *context = EG(current_fiber_context);
268-
269267
context->function(&transfer);
270268
context->status = ZEND_FIBER_STATUS_DEAD;
271269

@@ -276,6 +274,19 @@ static ZEND_NORETURN void zend_fiber_trampoline(boost_context_data data)
276274
abort();
277275
}
278276

277+
#ifndef ZEND_FIBER_UCONTEXT
278+
static ZEND_NORETURN void zend_fiber_start(boost_context_data data)
279+
{
280+
zend_fiber_context *context = data.context;
281+
282+
/* Get the context that resumed us and update its handle to allow for symmetric coroutines. */
283+
zend_fiber_context *from = context->transfer->context;
284+
from->handle = data.handle;
285+
286+
zend_fiber_trampoline(context);
287+
}
288+
#endif
289+
279290
ZEND_API void zend_fiber_switch_block(void)
280291
{
281292
++zend_fiber_switch_blocking;
@@ -300,11 +311,25 @@ ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, void *kind, z
300311
return false;
301312
}
302313

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

306-
context->handle = make_fcontext(stack, context->stack->size, zend_fiber_trampoline);
330+
context->handle = make_fcontext(stack, context->stack->size, zend_fiber_start);
307331
ZEND_ASSERT(context->handle != NULL && "make_fcontext() never returns NULL");
332+
#endif
308333

309334
context->kind = kind;
310335
context->function = coroutine;
@@ -318,6 +343,10 @@ ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, void *kind, z
318343
ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context)
319344
{
320345
zend_fiber_stack_free(context->stack);
346+
347+
#ifdef ZEND_FIBER_UCONTEXT
348+
efree(context->handle);
349+
#endif
321350
}
322351

323352
ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer)
@@ -352,6 +381,7 @@ ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer)
352381

353382
/* Update transfer context with the current fiber before switching. */
354383
transfer->context = from;
384+
to->transfer = transfer;
355385

356386
EG(current_fiber_context) = to;
357387

@@ -363,14 +393,20 @@ ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer)
363393
to->stack->asan_size);
364394
#endif
365395

366-
boost_context_data data = jump_fcontext(to->handle, transfer);
396+
#ifdef ZEND_FIBER_UCONTEXT
397+
swapcontext(from->handle, to->handle);
398+
#else
399+
boost_context_data data = jump_fcontext(to->handle, to);
400+
#endif
367401

368402
/* Copy transfer struct because it might live on the other fiber's stack that will eventually be destroyed. */
369-
*transfer = *data.transfer;
403+
*transfer = *from->transfer;
370404

371-
/* Get a hold of the context that resumed us and update its handle to allow for symmetric coroutines. */
405+
/* Get the context that resumed us and update its handle to allow for symmetric coroutines. */
372406
to = transfer->context;
407+
#ifndef ZEND_FIBER_UCONTEXT
373408
to->handle = data.handle;
409+
#endif
374410

375411
#ifdef __SANITIZE_ADDRESS__
376412
__sanitizer_finish_switch_fiber(fake_stack, &to->stack->asan_pointer, &to->stack->asan_size);
@@ -844,6 +880,10 @@ void zend_fiber_init(void)
844880
context->stack = emalloc(sizeof(zend_fiber_stack));
845881
#endif
846882

883+
#ifdef ZEND_FIBER_UCONTEXT
884+
context->handle = emalloc(sizeof(ucontext_t));
885+
#endif
886+
847887
context->status = ZEND_FIBER_STATUS_RUNNING;
848888

849889
EG(main_fiber_context) = context;
@@ -855,9 +895,14 @@ void zend_fiber_init(void)
855895

856896
void zend_fiber_shutdown(void)
857897
{
898+
#ifdef ZEND_FIBER_UCONTEXT
899+
efree(EG(main_fiber_context)->handle);
900+
#endif
901+
858902
#ifdef __SANITIZE_ADDRESS__
859903
efree(EG(main_fiber_context)->stack);
860904
#endif
905+
861906
efree(EG(main_fiber_context));
862907

863908
zend_fiber_switch_block();

Zend/zend_fibers.h

Lines changed: 4 additions & 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. */
@@ -82,6 +82,9 @@ struct _zend_fiber_context {
8282
/* Entrypoint function of the fiber. */
8383
zend_fiber_coroutine function;
8484

85+
/* Data transferred from calling context. */
86+
zend_fiber_transfer *transfer;
87+
8588
/* Assigned C stack. */
8689
zend_fiber_stack *stack;
8790

configure.ac

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,8 +1192,14 @@ dnl ----------------------------------------------------------------------------
11921192
PHP_HELP_SEPARATOR([Zend:])
11931193
PHP_CONFIGURE_PART(Configuring Zend)
11941194

1195+
fiber_asm="yes"
1196+
fiber_ucontext="no"
1197+
1198+
AC_CHECK_HEADER(ucontext.h, [
1199+
fiber_ucontext="yes"
1200+
])
1201+
11951202
AC_MSG_CHECKING(for fiber switching context)
1196-
fibers="yes"
11971203

11981204
AS_CASE([$host_cpu],
11991205
[x86_64*|amd64*], [fiber_cpu="x86_64"],
@@ -1231,14 +1237,18 @@ if test "$fiber_os" = 'mac'; then
12311237
elif test "$fiber_asm_file_prefix" != 'unknown'; then
12321238
fiber_asm_file="${fiber_asm_file_prefix}_elf_gas"
12331239
else
1234-
fibers="no"
1240+
fiber_asm="no"
12351241
fi
12361242

1237-
if test "$fibers" = 'yes'; then
1243+
if test "$fiber_asm" = 'yes'; then
1244+
AC_DEFINE([ZEND_FIBER_ASM], 1, [ ])
12381245
PHP_ADD_SOURCES(Zend/asm, make_${fiber_asm_file}.S jump_${fiber_asm_file}.S)
12391246
AC_MSG_RESULT([$fiber_asm_file])
1247+
elif test "$fiber_ucontext" = 'yes'; then
1248+
AC_DEFINE([ZEND_FIBER_UCONTEXT], 1, [ ])
1249+
AC_MSG_RESULT([ucontext.h])
12401250
else
1241-
AC_MSG_ERROR([Unable to determine platform!])
1251+
AC_MSG_ERROR([No fiber switching context available on this platform])
12421252
fi
12431253

12441254
LIBZEND_BASIC_CHECKS

0 commit comments

Comments
 (0)