From 0d71fc0d0e16564830b06b2069bb9084ac5d94ff Mon Sep 17 00:00:00 2001 From: Edmond <1571649+EdmondDantes@users.noreply.github.com> Date: Sat, 12 Jul 2025 11:03:50 +0300 Subject: [PATCH 1/2] #13: Initial classes for Future --- async.c | 5 +- config.m4 | 4 +- future.c | 549 +++++++++++++++++++++++++++++++++++++++++++++++ future.h | 65 ++++++ future.stub.php | 136 ++++++++++++ future_arginfo.h | 114 ++++++++++ php_async.h | 2 + run-tests.sh | 1 + 8 files changed, 871 insertions(+), 5 deletions(-) create mode 100644 future.c create mode 100644 future.h create mode 100644 future.stub.php create mode 100644 future_arginfo.h diff --git a/async.c b/async.c index 9fafc43..da81257 100644 --- a/async.c +++ b/async.c @@ -28,6 +28,7 @@ #include "context.h" #include "async_API.h" #include "async_arginfo.h" +#include "future.h" #include "zend_interfaces.h" #ifdef PHP_ASYNC_LIBUV #include "libuv_reactor.h" @@ -928,11 +929,9 @@ ZEND_MINIT_FUNCTION(async) async_register_coroutine_ce(); async_register_context_ce(); async_register_exceptions_ce(); - //async_register_notifier_ce(); - //async_register_handlers_ce(); //async_register_channel_ce(); //async_register_iterator_ce(); - //async_register_future_ce(); + async_register_future_ce(); async_scheduler_startup(); diff --git a/config.m4 b/config.m4 index 11dd57e..3038a20 100644 --- a/config.m4 +++ b/config.m4 @@ -11,14 +11,14 @@ if test "$PHP_ASYNC" = "yes"; then dnl Register extension source files. PHP_NEW_EXTENSION([async], [async.c coroutine.c scope.c scheduler.c exceptions.c iterator.c async_API.c \ - context.c \ + context.c future.c \ internal/allocator.c internal/circular_buffer.c \ zend_common.c], $ext_shared) dnl Optionally install headers (if desired for public use). PHP_INSTALL_HEADERS([ext/async], - [php_async.h coroutine.h scope.h scheduler.h exceptions.h iterator.h async_API.h context.h]) + [php_async.h coroutine.h scope.h scheduler.h exceptions.h iterator.h async_API.h context.h future.h]) AC_PATH_PROG(PKG_CONFIG, pkg-config, no) diff --git a/future.c b/future.c new file mode 100644 index 0000000..8b16bf7 --- /dev/null +++ b/future.c @@ -0,0 +1,549 @@ +/* ++----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Edmond | + +----------------------------------------------------------------------+ +*/ + +#include "future.h" +#include "php_async.h" +#include "exceptions.h" +#include "future_arginfo.h" +#include "zend_exceptions.h" +#include "zend_closures.h" +#include "zend_smart_str.h" + +#define METHOD(name) PHP_METHOD(Async_Future, name) +#define STATE_METHOD(name) PHP_METHOD(Async_FutureState, name) + +zend_class_entry *async_ce_future_state = NULL; +zend_class_entry *async_ce_future = NULL; + +static zend_object_handlers async_future_state_handlers; +static zend_object_handlers async_future_handlers; + +/////////////////////////////////////////////////////////// +/// FutureState event functions (like coroutine) +/////////////////////////////////////////////////////////// + +static void future_state_event_start(zend_async_event_t *event) +{ + /* Nothing to start for FutureState */ +} + +static void future_state_event_stop(zend_async_event_t *event) +{ + /* Nothing to stop for FutureState */ +} + +static void future_state_add_callback(zend_async_event_t *event, zend_async_event_callback_t *callback) +{ + zend_async_callbacks_push(event, callback); +} + +static void future_state_del_callback(zend_async_event_t *event, zend_async_event_callback_t *callback) +{ + zend_async_callbacks_remove(event, callback); +} + +static bool future_state_replay(zend_async_event_t *event, zend_async_event_callback_t *callback, zval *result, zend_object **exception) +{ + async_future_state_t *state = (async_future_state_t *) event; + + if (!state->completed) { + return false; + } + + if (callback != NULL) { + callback->callback(event, callback, &state->result, state->exception); + return true; + } + + if (result != NULL) { + ZVAL_COPY(result, &state->result); + } + + if (exception == NULL && state->exception != NULL) { + GC_ADDREF(state->exception); + async_rethrow_exception(state->exception); + } else if (exception != NULL && state->exception != NULL) { + *exception = state->exception; + GC_ADDREF(*exception); + } + + return state->exception != NULL || Z_TYPE(state->result) != IS_UNDEF; +} + +static zend_string* future_state_info(zend_async_event_t *event) +{ + async_future_state_t *state = (async_future_state_t *) event; + + return zend_strpprintf(0, "FutureState(%s)", state->completed ? "completed" : "pending"); +} + +static void future_state_dispose(zend_async_event_t *event) +{ + async_future_state_t *state = (async_future_state_t *) event; + OBJ_RELEASE(&state->std); +} + +/////////////////////////////////////////////////////////// +/// FutureState object lifecycle +/////////////////////////////////////////////////////////// + +static zend_object *async_future_state_object_create(zend_class_entry *ce) +{ + async_future_state_t *state = zend_object_alloc(sizeof(async_future_state_t), ce); + + zend_future_t *zend_future = ecalloc(1, sizeof(zend_future_t)); + zend_async_event_t *event = &zend_future->event; + + ZVAL_UNDEF(&zend_future->result); + zend_future->exception = NULL; + + /* Set event handlers */ + event->start = future_state_event_start; + event->stop = future_state_event_stop; + event->add_callback = future_state_add_callback; + event->del_callback = future_state_del_callback; + event->replay = future_state_replay; + event->info = future_state_info; + event->dispose = future_state_dispose; + event->ref_count = 1; + + ZEND_ASYNC_EVENT_REF_SET(state, XtOffsetOf(async_future_state_t, std), event); + + state->event = event; + + zend_object_std_init(&state->std, ce); + object_properties_init(&state->std, ce); + + return &state->std; +} + +static void async_future_state_object_destroy(zend_object *object) +{ + async_future_state_t *state = ASYNC_FUTURE_STATE_FROM_OBJ(object); + + zval_ptr_dtor(&state->result); + + if (state->exception != NULL) { + OBJ_RELEASE(state->exception); + } + + zend_object_std_dtor(&state->std); +} + +/////////////////////////////////////////////////////////// +/// Future object lifecycle +/////////////////////////////////////////////////////////// + +static zend_object *async_future_object_create(zend_class_entry *ce) +{ + async_future_t *future = emalloc(sizeof(async_future_t)); + + zend_object_std_init(&future->std, ce); + object_properties_init(&future->std, ce); + + future->std.handlers = &async_future_handlers; + future->state = NULL; + + return &future->std; +} + +static void async_future_object_destroy(zend_object *object) +{ + async_future_t *future = ASYNC_FUTURE_FROM_OBJ(object); + + if (future->state != NULL) { + OBJ_RELEASE(&future->state->std); + } + + zend_object_std_dtor(&future->std); +} + +/////////////////////////////////////////////////////////// +/// FutureState methods +/////////////////////////////////////////////////////////// + +#define THIS_FUTURE_STATE ((async_future_state_t *) ASYNC_FUTURE_STATE_FROM_OBJ(Z_OBJ_P(ZEND_THIS))) + +STATE_METHOD(__construct) +{ + ZEND_PARSE_PARAMETERS_NONE(); +} + +STATE_METHOD(complete) +{ + zval *result; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(result) + ZEND_PARSE_PARAMETERS_END(); + + async_future_state_t *state = THIS_FUTURE_STATE; + + if (state->completed) { + async_throw_error("FutureState is already completed"); + RETURN_THROWS(); + } + + ZVAL_COPY(&state->result, result); + state->completed = true; + + /* Mark event as closed (completed) */ + ZEND_ASYNC_EVENT_SET_CLOSED(&state->event); + + /* Notify all callbacks */ + zend_async_callbacks_notify(&state->event, &state->result, NULL, false); +} + +STATE_METHOD(error) +{ + zval *throwable; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(throwable, zend_ce_throwable) + ZEND_PARSE_PARAMETERS_END(); + + async_future_state_t *state = THIS_FUTURE_STATE; + + if (state->completed) { + async_throw_error("FutureState is already completed"); + RETURN_THROWS(); + } + + state->exception = Z_OBJ_P(throwable); + GC_ADDREF(state->exception); + state->completed = true; + + /* Mark event as closed (completed) */ + ZEND_ASYNC_EVENT_SET_CLOSED(&state->event); + + /* Notify all callbacks with exception */ + zend_async_callbacks_notify(&state->event, NULL, state->exception, false); +} + +STATE_METHOD(isComplete) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + async_future_state_t *state = THIS_FUTURE_STATE; + + RETURN_BOOL(state->completed); +} + +STATE_METHOD(ignore) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + async_future_state_t *state = THIS_FUTURE_STATE; + + state->ignored = true; +} + +STATE_METHOD(getInfo) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + async_future_state_t *state = THIS_FUTURE_STATE; + + smart_str info = {0}; + smart_str_appends(&info, "FutureState("); + smart_str_appends(&info, state->completed ? "completed" : "pending"); + + if (state->completed) { + if (state->exception != NULL) { + smart_str_appends(&info, ", error"); + } else { + smart_str_appends(&info, ", value"); + } + } + + smart_str_appendc(&info, ')'); + smart_str_0(&info); + + RETURN_STR(info.s); +} + +/////////////////////////////////////////////////////////// +/// Future methods +/////////////////////////////////////////////////////////// + +#define THIS_FUTURE ((async_future_t *) ASYNC_FUTURE_FROM_OBJ(Z_OBJ_P(ZEND_THIS))) + +METHOD(complete) +{ + zval *value = NULL; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(value) + ZEND_PARSE_PARAMETERS_END(); + + /* Create new FutureState */ + async_future_state_t *state = async_future_state_create(); + + /* Complete it immediately */ + if (value != NULL) { + ZVAL_COPY(&state->result, value); + } else { + ZVAL_NULL(&state->result); + } + state->completed = true; + + /* Create Future wrapping the state */ + async_future_t *future = async_future_wrap_state(state); + + RETURN_OBJ_COPY(&future->std); +} + +METHOD(error) +{ + zval *throwable; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(throwable, zend_ce_throwable) + ZEND_PARSE_PARAMETERS_END(); + + /* Create new FutureState */ + async_future_state_t *state = async_future_state_create(); + + /* Set error immediately */ + state->exception = Z_OBJ_P(throwable); + GC_ADDREF(state->exception); + state->completed = true; + + /* Create Future wrapping the state */ + async_future_t *future = async_future_wrap_state(state); + + RETURN_OBJ_COPY(&future->std); +} + +METHOD(__construct) +{ + zval *state_obj; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(state_obj, async_ce_future_state) + ZEND_PARSE_PARAMETERS_END(); + + async_future_t *future = THIS_FUTURE; + async_future_state_t *state = ASYNC_FUTURE_STATE_FROM_OBJ(Z_OBJ_P(state_obj)); + + future->state = state; + GC_ADDREF(&state->std); +} + +METHOD(isComplete) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + async_future_t *future = THIS_FUTURE; + + if (future->state == NULL) { + RETURN_FALSE; + } + + RETURN_BOOL(future->state->completed); +} + +METHOD(ignore) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + async_future_t *future = THIS_FUTURE; + + if (future->state != NULL) { + future->state->ignored = true; + } + + RETURN_ZVAL(ZEND_THIS, 1, 0); +} + +METHOD(await) +{ + zval *cancellation = NULL; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_OBJECT_OF_CLASS_OR_NULL(cancellation, async_ce_awaitable) + ZEND_PARSE_PARAMETERS_END(); + + async_future_t *future = THIS_FUTURE; + + if (future->state == NULL) { + async_throw_error("Future has no state"); + RETURN_THROWS(); + } + + async_future_state_t *state = future->state; + + /* If already completed, return result immediately */ + if (state->completed) { + if (state->exception != NULL) { + zval exception_zv; + ZVAL_OBJ(&exception_zv, state->exception); + zend_throw_exception_object(&exception_zv); + RETURN_THROWS(); + } + + RETURN_ZVAL(&state->result, 1, 0); + } + + /* Get current coroutine */ + zend_coroutine_t *coroutine = ZEND_ASYNC_CURRENT_COROUTINE; + if (coroutine == NULL) { + async_throw_error("Cannot await outside of coroutine context"); + RETURN_THROWS(); + } + + /* TODO: Handle cancellation if provided */ + if (cancellation != NULL) { + /* Currently not implemented */ + } + + /* Create callback for when future completes */ + zend_coroutine_event_callback_t *callback = emalloc(sizeof(zend_coroutine_event_callback_t)); + callback->base.ref_count = 1; + callback->base.callback = zend_async_waker_callback_resolve; + callback->base.dispose = NULL; + callback->coroutine = coroutine; + + /* Suspend coroutine until future completes */ + zend_async_resume_when(coroutine, &state->event, false, NULL, callback); + + /* Execution resumes here when future completes */ + + /* Check result after await */ + if (state->exception != NULL) { + zval exception_zv; + ZVAL_OBJ(&exception_zv, state->exception); + zend_throw_exception_object(&exception_zv); + RETURN_THROWS(); + } + + RETURN_ZVAL(&state->result, 1, 0); +} + +/* TODO: Implement map, catch, finally methods */ +METHOD(map) +{ + async_throw_error("Future::map() not yet implemented"); + RETURN_THROWS(); +} + +METHOD(catch) +{ + async_throw_error("Future::catch() not yet implemented"); + RETURN_THROWS(); +} + +METHOD(finally) +{ + async_throw_error("Future::finally() not yet implemented"); + RETURN_THROWS(); +} + +/////////////////////////////////////////////////////////// +/// Helper functions +/////////////////////////////////////////////////////////// + +async_future_state_t *async_future_state_create(void) +{ + zend_object *obj = async_future_state_object_create(async_ce_future_state); + return ASYNC_FUTURE_STATE_FROM_OBJ(obj); +} + +async_future_t *async_future_wrap_state(async_future_state_t *state) +{ + zend_object *obj = async_future_object_create(async_ce_future); + async_future_t *future = ASYNC_FUTURE_FROM_OBJ(obj); + + future->state = state; + GC_ADDREF(&state->std); + + return future; +} + +/////////////////////////////////////////////////////////// +/// API function implementations +/////////////////////////////////////////////////////////// + +zend_future_t *async_future_create(void) +{ + async_future_state_t *state = async_future_state_create(); + return (zend_future_t *)state; +} + +void async_future_complete(zend_future_t *future, zval *value) +{ + async_future_state_t *state = (async_future_state_t *)future; + + if (state->completed) { + return; + } + + ZVAL_COPY(&state->result, value); + state->completed = true; + + /* Mark event as closed (completed) */ + ZEND_ASYNC_EVENT_SET_CLOSED(&state->event); + + /* Notify all callbacks */ + zend_async_callbacks_notify(&state->event, &state->result, NULL, false); +} + +void async_future_error(zend_future_t *future, zend_object *exception) +{ + async_future_state_t *state = (async_future_state_t *)future; + + if (state->completed) { + return; + } + + state->exception = exception; + GC_ADDREF(exception); + state->completed = true; + + /* Mark event as closed (completed) */ + ZEND_ASYNC_EVENT_SET_CLOSED(&state->event); + + /* Notify all callbacks with exception */ + zend_async_callbacks_notify(&state->event, NULL, state->exception, false); +} + +/////////////////////////////////////////////////////////// +/// Class registration +/////////////////////////////////////////////////////////// + +void async_register_future_ce(void) +{ + /* Register FutureState class using generated registration */ + async_ce_future_state = register_class_Async_FutureState(); + async_ce_future_state->create_object = async_future_state_object_create; + + memcpy(&async_future_state_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + async_future_state_handlers.offset = XtOffsetOf(async_future_state_t, std); + async_future_state_handlers.free_obj = async_future_state_object_destroy; + + /* Register Future class using generated registration */ + async_ce_future = register_class_Async_Future(); + async_ce_future->create_object = async_future_object_create; + + memcpy(&async_future_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + async_future_handlers.offset = XtOffsetOf(async_future_t, std); + async_future_handlers.free_obj = async_future_object_destroy; + + /* Make Future implement Awaitable */ + zend_class_implements(async_ce_future, 1, async_ce_awaitable); +} \ No newline at end of file diff --git a/future.h b/future.h new file mode 100644 index 0000000..ccb0180 --- /dev/null +++ b/future.h @@ -0,0 +1,65 @@ +/* ++----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Edmond | + +----------------------------------------------------------------------+ +*/ +#ifndef FUTURE_H +#define FUTURE_H + +#include +#include + +typedef struct _async_future_state_s async_future_state_t; +typedef struct _async_future_s async_future_t; + +/** + * FutureState object structure. + * Inherits from zend_async_event_t to participate in the event system. + */ +struct _async_future_state_s { + ZEND_ASYNC_EVENT_REF_FIELDS + zend_object std; +}; + +/** + * Future object structure. + * Holds a reference to FutureState. + */ +struct _async_future_s { + ZEND_ASYNC_EVENT_REF_FIELDS + zend_object std; /* Standard object */ +}; + +/* Class entry declarations */ +extern zend_class_entry *async_ce_future_state; +extern zend_class_entry *async_ce_future; + +#define ASYNC_FUTURE_STATE_FROM_EVENT(ev) ((async_future_state_t *)(ev)->extra_offset) +#define ASYNC_FUTURE_STATE_FROM_OBJ(obj) ((async_future_state_t *)((char *)(obj) - (obj)->handlers->offset)) + +#define ASYNC_FUTURE_FROM_EVENT(ev) ((async_future_t *)(ev)->extra_offset) +#define ASYNC_FUTURE_FROM_OBJ(obj) ((async_future_t *)((char *)(obj) - (obj)->handlers->offset)) + +/* Registration function */ +void async_register_future_ce(void); + +/* API function implementations */ +zend_future_t *async_future_create(void); +void async_future_complete(zend_future_t *future, zval *value); +void async_future_error(zend_future_t *future, zend_object *exception); + +/* Internal helper functions */ +async_future_state_t *async_future_state_create(void); +async_future_t *async_future_wrap_state(async_future_state_t *state); + +#endif /* FUTURE_H */ \ No newline at end of file diff --git a/future.stub.php b/future.stub.php new file mode 100644 index 0000000..31ba8eb --- /dev/null +++ b/future.stub.php @@ -0,0 +1,136 @@ + + */ + public static function complete(mixed $value = null): Future {} + + /** + * @return Future + */ + public static function error(\Throwable $throwable): Future {} + + /** + * param FutureState $state + */ + public function __construct(FutureState $state) {} + + /** + * @return bool True if the operation has completed. + */ + public function isComplete(): bool {} + + /** + * Do not forward unhandled errors to the event loop handler. + * + * @return Future + */ + public function ignore(): Future {} + + /** + * Attaches a callback that is invoked if this future completes. The returned future is completed with the return + * value of the callback, or errors with an exception thrown from the callback. + * + * @psalm-suppress InvalidTemplateParam + * + * @template Tr + * + * @param callable(T):Tr $map + * + * @return Future + */ + public function map(callable $map): Future {} + + /** + * Attaches a callback that is invoked if this future errors. The returned future is completed with the return + * value of the callback, or errors with an exception thrown from the callback. + * + * @template Tr + * + * @param callable(\Throwable):Tr $catch + * + * @return Future + */ + public function catch(callable $catch): Future {} + + /** + * Attaches a callback that is always invoked when the future is completed. The returned future resolves with the + * same value as this future once the callback has finished execution. If the callback throws, the returned future + * will error with the thrown exception. + * + * @param \Closure():void $finally + * + * @return Future + */ + public function finally(callable $finally): Future {} + + /** + * Awaits the operation to complete. + * + * Throws an exception if the operation fails. + * + * @return T + */ + public function await(?Awaitable $cancellation = null): mixed {} + + /** + * Return awaiting debug information. + */ + public function getAwaitingInfo(): array {} +} \ No newline at end of file diff --git a/future_arginfo.h b/future_arginfo.h new file mode 100644 index 0000000..96b5751 --- /dev/null +++ b/future_arginfo.h @@ -0,0 +1,114 @@ +/* This is a generated file, edit the .stub.php file instead. + * Stub hash: 3da547a8570c93f495ee7893744425efcc4c0490 */ + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Async_FutureState___construct, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Async_FutureState_complete, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, result, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Async_FutureState_error, 0, 1, IS_VOID, 0) + ZEND_ARG_OBJ_INFO(0, throwable, Throwable, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Async_FutureState_isComplete, 0, 0, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Async_FutureState_ignore, 0, 0, IS_VOID, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Async_FutureState_getInfo, 0, 0, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Async_Future_complete, 0, 0, Async\\Future, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_MIXED, 0, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Async_Future_error, 0, 1, Async\\Future, 0) + ZEND_ARG_OBJ_INFO(0, throwable, Throwable, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Async_Future___construct, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, state, Async\\FutureState, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_Async_Future_isComplete arginfo_class_Async_FutureState_isComplete + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Async_Future_ignore, 0, 0, Async\\Future, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Async_Future_map, 0, 1, Async\\Future, 0) + ZEND_ARG_TYPE_INFO(0, map, IS_CALLABLE, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Async_Future_catch, 0, 1, Async\\Future, 0) + ZEND_ARG_TYPE_INFO(0, catch, IS_CALLABLE, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Async_Future_finally, 0, 1, Async\\Future, 0) + ZEND_ARG_TYPE_INFO(0, finally, IS_CALLABLE, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Async_Future_await, 0, 0, IS_MIXED, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, cancellation, Async\\Awaitable, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_METHOD(Async_FutureState, __construct); +ZEND_METHOD(Async_FutureState, complete); +ZEND_METHOD(Async_FutureState, error); +ZEND_METHOD(Async_FutureState, isComplete); +ZEND_METHOD(Async_FutureState, ignore); +ZEND_METHOD(Async_FutureState, getInfo); +ZEND_METHOD(Async_Future, complete); +ZEND_METHOD(Async_Future, error); +ZEND_METHOD(Async_Future, __construct); +ZEND_METHOD(Async_Future, isComplete); +ZEND_METHOD(Async_Future, ignore); +ZEND_METHOD(Async_Future, map); +ZEND_METHOD(Async_Future, catch); +ZEND_METHOD(Async_Future, finally); +ZEND_METHOD(Async_Future, await); + +static const zend_function_entry class_Async_FutureState_methods[] = { + ZEND_ME(Async_FutureState, __construct, arginfo_class_Async_FutureState___construct, ZEND_ACC_PUBLIC) + ZEND_ME(Async_FutureState, complete, arginfo_class_Async_FutureState_complete, ZEND_ACC_PUBLIC) + ZEND_ME(Async_FutureState, error, arginfo_class_Async_FutureState_error, ZEND_ACC_PUBLIC) + ZEND_ME(Async_FutureState, isComplete, arginfo_class_Async_FutureState_isComplete, ZEND_ACC_PUBLIC) + ZEND_ME(Async_FutureState, ignore, arginfo_class_Async_FutureState_ignore, ZEND_ACC_PUBLIC) + ZEND_ME(Async_FutureState, getInfo, arginfo_class_Async_FutureState_getInfo, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +static const zend_function_entry class_Async_Future_methods[] = { + ZEND_ME(Async_Future, complete, arginfo_class_Async_Future_complete, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + ZEND_ME(Async_Future, error, arginfo_class_Async_Future_error, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + ZEND_ME(Async_Future, __construct, arginfo_class_Async_Future___construct, ZEND_ACC_PUBLIC) + ZEND_ME(Async_Future, isComplete, arginfo_class_Async_Future_isComplete, ZEND_ACC_PUBLIC) + ZEND_ME(Async_Future, ignore, arginfo_class_Async_Future_ignore, ZEND_ACC_PUBLIC) + ZEND_ME(Async_Future, map, arginfo_class_Async_Future_map, ZEND_ACC_PUBLIC) + ZEND_ME(Async_Future, catch, arginfo_class_Async_Future_catch, ZEND_ACC_PUBLIC) + ZEND_ME(Async_Future, finally, arginfo_class_Async_Future_finally, ZEND_ACC_PUBLIC) + ZEND_ME(Async_Future, await, arginfo_class_Async_Future_await, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +static zend_class_entry *register_class_Async_FutureState(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Async", "FutureState", class_Async_FutureState_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + + return class_entry; +} + +static zend_class_entry *register_class_Async_Future(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Async", "Future", class_Async_Future_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL); + + return class_entry; +} diff --git a/php_async.h b/php_async.h index 4fe08a0..b88d371 100644 --- a/php_async.h +++ b/php_async.h @@ -40,6 +40,8 @@ extern zend_module_entry async_module_entry; extern zend_class_entry * async_ce_awaitable; extern zend_class_entry * async_ce_timeout; +extern zend_class_entry * async_ce_future_state; +extern zend_class_entry * async_ce_future; #define PHP_ASYNC_NAME "TrueAsync" #define PHP_ASYNC_VERSION "0.5.0" diff --git a/run-tests.sh b/run-tests.sh index 2a7df47..0da531b 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -4,6 +4,7 @@ BASE_PATH="$(cd "$(dirname "$0")/tests" && pwd)" RUN_TESTS_PATH="$(cd "$(dirname "$0")/../../" && pwd)/run-tests.php" PHP_EXECUTABLE="$(cd "$(dirname "$0")/../../" && pwd)/sapi/cli/php" export VALGRIND_OPTS="--leak-check=full --track-origins=yes" +#export ASAN_OPTIONS=detect_leaks=1:abort_on_error=1:symbolize=1 if [ -z "$1" ]; then TEST_PATH="$BASE_PATH" From 412fe62d735e59c1592a0bb87a839d2f805800d7 Mon Sep 17 00:00:00 2001 From: Edmond <1571649+EdmondDantes@users.noreply.github.com> Date: Sat, 12 Jul 2025 15:23:44 +0300 Subject: [PATCH 2/2] #13: Future methods --- async.c | 2 +- future.c | 431 +++++++++++++++++++++++++++---------------------------- future.h | 4 - 3 files changed, 210 insertions(+), 227 deletions(-) diff --git a/async.c b/async.c index da81257..1de64b1 100644 --- a/async.c +++ b/async.c @@ -237,7 +237,7 @@ PHP_FUNCTION(Async_await) // If the cancellation event is already resolved, we can return exception immediately. if (cancellation_event != NULL && ZEND_ASYNC_EVENT_IS_CLOSED(cancellation_event)) { - if (ZEND_ASYNC_EVENT_EXTRACT_RESULT(awaitable_event, return_value)) { + if (ZEND_ASYNC_EVENT_EXTRACT_RESULT(cancellation_event, return_value)) { return; } diff --git a/future.c b/future.c index 8b16bf7..7a803b0 100644 --- a/future.c +++ b/future.c @@ -20,11 +20,21 @@ #include "future_arginfo.h" #include "zend_exceptions.h" #include "zend_closures.h" +#include "zend_common.h" +#include "scheduler.h" #include "zend_smart_str.h" #define METHOD(name) PHP_METHOD(Async_Future, name) #define STATE_METHOD(name) PHP_METHOD(Async_FutureState, name) +#define SCHEDULER_LAUNCH if (UNEXPECTED(ZEND_ASYNC_CURRENT_COROUTINE == NULL)) { \ + async_scheduler_launch(); \ + if (UNEXPECTED(EG(exception) != NULL)) { \ + RETURN_THROWS(); \ + } \ + } + + zend_class_entry *async_ce_future_state = NULL; zend_class_entry *async_ce_future = NULL; @@ -130,16 +140,17 @@ static zend_object *async_future_state_object_create(zend_class_entry *ce) return &state->std; } -static void async_future_state_object_destroy(zend_object *object) +static void async_future_state_object_free(zend_object *object) { async_future_state_t *state = ASYNC_FUTURE_STATE_FROM_OBJ(object); - - zval_ptr_dtor(&state->result); - - if (state->exception != NULL) { - OBJ_RELEASE(state->exception); + + zend_future_t *future = (zend_future_t *)state->event; + state->event = NULL; + + if (future != NULL) { + future->event.dispose(&future->event); } - + zend_object_std_dtor(&state->std); } @@ -149,25 +160,26 @@ static void async_future_state_object_destroy(zend_object *object) static zend_object *async_future_object_create(zend_class_entry *ce) { - async_future_t *future = emalloc(sizeof(async_future_t)); - + async_future_t *future = zend_object_alloc(sizeof(async_future_t), ce); + future->event = NULL; + zend_object_std_init(&future->std, ce); object_properties_init(&future->std, ce); - - future->std.handlers = &async_future_handlers; - future->state = NULL; - + return &future->std; } -static void async_future_object_destroy(zend_object *object) +static void async_future_object_free(zend_object *object) { async_future_t *future = ASYNC_FUTURE_FROM_OBJ(object); - - if (future->state != NULL) { - OBJ_RELEASE(&future->state->std); + + zend_future_t *zend_future = (zend_future_t *)future->event; + future->event = NULL; + + if (zend_future != NULL) { + zend_future->event.dispose(&zend_future->event); } - + zend_object_std_dtor(&future->std); } @@ -191,20 +203,25 @@ STATE_METHOD(complete) ZEND_PARSE_PARAMETERS_END(); async_future_state_t *state = THIS_FUTURE_STATE; - - if (state->completed) { - async_throw_error("FutureState is already completed"); + + if (state->event == NULL) { + async_throw_error("FutureState is already destroyed"); RETURN_THROWS(); } - - ZVAL_COPY(&state->result, result); - state->completed = true; - - /* Mark event as closed (completed) */ - ZEND_ASYNC_EVENT_SET_CLOSED(&state->event); - - /* Notify all callbacks */ - zend_async_callbacks_notify(&state->event, &state->result, NULL, false); + + zend_future_t *future = (zend_future_t *)state->event; + + if (ZEND_FUTURE_IS_COMPLETED(future)) { + if (future->completed_filename != NULL) { + async_throw_error("FutureState is already completed at %s:%d", future->completed_filename, future->completed_lineno); + } else { + async_throw_error("FutureState is already completed at Unknown:0"); + } + + RETURN_THROWS(); + } + + ZEND_FUTURE_COMPLETE(future, result); } STATE_METHOD(error) @@ -217,20 +234,24 @@ STATE_METHOD(error) async_future_state_t *state = THIS_FUTURE_STATE; - if (state->completed) { - async_throw_error("FutureState is already completed"); + if (state->event == NULL) { + async_throw_error("FutureState is already destroyed"); RETURN_THROWS(); } - - state->exception = Z_OBJ_P(throwable); - GC_ADDREF(state->exception); - state->completed = true; - - /* Mark event as closed (completed) */ - ZEND_ASYNC_EVENT_SET_CLOSED(&state->event); - - /* Notify all callbacks with exception */ - zend_async_callbacks_notify(&state->event, NULL, state->exception, false); + + zend_future_t *future = (zend_future_t *)state->event; + + if (ZEND_FUTURE_IS_COMPLETED(future)) { + if (future->completed_filename != NULL) { + async_throw_error("FutureState is already completed at %s:%d", future->completed_filename, future->completed_lineno); + } else { + async_throw_error("FutureState is already completed at Unknown:0"); + } + + RETURN_THROWS(); + } + + ZEND_FUTURE_REJECT(future, Z_OBJ_P(throwable)); } STATE_METHOD(isComplete) @@ -238,8 +259,9 @@ STATE_METHOD(isComplete) ZEND_PARSE_PARAMETERS_NONE(); async_future_state_t *state = THIS_FUTURE_STATE; + zend_future_t *future = (zend_future_t *)state->event; - RETURN_BOOL(state->completed); + RETURN_BOOL(ZEND_FUTURE_IS_COMPLETED(future)); } STATE_METHOD(ignore) @@ -247,32 +269,18 @@ STATE_METHOD(ignore) ZEND_PARSE_PARAMETERS_NONE(); async_future_state_t *state = THIS_FUTURE_STATE; + zend_future_t *future = (zend_future_t *)state->event; - state->ignored = true; + ZEND_FUTURE_SET_IGNORED(future); } -STATE_METHOD(getInfo) +STATE_METHOD(getAwaitingInfo) { ZEND_PARSE_PARAMETERS_NONE(); async_future_state_t *state = THIS_FUTURE_STATE; - - smart_str info = {0}; - smart_str_appends(&info, "FutureState("); - smart_str_appends(&info, state->completed ? "completed" : "pending"); - - if (state->completed) { - if (state->exception != NULL) { - smart_str_appends(&info, ", error"); - } else { - smart_str_appends(&info, ", value"); - } - } - - smart_str_appendc(&info, ')'); - smart_str_0(&info); - - RETURN_STR(info.s); + + /* @TODO STATE_METHOD(getInfo) */ } /////////////////////////////////////////////////////////// @@ -280,31 +288,50 @@ STATE_METHOD(getInfo) /////////////////////////////////////////////////////////// #define THIS_FUTURE ((async_future_t *) ASYNC_FUTURE_FROM_OBJ(Z_OBJ_P(ZEND_THIS))) +#define THIS_ZEND_FUTURE (zend_future_t *) THIS_FUTURE->event + +METHOD(__construct) +{ + zval *state_obj; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(state_obj, async_ce_future_state) + ZEND_PARSE_PARAMETERS_END(); + + async_future_t *future = THIS_FUTURE; + async_future_state_t *state = ASYNC_FUTURE_STATE_FROM_OBJ(Z_OBJ_P(state_obj)); + + future->event = state->event; + ZEND_ASYNC_EVENT_ADD_REF(state->event); +} METHOD(complete) { - zval *value = NULL; + zval *result = NULL; ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL - Z_PARAM_ZVAL(value) + Z_PARAM_ZVAL(result) ZEND_PARSE_PARAMETERS_END(); - - /* Create new FutureState */ - async_future_state_t *state = async_future_state_create(); - - /* Complete it immediately */ - if (value != NULL) { - ZVAL_COPY(&state->result, value); - } else { - ZVAL_NULL(&state->result); + + zend_future_t *future = THIS_ZEND_FUTURE; + + if (future == NULL) { + async_throw_error("Future is already destroyed"); + RETURN_THROWS(); } - state->completed = true; - - /* Create Future wrapping the state */ - async_future_t *future = async_future_wrap_state(state); - - RETURN_OBJ_COPY(&future->std); + + if (ZEND_FUTURE_IS_COMPLETED(future)) { + if (future->completed_filename != NULL) { + async_throw_error("Future is already completed at %s:%d", future->completed_filename, future->completed_lineno); + } else { + async_throw_error("Future is already completed at Unknown:0"); + } + + RETURN_THROWS(); + } + + ZEND_FUTURE_COMPLETE(future, result); } METHOD(error) @@ -314,125 +341,139 @@ METHOD(error) ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_OBJECT_OF_CLASS(throwable, zend_ce_throwable) ZEND_PARSE_PARAMETERS_END(); - - /* Create new FutureState */ - async_future_state_t *state = async_future_state_create(); - - /* Set error immediately */ - state->exception = Z_OBJ_P(throwable); - GC_ADDREF(state->exception); - state->completed = true; - - /* Create Future wrapping the state */ - async_future_t *future = async_future_wrap_state(state); - - RETURN_OBJ_COPY(&future->std); -} -METHOD(__construct) -{ - zval *state_obj; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT_OF_CLASS(state_obj, async_ce_future_state) - ZEND_PARSE_PARAMETERS_END(); - - async_future_t *future = THIS_FUTURE; - async_future_state_t *state = ASYNC_FUTURE_STATE_FROM_OBJ(Z_OBJ_P(state_obj)); - - future->state = state; - GC_ADDREF(&state->std); + zend_future_t *future = THIS_ZEND_FUTURE; + + if (future == NULL) { + async_throw_error("Future is already destroyed"); + RETURN_THROWS(); + } + + if (ZEND_FUTURE_IS_COMPLETED(future)) { + if (future->completed_filename != NULL) { + async_throw_error("Future is already completed at %s:%d", future->completed_filename, future->completed_lineno); + } else { + async_throw_error("Future is already completed at Unknown:0"); + } + + RETURN_THROWS(); + } + + ZEND_FUTURE_REJECT(future, Z_OBJ_P(throwable)); } METHOD(isComplete) { ZEND_PARSE_PARAMETERS_NONE(); - async_future_t *future = THIS_FUTURE; - - if (future->state == NULL) { - RETURN_FALSE; - } - - RETURN_BOOL(future->state->completed); + zend_future_t *future = THIS_ZEND_FUTURE; + + RETURN_BOOL(ZEND_FUTURE_IS_COMPLETED(future)); } METHOD(ignore) { ZEND_PARSE_PARAMETERS_NONE(); - - async_future_t *future = THIS_FUTURE; - - if (future->state != NULL) { - future->state->ignored = true; - } - + + + RETURN_ZVAL(ZEND_THIS, 1, 0); } METHOD(await) { - zval *cancellation = NULL; + zend_object * cancellation = NULL; ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL - Z_PARAM_OBJECT_OF_CLASS_OR_NULL(cancellation, async_ce_awaitable) + Z_PARAM_OBJ_OF_CLASS_OR_NULL(cancellation, async_ce_awaitable) ZEND_PARSE_PARAMETERS_END(); + + SCHEDULER_LAUNCH; + + zend_coroutine_t *coroutine = ZEND_ASYNC_CURRENT_COROUTINE; + + if (UNEXPECTED(coroutine == NULL)) { + RETURN_NULL(); + } + + zend_future_t *future = THIS_ZEND_FUTURE; - async_future_t *future = THIS_FUTURE; - - if (future->state == NULL) { + if (future == NULL) { async_throw_error("Future has no state"); RETURN_THROWS(); } - - async_future_state_t *state = future->state; - - /* If already completed, return result immediately */ - if (state->completed) { - if (state->exception != NULL) { - zval exception_zv; - ZVAL_OBJ(&exception_zv, state->exception); - zend_throw_exception_object(&exception_zv); - RETURN_THROWS(); + + zend_async_event_t *event = &future->event; + + if (ZEND_ASYNC_EVENT_IS_CLOSED(event)) { + + if (ZEND_ASYNC_EVENT_EXTRACT_RESULT(event, return_value)) { + return; } - - RETURN_ZVAL(&state->result, 1, 0); + + RETURN_NULL(); } - - /* Get current coroutine */ - zend_coroutine_t *coroutine = ZEND_ASYNC_CURRENT_COROUTINE; - if (coroutine == NULL) { - async_throw_error("Cannot await outside of coroutine context"); + + zend_async_event_t *cancellation_event = cancellation != NULL ? ZEND_ASYNC_OBJECT_TO_EVENT(cancellation) : NULL; + + // If the cancellation event is already resolved, we can return exception immediately. + if (cancellation_event != NULL && ZEND_ASYNC_EVENT_IS_CLOSED(cancellation_event)) { + if (ZEND_ASYNC_EVENT_EXTRACT_RESULT(cancellation_event, return_value)) { + return; + } + + async_throw_cancellation("Future awaiting has been cancelled"); RETURN_THROWS(); } - - /* TODO: Handle cancellation if provided */ - if (cancellation != NULL) { - /* Currently not implemented */ + + zend_async_waker_new(coroutine); + + if (UNEXPECTED(EG(exception) != NULL)) { + RETURN_THROWS(); } - - /* Create callback for when future completes */ - zend_coroutine_event_callback_t *callback = emalloc(sizeof(zend_coroutine_event_callback_t)); - callback->base.ref_count = 1; - callback->base.callback = zend_async_waker_callback_resolve; - callback->base.dispose = NULL; - callback->coroutine = coroutine; - - /* Suspend coroutine until future completes */ - zend_async_resume_when(coroutine, &state->event, false, NULL, callback); - - /* Execution resumes here when future completes */ - - /* Check result after await */ - if (state->exception != NULL) { - zval exception_zv; - ZVAL_OBJ(&exception_zv, state->exception); - zend_throw_exception_object(&exception_zv); + + zend_async_resume_when( + coroutine, + event, + false, + zend_async_waker_callback_resolve, + NULL + ); + + if (UNEXPECTED(EG(exception) != NULL)) { RETURN_THROWS(); } - - RETURN_ZVAL(&state->result, 1, 0); + + if (cancellation_event != NULL) { + zend_async_resume_when( + coroutine, + cancellation_event, + false, + zend_async_waker_callback_cancel, + NULL + ); + + if (UNEXPECTED(EG(exception) != NULL)) { + RETURN_THROWS(); + } + } + + ZEND_ASYNC_SUSPEND(); + + if (UNEXPECTED(EG(exception) != NULL)) { + RETURN_THROWS(); + } + + ZEND_ASSERT(coroutine->waker != NULL && "coroutine->waker must not be NULL"); + + if (Z_TYPE(coroutine->waker->result) == IS_UNDEF) { + ZVAL_NULL(return_value); + } else { + ZVAL_COPY(return_value, &coroutine->waker->result); + } + + zend_async_waker_destroy(coroutine); } /* TODO: Implement map, catch, finally methods */ @@ -464,64 +505,10 @@ async_future_state_t *async_future_state_create(void) return ASYNC_FUTURE_STATE_FROM_OBJ(obj); } -async_future_t *async_future_wrap_state(async_future_state_t *state) -{ - zend_object *obj = async_future_object_create(async_ce_future); - async_future_t *future = ASYNC_FUTURE_FROM_OBJ(obj); - - future->state = state; - GC_ADDREF(&state->std); - - return future; -} - /////////////////////////////////////////////////////////// /// API function implementations /////////////////////////////////////////////////////////// -zend_future_t *async_future_create(void) -{ - async_future_state_t *state = async_future_state_create(); - return (zend_future_t *)state; -} - -void async_future_complete(zend_future_t *future, zval *value) -{ - async_future_state_t *state = (async_future_state_t *)future; - - if (state->completed) { - return; - } - - ZVAL_COPY(&state->result, value); - state->completed = true; - - /* Mark event as closed (completed) */ - ZEND_ASYNC_EVENT_SET_CLOSED(&state->event); - - /* Notify all callbacks */ - zend_async_callbacks_notify(&state->event, &state->result, NULL, false); -} - -void async_future_error(zend_future_t *future, zend_object *exception) -{ - async_future_state_t *state = (async_future_state_t *)future; - - if (state->completed) { - return; - } - - state->exception = exception; - GC_ADDREF(exception); - state->completed = true; - - /* Mark event as closed (completed) */ - ZEND_ASYNC_EVENT_SET_CLOSED(&state->event); - - /* Notify all callbacks with exception */ - zend_async_callbacks_notify(&state->event, NULL, state->exception, false); -} - /////////////////////////////////////////////////////////// /// Class registration /////////////////////////////////////////////////////////// @@ -534,7 +521,7 @@ void async_register_future_ce(void) memcpy(&async_future_state_handlers, &std_object_handlers, sizeof(zend_object_handlers)); async_future_state_handlers.offset = XtOffsetOf(async_future_state_t, std); - async_future_state_handlers.free_obj = async_future_state_object_destroy; + async_future_state_handlers.free_obj = async_future_state_object_free; /* Register Future class using generated registration */ async_ce_future = register_class_Async_Future(); @@ -542,7 +529,7 @@ void async_register_future_ce(void) memcpy(&async_future_handlers, &std_object_handlers, sizeof(zend_object_handlers)); async_future_handlers.offset = XtOffsetOf(async_future_t, std); - async_future_handlers.free_obj = async_future_object_destroy; + async_future_handlers.free_obj = async_future_object_free; /* Make Future implement Awaitable */ zend_class_implements(async_ce_future, 1, async_ce_awaitable); diff --git a/future.h b/future.h index ccb0180..726e319 100644 --- a/future.h +++ b/future.h @@ -55,11 +55,7 @@ void async_register_future_ce(void); /* API function implementations */ zend_future_t *async_future_create(void); -void async_future_complete(zend_future_t *future, zval *value); -void async_future_error(zend_future_t *future, zend_object *exception); - /* Internal helper functions */ async_future_state_t *async_future_state_create(void); -async_future_t *async_future_wrap_state(async_future_state_t *state); #endif /* FUTURE_H */ \ No newline at end of file