mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
Merge fiber switching functions (#7106)
Co-authored-by: Martin Schröder <m.schroeder2007@gmail.com>
This commit is contained in:
parent
291d8db7c5
commit
2184422adc
5 changed files with 175 additions and 160 deletions
|
@ -10,6 +10,18 @@ $fiber = new Fiber(function (): void {
|
||||||
var_dump($fiber->isRunning());
|
var_dump($fiber->isRunning());
|
||||||
var_dump($fiber->isSuspended());
|
var_dump($fiber->isSuspended());
|
||||||
var_dump($fiber->isTerminated());
|
var_dump($fiber->isTerminated());
|
||||||
|
|
||||||
|
$nested = new Fiber(function () use ($fiber): void {
|
||||||
|
echo "\nWithin Nested Fiber:\n";
|
||||||
|
var_dump($fiber->isStarted());
|
||||||
|
var_dump($fiber->isRunning());
|
||||||
|
var_dump($fiber->isSuspended());
|
||||||
|
var_dump($fiber->isTerminated());
|
||||||
|
Fiber::suspend();
|
||||||
|
});
|
||||||
|
|
||||||
|
$nested->start();
|
||||||
|
|
||||||
Fiber::suspend();
|
Fiber::suspend();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -49,6 +61,12 @@ bool(true)
|
||||||
bool(false)
|
bool(false)
|
||||||
bool(false)
|
bool(false)
|
||||||
|
|
||||||
|
Within Nested Fiber:
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(false)
|
||||||
|
bool(false)
|
||||||
|
|
||||||
After Start:
|
After Start:
|
||||||
bool(true)
|
bool(true)
|
||||||
bool(false)
|
bool(false)
|
||||||
|
|
28
Zend/tests/fibers/resume-previous-fiber.phpt
Normal file
28
Zend/tests/fibers/resume-previous-fiber.phpt
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
--TEST--
|
||||||
|
Resume previous fiber
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$fiber = new Fiber(function (): void {
|
||||||
|
$fiber1 = Fiber::this();
|
||||||
|
|
||||||
|
$fiber2 = new Fiber(function () use ($fiber1): void {
|
||||||
|
$fiber1->resume();
|
||||||
|
});
|
||||||
|
|
||||||
|
$fiber2->start();
|
||||||
|
});
|
||||||
|
|
||||||
|
$fiber->start();
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Uncaught FiberError: Cannot resume a fiber that is not suspended in %sresume-previous-fiber.php:%d
|
||||||
|
Stack trace:
|
||||||
|
#0 %sresume-previous-fiber.php(%d): Fiber->resume()
|
||||||
|
#1 [internal function]: {closure}()
|
||||||
|
#2 %sresume-previous-fiber.php(%d): Fiber->start()
|
||||||
|
#3 [internal function]: {closure}()
|
||||||
|
#4 %sresume-previous-fiber.php(%d): Fiber->start()
|
||||||
|
#5 {main}
|
||||||
|
thrown in %sresume-previous-fiber.php on line %d
|
|
@ -192,8 +192,12 @@ static ZEND_NORETURN void zend_fiber_trampoline(transfer_t transfer)
|
||||||
__sanitizer_finish_switch_fiber(NULL, &from->stack.prior_pointer, &from->stack.prior_size);
|
__sanitizer_finish_switch_fiber(NULL, &from->stack.prior_pointer, &from->stack.prior_size);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
zend_fiber_context *to = context->function(context);
|
||||||
|
|
||||||
|
context->status = ZEND_FIBER_STATUS_DEAD;
|
||||||
|
|
||||||
/* Final context switch, the fiber must not be resumed afterwards! */
|
/* Final context switch, the fiber must not be resumed afterwards! */
|
||||||
zend_fiber_switch_context(context->function(context));
|
zend_fiber_switch_context(to);
|
||||||
|
|
||||||
/* Abort here because we are in an inconsistent program state. */
|
/* Abort here because we are in an inconsistent program state. */
|
||||||
abort();
|
abort();
|
||||||
|
@ -222,14 +226,27 @@ ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context)
|
||||||
zend_fiber_stack_free(&context->stack);
|
zend_fiber_stack_free(&context->stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
ZEND_API void zend_fiber_switch_context(zend_fiber_context *to)
|
ZEND_API zend_fiber_context *zend_fiber_switch_context(zend_fiber_context *to)
|
||||||
{
|
{
|
||||||
zend_fiber_context *from = EG(current_fiber);
|
zend_fiber_context *from = EG(current_fiber);
|
||||||
|
zend_fiber_vm_state state;
|
||||||
|
|
||||||
ZEND_ASSERT(to && to->handle && "Invalid fiber context");
|
ZEND_ASSERT(to && to->handle && "Invalid fiber context");
|
||||||
ZEND_ASSERT(from && "From fiber context must be present");
|
ZEND_ASSERT(from && "From fiber context must be present");
|
||||||
ZEND_ASSERT(to != from && "Cannot switch into the running fiber context");
|
ZEND_ASSERT(to != from && "Cannot switch into the running fiber context");
|
||||||
|
|
||||||
|
zend_observer_fiber_switch_notify(from, to);
|
||||||
|
|
||||||
|
zend_fiber_capture_vm_state(&state);
|
||||||
|
|
||||||
|
to->status = ZEND_FIBER_STATUS_RUNNING;
|
||||||
|
|
||||||
|
if (from->status == ZEND_FIBER_STATUS_RUNNING) {
|
||||||
|
from->status = ZEND_FIBER_STATUS_SUSPENDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
EG(current_fiber) = to;
|
||||||
|
|
||||||
#ifdef __SANITIZE_ADDRESS__
|
#ifdef __SANITIZE_ADDRESS__
|
||||||
void *fake_stack = NULL;
|
void *fake_stack = NULL;
|
||||||
__sanitizer_start_switch_fiber(
|
__sanitizer_start_switch_fiber(
|
||||||
|
@ -238,50 +255,30 @@ ZEND_API void zend_fiber_switch_context(zend_fiber_context *to)
|
||||||
to->stack.prior_size);
|
to->stack.prior_size);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
EG(current_fiber) = to;
|
|
||||||
|
|
||||||
transfer_t transfer = jump_fcontext(to->handle, from);
|
transfer_t transfer = jump_fcontext(to->handle, from);
|
||||||
((zend_fiber_context *) transfer.data)->handle = transfer.context;
|
|
||||||
|
|
||||||
EG(current_fiber) = from;
|
|
||||||
|
|
||||||
#ifdef __SANITIZE_ADDRESS__
|
#ifdef __SANITIZE_ADDRESS__
|
||||||
__sanitizer_finish_switch_fiber(fake_stack, &to->stack.prior_pointer, &to->stack.prior_size);
|
__sanitizer_finish_switch_fiber(fake_stack, &to->stack.prior_pointer, &to->stack.prior_size);
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
|
|
||||||
static void zend_fiber_suspend_from(zend_fiber *fiber)
|
EG(current_fiber) = from;
|
||||||
{
|
|
||||||
zend_fiber_vm_state state;
|
|
||||||
|
|
||||||
ZEND_ASSERT(fiber->caller && "Fiber has no caller");
|
zend_fiber_context *previous = transfer.data;
|
||||||
|
previous->handle = transfer.context;
|
||||||
|
|
||||||
zend_fiber_capture_vm_state(&state);
|
|
||||||
zend_fiber_switch_context(fiber->caller);
|
|
||||||
zend_fiber_restore_vm_state(&state);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void zend_fiber_switch_to(zend_fiber *fiber)
|
|
||||||
{
|
|
||||||
zend_fiber_context *context = zend_fiber_get_context(fiber);
|
|
||||||
zend_fiber_vm_state state;
|
|
||||||
|
|
||||||
zend_observer_fiber_switch_notify(EG(current_fiber), context);
|
|
||||||
|
|
||||||
fiber->caller = EG(current_fiber);
|
|
||||||
|
|
||||||
zend_fiber_capture_vm_state(&state);
|
|
||||||
zend_fiber_switch_context(context);
|
|
||||||
zend_fiber_restore_vm_state(&state);
|
zend_fiber_restore_vm_state(&state);
|
||||||
|
|
||||||
fiber->caller = NULL;
|
/* Destroy context first to ensure it does not leak if some extension does custom bailout handling. */
|
||||||
|
if (previous->status == ZEND_FIBER_STATUS_DEAD) {
|
||||||
|
zend_fiber_destroy_context(previous);
|
||||||
|
}
|
||||||
|
|
||||||
zend_observer_fiber_switch_notify(context, EG(current_fiber));
|
/* Propagate bailout to current fiber / main. */
|
||||||
|
if (UNEXPECTED(previous->flags & ZEND_FIBER_FLAG_BAILOUT)) {
|
||||||
if (UNEXPECTED(fiber->flags & ZEND_FIBER_FLAG_BAILOUT)) {
|
|
||||||
// zend_bailout() was called in the fiber, so call it again in the previous fiber or {main}.
|
|
||||||
zend_bailout();
|
zend_bailout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -328,8 +325,6 @@ static ZEND_STACK_ALIGNED zend_fiber_context *zend_fiber_execute(zend_fiber_cont
|
||||||
|
|
||||||
fiber->fci.retval = &fiber->value;
|
fiber->fci.retval = &fiber->value;
|
||||||
|
|
||||||
fiber->status = ZEND_FIBER_STATUS_RUNNING;
|
|
||||||
|
|
||||||
zend_call_function(&fiber->fci, &fiber->fci_cache);
|
zend_call_function(&fiber->fci, &fiber->fci_cache);
|
||||||
|
|
||||||
zval_ptr_dtor(&fiber->fci.function_name);
|
zval_ptr_dtor(&fiber->fci.function_name);
|
||||||
|
@ -347,13 +342,14 @@ static ZEND_STACK_ALIGNED zend_fiber_context *zend_fiber_execute(zend_fiber_cont
|
||||||
fiber->flags |= ZEND_FIBER_FLAG_BAILOUT;
|
fiber->flags |= ZEND_FIBER_FLAG_BAILOUT;
|
||||||
} zend_end_try();
|
} zend_end_try();
|
||||||
|
|
||||||
fiber->status = ZEND_FIBER_STATUS_DEAD;
|
|
||||||
|
|
||||||
zend_vm_stack_destroy();
|
zend_vm_stack_destroy();
|
||||||
fiber->execute_data = NULL;
|
fiber->execute_data = NULL;
|
||||||
fiber->stack_bottom = NULL;
|
fiber->stack_bottom = NULL;
|
||||||
|
|
||||||
return fiber->caller;
|
zend_fiber_context *caller = fiber->caller;
|
||||||
|
fiber->caller = NULL;
|
||||||
|
|
||||||
|
return caller;
|
||||||
}
|
}
|
||||||
|
|
||||||
static zend_object *zend_fiber_object_create(zend_class_entry *ce)
|
static zend_object *zend_fiber_object_create(zend_class_entry *ce)
|
||||||
|
@ -380,10 +376,10 @@ static void zend_fiber_object_destroy(zend_object *object)
|
||||||
zend_object *exception = EG(exception);
|
zend_object *exception = EG(exception);
|
||||||
EG(exception) = NULL;
|
EG(exception) = NULL;
|
||||||
|
|
||||||
fiber->status = ZEND_FIBER_STATUS_RUNNING;
|
fiber->caller = EG(current_fiber);
|
||||||
fiber->flags |= ZEND_FIBER_FLAG_DESTROYED;
|
fiber->flags |= ZEND_FIBER_FLAG_DESTROYED;
|
||||||
|
|
||||||
zend_fiber_switch_to(fiber);
|
zend_fiber_switch_context(zend_fiber_get_context(fiber));
|
||||||
|
|
||||||
if (EG(exception)) {
|
if (EG(exception)) {
|
||||||
if (!exception && EG(current_execute_data) && EG(current_execute_data)->func
|
if (!exception && EG(current_execute_data) && EG(current_execute_data)->func
|
||||||
|
@ -412,23 +408,9 @@ static void zend_fiber_object_free(zend_object *object)
|
||||||
|
|
||||||
zval_ptr_dtor(&fiber->value);
|
zval_ptr_dtor(&fiber->value);
|
||||||
|
|
||||||
zend_fiber_destroy_context(zend_fiber_get_context(fiber));
|
|
||||||
|
|
||||||
zend_object_std_dtor(&fiber->std);
|
zend_object_std_dtor(&fiber->std);
|
||||||
}
|
}
|
||||||
|
|
||||||
ZEND_API zend_fiber *zend_fiber_create(const zend_fcall_info *fci, const zend_fcall_info_cache *fci_cache)
|
|
||||||
{
|
|
||||||
zend_fiber *fiber = (zend_fiber *) zend_fiber_object_create(zend_ce_fiber);
|
|
||||||
|
|
||||||
fiber->fci = *fci;
|
|
||||||
fiber->fci_cache = *fci_cache;
|
|
||||||
|
|
||||||
Z_TRY_ADDREF(fiber->fci.function_name);
|
|
||||||
|
|
||||||
return fiber;
|
|
||||||
}
|
|
||||||
|
|
||||||
ZEND_METHOD(Fiber, __construct)
|
ZEND_METHOD(Fiber, __construct)
|
||||||
{
|
{
|
||||||
zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(getThis());
|
zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(getThis());
|
||||||
|
@ -441,31 +423,6 @@ ZEND_METHOD(Fiber, __construct)
|
||||||
Z_TRY_ADDREF(fiber->fci.function_name);
|
Z_TRY_ADDREF(fiber->fci.function_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
ZEND_API void zend_fiber_start(zend_fiber *fiber, zval *params, uint32_t param_count, zend_array *named_params, zval *return_value)
|
|
||||||
{
|
|
||||||
if (fiber->status != ZEND_FIBER_STATUS_INIT) {
|
|
||||||
zend_throw_error(zend_ce_fiber_error, "Cannot start a fiber that has already been started");
|
|
||||||
RETURN_THROWS();
|
|
||||||
}
|
|
||||||
|
|
||||||
fiber->fci.params = params;
|
|
||||||
fiber->fci.param_count = param_count;
|
|
||||||
fiber->fci.named_params = named_params;
|
|
||||||
|
|
||||||
if (!zend_fiber_init_context(zend_fiber_get_context(fiber), zend_ce_fiber, zend_fiber_execute, EG(fiber_stack_size))) {
|
|
||||||
RETURN_THROWS();
|
|
||||||
}
|
|
||||||
|
|
||||||
zend_fiber_switch_to(fiber);
|
|
||||||
|
|
||||||
if (fiber->status == ZEND_FIBER_STATUS_DEAD) {
|
|
||||||
RETURN_NULL();
|
|
||||||
}
|
|
||||||
|
|
||||||
RETVAL_COPY_VALUE(&fiber->value);
|
|
||||||
ZVAL_UNDEF(&fiber->value);
|
|
||||||
}
|
|
||||||
|
|
||||||
ZEND_METHOD(Fiber, start)
|
ZEND_METHOD(Fiber, start)
|
||||||
{
|
{
|
||||||
zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(getThis());
|
zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(getThis());
|
||||||
|
@ -477,51 +434,27 @@ ZEND_METHOD(Fiber, start)
|
||||||
Z_PARAM_VARIADIC_WITH_NAMED(params, param_count, named_params);
|
Z_PARAM_VARIADIC_WITH_NAMED(params, param_count, named_params);
|
||||||
ZEND_PARSE_PARAMETERS_END();
|
ZEND_PARSE_PARAMETERS_END();
|
||||||
|
|
||||||
zend_fiber_start(fiber, params, param_count, named_params, return_value);
|
if (fiber->status != ZEND_FIBER_STATUS_INIT) {
|
||||||
}
|
zend_throw_error(zend_ce_fiber_error, "Cannot start a fiber that has already been started");
|
||||||
|
|
||||||
ZEND_API void zend_fiber_suspend(zval *value, zval *return_value)
|
|
||||||
{
|
|
||||||
if (UNEXPECTED(EG(current_fiber)->kind != zend_ce_fiber)) {
|
|
||||||
zend_throw_error(zend_ce_fiber_error, "Cannot suspend outside of a fiber");
|
|
||||||
RETURN_THROWS();
|
RETURN_THROWS();
|
||||||
}
|
}
|
||||||
|
|
||||||
zend_fiber *fiber = zend_fiber_from_context(EG(current_fiber));
|
fiber->fci.params = params;
|
||||||
|
fiber->fci.param_count = param_count;
|
||||||
|
fiber->fci.named_params = named_params;
|
||||||
|
|
||||||
if (UNEXPECTED(fiber->flags & ZEND_FIBER_FLAG_DESTROYED)) {
|
zend_fiber_context *context = zend_fiber_get_context(fiber);
|
||||||
zend_throw_error(zend_ce_fiber_error, "Cannot suspend in a force-closed fiber");
|
|
||||||
|
if (!zend_fiber_init_context(context, zend_ce_fiber, zend_fiber_execute, EG(fiber_stack_size))) {
|
||||||
RETURN_THROWS();
|
RETURN_THROWS();
|
||||||
}
|
}
|
||||||
|
|
||||||
ZEND_ASSERT(fiber->status == ZEND_FIBER_STATUS_RUNNING);
|
fiber->caller = EG(current_fiber);
|
||||||
|
|
||||||
if (value) {
|
zend_fiber_switch_context(context);
|
||||||
ZVAL_COPY(&fiber->value, value);
|
|
||||||
} else {
|
|
||||||
ZVAL_NULL(&fiber->value);
|
|
||||||
}
|
|
||||||
|
|
||||||
fiber->execute_data = EG(current_execute_data);
|
if (fiber->status == ZEND_FIBER_STATUS_DEAD) {
|
||||||
fiber->status = ZEND_FIBER_STATUS_SUSPENDED;
|
RETURN_NULL();
|
||||||
fiber->stack_bottom->prev_execute_data = NULL;
|
|
||||||
|
|
||||||
zend_fiber_suspend_from(fiber);
|
|
||||||
|
|
||||||
if (fiber->flags & ZEND_FIBER_FLAG_DESTROYED) {
|
|
||||||
// This occurs when the fiber is GC'ed while suspended.
|
|
||||||
zend_throw_graceful_exit();
|
|
||||||
RETURN_THROWS();
|
|
||||||
}
|
|
||||||
|
|
||||||
fiber->status = ZEND_FIBER_STATUS_RUNNING;
|
|
||||||
|
|
||||||
if (fiber->exception) {
|
|
||||||
zval *exception = fiber->exception;
|
|
||||||
fiber->exception = NULL;
|
|
||||||
|
|
||||||
zend_throw_exception_object(exception);
|
|
||||||
RETURN_THROWS();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RETVAL_COPY_VALUE(&fiber->value);
|
RETVAL_COPY_VALUE(&fiber->value);
|
||||||
|
@ -537,29 +470,46 @@ ZEND_METHOD(Fiber, suspend)
|
||||||
Z_PARAM_ZVAL(value);
|
Z_PARAM_ZVAL(value);
|
||||||
ZEND_PARSE_PARAMETERS_END();
|
ZEND_PARSE_PARAMETERS_END();
|
||||||
|
|
||||||
zend_fiber_suspend(value, return_value);
|
if (UNEXPECTED(EG(current_fiber)->kind != zend_ce_fiber)) {
|
||||||
}
|
zend_throw_error(zend_ce_fiber_error, "Cannot suspend outside of a fiber");
|
||||||
|
|
||||||
ZEND_API void zend_fiber_resume(zend_fiber *fiber, zval *value, zval *return_value)
|
|
||||||
{
|
|
||||||
if (UNEXPECTED(fiber->status != ZEND_FIBER_STATUS_SUSPENDED)) {
|
|
||||||
zend_throw_error(zend_ce_fiber_error, "Cannot resume a fiber that is not suspended");
|
|
||||||
RETURN_THROWS();
|
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);
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
ZVAL_COPY(&fiber->value, value);
|
ZVAL_COPY(&fiber->value, value);
|
||||||
} else {
|
} else {
|
||||||
ZVAL_NULL(&fiber->value);
|
ZVAL_NULL(&fiber->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
fiber->status = ZEND_FIBER_STATUS_RUNNING;
|
fiber->caller = NULL;
|
||||||
fiber->stack_bottom->prev_execute_data = EG(current_execute_data);
|
fiber->execute_data = EG(current_execute_data);
|
||||||
|
fiber->stack_bottom->prev_execute_data = NULL;
|
||||||
|
|
||||||
zend_fiber_switch_to(fiber);
|
zend_fiber_switch_context(caller);
|
||||||
|
|
||||||
if (fiber->status == ZEND_FIBER_STATUS_DEAD) {
|
if (fiber->flags & ZEND_FIBER_FLAG_DESTROYED) {
|
||||||
RETURN_NULL();
|
// This occurs when the fiber is GC'ed while suspended.
|
||||||
|
zend_throw_graceful_exit();
|
||||||
|
RETURN_THROWS();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fiber->exception) {
|
||||||
|
zval *exception = fiber->exception;
|
||||||
|
fiber->exception = NULL;
|
||||||
|
|
||||||
|
zend_throw_exception_object(exception);
|
||||||
|
RETURN_THROWS();
|
||||||
}
|
}
|
||||||
|
|
||||||
RETVAL_COPY_VALUE(&fiber->value);
|
RETVAL_COPY_VALUE(&fiber->value);
|
||||||
|
@ -578,23 +528,21 @@ ZEND_METHOD(Fiber, resume)
|
||||||
|
|
||||||
fiber = (zend_fiber *) Z_OBJ_P(getThis());
|
fiber = (zend_fiber *) Z_OBJ_P(getThis());
|
||||||
|
|
||||||
zend_fiber_resume(fiber, value, return_value);
|
if (UNEXPECTED(fiber->status != ZEND_FIBER_STATUS_SUSPENDED || fiber->caller != NULL)) {
|
||||||
}
|
|
||||||
|
|
||||||
ZEND_API void zend_fiber_throw(zend_fiber *fiber, zval *exception, zval *return_value)
|
|
||||||
{
|
|
||||||
if (UNEXPECTED(fiber->status != ZEND_FIBER_STATUS_SUSPENDED)) {
|
|
||||||
zend_throw_error(zend_ce_fiber_error, "Cannot resume a fiber that is not suspended");
|
zend_throw_error(zend_ce_fiber_error, "Cannot resume a fiber that is not suspended");
|
||||||
RETURN_THROWS();
|
RETURN_THROWS();
|
||||||
}
|
}
|
||||||
|
|
||||||
Z_ADDREF_P(exception);
|
if (value) {
|
||||||
fiber->exception = exception;
|
ZVAL_COPY(&fiber->value, value);
|
||||||
|
} else {
|
||||||
|
ZVAL_NULL(&fiber->value);
|
||||||
|
}
|
||||||
|
|
||||||
fiber->status = ZEND_FIBER_STATUS_RUNNING;
|
fiber->caller = EG(current_fiber);
|
||||||
fiber->stack_bottom->prev_execute_data = EG(current_execute_data);
|
fiber->stack_bottom->prev_execute_data = EG(current_execute_data);
|
||||||
|
|
||||||
zend_fiber_switch_to(fiber);
|
zend_fiber_switch_context(zend_fiber_get_context(fiber));
|
||||||
|
|
||||||
if (fiber->status == ZEND_FIBER_STATUS_DEAD) {
|
if (fiber->status == ZEND_FIBER_STATUS_DEAD) {
|
||||||
RETURN_NULL();
|
RETURN_NULL();
|
||||||
|
@ -615,7 +563,25 @@ ZEND_METHOD(Fiber, throw)
|
||||||
|
|
||||||
fiber = (zend_fiber *) Z_OBJ_P(getThis());
|
fiber = (zend_fiber *) Z_OBJ_P(getThis());
|
||||||
|
|
||||||
zend_fiber_throw(fiber, exception, return_value);
|
if (UNEXPECTED(fiber->status != ZEND_FIBER_STATUS_SUSPENDED || fiber->caller != NULL)) {
|
||||||
|
zend_throw_error(zend_ce_fiber_error, "Cannot resume a fiber that is not suspended");
|
||||||
|
RETURN_THROWS();
|
||||||
|
}
|
||||||
|
|
||||||
|
Z_ADDREF_P(exception);
|
||||||
|
fiber->exception = exception;
|
||||||
|
|
||||||
|
fiber->caller = EG(current_fiber);
|
||||||
|
fiber->stack_bottom->prev_execute_data = EG(current_execute_data);
|
||||||
|
|
||||||
|
zend_fiber_switch_context(zend_fiber_get_context(fiber));
|
||||||
|
|
||||||
|
if (fiber->status == ZEND_FIBER_STATUS_DEAD) {
|
||||||
|
RETURN_NULL();
|
||||||
|
}
|
||||||
|
|
||||||
|
RETVAL_COPY_VALUE(&fiber->value);
|
||||||
|
ZVAL_UNDEF(&fiber->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
ZEND_METHOD(Fiber, isStarted)
|
ZEND_METHOD(Fiber, isStarted)
|
||||||
|
@ -637,7 +603,7 @@ ZEND_METHOD(Fiber, isSuspended)
|
||||||
|
|
||||||
fiber = (zend_fiber *) Z_OBJ_P(getThis());
|
fiber = (zend_fiber *) Z_OBJ_P(getThis());
|
||||||
|
|
||||||
RETURN_BOOL(fiber->status == ZEND_FIBER_STATUS_SUSPENDED);
|
RETURN_BOOL(fiber->status == ZEND_FIBER_STATUS_SUSPENDED && fiber->caller == NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
ZEND_METHOD(Fiber, isRunning)
|
ZEND_METHOD(Fiber, isRunning)
|
||||||
|
@ -648,7 +614,7 @@ ZEND_METHOD(Fiber, isRunning)
|
||||||
|
|
||||||
fiber = (zend_fiber *) Z_OBJ_P(getThis());
|
fiber = (zend_fiber *) Z_OBJ_P(getThis());
|
||||||
|
|
||||||
RETURN_BOOL(fiber->status == ZEND_FIBER_STATUS_RUNNING);
|
RETURN_BOOL(fiber->status == ZEND_FIBER_STATUS_RUNNING || fiber->caller != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
ZEND_METHOD(Fiber, isTerminated)
|
ZEND_METHOD(Fiber, isTerminated)
|
||||||
|
|
|
@ -89,6 +89,8 @@ struct _zend_fiber_context {
|
||||||
/* Zend VM state that needs to be captured / restored during fiber context switch. */
|
/* Zend VM state that needs to be captured / restored during fiber context switch. */
|
||||||
typedef struct _zend_fiber_vm_state {
|
typedef struct _zend_fiber_vm_state {
|
||||||
zend_vm_stack vm_stack;
|
zend_vm_stack vm_stack;
|
||||||
|
zval *vm_stack_top;
|
||||||
|
zval *vm_stack_end;
|
||||||
size_t vm_stack_page_size;
|
size_t vm_stack_page_size;
|
||||||
zend_execute_data *current_execute_data;
|
zend_execute_data *current_execute_data;
|
||||||
int error_reporting;
|
int error_reporting;
|
||||||
|
@ -100,12 +102,12 @@ struct _zend_fiber {
|
||||||
/* PHP object handle. */
|
/* PHP object handle. */
|
||||||
zend_object std;
|
zend_object std;
|
||||||
|
|
||||||
/* Fiber that resumed us. */
|
|
||||||
zend_fiber_context *caller;
|
|
||||||
|
|
||||||
/* Fiber context fields (embedded to avoid memory allocation). */
|
/* Fiber context fields (embedded to avoid memory allocation). */
|
||||||
ZEND_FIBER_CONTEXT_FIELDS;
|
ZEND_FIBER_CONTEXT_FIELDS;
|
||||||
|
|
||||||
|
/* Fiber that resumed us. */
|
||||||
|
zend_fiber_context *caller;
|
||||||
|
|
||||||
/* Callback and info / cache to be used when fiber is started. */
|
/* Callback and info / cache to be used when fiber is started. */
|
||||||
zend_fcall_info fci;
|
zend_fcall_info fci;
|
||||||
zend_fcall_info_cache fci_cache;
|
zend_fcall_info_cache fci_cache;
|
||||||
|
@ -123,17 +125,10 @@ struct _zend_fiber {
|
||||||
zval value;
|
zval value;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* These functions create and manipulate a Fiber object, allowing any internal function to start, resume, or suspend a fiber. */
|
/* These functions may be used to create custom fiber objects using the bundled fiber switching context. */
|
||||||
ZEND_API zend_fiber *zend_fiber_create(const zend_fcall_info *fci, const zend_fcall_info_cache *fci_cache);
|
|
||||||
ZEND_API void zend_fiber_start(zend_fiber *fiber, zval *params, uint32_t param_count, zend_array *named_params, zval *return_value);
|
|
||||||
ZEND_API void zend_fiber_suspend(zval *value, zval *return_value);
|
|
||||||
ZEND_API void zend_fiber_resume(zend_fiber *fiber, zval *value, zval *return_value);
|
|
||||||
ZEND_API void zend_fiber_throw(zend_fiber *fiber, zval *exception, zval *return_value);
|
|
||||||
|
|
||||||
/* These functions may be used to create custom fibers (coroutines) using the bundled fiber switching context. */
|
|
||||||
ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, void *kind, zend_fiber_coroutine coroutine, size_t stack_size);
|
ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, void *kind, zend_fiber_coroutine coroutine, size_t stack_size);
|
||||||
ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context);
|
ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context);
|
||||||
ZEND_API void zend_fiber_switch_context(zend_fiber_context *to);
|
ZEND_API zend_fiber_context *zend_fiber_switch_context(zend_fiber_context *to);
|
||||||
|
|
||||||
END_EXTERN_C()
|
END_EXTERN_C()
|
||||||
|
|
||||||
|
@ -152,8 +147,8 @@ static zend_always_inline zend_fiber_context *zend_fiber_get_context(zend_fiber
|
||||||
static zend_always_inline void zend_fiber_capture_vm_state(zend_fiber_vm_state *state)
|
static zend_always_inline void zend_fiber_capture_vm_state(zend_fiber_vm_state *state)
|
||||||
{
|
{
|
||||||
state->vm_stack = EG(vm_stack);
|
state->vm_stack = EG(vm_stack);
|
||||||
state->vm_stack->top = EG(vm_stack_top);
|
state->vm_stack_top = EG(vm_stack_top);
|
||||||
state->vm_stack->end = EG(vm_stack_end);
|
state->vm_stack_end = EG(vm_stack_end);
|
||||||
state->vm_stack_page_size = EG(vm_stack_page_size);
|
state->vm_stack_page_size = EG(vm_stack_page_size);
|
||||||
state->current_execute_data = EG(current_execute_data);
|
state->current_execute_data = EG(current_execute_data);
|
||||||
state->error_reporting = EG(error_reporting);
|
state->error_reporting = EG(error_reporting);
|
||||||
|
@ -164,8 +159,8 @@ static zend_always_inline void zend_fiber_capture_vm_state(zend_fiber_vm_state *
|
||||||
static zend_always_inline void zend_fiber_restore_vm_state(zend_fiber_vm_state *state)
|
static zend_always_inline void zend_fiber_restore_vm_state(zend_fiber_vm_state *state)
|
||||||
{
|
{
|
||||||
EG(vm_stack) = state->vm_stack;
|
EG(vm_stack) = state->vm_stack;
|
||||||
EG(vm_stack_top) = state->vm_stack->top;
|
EG(vm_stack_top) = state->vm_stack_top;
|
||||||
EG(vm_stack_end) = state->vm_stack->end;
|
EG(vm_stack_end) = state->vm_stack_end;
|
||||||
EG(vm_stack_page_size) = state->vm_stack_page_size;
|
EG(vm_stack_page_size) = state->vm_stack_page_size;
|
||||||
EG(current_execute_data) = state->current_execute_data;
|
EG(current_execute_data) = state->current_execute_data;
|
||||||
EG(error_reporting) = state->error_reporting;
|
EG(error_reporting) = state->error_reporting;
|
||||||
|
|
|
@ -436,7 +436,12 @@ static void fiber_enter_observer(zend_fiber_context *from, zend_fiber_context *t
|
||||||
if (ZT_G(observer_fiber_switch)) {
|
if (ZT_G(observer_fiber_switch)) {
|
||||||
if (to->status == ZEND_FIBER_STATUS_INIT) {
|
if (to->status == ZEND_FIBER_STATUS_INIT) {
|
||||||
php_printf("<init '%p'>\n", to);
|
php_printf("<init '%p'>\n", to);
|
||||||
} else if (to->status == ZEND_FIBER_STATUS_RUNNING && from->status == ZEND_FIBER_STATUS_RUNNING) {
|
} else if (to->kind == zend_ce_fiber) {
|
||||||
|
zend_fiber *fiber = zend_fiber_from_context(to);
|
||||||
|
if (fiber->caller != from) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (to->flags & ZEND_FIBER_FLAG_DESTROYED) {
|
if (to->flags & ZEND_FIBER_FLAG_DESTROYED) {
|
||||||
php_printf("<destroying '%p'>\n", to);
|
php_printf("<destroying '%p'>\n", to);
|
||||||
} else if (to->status != ZEND_FIBER_STATUS_DEAD) {
|
} else if (to->status != ZEND_FIBER_STATUS_DEAD) {
|
||||||
|
@ -449,9 +454,7 @@ static void fiber_enter_observer(zend_fiber_context *from, zend_fiber_context *t
|
||||||
static void fiber_suspend_observer(zend_fiber_context *from, zend_fiber_context *to)
|
static void fiber_suspend_observer(zend_fiber_context *from, zend_fiber_context *to)
|
||||||
{
|
{
|
||||||
if (ZT_G(observer_fiber_switch)) {
|
if (ZT_G(observer_fiber_switch)) {
|
||||||
if (from->status == ZEND_FIBER_STATUS_SUSPENDED) {
|
if (from->status == ZEND_FIBER_STATUS_DEAD) {
|
||||||
php_printf("<suspend '%p'>\n", from);
|
|
||||||
} else if (from->status == ZEND_FIBER_STATUS_DEAD) {
|
|
||||||
if (from->flags & ZEND_FIBER_FLAG_THREW) {
|
if (from->flags & ZEND_FIBER_FLAG_THREW) {
|
||||||
php_printf("<threw '%p'>\n", from);
|
php_printf("<threw '%p'>\n", from);
|
||||||
} else if (from->flags & ZEND_FIBER_FLAG_DESTROYED) {
|
} else if (from->flags & ZEND_FIBER_FLAG_DESTROYED) {
|
||||||
|
@ -459,6 +462,11 @@ static void fiber_suspend_observer(zend_fiber_context *from, zend_fiber_context
|
||||||
} else {
|
} else {
|
||||||
php_printf("<returned '%p'>\n", from);
|
php_printf("<returned '%p'>\n", from);
|
||||||
}
|
}
|
||||||
|
} else if (from->kind == zend_ce_fiber) {
|
||||||
|
zend_fiber *fiber = zend_fiber_from_context(from);
|
||||||
|
if (fiber->caller == NULL) {
|
||||||
|
php_printf("<suspend '%p'>\n", from);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue