Flexible fiber bailout handling (#7163)

This commit is contained in:
Martin Schröder 2021-06-18 18:01:10 +02:00 committed by GitHub
parent e9e06279c1
commit c5f9cdedd6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 48 additions and 23 deletions

View file

@ -343,7 +343,7 @@ ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer)
to->status = ZEND_FIBER_STATUS_RUNNING;
if (from->status == ZEND_FIBER_STATUS_RUNNING) {
if (EXPECTED(from->status == ZEND_FIBER_STATUS_RUNNING)) {
from->status = ZEND_FIBER_STATUS_SUSPENDED;
}
@ -381,11 +381,6 @@ ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer)
if (to->status == ZEND_FIBER_STATUS_DEAD) {
zend_fiber_destroy_context(to);
}
/* Propagate bailout to current fiber / main. */
if (UNEXPECTED(to->flags & ZEND_FIBER_FLAG_BAILOUT)) {
zend_bailout();
}
}
static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer)
@ -400,6 +395,7 @@ static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer)
}
EG(vm_stack) = NULL;
transfer->flags = 0;
zend_first_try {
zend_vm_stack stack = zend_vm_stack_new_page(ZEND_FIBER_VM_STACK_SIZE, NULL);
@ -427,10 +423,10 @@ static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer)
zval_ptr_dtor(&fiber->fci.function_name);
if (EG(exception)) {
if (!(fiber->context.flags & ZEND_FIBER_FLAG_DESTROYED)
if (!(fiber->flags & ZEND_FIBER_FLAG_DESTROYED)
|| !(zend_is_graceful_exit(EG(exception)) || zend_is_unwind_exit(EG(exception)))
) {
fiber->context.flags |= ZEND_FIBER_FLAG_THREW;
fiber->flags |= ZEND_FIBER_FLAG_THREW;
transfer->flags = ZEND_FIBER_TRANSFER_FLAG_ERROR;
ZVAL_OBJ_COPY(&transfer->value, EG(exception));
@ -441,7 +437,8 @@ static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer)
ZVAL_COPY(&transfer->value, &fiber->result);
}
} zend_catch {
fiber->context.flags |= ZEND_FIBER_FLAG_BAILOUT;
fiber->flags |= ZEND_FIBER_FLAG_BAILOUT;
transfer->flags = ZEND_FIBER_TRANSFER_FLAG_BAILOUT;
} zend_end_try();
transfer->context = fiber->caller;
@ -486,6 +483,11 @@ static zend_always_inline zend_fiber_transfer zend_fiber_switch_to(
zend_fiber_switch_context(&transfer);
/* Forward bailout into current fiber. */
if (UNEXPECTED(transfer.flags & ZEND_FIBER_TRANSFER_FLAG_BAILOUT)) {
zend_bailout();
}
return transfer;
}
@ -538,7 +540,7 @@ static void zend_fiber_object_destroy(zend_object *object)
zend_object *exception = EG(exception);
EG(exception) = NULL;
fiber->context.flags |= ZEND_FIBER_FLAG_DESTROYED;
fiber->flags |= ZEND_FIBER_FLAG_DESTROYED;
zend_fiber_transfer transfer = zend_fiber_resume(fiber, NULL, false);
@ -639,7 +641,7 @@ ZEND_METHOD(Fiber, suspend)
RETURN_THROWS();
}
if (UNEXPECTED(fiber->context.flags & ZEND_FIBER_FLAG_DESTROYED)) {
if (UNEXPECTED(fiber->flags & ZEND_FIBER_FLAG_DESTROYED)) {
zend_throw_error(zend_ce_fiber_error, "Cannot suspend in a force-closed fiber");
RETURN_THROWS();
}
@ -656,7 +658,7 @@ ZEND_METHOD(Fiber, suspend)
zend_fiber_transfer transfer = zend_fiber_suspend(fiber, value);
if (fiber->context.flags & ZEND_FIBER_FLAG_DESTROYED) {
if (fiber->flags & ZEND_FIBER_FLAG_DESTROYED) {
// This occurs when the fiber is GC'ed while suspended.
zval_ptr_dtor(&transfer.value);
zend_throw_graceful_exit();
@ -777,9 +779,9 @@ ZEND_METHOD(Fiber, getReturn)
fiber = (zend_fiber *) Z_OBJ_P(getThis());
if (fiber->context.status == ZEND_FIBER_STATUS_DEAD) {
if (fiber->context.flags & ZEND_FIBER_FLAG_THREW) {
if (fiber->flags & ZEND_FIBER_FLAG_THREW) {
message = "The fiber threw an exception";
} else if (fiber->context.flags & ZEND_FIBER_FLAG_BAILOUT) {
} else if (fiber->flags & ZEND_FIBER_FLAG_BAILOUT) {
message = "The fiber exited with a fatal error";
} else {
RETURN_COPY(&fiber->result);

View file

@ -45,6 +45,7 @@ typedef enum {
typedef enum {
ZEND_FIBER_TRANSFER_FLAG_ERROR = 1 << 0,
ZEND_FIBER_TRANSFER_FLAG_BAILOUT = 1 << 1
} zend_fiber_transfer_flag;
void zend_register_fiber_ce(void);
@ -61,8 +62,10 @@ typedef struct _zend_fiber_stack zend_fiber_stack;
typedef struct _zend_fiber_transfer {
/* Fiber that will be switched to / has resumed us. */
zend_fiber_context *context;
/* Value to that should be send to (or was received from) a fiber. */
zval value;
/* Bitmask of flags defined in enum zend_fiber_transfer_flag. */
uint8_t flags;
} zend_fiber_transfer;
@ -74,18 +77,28 @@ typedef void (*zend_fiber_coroutine)(zend_fiber_transfer *transfer);
struct _zend_fiber_context {
/* Handle to fiber state as needed by boost.context */
void *handle;
/* Pointer that identifies the fiber type. */
void *kind;
/* Entrypoint function of the fiber. */
zend_fiber_coroutine function;
/* Assigned C stack. */
zend_fiber_stack *stack;
/* Fiber status. */
zend_fiber_status status;
uint8_t flags;
};
struct _zend_fiber {
/* PHP object handle. */
zend_object std;
/* Flags are defined in enum zend_fiber_flag. */
uint8_t flags;
/* Native C fiber context. */
zend_fiber_context context;
/* Fiber that resumed us. */

View file

@ -38,6 +38,11 @@ static zend_fiber_transfer zend_test_fiber_switch_to(zend_fiber_context *context
zend_fiber_switch_context(&transfer);
/* Forward bailout into current fiber. */
if (UNEXPECTED(transfer.flags & ZEND_FIBER_TRANSFER_FLAG_BAILOUT)) {
zend_bailout();
}
return transfer;
}
@ -74,6 +79,7 @@ static ZEND_STACK_ALIGNED void zend_test_fiber_execute(zend_fiber_transfer *tran
zend_execute_data *execute_data;
EG(vm_stack) = NULL;
transfer->flags = 0;
zend_first_try {
zend_vm_stack stack = zend_vm_stack_new_page(ZEND_FIBER_VM_STACK_SIZE, NULL);
@ -97,10 +103,10 @@ static ZEND_STACK_ALIGNED void zend_test_fiber_execute(zend_fiber_transfer *tran
zval_ptr_dtor(&fiber->fci.function_name);
if (EG(exception)) {
if (!(fiber->context.flags & ZEND_FIBER_FLAG_DESTROYED)
if (!(fiber->flags & ZEND_FIBER_FLAG_DESTROYED)
|| !(zend_is_graceful_exit(EG(exception)) || zend_is_unwind_exit(EG(exception)))
) {
fiber->context.flags |= ZEND_FIBER_FLAG_THREW;
fiber->flags |= ZEND_FIBER_FLAG_THREW;
transfer->flags = ZEND_FIBER_TRANSFER_FLAG_ERROR;
ZVAL_OBJ_COPY(&transfer->value, EG(exception));
@ -112,7 +118,8 @@ static ZEND_STACK_ALIGNED void zend_test_fiber_execute(zend_fiber_transfer *tran
ZVAL_COPY(&transfer->value, &fiber->result);
}
} zend_catch {
fiber->context.flags |= ZEND_FIBER_FLAG_BAILOUT;
fiber->flags |= ZEND_FIBER_FLAG_BAILOUT;
transfer->flags = ZEND_FIBER_TRANSFER_FLAG_BAILOUT;
} zend_end_try();
zend_vm_stack_destroy();
@ -159,7 +166,7 @@ static void zend_test_fiber_object_destroy(zend_object *object)
zend_object *exception = EG(exception);
EG(exception) = NULL;
fiber->context.flags |= ZEND_FIBER_FLAG_DESTROYED;
fiber->flags |= ZEND_FIBER_FLAG_DESTROYED;
zend_fiber_transfer transfer = zend_test_fiber_resume(fiber, NULL, false);
@ -274,7 +281,7 @@ static ZEND_METHOD(_ZendTestFiber, suspend)
zend_fiber_transfer transfer = zend_test_fiber_suspend(fiber, value);
if (fiber->context.flags & ZEND_FIBER_FLAG_DESTROYED) {
if (fiber->flags & ZEND_FIBER_FLAG_DESTROYED) {
// This occurs when the test fiber is GC'ed while suspended.
zval_ptr_dtor(&transfer.value);
zend_throw_graceful_exit();

View file

@ -23,6 +23,7 @@ typedef struct _zend_test_fiber zend_test_fiber;
struct _zend_test_fiber {
zend_object std;
uint8_t flags;
zend_fiber_context context;
zend_fiber_context *caller;
zend_fiber_context *previous;

View file

@ -207,7 +207,7 @@ static void fiber_enter_observer(zend_fiber_context *from, zend_fiber_context *t
return;
}
if (to->flags & ZEND_FIBER_FLAG_DESTROYED) {
if (fiber->flags & ZEND_FIBER_FLAG_DESTROYED) {
php_printf("<destroying '%p'>\n", to);
} else if (to->status != ZEND_FIBER_STATUS_DEAD) {
php_printf("<resume '%p'>\n", to);
@ -220,9 +220,11 @@ static void fiber_suspend_observer(zend_fiber_context *from, zend_fiber_context
{
if (ZT_G(observer_fiber_switch)) {
if (from->status == ZEND_FIBER_STATUS_DEAD) {
if (from->flags & ZEND_FIBER_FLAG_THREW) {
zend_fiber *fiber = (from->kind == zend_ce_fiber) ? zend_fiber_from_context(from) : NULL;
if (fiber && fiber->flags & ZEND_FIBER_FLAG_THREW) {
php_printf("<threw '%p'>\n", from);
} else if (from->flags & ZEND_FIBER_FLAG_DESTROYED) {
} else if (fiber && fiber->flags & ZEND_FIBER_FLAG_DESTROYED) {
php_printf("<destroyed '%p'>\n", from);
} else {
php_printf("<returned '%p'>\n", from);