mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
Add internal Fiber API (#7045)
This additional internal fiber API creates and manipulates a Fiber object, allowing any internal function to start, resume, or suspend a fiber. The existing zend_fiber_context API allows custom C-based fiber creation using the bundled switching context, but does not interact with the PHP VM. This API behaves the same as calling Fiber object methods from user code, switching EGs, and triggering the fiber switch observer. In general, the Fiber object methods call these new API methods.
This commit is contained in:
parent
0e2ac303af
commit
3939c9b07e
2 changed files with 87 additions and 47 deletions
|
@ -266,7 +266,7 @@ ZEND_API void zend_fiber_suspend_context(zend_fiber_context *current)
|
||||||
current->caller = transfer.context;
|
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;
|
zend_vm_stack stack;
|
||||||
size_t stack_page_size;
|
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_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());
|
||||||
|
@ -457,17 +469,8 @@ ZEND_METHOD(Fiber, __construct)
|
||||||
Z_TRY_ADDREF(fiber->fci.function_name);
|
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) {
|
if (fiber->status != ZEND_FIBER_STATUS_INIT) {
|
||||||
zend_throw_error(zend_ce_fiber_error, "Cannot start a fiber that has already been started");
|
zend_throw_error(zend_ce_fiber_error, "Cannot start a fiber that has already been started");
|
||||||
RETURN_THROWS();
|
RETURN_THROWS();
|
||||||
|
@ -492,15 +495,23 @@ ZEND_METHOD(Fiber, start)
|
||||||
ZVAL_UNDEF(&fiber->value);
|
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);
|
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)) {
|
if (UNEXPECTED(!fiber)) {
|
||||||
zend_throw_error(zend_ce_fiber_error, "Cannot suspend outside of a 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);
|
ZVAL_NULL(&fiber->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
fiber->execute_data = execute_data;
|
fiber->execute_data = EG(current_execute_data);
|
||||||
fiber->status = ZEND_FIBER_STATUS_SUSPENDED;
|
fiber->status = ZEND_FIBER_STATUS_SUSPENDED;
|
||||||
fiber->stack_bottom->prev_execute_data = NULL;
|
fiber->stack_bottom->prev_execute_data = NULL;
|
||||||
|
|
||||||
zend_fiber_suspend(fiber);
|
zend_fiber_suspend_from(fiber);
|
||||||
|
|
||||||
if (fiber->status == ZEND_FIBER_STATUS_SHUTDOWN) {
|
if (fiber->status == ZEND_FIBER_STATUS_SHUTDOWN) {
|
||||||
// This occurs when the fiber is GC'ed while suspended.
|
// This occurs when the fiber is GC'ed while suspended.
|
||||||
|
@ -535,7 +546,7 @@ ZEND_METHOD(Fiber, suspend)
|
||||||
fiber->status = ZEND_FIBER_STATUS_RUNNING;
|
fiber->status = ZEND_FIBER_STATUS_RUNNING;
|
||||||
|
|
||||||
if (fiber->exception) {
|
if (fiber->exception) {
|
||||||
exception = fiber->exception;
|
zval *exception = fiber->exception;
|
||||||
fiber->exception = NULL;
|
fiber->exception = NULL;
|
||||||
|
|
||||||
zend_throw_exception_object(exception);
|
zend_throw_exception_object(exception);
|
||||||
|
@ -546,6 +557,44 @@ ZEND_METHOD(Fiber, suspend)
|
||||||
ZVAL_UNDEF(&fiber->value);
|
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_METHOD(Fiber, resume)
|
||||||
{
|
{
|
||||||
zend_fiber *fiber;
|
zend_fiber *fiber;
|
||||||
|
@ -558,19 +607,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);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZEND_API void zend_fiber_throw(zend_fiber *fiber, zval *exception, zval *return_value)
|
||||||
|
{
|
||||||
if (UNEXPECTED(fiber->status != ZEND_FIBER_STATUS_SUSPENDED)) {
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value) {
|
Z_ADDREF_P(exception);
|
||||||
ZVAL_COPY(&fiber->value, value);
|
fiber->exception = exception;
|
||||||
} else {
|
|
||||||
ZVAL_NULL(&fiber->value);
|
|
||||||
}
|
|
||||||
|
|
||||||
fiber->status = ZEND_FIBER_STATUS_RUNNING;
|
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);
|
zend_fiber_switch_to(fiber);
|
||||||
|
|
||||||
|
@ -593,25 +644,7 @@ ZEND_METHOD(Fiber, throw)
|
||||||
|
|
||||||
fiber = (zend_fiber *) Z_OBJ_P(getThis());
|
fiber = (zend_fiber *) Z_OBJ_P(getThis());
|
||||||
|
|
||||||
if (UNEXPECTED(fiber->status != ZEND_FIBER_STATUS_SUSPENDED)) {
|
zend_fiber_throw(fiber, exception, return_value);
|
||||||
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_METHOD(Fiber, isStarted)
|
ZEND_METHOD(Fiber, isStarted)
|
||||||
|
|
|
@ -92,9 +92,16 @@ static const zend_uchar ZEND_FIBER_STATUS_BAILOUT = 0x20;
|
||||||
|
|
||||||
static const zend_uchar ZEND_FIBER_STATUS_FINISHED = 0x2c;
|
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 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_destroy_context(zend_fiber_context *context);
|
||||||
|
|
||||||
ZEND_API void zend_fiber_switch_context(zend_fiber_context *to);
|
ZEND_API void zend_fiber_switch_context(zend_fiber_context *to);
|
||||||
ZEND_API void zend_fiber_suspend_context(zend_fiber_context *current);
|
ZEND_API void zend_fiber_suspend_context(zend_fiber_context *current);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue