diff --git a/Zend/zend.c b/Zend/zend.c index e3da71bd4e1..63f90c4d4c2 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -774,7 +774,9 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{ executor_globals->exception_class = NULL; executor_globals->exception = NULL; executor_globals->objects_store.object_buckets = NULL; - executor_globals->current_fiber = NULL; + executor_globals->current_fiber_context = NULL; + executor_globals->main_fiber_context = NULL; + executor_globals->active_fiber = NULL; #ifdef ZEND_WIN32 zend_get_windows_version_info(&executor_globals->windows_version_info); #endif diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 9360ea8a039..1a3757041ae 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -204,7 +204,7 @@ static ZEND_NORETURN void zend_fiber_trampoline(boost_context_data data) /* Initialize transfer struct with a copy of passed data. */ zend_fiber_transfer transfer = *data.transfer; - zend_fiber_context *context = EG(current_fiber); + zend_fiber_context *context = EG(current_fiber_context); context->function(&transfer); context->status = ZEND_FIBER_STATUS_DEAD; @@ -241,7 +241,7 @@ ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context) ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer) { - zend_fiber_context *from = EG(current_fiber); + zend_fiber_context *from = EG(current_fiber_context); zend_fiber_context *to = transfer->context; zend_fiber_vm_state state; @@ -272,7 +272,7 @@ ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer) /* Update transfer context with the current fiber before switching. */ transfer->context = from; - EG(current_fiber) = to; + EG(current_fiber_context) = to; #ifdef __SANITIZE_ADDRESS__ void *fake_stack = NULL; @@ -295,7 +295,7 @@ ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer) __sanitizer_finish_switch_fiber(fake_stack, &to->stack.prior_pointer, &to->stack.prior_size); #endif - EG(current_fiber) = from; + EG(current_fiber_context) = from; zend_fiber_restore_vm_state(&state); @@ -312,11 +312,11 @@ ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer) static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer) { - ZEND_ASSERT(Z_TYPE(transfer->value) == IS_UNDEF && "First context switch into a fiber must not transmit data"); - - zend_fiber *fiber = zend_fiber_from_context(EG(current_fiber)); + zend_fiber *fiber = EG(active_fiber); + /* Determine the current error_reporting ini setting. */ zend_long error_reporting = INI_INT("error_reporting"); + /* If error_reporting is 0 and not explicitly set to 0, INI_STR returns a null pointer. */ if (!error_reporting && !INI_STR("error_reporting")) { error_reporting = E_ALL; } @@ -392,6 +392,50 @@ static zend_always_inline void delegate_transfer_result( RETURN_COPY_VALUE(&transfer->value); } +static zend_always_inline zend_fiber_transfer zend_fiber_switch_to( + zend_fiber_context *context, zval *value, bool exception +) { + zend_fiber_transfer transfer = { + .context = context, + .flags = exception ? ZEND_FIBER_TRANSFER_FLAG_ERROR : 0, + }; + + if (value) { + ZVAL_COPY(&transfer.value, value); + } else { + ZVAL_NULL(&transfer.value); + } + + zend_fiber_switch_context(&transfer); + + return transfer; +} + +static zend_always_inline zend_fiber_transfer zend_fiber_resume(zend_fiber *fiber, zval *value, bool exception) +{ + zend_fiber *previous = EG(active_fiber); + + fiber->caller = EG(current_fiber_context); + EG(active_fiber) = fiber; + + zend_fiber_transfer transfer = zend_fiber_switch_to(fiber->previous, value, exception); + + EG(active_fiber) = previous; + + return transfer; +} + +static zend_always_inline zend_fiber_transfer zend_fiber_suspend(zend_fiber *fiber, zval *value) +{ + ZEND_ASSERT(fiber->caller != NULL); + + zend_fiber_context *caller = fiber->caller; + fiber->previous = EG(current_fiber_context); + fiber->caller = NULL; + + return zend_fiber_switch_to(caller, value, false); +} + static zend_object *zend_fiber_object_create(zend_class_entry *ce) { zend_fiber *fiber; @@ -416,14 +460,9 @@ static void zend_fiber_object_destroy(zend_object *object) zend_object *exception = EG(exception); EG(exception) = NULL; - fiber->caller = EG(current_fiber); fiber->flags |= ZEND_FIBER_FLAG_DESTROYED; - zend_fiber_transfer transfer = { - .context = zend_fiber_get_context(fiber) - }; - - zend_fiber_switch_context(&transfer); + zend_fiber_transfer transfer = zend_fiber_resume(fiber, NULL, false); if (transfer.flags & ZEND_FIBER_TRANSFER_FLAG_ERROR) { EG(exception) = Z_OBJ(transfer.value); @@ -496,13 +535,9 @@ ZEND_METHOD(Fiber, start) RETURN_THROWS(); } - fiber->caller = EG(current_fiber); + fiber->previous = context; - zend_fiber_transfer transfer = { - .context = context - }; - - zend_fiber_switch_context(&transfer); + zend_fiber_transfer transfer = zend_fiber_resume(fiber, NULL, false); delegate_transfer_result(fiber, &transfer, INTERNAL_FUNCTION_PARAM_PASSTHRU); } @@ -516,38 +551,24 @@ ZEND_METHOD(Fiber, suspend) Z_PARAM_ZVAL(value); ZEND_PARSE_PARAMETERS_END(); - if (UNEXPECTED(EG(current_fiber)->kind != zend_ce_fiber)) { + zend_fiber *fiber = EG(active_fiber); + + if (UNEXPECTED(!fiber)) { zend_throw_error(zend_ce_fiber_error, "Cannot suspend outside of a fiber"); RETURN_THROWS(); } - zend_fiber *fiber = zend_fiber_from_context(EG(current_fiber)); - zend_fiber_context *caller = fiber->caller; - if (UNEXPECTED(fiber->flags & ZEND_FIBER_FLAG_DESTROYED)) { zend_throw_error(zend_ce_fiber_error, "Cannot suspend in a force-closed fiber"); RETURN_THROWS(); } - ZEND_ASSERT(fiber->status == ZEND_FIBER_STATUS_RUNNING); - ZEND_ASSERT(caller != NULL); - - fiber->caller = NULL; + ZEND_ASSERT(fiber->status == ZEND_FIBER_STATUS_RUNNING || fiber->status == ZEND_FIBER_STATUS_SUSPENDED); fiber->execute_data = EG(current_execute_data); fiber->stack_bottom->prev_execute_data = NULL; - zend_fiber_transfer transfer = { - .context = caller - }; - - if (value) { - ZVAL_COPY(&transfer.value, value); - } else { - ZVAL_NULL(&transfer.value); - } - - zend_fiber_switch_context(&transfer); + zend_fiber_transfer transfer = zend_fiber_suspend(fiber, value); if (fiber->flags & ZEND_FIBER_FLAG_DESTROYED) { // This occurs when the fiber is GC'ed while suspended. @@ -576,21 +597,9 @@ ZEND_METHOD(Fiber, resume) RETURN_THROWS(); } - fiber->caller = EG(current_fiber); - fiber->stack_bottom->prev_execute_data = EG(current_execute_data); - zend_fiber_transfer transfer = { - .context = zend_fiber_get_context(fiber) - }; - - if (value) { - ZVAL_COPY(&transfer.value, value); - } else { - ZVAL_NULL(&transfer.value); - } - - zend_fiber_switch_context(&transfer); + zend_fiber_transfer transfer = zend_fiber_resume(fiber, value, false); delegate_transfer_result(fiber, &transfer, INTERNAL_FUNCTION_PARAM_PASSTHRU); } @@ -611,18 +620,9 @@ ZEND_METHOD(Fiber, throw) RETURN_THROWS(); } - fiber->caller = EG(current_fiber); - fiber->stack_bottom->prev_execute_data = EG(current_execute_data); - zend_fiber_transfer transfer = { - .context = zend_fiber_get_context(fiber), - .flags = ZEND_FIBER_TRANSFER_FLAG_ERROR - }; - - ZVAL_COPY(&transfer.value, exception); - - zend_fiber_switch_context(&transfer); + zend_fiber_transfer transfer = zend_fiber_resume(fiber, exception, true); delegate_transfer_result(fiber, &transfer, INTERNAL_FUNCTION_PARAM_PASSTHRU); } @@ -702,11 +702,13 @@ ZEND_METHOD(Fiber, this) { ZEND_PARSE_PARAMETERS_NONE(); - if (EG(current_fiber)->kind != zend_ce_fiber) { + zend_fiber *fiber = EG(active_fiber); + + if (!fiber) { RETURN_NULL(); } - RETURN_OBJ_COPY(&zend_fiber_from_context(EG(current_fiber))->std); + RETURN_OBJ_COPY(&fiber->std); } ZEND_METHOD(FiberError, __construct) @@ -741,11 +743,12 @@ void zend_fiber_init(void) context->status = ZEND_FIBER_STATUS_RUNNING; - EG(main_fiber) = context; - EG(current_fiber) = context; + EG(main_fiber_context) = context; + EG(current_fiber_context) = context; + EG(active_fiber) = NULL; } void zend_fiber_shutdown(void) { - efree(EG(main_fiber)); + efree(EG(main_fiber_context)); } diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index be07475b855..2f944d2364e 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -111,6 +111,7 @@ typedef struct _zend_fiber_vm_state { int error_reporting; uint32_t jit_trace_num; JMP_BUF *bailout; + zend_fiber *active_fiber; } zend_fiber_vm_state; struct _zend_fiber { @@ -123,6 +124,9 @@ struct _zend_fiber { /* Fiber that resumed us. */ zend_fiber_context *caller; + /* Fiber that suspended us. */ + zend_fiber_context *previous; + /* Callback and info / cache to be used when fiber is started. */ zend_fcall_info fci; zend_fcall_info_cache fci_cache; @@ -166,6 +170,7 @@ static zend_always_inline void zend_fiber_capture_vm_state(zend_fiber_vm_state * state->error_reporting = EG(error_reporting); state->jit_trace_num = EG(jit_trace_num); state->bailout = EG(bailout); + state->active_fiber = EG(active_fiber); } static zend_always_inline void zend_fiber_restore_vm_state(zend_fiber_vm_state *state) @@ -178,6 +183,7 @@ static zend_always_inline void zend_fiber_restore_vm_state(zend_fiber_vm_state * EG(error_reporting) = state->error_reporting; EG(jit_trace_num) = state->jit_trace_num; EG(bailout) = state->bailout; + EG(active_fiber) = state->active_fiber; } #endif diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index b2e7e03cca3..d1ac6dbacfd 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -62,6 +62,7 @@ END_EXTERN_C() typedef struct _zend_vm_stack *zend_vm_stack; typedef struct _zend_ini_entry zend_ini_entry; typedef struct _zend_fiber_context zend_fiber_context; +typedef struct _zend_fiber zend_fiber; struct _zend_compiler_globals { zend_stack loop_var_stack; @@ -249,8 +250,11 @@ struct _zend_executor_globals { zend_get_gc_buffer get_gc_buffer; - zend_fiber_context *main_fiber; - zend_fiber_context *current_fiber; + zend_fiber_context *main_fiber_context; + zend_fiber_context *current_fiber_context; + + /* Active instance of Fiber. */ + zend_fiber *active_fiber; /* Default fiber C stack size. */ zend_long fiber_stack_size; diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 7e4a26ad8ea..48b9a8815d2 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6895,7 +6895,7 @@ ZEND_METHOD(ReflectionFiber, getTrace) prev_execute_data = fiber->stack_bottom->prev_execute_data; fiber->stack_bottom->prev_execute_data = NULL; - if (EG(current_fiber) != zend_fiber_get_context(fiber)) { + if (EG(active_fiber) != fiber) { // No need to replace current execute data if within the current fiber. EG(current_execute_data) = fiber->execute_data; } @@ -6915,7 +6915,7 @@ ZEND_METHOD(ReflectionFiber, getExecutingLine) REFLECTION_CHECK_VALID_FIBER(fiber); - if (EG(current_fiber) == zend_fiber_get_context(fiber)) { + if (EG(active_fiber) == fiber) { prev_execute_data = execute_data->prev_execute_data; } else { prev_execute_data = fiber->execute_data->prev_execute_data; @@ -6933,7 +6933,7 @@ ZEND_METHOD(ReflectionFiber, getExecutingFile) REFLECTION_CHECK_VALID_FIBER(fiber); - if (EG(current_fiber) == zend_fiber_get_context(fiber)) { + if (EG(active_fiber) == fiber) { prev_execute_data = execute_data->prev_execute_data; } else { prev_execute_data = fiber->execute_data->prev_execute_data;