Skip to content

Commit 8fd747a

Browse files
trowskikooldev
andauthored
Fiber ucontext support (#7226)
Co-authored-by: Martin Schröder <m.schroeder2007@gmail.com>
1 parent 1f42777 commit 8fd747a

File tree

4 files changed

+83
-18
lines changed

4 files changed

+83
-18
lines changed

NEWS

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? ????, PHP 8.1.0beta1
44

5+
- Core:
6+
. Fixed bug #81238 (Fiber support missing for Solaris Sparc). (trowski)
7+
58
- Reflection:
69
. Fixed bug #80097 (ReflectionAttribute is not a Reflector). (beberlei)
710

Zend/zend_fibers.c

+62-11
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ struct _zend_fiber_stack {
7272
const void *asan_pointer;
7373
size_t asan_size;
7474
#endif
75+
76+
#ifdef ZEND_FIBER_UCONTEXT
77+
/* Embedded ucontext to avoid unnecessary memory allocations. */
78+
ucontext_t ucontext;
79+
#endif
7580
};
7681

7782
/* 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 *
113118
EG(active_fiber) = state->active_fiber;
114119
}
115120

121+
#ifdef ZEND_FIBER_UCONTEXT
122+
# include <ucontext.h>
123+
ZEND_TLS zend_fiber_transfer *transfer_data;
124+
#else
116125
/* boost_context_data is our customized definition of struct transfer_t as
117126
* provided by boost.context in fcontext.hpp:
118127
*
@@ -130,7 +139,8 @@ typedef struct {
130139

131140
/* These functions are defined in assembler files provided by boost.context (located in "Zend/asm"). */
132141
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
134144

135145
ZEND_API zend_class_entry *zend_ce_fiber;
136146
static zend_class_entry *zend_ce_fiber_error;
@@ -244,20 +254,29 @@ static void zend_fiber_stack_free(zend_fiber_stack *stack)
244254

245255
efree(stack);
246256
}
247-
257+
#ifdef ZEND_FIBER_UCONTEXT
258+
static ZEND_NORETURN void zend_fiber_trampoline(void)
259+
#else
248260
static ZEND_NORETURN void zend_fiber_trampoline(boost_context_data data)
261+
#endif
249262
{
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;
251271

252272
#ifdef __SANITIZE_ADDRESS__
253273
__sanitizer_finish_switch_fiber(NULL, &from->stack->asan_pointer, &from->stack->asan_size);
254274
#endif
255275

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. */
257278
from->handle = data.handle;
258-
259-
/* Initialize transfer struct with a copy of passed data. */
260-
zend_fiber_transfer transfer = *data.transfer;
279+
#endif
261280

262281
/* Ensure that previous fiber will be cleaned up (needed by symmetric coroutines). */
263282
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
300319
return false;
301320
}
302321

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
303336
// Stack grows down, calculate the top of the stack. make_fcontext then shifts pointer to lower 16-byte boundary.
304337
void *stack = (void *) ((uintptr_t) context->stack->pointer + context->stack->size);
305338

306339
context->handle = make_fcontext(stack, context->stack->size, zend_fiber_trampoline);
307340
ZEND_ASSERT(context->handle != NULL && "make_fcontext() never returns NULL");
341+
#endif
308342

309343
context->kind = kind;
310344
context->function = coroutine;
@@ -363,14 +397,26 @@ ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer)
363397
to->stack->asan_size);
364398
#endif
365399

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
366408
boost_context_data data = jump_fcontext(to->handle, transfer);
367409

368410
/* Copy transfer struct because it might live on the other fiber's stack that will eventually be destroyed. */
369411
*transfer = *data.transfer;
412+
#endif
370413

371-
/* Get a hold of the context that resumed us and update its handle to allow for symmetric coroutines. */
372414
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. */
373418
to->handle = data.handle;
419+
#endif
374420

375421
#ifdef __SANITIZE_ADDRESS__
376422
__sanitizer_finish_switch_fiber(fake_stack, &to->stack->asan_pointer, &to->stack->asan_size);
@@ -839,9 +885,13 @@ void zend_fiber_init(void)
839885
{
840886
zend_fiber_context *context = ecalloc(1, sizeof(zend_fiber_context));
841887

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.
844890
context->stack = emalloc(sizeof(zend_fiber_stack));
891+
892+
#ifdef ZEND_FIBER_UCONTEXT
893+
context->handle = &context->stack->ucontext;
894+
#endif
845895
#endif
846896

847897
context->status = ZEND_FIBER_STATUS_RUNNING;
@@ -855,9 +905,10 @@ void zend_fiber_init(void)
855905

856906
void zend_fiber_shutdown(void)
857907
{
858-
#ifdef __SANITIZE_ADDRESS__
908+
#if defined(__SANITIZE_ADDRESS__) || defined(ZEND_FIBER_UCONTEXT)
859909
efree(EG(main_fiber_context)->stack);
860910
#endif
911+
861912
efree(EG(main_fiber_context));
862913

863914
zend_fiber_switch_block();

Zend/zend_fibers.h

+1-1
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

+17-6
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,23 @@ 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+
if test "$fiber_os" = 'mac'; then
1246+
AC_DEFINE([_XOPEN_SOURCE], 1, [ ])
1247+
fi
1248+
AC_CHECK_HEADER(ucontext.h, [
1249+
AC_DEFINE([ZEND_FIBER_UCONTEXT], 1, [ ])
1250+
], [
1251+
AC_MSG_ERROR([fibers not available on this platform])
1252+
])
12421253
fi
12431254

12441255
LIBZEND_BASIC_CHECKS

0 commit comments

Comments
 (0)