Fiber ucontext support (#7226)

Co-authored-by: Martin Schröder <m.schroeder2007@gmail.com>
This commit is contained in:
Aaron Piotrowski 2021-07-11 15:40:11 -05:00 committed by GitHub
parent 1f42777927
commit 8fd747a2a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 18 deletions

3
NEWS
View File

@ -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)

View File

@ -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. */
@ -113,6 +118,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 <ucontext.h>
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 +139,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,20 +254,29 @@ 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;
/* 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
zend_fiber_context *from = transfer.context;
#ifdef __SANITIZE_ADDRESS__
__sanitizer_finish_switch_fiber(NULL, &from->stack->asan_pointer, &from->stack->asan_size);
#endif
/* Get a hold of the context that resumed us and update its handle to allow for symmetric coroutines. */
#ifndef ZEND_FIBER_UCONTEXT
/* Get the context that resumed us and update its handle to allow for symmetric coroutines. */
from->handle = data.handle;
/* Initialize transfer struct with a copy of passed data. */
zend_fiber_transfer transfer = *data.transfer;
#endif
/* Ensure that previous fiber will be cleaned up (needed by symmetric coroutines). */
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
return false;
}
#ifdef ZEND_FIBER_UCONTEXT
ucontext_t *handle = &context->stack->ucontext;
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;
@ -363,14 +397,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);
@ -839,9 +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));
#ifdef ZEND_FIBER_UCONTEXT
context->handle = &context->stack->ucontext;
#endif
#endif
context->status = ZEND_FIBER_STATUS_RUNNING;
@ -855,9 +905,10 @@ void zend_fiber_init(void)
void zend_fiber_shutdown(void)
{
#ifdef __SANITIZE_ADDRESS__
#if defined(__SANITIZE_ADDRESS__) || defined(ZEND_FIBER_UCONTEXT)
efree(EG(main_fiber_context)->stack);
#endif
efree(EG(main_fiber_context));
zend_fiber_switch_block();

View File

@ -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. */

View File

@ -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,23 @@ 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!])
if test "$fiber_os" = 'mac'; then
AC_DEFINE([_XOPEN_SOURCE], 1, [ ])
fi
AC_CHECK_HEADER(ucontext.h, [
AC_DEFINE([ZEND_FIBER_UCONTEXT], 1, [ ])
], [
AC_MSG_ERROR([fibers not available on this platform])
])
fi
LIBZEND_BASIC_CHECKS