From cb0da91aded5543456d8b1f328098e95efd79d6c Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Fri, 9 Jul 2021 20:18:59 -0500 Subject: [PATCH 1/5] Fiber ucontext support --- Zend/zend_fibers.c | 75 +++++++++++++++++++++++++++++++++++++++------- Zend/zend_fibers.h | 2 +- configure.ac | 20 +++++++++---- 3 files changed, 79 insertions(+), 18 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index dbfc3d6363db2..0790eed65f789 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -113,6 +113,10 @@ static zend_always_inline void zend_fiber_restore_vm_state(zend_fiber_vm_state * EG(active_fiber) = state->active_fiber; } +#ifdef ZEND_FIBER_UCONTEXT +# include +ZEND_TLS zend_fiber_transfer *transfer_data; +#else /* boost_context_data is our customized definition of struct transfer_t as * provided by boost.context in fcontext.hpp: * @@ -130,7 +134,8 @@ typedef struct { /* These functions are defined in assembler files provided by boost.context (located in "Zend/asm"). */ extern void *make_fcontext(void *sp, size_t size, void (*fn)(boost_context_data)); -extern boost_context_data jump_fcontext(void *to, zend_fiber_transfer *data); +extern boost_context_data jump_fcontext(void *to, zend_fiber_transfer *transfer); +#endif ZEND_API zend_class_entry *zend_ce_fiber; static zend_class_entry *zend_ce_fiber_error; @@ -244,28 +249,37 @@ static void zend_fiber_stack_free(zend_fiber_stack *stack) efree(stack); } - +#ifdef ZEND_FIBER_UCONTEXT +static ZEND_NORETURN void zend_fiber_trampoline(void) +#else static ZEND_NORETURN void zend_fiber_trampoline(boost_context_data data) +#endif { - zend_fiber_context *from = data.transfer->context; + zend_fiber_context *context = EG(current_fiber_context); -#ifdef __SANITIZE_ADDRESS__ - __sanitizer_finish_switch_fiber(NULL, &from->stack->asan_pointer, &from->stack->asan_size); + /* Initialize transfer struct with a copy of passed data. */ +#ifdef ZEND_FIBER_UCONTEXT + zend_fiber_transfer transfer = *transfer_data; +#else + zend_fiber_transfer transfer = *data.transfer; #endif - /* Get a hold of the context that resumed us and update its handle to allow for symmetric coroutines. */ + zend_fiber_context *from = transfer.context; + +#ifndef ZEND_FIBER_UCONTEXT + /* Get the context that resumed us and update its handle to allow for symmetric coroutines. */ from->handle = data.handle; +#endif - /* Initialize transfer struct with a copy of passed data. */ - zend_fiber_transfer transfer = *data.transfer; +#ifdef __SANITIZE_ADDRESS__ + __sanitizer_finish_switch_fiber(NULL, &from->stack->asan_pointer, &from->stack->asan_size); +#endif /* Ensure that previous fiber will be cleaned up (needed by symmetric coroutines). */ if (from->status == ZEND_FIBER_STATUS_DEAD) { zend_fiber_destroy_context(from); } - zend_fiber_context *context = EG(current_fiber_context); - context->function(&transfer); context->status = ZEND_FIBER_STATUS_DEAD; @@ -300,11 +314,25 @@ ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, void *kind, z return false; } +#ifdef ZEND_FIBER_UCONTEXT + ucontext_t *handle = emalloc(sizeof(ucontext_t)); + getcontext(handle); + + handle->uc_stack.ss_size = context->stack->size; + handle->uc_stack.ss_sp = context->stack->pointer; + handle->uc_stack.ss_flags = 0; + handle->uc_link = NULL; + + makecontext(handle, (void (*)(void)) zend_fiber_trampoline, 0); + + context->handle = handle; +#else // Stack grows down, calculate the top of the stack. make_fcontext then shifts pointer to lower 16-byte boundary. void *stack = (void *) ((uintptr_t) context->stack->pointer + context->stack->size); context->handle = make_fcontext(stack, context->stack->size, zend_fiber_trampoline); ZEND_ASSERT(context->handle != NULL && "make_fcontext() never returns NULL"); +#endif context->kind = kind; context->function = coroutine; @@ -318,6 +346,10 @@ ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, void *kind, z ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context) { zend_fiber_stack_free(context->stack); + +#ifdef ZEND_FIBER_UCONTEXT + efree(context->handle); +#endif } 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) to->stack->asan_size); #endif +#ifdef ZEND_FIBER_UCONTEXT + transfer_data = transfer; + + swapcontext(from->handle, to->handle); + + /* Copy transfer struct because it might live on the other fiber's stack that will eventually be destroyed. */ + *transfer = *transfer_data; +#else boost_context_data data = jump_fcontext(to->handle, transfer); /* Copy transfer struct because it might live on the other fiber's stack that will eventually be destroyed. */ *transfer = *data.transfer; +#endif - /* Get a hold of the context that resumed us and update its handle to allow for symmetric coroutines. */ to = transfer->context; + +#ifndef ZEND_FIBER_UCONTEXT + /* Get the context that resumed us and update its handle to allow for symmetric coroutines. */ to->handle = data.handle; +#endif #ifdef __SANITIZE_ADDRESS__ __sanitizer_finish_switch_fiber(fake_stack, &to->stack->asan_pointer, &to->stack->asan_size); @@ -844,6 +888,10 @@ void zend_fiber_init(void) context->stack = emalloc(sizeof(zend_fiber_stack)); #endif +#ifdef ZEND_FIBER_UCONTEXT + context->handle = emalloc(sizeof(ucontext_t)); +#endif + context->status = ZEND_FIBER_STATUS_RUNNING; EG(main_fiber_context) = context; @@ -855,9 +903,14 @@ void zend_fiber_init(void) void zend_fiber_shutdown(void) { +#ifdef ZEND_FIBER_UCONTEXT + efree(EG(main_fiber_context)->handle); +#endif + #ifdef __SANITIZE_ADDRESS__ efree(EG(main_fiber_context)->stack); #endif + efree(EG(main_fiber_context)); zend_fiber_switch_block(); diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index 3f910df9cc611..33c85d5d64687 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -73,7 +73,7 @@ typedef struct _zend_fiber_transfer { typedef void (*zend_fiber_coroutine)(zend_fiber_transfer *transfer); struct _zend_fiber_context { - /* Handle to fiber state as needed by boost.context */ + /* Pointer to boost.context or ucontext_t data. */ void *handle; /* Pointer that identifies the fiber type. */ diff --git a/configure.ac b/configure.ac index b9fc1a8d460e7..f97bbdf7347a7 100644 --- a/configure.ac +++ b/configure.ac @@ -1189,12 +1189,14 @@ fi dnl Configuring Zend and TSRM. dnl ---------------------------------------------------------------------------- +AC_ARG_ENABLE([fiber-asm], + [AS_HELP_STRING([--disable-fiber-asm], + [Disable the use of boost fiber assembly files])], + [fiber_asm=$enableval], [fiber_asm='yes']) + PHP_HELP_SEPARATOR([Zend:]) PHP_CONFIGURE_PART(Configuring Zend) -AC_MSG_CHECKING(for fiber switching context) -fibers="yes" - AS_CASE([$host_cpu], [x86_64*|amd64*], [fiber_cpu="x86_64"], [x86*|amd*|i?86*|pentium], [fiber_cpu="i386"], @@ -1231,14 +1233,20 @@ if test "$fiber_os" = 'mac'; then elif test "$fiber_asm_file_prefix" != 'unknown'; then fiber_asm_file="${fiber_asm_file_prefix}_elf_gas" else - fibers="no" + fiber_asm="no" fi -if test "$fibers" = 'yes'; then +if test "$fiber_asm" = 'yes'; then + AC_MSG_CHECKING([for fiber switching context]) + AC_DEFINE([ZEND_FIBER_ASM], 1, [ ]) PHP_ADD_SOURCES(Zend/asm, make_${fiber_asm_file}.S jump_${fiber_asm_file}.S) AC_MSG_RESULT([$fiber_asm_file]) else - AC_MSG_ERROR([Unable to determine platform!]) + AC_CHECK_HEADER(ucontext.h, [ + AC_DEFINE([ZEND_FIBER_UCONTEXT], 1, [ ]) + ], [ + AC_MSG_ERROR([fibers not available on this platform]) + ]) fi LIBZEND_BASIC_CHECKS From 9ad3b7223c1d1ac50ee451f87358e7a5ddd3db26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Sat, 10 Jul 2021 12:48:15 +0200 Subject: [PATCH 2/5] Avoid unnecessary ucontext memory allocations. --- Zend/zend_fibers.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 0790eed65f789..c2ffa630c01b1 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -72,6 +72,11 @@ struct _zend_fiber_stack { const void *asan_pointer; size_t asan_size; #endif + +#ifdef ZEND_FIBER_UCONTEXT + /* Embedded ucontext to avoid unnecessary memory allocations. */ + ucontext_t ucontext; +#endif }; /* Zend VM state that needs to be captured / restored during fiber context switch. */ @@ -315,7 +320,8 @@ ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, void *kind, z } #ifdef ZEND_FIBER_UCONTEXT - ucontext_t *handle = emalloc(sizeof(ucontext_t)); + ucontext_t *handle = &context->stack->ucontext; + getcontext(handle); handle->uc_stack.ss_size = context->stack->size; @@ -346,10 +352,6 @@ ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, void *kind, z ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context) { zend_fiber_stack_free(context->stack); - -#ifdef ZEND_FIBER_UCONTEXT - efree(context->handle); -#endif } ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer) @@ -883,13 +885,13 @@ void zend_fiber_init(void) { zend_fiber_context *context = ecalloc(1, sizeof(zend_fiber_context)); -#ifdef __SANITIZE_ADDRESS__ - // Main fiber context stack is only accessed if ASan is enabled. +#if defined(__SANITIZE_ADDRESS__) || defined(ZEND_FIBER_UCONTEXT) + // Main fiber stack is only needed if ASan or ucontext is enabled. context->stack = emalloc(sizeof(zend_fiber_stack)); -#endif #ifdef ZEND_FIBER_UCONTEXT - context->handle = emalloc(sizeof(ucontext_t)); + context->handle = &context->stack->ucontext; +#endif #endif context->status = ZEND_FIBER_STATUS_RUNNING; @@ -903,11 +905,7 @@ void zend_fiber_init(void) void zend_fiber_shutdown(void) { -#ifdef ZEND_FIBER_UCONTEXT - efree(EG(main_fiber_context)->handle); -#endif - -#ifdef __SANITIZE_ADDRESS__ +#if defined(__SANITIZE_ADDRESS__) || defined(ZEND_FIBER_UCONTEXT) efree(EG(main_fiber_context)->stack); #endif From 87bc711a265e3619236bfbfe4421768a39c68eb8 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Sun, 11 Jul 2021 11:05:04 -0500 Subject: [PATCH 3/5] Revert superfluous change --- Zend/zend_fibers.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index c2ffa630c01b1..aa052c7a66198 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -260,8 +260,6 @@ static ZEND_NORETURN void zend_fiber_trampoline(void) static ZEND_NORETURN void zend_fiber_trampoline(boost_context_data data) #endif { - zend_fiber_context *context = EG(current_fiber_context); - /* Initialize transfer struct with a copy of passed data. */ #ifdef ZEND_FIBER_UCONTEXT zend_fiber_transfer transfer = *transfer_data; @@ -271,20 +269,22 @@ static ZEND_NORETURN void zend_fiber_trampoline(boost_context_data data) zend_fiber_context *from = transfer.context; +#ifdef __SANITIZE_ADDRESS__ + __sanitizer_finish_switch_fiber(NULL, &from->stack->asan_pointer, &from->stack->asan_size); +#endif + #ifndef ZEND_FIBER_UCONTEXT /* Get the context that resumed us and update its handle to allow for symmetric coroutines. */ from->handle = data.handle; #endif -#ifdef __SANITIZE_ADDRESS__ - __sanitizer_finish_switch_fiber(NULL, &from->stack->asan_pointer, &from->stack->asan_size); -#endif - /* Ensure that previous fiber will be cleaned up (needed by symmetric coroutines). */ if (from->status == ZEND_FIBER_STATUS_DEAD) { zend_fiber_destroy_context(from); } + zend_fiber_context *context = EG(current_fiber_context); + context->function(&transfer); context->status = ZEND_FIBER_STATUS_DEAD; From 3e1ef1a42ec503b258782370f08ba7fe40f051c0 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Sun, 11 Jul 2021 12:07:10 -0500 Subject: [PATCH 4/5] Define _XOPEN_SOURCE on macOS with --disable-fiber-asm --- configure.ac | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure.ac b/configure.ac index f97bbdf7347a7..ba9985ed05665 100644 --- a/configure.ac +++ b/configure.ac @@ -1242,6 +1242,9 @@ if test "$fiber_asm" = 'yes'; then PHP_ADD_SOURCES(Zend/asm, make_${fiber_asm_file}.S jump_${fiber_asm_file}.S) AC_MSG_RESULT([$fiber_asm_file]) else + if test "$fiber_os" = 'mac'; then + AC_DEFINE([_XOPEN_SOURCE], 1, [ ]) + fi AC_CHECK_HEADER(ucontext.h, [ AC_DEFINE([ZEND_FIBER_UCONTEXT], 1, [ ]) ], [ From 3f8548d32b8ec02f5e7f898230f44443bc56a561 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Sun, 11 Jul 2021 15:39:06 -0500 Subject: [PATCH 5/5] NEWS entry --- NEWS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS b/NEWS index 2e6195e32a0bd..332fe4e4fe5cb 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.1.0beta1 +- Core: + . Fixed bug #81238 (Fiber support missing for Solaris Sparc). (trowski) + - Reflection: . Fixed bug #80097 (ReflectionAttribute is not a Reflector). (beberlei)