mirror of
https://github.com/php/php-src.git
synced 2024-11-23 09:54:15 +08:00
Fiber ucontext support (#7226)
Co-authored-by: Martin Schröder <m.schroeder2007@gmail.com>
This commit is contained in:
parent
1f42777927
commit
8fd747a2a0
3
NEWS
3
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)
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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. */
|
||||
|
23
configure.ac
23
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,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
|
||||
|
Loading…
Reference in New Issue
Block a user