diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 1ddf25a0c714f..5a844e6b2b054 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -304,7 +304,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; } @@ -342,11 +342,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) @@ -361,6 +356,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); @@ -388,10 +384,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)); @@ -402,7 +398,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; @@ -447,6 +444,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; } @@ -499,7 +501,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); @@ -600,7 +602,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(); } @@ -617,7 +619,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(); @@ -738,9 +740,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); diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index b0d59dc30c8e3..84dc92719a813 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -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,12 +77,18 @@ 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; }; /* Zend VM state that needs to be captured / restored during fiber context switch. */ @@ -99,6 +108,10 @@ 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. */ diff --git a/ext/zend_test/fiber.c b/ext/zend_test/fiber.c index 1b3e2db192e04..0254799e2bdb5 100644 --- a/ext/zend_test/fiber.c +++ b/ext/zend_test/fiber.c @@ -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(); diff --git a/ext/zend_test/fiber.h b/ext/zend_test/fiber.h index 4fb6c1b83e616..ad5a4c7b3342e 100644 --- a/ext/zend_test/fiber.h +++ b/ext/zend_test/fiber.h @@ -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; diff --git a/ext/zend_test/observer.c b/ext/zend_test/observer.c index 9039251e2814e..1579287bb6521 100644 --- a/ext/zend_test/observer.c +++ b/ext/zend_test/observer.c @@ -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("\n", to); } else if (to->status != ZEND_FIBER_STATUS_DEAD) { php_printf("\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("\n", from); - } else if (from->flags & ZEND_FIBER_FLAG_DESTROYED) { + } else if (fiber && fiber->flags & ZEND_FIBER_FLAG_DESTROYED) { php_printf("\n", from); } else { php_printf("\n", from);