diff --git a/async.c b/async.c index 9fafc43..1de64b1 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" @@ -236,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; } @@ -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..7a803b0 --- /dev/null +++ b/future.c @@ -0,0 +1,536 @@ +/* ++----------------------------------------------------------------------+ + | 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_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; + +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_free(zend_object *object) +{ + async_future_state_t *state = ASYNC_FUTURE_STATE_FROM_OBJ(object); + + 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); +} + +/////////////////////////////////////////////////////////// +/// Future object lifecycle +/////////////////////////////////////////////////////////// + +static zend_object *async_future_object_create(zend_class_entry *ce) +{ + 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); + + return &future->std; +} + +static void async_future_object_free(zend_object *object) +{ + async_future_t *future = ASYNC_FUTURE_FROM_OBJ(object); + + 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); +} + +/////////////////////////////////////////////////////////// +/// 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->event == NULL) { + async_throw_error("FutureState is already destroyed"); + RETURN_THROWS(); + } + + 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) +{ + 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->event == NULL) { + async_throw_error("FutureState is already destroyed"); + RETURN_THROWS(); + } + + 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) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + async_future_state_t *state = THIS_FUTURE_STATE; + zend_future_t *future = (zend_future_t *)state->event; + + RETURN_BOOL(ZEND_FUTURE_IS_COMPLETED(future)); +} + +STATE_METHOD(ignore) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + async_future_state_t *state = THIS_FUTURE_STATE; + zend_future_t *future = (zend_future_t *)state->event; + + ZEND_FUTURE_SET_IGNORED(future); +} + +STATE_METHOD(getAwaitingInfo) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + async_future_state_t *state = THIS_FUTURE_STATE; + + /* @TODO STATE_METHOD(getInfo) */ +} + +/////////////////////////////////////////////////////////// +/// Future methods +/////////////////////////////////////////////////////////// + +#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 *result = NULL; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(result) + ZEND_PARSE_PARAMETERS_END(); + + 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_COMPLETE(future, result); +} + +METHOD(error) +{ + zval *throwable; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(throwable, zend_ce_throwable) + ZEND_PARSE_PARAMETERS_END(); + + 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(); + + zend_future_t *future = THIS_ZEND_FUTURE; + + RETURN_BOOL(ZEND_FUTURE_IS_COMPLETED(future)); +} + +METHOD(ignore) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + + + RETURN_ZVAL(ZEND_THIS, 1, 0); +} + +METHOD(await) +{ + zend_object * cancellation = NULL; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + 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; + + if (future == NULL) { + async_throw_error("Future has no state"); + 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_NULL(); + } + + 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(); + } + + zend_async_waker_new(coroutine); + + if (UNEXPECTED(EG(exception) != NULL)) { + RETURN_THROWS(); + } + + zend_async_resume_when( + coroutine, + event, + false, + zend_async_waker_callback_resolve, + NULL + ); + + if (UNEXPECTED(EG(exception) != NULL)) { + RETURN_THROWS(); + } + + 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 */ +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); +} + +/////////////////////////////////////////////////////////// +/// API function implementations +/////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////// +/// 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_free; + + /* 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_free; + + /* 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..726e319 --- /dev/null +++ b/future.h @@ -0,0 +1,61 @@ +/* ++----------------------------------------------------------------------+ + | 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); +/* Internal helper functions */ +async_future_state_t *async_future_state_create(void); + +#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"