diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index dd246388858..efc1f1ccec4 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -266,7 +266,7 @@ ZEND_API void zend_fiber_suspend_context(zend_fiber_context *current) current->caller = transfer.context; } -static void zend_fiber_suspend(zend_fiber *fiber) +static void zend_fiber_suspend_from(zend_fiber *fiber) { zend_vm_stack stack; size_t stack_page_size; @@ -445,6 +445,18 @@ static void zend_fiber_object_free(zend_object *object) 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_fiber *fiber = (zend_fiber *) Z_OBJ_P(getThis()); @@ -457,17 +469,8 @@ ZEND_METHOD(Fiber, __construct) Z_TRY_ADDREF(fiber->fci.function_name); } -ZEND_METHOD(Fiber, start) +ZEND_API void zend_fiber_start(zend_fiber *fiber, zval *params, uint32_t param_count, zend_array *named_params, zval *return_value) { - zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(getThis()); - zval *params; - uint32_t param_count; - zend_array *named_params; - - ZEND_PARSE_PARAMETERS_START(0, -1) - Z_PARAM_VARIADIC_WITH_NAMED(params, param_count, named_params); - ZEND_PARSE_PARAMETERS_END(); - 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(); @@ -492,15 +495,23 @@ ZEND_METHOD(Fiber, start) ZVAL_UNDEF(&fiber->value); } -ZEND_METHOD(Fiber, suspend) +ZEND_METHOD(Fiber, start) +{ + zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(getThis()); + zval *params; + uint32_t param_count; + zend_array *named_params; + + ZEND_PARSE_PARAMETERS_START(0, -1) + Z_PARAM_VARIADIC_WITH_NAMED(params, param_count, named_params); + ZEND_PARSE_PARAMETERS_END(); + + zend_fiber_start(fiber, params, param_count, named_params, return_value); +} + +ZEND_API void zend_fiber_suspend(zval *value, zval *return_value) { zend_fiber *fiber = EG(current_fiber); - zval *exception, *value = NULL; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_ZVAL(value); - ZEND_PARSE_PARAMETERS_END(); if (UNEXPECTED(!fiber)) { zend_throw_error(zend_ce_fiber_error, "Cannot suspend outside of a fiber"); @@ -520,11 +531,11 @@ ZEND_METHOD(Fiber, suspend) ZVAL_NULL(&fiber->value); } - fiber->execute_data = execute_data; + fiber->execute_data = EG(current_execute_data); fiber->status = ZEND_FIBER_STATUS_SUSPENDED; fiber->stack_bottom->prev_execute_data = NULL; - zend_fiber_suspend(fiber); + zend_fiber_suspend_from(fiber); if (fiber->status == ZEND_FIBER_STATUS_SHUTDOWN) { // This occurs when the fiber is GC'ed while suspended. @@ -535,7 +546,7 @@ ZEND_METHOD(Fiber, suspend) fiber->status = ZEND_FIBER_STATUS_RUNNING; if (fiber->exception) { - exception = fiber->exception; + zval *exception = fiber->exception; fiber->exception = NULL; zend_throw_exception_object(exception); @@ -546,6 +557,44 @@ ZEND_METHOD(Fiber, suspend) ZVAL_UNDEF(&fiber->value); } +ZEND_METHOD(Fiber, suspend) +{ + zval *value = NULL; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(value); + ZEND_PARSE_PARAMETERS_END(); + + zend_fiber_suspend(value, return_value); +} + +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(); + } + + if (value) { + ZVAL_COPY(&fiber->value, value); + } else { + ZVAL_NULL(&fiber->value); + } + + fiber->status = ZEND_FIBER_STATUS_RUNNING; + fiber->stack_bottom->prev_execute_data = EG(current_execute_data); + + zend_fiber_switch_to(fiber); + + if (fiber->status & ZEND_FIBER_STATUS_FINISHED) { + RETURN_NULL(); + } + + RETVAL_COPY_VALUE(&fiber->value); + ZVAL_UNDEF(&fiber->value); +} + ZEND_METHOD(Fiber, resume) { zend_fiber *fiber; @@ -558,19 +607,21 @@ ZEND_METHOD(Fiber, resume) fiber = (zend_fiber *) Z_OBJ_P(getThis()); + zend_fiber_resume(fiber, value, return_value); +} + +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"); RETURN_THROWS(); } - if (value) { - ZVAL_COPY(&fiber->value, value); - } else { - ZVAL_NULL(&fiber->value); - } + Z_ADDREF_P(exception); + fiber->exception = exception; fiber->status = ZEND_FIBER_STATUS_RUNNING; - fiber->stack_bottom->prev_execute_data = execute_data; + fiber->stack_bottom->prev_execute_data = EG(current_execute_data); zend_fiber_switch_to(fiber); @@ -593,25 +644,7 @@ ZEND_METHOD(Fiber, throw) fiber = (zend_fiber *) Z_OBJ_P(getThis()); - 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(); - } - - Z_ADDREF_P(exception); - fiber->exception = exception; - - fiber->status = ZEND_FIBER_STATUS_RUNNING; - fiber->stack_bottom->prev_execute_data = execute_data; - - zend_fiber_switch_to(fiber); - - if (fiber->status & ZEND_FIBER_STATUS_FINISHED) { - RETURN_NULL(); - } - - RETVAL_COPY_VALUE(&fiber->value); - ZVAL_UNDEF(&fiber->value); + zend_fiber_throw(fiber, exception, return_value); } ZEND_METHOD(Fiber, isStarted) diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index de1364d08ac..d1a083baf18 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -92,9 +92,16 @@ static const zend_uchar ZEND_FIBER_STATUS_BAILOUT = 0x20; static const zend_uchar ZEND_FIBER_STATUS_FINISHED = 0x2c; +/* These functions create and manipulate a Fiber object, allowing any internal function to start, resume, or suspend a fiber. */ +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 zend_bool zend_fiber_init_context(zend_fiber_context *context, zend_fiber_coroutine coroutine, size_t stack_size); ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context); - ZEND_API void zend_fiber_switch_context(zend_fiber_context *to); ZEND_API void zend_fiber_suspend_context(zend_fiber_context *current);