diff --git a/CHANGELOG.md b/CHANGELOG.md index f2a76b6..c25745e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Optimized `network_async_accept_incoming` to try `accept()` before waiting - Enhanced stream_select functionality with event-driven architecture - Improved blocking operation handling with boolean return values +- **TrueAsync API Performance**: Optimized execution paths by replacing expensive `EG(exception)` checks with direct `bool` return values across all async functions - Upgrade `LibUV` to version `1.45` due to a timer bug that causes the application to hang ## [0.3.0] - 2025-07-16 diff --git a/async.c b/async.c index 4947785..1945d56 100644 --- a/async.c +++ b/async.c @@ -54,8 +54,7 @@ static zend_object *async_timeout_create(zend_ulong ms, bool is_periodic); #define SCHEDULER_LAUNCH \ if (UNEXPECTED(ZEND_ASYNC_CURRENT_COROUTINE == NULL)) { \ - async_scheduler_launch(); \ - if (UNEXPECTED(EG(exception) != NULL)) { \ + if (!async_scheduler_launch()) { \ RETURN_THROWS(); \ } \ } @@ -79,7 +78,7 @@ PHP_FUNCTION(Async_spawn) async_coroutine_t *coroutine = (async_coroutine_t *) ZEND_ASYNC_SPAWN(); - if (UNEXPECTED(EG(exception))) { + if (UNEXPECTED(coroutine == NULL)) { return; } @@ -275,9 +274,7 @@ PHP_FUNCTION(Async_await) } } - ZEND_ASYNC_SUSPEND(); - - if (UNEXPECTED(EG(exception) != NULL)) { + if (!ZEND_ASYNC_SUSPEND()) { zend_async_waker_clean(coroutine); RETURN_THROWS(); } diff --git a/async_API.c b/async_API.c index 6dbf418..2c4ef4a 100644 --- a/async_API.c +++ b/async_API.c @@ -58,26 +58,26 @@ zend_coroutine_t *spawn(zend_async_scope_t *scope, zend_object *scope_provider, async_throw_error("Cannot spawn a coroutine when async is disabled"); return NULL; } else if (UNEXPECTED(ZEND_ASYNC_IS_READY)) { - async_scheduler_launch(); - if (UNEXPECTED(EG(exception) != NULL)) { + if (UNEXPECTED(!async_scheduler_launch())) { return NULL; } } if (scope == NULL && scope_provider != NULL) { scope = async_provide_scope(scope_provider); - - if (UNEXPECTED(EG(exception) != NULL)) { + if (UNEXPECTED(EG(exception))) { return NULL; } } if (scope == NULL) { - if (UNEXPECTED(ZEND_ASYNC_CURRENT_SCOPE == NULL && ZEND_ASYNC_MAIN_SCOPE == NULL)) { - ZEND_ASYNC_MAIN_SCOPE = ZEND_ASYNC_NEW_SCOPE(NULL); + zend_async_scope_t **main_scope_ptr = &ZEND_ASYNC_MAIN_SCOPE; - if (UNEXPECTED(EG(exception))) { + if (UNEXPECTED(ZEND_ASYNC_CURRENT_SCOPE == NULL && *main_scope_ptr == NULL)) { + *main_scope_ptr = ZEND_ASYNC_NEW_SCOPE(NULL); + + if (UNEXPECTED(*main_scope_ptr == NULL)) { return NULL; } } @@ -85,7 +85,7 @@ zend_coroutine_t *spawn(zend_async_scope_t *scope, zend_object *scope_provider, if (EXPECTED(ZEND_ASYNC_CURRENT_SCOPE != NULL)) { scope = ZEND_ASYNC_CURRENT_SCOPE; } else { - scope = ZEND_ASYNC_MAIN_SCOPE; + scope = *main_scope_ptr; } } @@ -100,7 +100,7 @@ zend_coroutine_t *spawn(zend_async_scope_t *scope, zend_object *scope_provider, } async_coroutine_t *coroutine = (async_coroutine_t *) async_new_coroutine(scope); - if (UNEXPECTED(EG(exception))) { + if (UNEXPECTED(coroutine == NULL)) { return NULL; } @@ -108,13 +108,12 @@ zend_coroutine_t *spawn(zend_async_scope_t *scope, zend_object *scope_provider, zval options; ZVAL_UNDEF(&options); - scope->before_coroutine_enqueue(&coroutine->coroutine, scope, &options); - zval_dtor(&options); - - if (UNEXPECTED(EG(exception))) { + if (!scope->before_coroutine_enqueue(&coroutine->coroutine, scope, &options)) { + zval_dtor(&options); coroutine->coroutine.event.dispose(&coroutine->coroutine.event); return NULL; } + zval_dtor(&options); const bool is_spawn_strategy = scope_provider != NULL && instanceof_function(scope_provider->ce, async_ce_spawn_strategy); @@ -141,7 +140,7 @@ zend_coroutine_t *spawn(zend_async_scope_t *scope, zend_object *scope_provider, } zend_async_waker_t *waker = zend_async_waker_new(&coroutine->coroutine); - if (UNEXPECTED(EG(exception))) { + if (UNEXPECTED(waker == NULL)) { coroutine->coroutine.event.dispose(&coroutine->coroutine.event); return NULL; } @@ -202,7 +201,7 @@ zend_coroutine_t *spawn(zend_async_scope_t *scope, zend_object *scope_provider, return &coroutine->coroutine; } -static void engine_shutdown(void) +static bool engine_shutdown(void) { ZEND_ASYNC_REACTOR_SHUTDOWN(); @@ -218,6 +217,7 @@ static void engine_shutdown(void) } // async_host_name_list_dtor(); + return true; } zend_array *get_coroutines(void) @@ -225,18 +225,19 @@ zend_array *get_coroutines(void) return &ASYNC_G(coroutines); } -void add_microtask(zend_async_microtask_t *microtask) +bool add_microtask(zend_async_microtask_t *microtask) { if (microtask->is_cancelled) { - return; + return true; } if (UNEXPECTED(circular_buffer_push(&ASYNC_G(microtasks), µtask, true) == FAILURE)) { async_throw_error("Failed to enqueue microtask"); - return; + return false; } microtask->ref_count++; + return true; } zend_array *get_awaiting_info(zend_coroutine_t *coroutine) @@ -351,7 +352,9 @@ static void async_waiting_callback(zend_async_event_t *event, // We remove the callback because we treat all events // as FUTURE-type objects, where the trigger can be activated only once. ZEND_ASYNC_EVENT_CALLBACK_ADD_REF(callback); - event->del_callback(event, callback); + if (!event->del_callback(event, callback)) { + // Optimized: ignore del_callback failure in callback context + } ZEND_ASYNC_EVENT_CALLBACK_DEC_REF(callback); if (exception != NULL) { @@ -446,7 +449,9 @@ static void async_waiting_cancellation_callback(zend_async_event_t *event, await_context->resolved_count++; ZEND_ASYNC_EVENT_CALLBACK_ADD_REF(callback); - event->del_callback(event, callback); + if (!event->del_callback(event, callback)) { + // Optimized: ignore del_callback failure in callback context + } ZEND_ASYNC_EVENT_CALLBACK_DEC_REF(callback); if (exception != NULL) { @@ -1009,7 +1014,7 @@ void async_await_futures(zval *iterable, // which ensures that all child tasks are stopped if the main task is cancelled. zend_async_scope_t *scope = ZEND_ASYNC_NEW_SCOPE(ZEND_ASYNC_CURRENT_SCOPE); - if (UNEXPECTED(scope == NULL || EG(exception))) { + if (UNEXPECTED(scope == NULL)) { zend_iterator_dtor(zend_iterator); await_context->dtor(await_context); return; @@ -1017,7 +1022,7 @@ void async_await_futures(zval *iterable, zend_coroutine_t *iterator_coroutine = ZEND_ASYNC_SPAWN_WITH_SCOPE_EX(scope, ZEND_COROUTINE_NORMAL); - if (UNEXPECTED(iterator_coroutine == NULL || EG(exception))) { + if (UNEXPECTED(iterator_coroutine == NULL)) { zend_iterator_dtor(zend_iterator); await_context->dtor(await_context); scope->try_to_dispose(scope); diff --git a/coroutine.c b/coroutine.c index 8d00cc3..c12f108 100644 --- a/coroutine.c +++ b/coroutine.c @@ -45,16 +45,16 @@ static void coroutine_call_finally_handlers(async_coroutine_t *coroutine); static void finally_context_dtor(finally_handlers_context_t *context); // Forward declarations for event system -static void coroutine_event_start(zend_async_event_t *event); -static void coroutine_event_stop(zend_async_event_t *event); -static void coroutine_add_callback(zend_async_event_t *event, zend_async_event_callback_t *callback); -static void coroutine_del_callback(zend_async_event_t *event, zend_async_event_callback_t *callback); +static bool coroutine_event_start(zend_async_event_t *event); +static bool coroutine_event_stop(zend_async_event_t *event); +static bool coroutine_add_callback(zend_async_event_t *event, zend_async_event_callback_t *callback); +static bool coroutine_del_callback(zend_async_event_t *event, zend_async_event_callback_t *callback); static bool coroutine_replay(zend_async_event_t *event, zend_async_event_callback_t *callback, zval *result, zend_object **exception); static zend_string *coroutine_info(zend_async_event_t *event); -static void coroutine_dispose(zend_async_event_t *event); +static bool coroutine_dispose(zend_async_event_t *event); /////////////////////////////////////////////////////////// /// 2. Object Lifecycle Management @@ -645,29 +645,28 @@ void async_coroutine_finalize(async_coroutine_t *coroutine) * * @param from_main For main coroutine */ -void async_coroutine_suspend(const bool from_main) +bool async_coroutine_suspend(const bool from_main) { if (UNEXPECTED(from_main)) { // If the Scheduler was never used, it means no coroutines were created, // so execution can be finished without doing anything. if (circular_buffer_is_empty(&ASYNC_G(microtasks)) && zend_hash_num_elements(&ASYNC_G(coroutines)) == 0) { - return; + return true; } - async_scheduler_main_coroutine_suspend(); - return; + return async_scheduler_main_coroutine_suspend(); } - async_scheduler_coroutine_suspend(); + return async_scheduler_coroutine_suspend(); } -void async_coroutine_resume(zend_coroutine_t *coroutine, zend_object *error, const bool transfer_error) +bool async_coroutine_resume(zend_coroutine_t *coroutine, zend_object *error, const bool transfer_error) { zend_async_waker_t *waker = coroutine->waker; if (UNEXPECTED(waker == NULL || waker->status == ZEND_ASYNC_WAKER_NO_STATUS)) { async_throw_error("Cannot resume a coroutine that has not been suspended"); - return; + return false; } if (error != NULL) { @@ -695,7 +694,7 @@ void async_coroutine_resume(zend_coroutine_t *coroutine, zend_object *error, con } if (UNEXPECTED(waker->status == ZEND_ASYNC_WAKER_QUEUED)) { - return; + return true; } const bool in_scheduler_context = ZEND_ASYNC_SCHEDULER_CONTEXT; @@ -707,12 +706,12 @@ void async_coroutine_resume(zend_coroutine_t *coroutine, zend_object *error, con // we will execute it immediately! if (UNEXPECTED(in_scheduler_context && coroutine == ZEND_ASYNC_CURRENT_COROUTINE)) { waker->status = ZEND_ASYNC_WAKER_RESULT; - return; + return true; } if (UNEXPECTED(circular_buffer_push_ptr_with_resize(&ASYNC_G(coroutine_queue), coroutine)) == FAILURE) { async_throw_error("Failed to enqueue coroutine"); - return; + return false; } waker->status = ZEND_ASYNC_WAKER_QUEUED; @@ -721,9 +720,11 @@ void async_coroutine_resume(zend_coroutine_t *coroutine, zend_object *error, con if (in_scheduler_context) { circular_buffer_push_ptr_with_resize(&ASYNC_G(resumed_coroutines), coroutine); } + + return true; } -void async_coroutine_cancel(zend_coroutine_t *zend_coroutine, +bool async_coroutine_cancel(zend_coroutine_t *zend_coroutine, zend_object *error, bool transfer_error, const bool is_safely) @@ -734,7 +735,7 @@ void async_coroutine_cancel(zend_coroutine_t *zend_coroutine, OBJ_RELEASE(error); } - return; + return true; } // An attempt to cancel a coroutine that is currently running. @@ -757,7 +758,7 @@ void async_coroutine_cancel(zend_coroutine_t *zend_coroutine, zend_coroutine->exception = async_new_exception(async_ce_cancellation_exception, "Coroutine cancelled"); } - return; + return true; } zend_async_waker_t *waker = zend_async_waker_define(zend_coroutine); @@ -768,7 +769,7 @@ void async_coroutine_cancel(zend_coroutine_t *zend_coroutine, error = async_new_exception(async_ce_cancellation_exception, "Coroutine cancelled"); transfer_error = true; if (UNEXPECTED(EG(exception))) { - return; + return false; } } @@ -786,7 +787,7 @@ void async_coroutine_cancel(zend_coroutine_t *zend_coroutine, OBJ_RELEASE(error); } - return; + return true; } bool was_cancelled = ZEND_COROUTINE_IS_CANCELLED(zend_coroutine); @@ -815,8 +816,7 @@ void async_coroutine_cancel(zend_coroutine_t *zend_coroutine, // In any other case, the cancellation exception overrides the existing exception. // ZEND_ASYNC_WAKER_APPLY_CANCELLATION(waker, error, transfer_error); - async_scheduler_coroutine_enqueue(zend_coroutine); - return; + return async_scheduler_coroutine_enqueue(zend_coroutine); } // In safely mode, we don't forcibly terminate the coroutine, @@ -827,7 +827,8 @@ void async_coroutine_cancel(zend_coroutine_t *zend_coroutine, if (transfer_error && error != NULL) { OBJ_RELEASE(error); } - return; + + return true; } if (was_cancelled && waker->error != NULL && @@ -839,29 +840,32 @@ void async_coroutine_cancel(zend_coroutine_t *zend_coroutine, ZEND_ASYNC_WAKER_APPLY_CANCELLATION(waker, error, transfer_error); } - async_scheduler_coroutine_enqueue(zend_coroutine); + return async_scheduler_coroutine_enqueue(zend_coroutine); } /////////////////////////////////////////////////////////// /// 4. Event System Interface /////////////////////////////////////////////////////////// -static void coroutine_event_start(zend_async_event_t *event) +static bool coroutine_event_start(zend_async_event_t *event) { + return true; } -static void coroutine_event_stop(zend_async_event_t *event) +static bool coroutine_event_stop(zend_async_event_t *event) { + // Empty implementation - coroutines don't need explicit stop + return true; } -static void coroutine_add_callback(zend_async_event_t *event, zend_async_event_callback_t *callback) +static bool coroutine_add_callback(zend_async_event_t *event, zend_async_event_callback_t *callback) { - zend_async_callbacks_push(event, callback); + return zend_async_callbacks_push(event, callback); } -static void coroutine_del_callback(zend_async_event_t *event, zend_async_event_callback_t *callback) +static bool coroutine_del_callback(zend_async_event_t *event, zend_async_event_callback_t *callback) { - zend_async_callbacks_remove(event, callback); + return zend_async_callbacks_remove(event, callback); } /** @@ -930,10 +934,11 @@ static zend_string *coroutine_info(zend_async_event_t *event) } } -static void coroutine_dispose(zend_async_event_t *event) +static bool coroutine_dispose(zend_async_event_t *event) { async_coroutine_t *coroutine = (async_coroutine_t *) event; OBJ_RELEASE(&coroutine->std); + return true; } /////////////////////////////////////////////////////////// diff --git a/coroutine.h b/coroutine.h index b81711a..8eacb62 100644 --- a/coroutine.h +++ b/coroutine.h @@ -88,9 +88,9 @@ zend_coroutine_t *async_new_coroutine(zend_async_scope_t *scope); void async_coroutine_cleanup(zend_fiber_context *context); void async_coroutine_finalize(async_coroutine_t *coroutine); void async_coroutine_finalize_from_scheduler(async_coroutine_t *coroutine); -void async_coroutine_suspend(const bool from_main); -void async_coroutine_resume(zend_coroutine_t *coroutine, zend_object *error, const bool transfer_error); -void async_coroutine_cancel(zend_coroutine_t *zend_coroutine, +bool async_coroutine_suspend(const bool from_main); +bool async_coroutine_resume(zend_coroutine_t *coroutine, zend_object *error, const bool transfer_error); +bool async_coroutine_cancel(zend_coroutine_t *zend_coroutine, zend_object *error, bool transfer_error, const bool is_safely); diff --git a/libuv_reactor.c b/libuv_reactor.c index aecbfaa..c1bb093 100644 --- a/libuv_reactor.c +++ b/libuv_reactor.c @@ -76,11 +76,11 @@ static void libuv_cleanup_process_events(void); #define EVENT_START_PROLOGUE(event) \ if (UNEXPECTED(ZEND_ASYNC_EVENT_IS_CLOSED(event))) { \ - return; \ + return true; \ } \ if (event->loop_ref_count > 0) { \ event->loop_ref_count++; \ - return; \ + return true; \ } #define EVENT_STOP_PROLOGUE(event) \ @@ -89,12 +89,12 @@ static void libuv_cleanup_process_events(void); if (UNEXPECTED(ZEND_ASYNC_EVENT_IS_CLOSED(event))) { \ event->loop_ref_count = 0; \ } else { \ - return; \ + return true; \ } \ } \ if (UNEXPECTED(ZEND_ASYNC_EVENT_IS_CLOSED(event))) { \ event->loop_ref_count = 0; \ - return; \ + return true; \ } static zend_always_inline void close_event(zend_async_event_t *event) @@ -107,26 +107,27 @@ static zend_always_inline void close_event(zend_async_event_t *event) } /* {{{ libuv_reactor_startup */ -void libuv_reactor_startup(void) +bool libuv_reactor_startup(void) { if (ASYNC_G(reactor_started)) { - return; + return true; } if (ZEND_ASYNC_IS_OFF) { async_throw_error(ASYNC_OF_EXCEPTION_MESSAGE); - return; + return false; } const int result = uv_loop_init(UVLOOP); if (result != 0) { async_throw_error("Failed to initialize loop: %s", uv_strerror(result)); - return; + return false; } uv_loop_set_data(UVLOOP, ASYNC_GLOBALS); ASYNC_G(reactor_started) = true; + return true; } /* }}} */ @@ -140,7 +141,7 @@ static void libuv_reactor_stop_with_exception(void) /* }}} */ /* {{{ libuv_reactor_shutdown */ -void libuv_reactor_shutdown(void) +bool libuv_reactor_shutdown(void) { if (EXPECTED(ASYNC_G(reactor_started))) { @@ -157,6 +158,7 @@ void libuv_reactor_shutdown(void) uv_loop_close(UVLOOP); ASYNC_G(reactor_started) = false; } + return true; } /* }}} */ @@ -226,17 +228,17 @@ static void libuv_close_poll_handle_cb(uv_handle_t *handle) /* }}} */ /* {{{ libuv_add_callback */ -static void libuv_add_callback(zend_async_event_t *event, zend_async_event_callback_t *callback) +static bool libuv_add_callback(zend_async_event_t *event, zend_async_event_callback_t *callback) { - zend_async_callbacks_push(event, callback); + return zend_async_callbacks_push(event, callback); } /* }}} */ /* {{{ libuv_remove_callback */ -static void libuv_remove_callback(zend_async_event_t *event, zend_async_event_callback_t *callback) +static bool libuv_remove_callback(zend_async_event_t *event, zend_async_event_callback_t *callback) { - zend_async_callbacks_remove(event, callback); + return zend_async_callbacks_remove(event, callback); } /* }}} */ @@ -291,7 +293,7 @@ static void on_poll_event(uv_poll_t *handle, int status, int events) /* }}} */ /* {{{ libuv_poll_start */ -static void libuv_poll_start(zend_async_event_t *event) +static bool libuv_poll_start(zend_async_event_t *event) { EVENT_START_PROLOGUE(event); @@ -301,17 +303,18 @@ static void libuv_poll_start(zend_async_event_t *event) if (error < 0) { async_throw_error("Failed to start poll handle: %s", uv_strerror(error)); - return; + return false; } event->loop_ref_count++; ZEND_ASYNC_INCREASE_EVENT_COUNT; + return true; } /* }}} */ /* {{{ libuv_poll_stop */ -static void libuv_poll_stop(zend_async_event_t *event) +static bool libuv_poll_stop(zend_async_event_t *event) { EVENT_STOP_PROLOGUE(event); @@ -324,18 +327,20 @@ static void libuv_poll_stop(zend_async_event_t *event) if (error < 0) { async_throw_error("Failed to stop poll handle: %s", uv_strerror(error)); - return; + return false; } + + return true; } /* }}} */ /* {{{ libuv_poll_dispose */ -static void libuv_poll_dispose(zend_async_event_t *event) +static bool libuv_poll_dispose(zend_async_event_t *event) { if (ZEND_ASYNC_EVENT_REFCOUNT(event) > 1) { ZEND_ASYNC_EVENT_DEL_REF(event); - return; + return true; } if (event->loop_ref_count > 0) { @@ -355,6 +360,7 @@ static void libuv_poll_dispose(zend_async_event_t *event) /* Use poll-specific callback for poll events that may need descriptor cleanup */ uv_close((uv_handle_t *) &poll->uv_handle, libuv_close_poll_handle_cb); + return true; } /* }}} */ @@ -439,7 +445,7 @@ static zend_always_inline async_poll_event async_poll_aggregate_events(async_pol /* }}} */ /* {{{ libuv_poll_proxy_start */ -static void libuv_poll_proxy_start(zend_async_event_t *event) +static bool libuv_poll_proxy_start(zend_async_event_t *event) { EVENT_START_PROLOGUE(event); @@ -458,18 +464,19 @@ static void libuv_poll_proxy_start(zend_async_event_t *event) if (error < 0) { async_throw_error("Failed to update poll handle events: %s", uv_strerror(error)); - return; + return false; } } ZEND_ASYNC_INCREASE_EVENT_COUNT; event->loop_ref_count = 1; + return true; } /* }}} */ /* {{{ libuv_poll_proxy_stop */ -static void libuv_poll_proxy_stop(zend_async_event_t *event) +static bool libuv_poll_proxy_stop(zend_async_event_t *event) { EVENT_STOP_PROLOGUE(event); @@ -496,16 +503,17 @@ static void libuv_poll_proxy_stop(zend_async_event_t *event) event->loop_ref_count = 0; ZEND_ASYNC_DECREASE_EVENT_COUNT; + return true; } /* }}} */ /* {{{ libuv_poll_proxy_dispose */ -static void libuv_poll_proxy_dispose(zend_async_event_t *event) +static bool libuv_poll_proxy_dispose(zend_async_event_t *event) { if (ZEND_ASYNC_EVENT_REFCOUNT(event) > 1) { ZEND_ASYNC_EVENT_DEL_REF(event); - return; + return true; } zend_async_poll_proxy_t *proxy = (zend_async_poll_proxy_t *) event; @@ -522,6 +530,7 @@ static void libuv_poll_proxy_dispose(zend_async_event_t *event) ZEND_ASYNC_EVENT_RELEASE(&poll->event.base); pefree(proxy, 0); + return true; } /* }}} */ @@ -640,7 +649,7 @@ static void on_timer_event(uv_timer_t *handle) /* }}} */ /* {{{ libuv_timer_start */ -static void libuv_timer_start(zend_async_event_t *event) +static bool libuv_timer_start(zend_async_event_t *event) { EVENT_START_PROLOGUE(event); @@ -653,17 +662,18 @@ static void libuv_timer_start(zend_async_event_t *event) if (error < 0) { async_throw_error("Failed to start timer handle: %s", uv_strerror(error)); - return; + return false; } event->loop_ref_count++; ZEND_ASYNC_INCREASE_EVENT_COUNT; + return true; } /* }}} */ /* {{{ libuv_timer_stop */ -static void libuv_timer_stop(zend_async_event_t *event) +static bool libuv_timer_stop(zend_async_event_t *event) { EVENT_STOP_PROLOGUE(event); @@ -676,18 +686,20 @@ static void libuv_timer_stop(zend_async_event_t *event) if (error < 0) { async_throw_error("Failed to stop timer handle: %s", uv_strerror(error)); - return; + return false; } + + return true; } /* }}} */ /* {{{ libuv_timer_dispose */ -static void libuv_timer_dispose(zend_async_event_t *event) +static bool libuv_timer_dispose(zend_async_event_t *event) { if (ZEND_ASYNC_EVENT_REFCOUNT(event) > 1) { ZEND_ASYNC_EVENT_DEL_REF(event); - return; + return true; } if (event->loop_ref_count > 0) { @@ -700,6 +712,7 @@ static void libuv_timer_dispose(zend_async_event_t *event) async_timer_event_t *timer = (async_timer_event_t *) (event); uv_close((uv_handle_t *) &timer->uv_handle, libuv_close_handle_cb); + return true; } /* }}} */ @@ -753,7 +766,7 @@ libuv_new_timer_event(const zend_ulong timeout, const zend_ulong nanoseconds, co /* NOTE: on_signal_event removed - now using global signal management */ /* {{{ libuv_signal_start */ -static void libuv_signal_start(zend_async_event_t *event) +static bool libuv_signal_start(zend_async_event_t *event) { EVENT_START_PROLOGUE(event); @@ -763,12 +776,13 @@ static void libuv_signal_start(zend_async_event_t *event) event->loop_ref_count++; ZEND_ASYNC_INCREASE_EVENT_COUNT; + return true; } /* }}} */ /* {{{ libuv_signal_stop */ -static void libuv_signal_stop(zend_async_event_t *event) +static bool libuv_signal_stop(zend_async_event_t *event) { EVENT_STOP_PROLOGUE(event); @@ -778,16 +792,17 @@ static void libuv_signal_stop(zend_async_event_t *event) event->loop_ref_count = 0; ZEND_ASYNC_DECREASE_EVENT_COUNT; + return true; } /* }}} */ /* {{{ libuv_signal_dispose */ -static void libuv_signal_dispose(zend_async_event_t *event) +static bool libuv_signal_dispose(zend_async_event_t *event) { if (ZEND_ASYNC_EVENT_REFCOUNT(event) > 1) { ZEND_ASYNC_EVENT_DEL_REF(event); - return; + return true; } if (event->loop_ref_count > 0) { @@ -799,6 +814,7 @@ static void libuv_signal_dispose(zend_async_event_t *event) // Signal cleanup handled by global signal management pefree(event, 0); + return true; } /* }}} */ @@ -1371,20 +1387,20 @@ static void libuv_stop_process_watcher(void) /* }}} */ /* {{{ libuv_process_event_start */ -static void libuv_process_event_start(zend_async_event_t *event) +static bool libuv_process_event_start(zend_async_event_t *event) { EVENT_START_PROLOGUE(event); async_process_event_t *process = (async_process_event_t *) (event); if (process->hJob != NULL) { - return; + return true; } DWORD exitCode; if (GetExitCodeProcess(process->event.process, &exitCode) && exitCode != STILL_ACTIVE) { async_throw_error("Process has already terminated: %d", exitCode); - return; + return false; } process->hJob = CreateJobObject(NULL, NULL); @@ -1398,13 +1414,13 @@ static void libuv_process_event_start(zend_async_event_t *event) error = GetLastError(); if (error == ERROR_SUCCESS) { - return; + return true; } char *error_msg = php_win32_error_to_msg((HRESULT) error); async_throw_error("Failed to assign process to job object: %s", error_msg); php_win32_error_msg_free(error_msg); - return; + return false; } if (WATCHER == NULL) { @@ -1421,24 +1437,25 @@ static void libuv_process_event_start(zend_async_event_t *event) error = GetLastError(); if (error == ERROR_SUCCESS) { - return; + return true; } char *error_msg = php_win32_error_to_msg((HRESULT) error); async_throw_error("Failed to associate IO completion port with Job for process: %s", error_msg); php_win32_error_msg_free(error_msg); - return; + return false; } event->loop_ref_count++; ZEND_ASYNC_INCREASE_EVENT_COUNT; LIBUV_REACTOR->countWaitingDescriptors++; + return true; } /* }}} */ /* {{{ libuv_process_event_stop */ -static void libuv_process_event_stop(zend_async_event_t *event) +static bool libuv_process_event_stop(zend_async_event_t *event) { EVENT_STOP_PROLOGUE(event); @@ -1452,13 +1469,14 @@ static void libuv_process_event_stop(zend_async_event_t *event) } ZEND_ASYNC_DECREASE_EVENT_COUNT; + return true; } /* }}} */ #else // Unix process handle -static void libuv_process_event_start(zend_async_event_t *event) +static bool libuv_process_event_start(zend_async_event_t *event) { EVENT_START_PROLOGUE(event); @@ -1484,10 +1502,12 @@ static void libuv_process_event_start(zend_async_event_t *event) event->stop(event); ZEND_ASYNC_CALLBACKS_NOTIFY(&process->event.base, NULL, NULL); + return true; } else if (result == 0) { // Process still running, wait for SIGCHLD event->loop_ref_count = 1; ZEND_ASYNC_INCREASE_EVENT_COUNT; + return true; } else { // Error: process doesn't exist or already reaped libuv_remove_process_event(event); @@ -1495,10 +1515,11 @@ static void libuv_process_event_start(zend_async_event_t *event) async_ce_async_exception, "Failed to monitor process %d: %s", (int) pid, strerror(errno)); ZEND_ASYNC_CALLBACKS_NOTIFY(&process->event.base, NULL, exception); OBJ_RELEASE(exception); + return EG(exception) == NULL; } } -static void libuv_process_event_stop(zend_async_event_t *event) +static bool libuv_process_event_stop(zend_async_event_t *event) { EVENT_STOP_PROLOGUE(event); ZEND_ASYNC_EVENT_SET_CLOSED(event); @@ -1508,15 +1529,16 @@ static void libuv_process_event_stop(zend_async_event_t *event) event->loop_ref_count = 0; ZEND_ASYNC_DECREASE_EVENT_COUNT; + return true; } #endif /* {{{ libuv_process_event_dispose */ -static void libuv_process_event_dispose(zend_async_event_t *event) +static bool libuv_process_event_dispose(zend_async_event_t *event) { if (ZEND_ASYNC_EVENT_REFCOUNT(event) > 1) { ZEND_ASYNC_EVENT_DEL_REF(event); - return; + return true; } if (event->loop_ref_count > 0) { @@ -1541,6 +1563,7 @@ static void libuv_process_event_dispose(zend_async_event_t *event) #endif pefree(event, 0); + return true; } /* }}} */ @@ -1642,7 +1665,7 @@ static void on_filesystem_event(uv_fs_event_t *handle, const char *filename, int /* }}} */ /* {{{ libuv_filesystem_start */ -static void libuv_filesystem_start(zend_async_event_t *event) +static bool libuv_filesystem_start(zend_async_event_t *event) { EVENT_START_PROLOGUE(event); @@ -1653,17 +1676,18 @@ static void libuv_filesystem_start(zend_async_event_t *event) if (error < 0) { async_throw_error("Failed to start filesystem handle: %s", uv_strerror(error)); - return; + return false; } event->loop_ref_count++; ZEND_ASYNC_INCREASE_EVENT_COUNT; + return true; } /* }}} */ /* {{{ libuv_filesystem_stop */ -static void libuv_filesystem_stop(zend_async_event_t *event) +static bool libuv_filesystem_stop(zend_async_event_t *event) { EVENT_STOP_PROLOGUE(event); @@ -1676,18 +1700,19 @@ static void libuv_filesystem_stop(zend_async_event_t *event) if (error < 0) { async_throw_error("Failed to stop filesystem handle: %s", uv_strerror(error)); - return; + return false; } + return true; } /* }}} */ /* {{{ libuv_filesystem_dispose */ -static void libuv_filesystem_dispose(zend_async_event_t *event) +static bool libuv_filesystem_dispose(zend_async_event_t *event) { if (ZEND_ASYNC_EVENT_REFCOUNT(event) > 1) { ZEND_ASYNC_EVENT_DEL_REF(event); - return; + return true; } if (event->loop_ref_count > 0) { @@ -1710,6 +1735,7 @@ static void libuv_filesystem_dispose(zend_async_event_t *event) } uv_close((uv_handle_t *) &fs_event->uv_handle, libuv_close_handle_cb); + return true; } /* }}} */ @@ -1794,33 +1820,35 @@ static void on_nameinfo_event(uv_getnameinfo_t *req, int status, const char *hos /* }}} */ /* {{{ libuv_dns_nameinfo_start */ -static void libuv_dns_nameinfo_start(zend_async_event_t *event) +static bool libuv_dns_nameinfo_start(zend_async_event_t *event) { EVENT_START_PROLOGUE(event); event->loop_ref_count++; ZEND_ASYNC_INCREASE_EVENT_COUNT; + return true; } /* }}} */ /* {{{ libuv_dns_nameinfo_stop */ -static void libuv_dns_nameinfo_stop(zend_async_event_t *event) +static bool libuv_dns_nameinfo_stop(zend_async_event_t *event) { EVENT_STOP_PROLOGUE(event); event->loop_ref_count = 0; ZEND_ASYNC_DECREASE_EVENT_COUNT; + return true; } /* }}} */ /* {{{ libuv_dns_nameinfo_dispose */ -static void libuv_dns_nameinfo_dispose(zend_async_event_t *event) +static bool libuv_dns_nameinfo_dispose(zend_async_event_t *event) { if (ZEND_ASYNC_EVENT_REFCOUNT(event) > 1) { ZEND_ASYNC_EVENT_DEL_REF(event); - return; + return true; } if (event->loop_ref_count > 0) { @@ -1843,6 +1871,7 @@ static void libuv_dns_nameinfo_dispose(zend_async_event_t *event) } pefree(event, 0); + return true; } /* }}} */ @@ -1907,33 +1936,35 @@ static void on_addrinfo_event(uv_getaddrinfo_t *req, int status, struct addrinfo /* }}} */ /* {{{ libuv_dns_getaddrinfo_start */ -static void libuv_dns_getaddrinfo_start(zend_async_event_t *event) +static bool libuv_dns_getaddrinfo_start(zend_async_event_t *event) { EVENT_START_PROLOGUE(event); event->loop_ref_count++; ZEND_ASYNC_INCREASE_EVENT_COUNT; + return true; } /* }}} */ /* {{{ libuv_dns_getaddrinfo_stop */ -static void libuv_dns_getaddrinfo_stop(zend_async_event_t *event) +static bool libuv_dns_getaddrinfo_stop(zend_async_event_t *event) { EVENT_STOP_PROLOGUE(event); event->loop_ref_count = 0; ZEND_ASYNC_DECREASE_EVENT_COUNT; + return true; } /* }}} */ /* {{{ libuv_dns_getaddrinfo_dispose */ -static void libuv_dns_getaddrinfo_dispose(zend_async_event_t *event) +static bool libuv_dns_getaddrinfo_dispose(zend_async_event_t *event) { if (ZEND_ASYNC_EVENT_REFCOUNT(event) > 1) { ZEND_ASYNC_EVENT_DEL_REF(event); - return; + return true; } if (event->loop_ref_count > 0) { @@ -1947,6 +1978,7 @@ static void libuv_dns_getaddrinfo_dispose(zend_async_event_t *event) // Note: The addrinfo structure is allocated by libuv and should not be freed manually! libuv_close_handle_cb((uv_handle_t *) &addr_info->uv_handle); + return true; } /* }}} */ @@ -1986,11 +2018,13 @@ libuv_getaddrinfo(const char *node, const char *service, const struct addrinfo * /* }}} */ /* {{{ libuv_freeaddrinfo */ -static void libuv_freeaddrinfo(struct addrinfo *ai) +static bool libuv_freeaddrinfo(struct addrinfo *ai) { if (ai != NULL) { uv_freeaddrinfo(ai); } + + return true; } /* }}} */ @@ -2148,24 +2182,25 @@ static void exec_std_err_read_cb(uv_stream_t *stream, ssize_t nread, const uv_bu /* }}} */ /* {{{ libuv_exec_start */ -static void libuv_exec_start(zend_async_event_t *event) +static bool libuv_exec_start(zend_async_event_t *event) { EVENT_START_PROLOGUE(event); async_exec_event_t *exec = (async_exec_event_t *) (event); if (exec->process == NULL) { - return; + return true; } event->loop_ref_count++; ZEND_ASYNC_INCREASE_EVENT_COUNT; + return true; } /* }}} */ /* {{{ libuv_exec_stop */ -static void libuv_exec_stop(zend_async_event_t *event) +static bool libuv_exec_stop(zend_async_event_t *event) { EVENT_STOP_PROLOGUE(event); @@ -2177,16 +2212,17 @@ static void libuv_exec_stop(zend_async_event_t *event) if (exec->process != NULL) { uv_process_kill(exec->process, ZEND_ASYNC_SIGTERM); } + return true; } /* }}} */ /* {{{ libuv_exec_dispose */ -static void libuv_exec_dispose(zend_async_event_t *event) +static bool libuv_exec_dispose(zend_async_event_t *event) { if (ZEND_ASYNC_EVENT_REFCOUNT(event) > 1) { ZEND_ASYNC_EVENT_DEL_REF(event); - return; + return true; } if (event->loop_ref_count > 0) { @@ -2238,6 +2274,7 @@ static void libuv_exec_dispose(zend_async_event_t *event) // Free the event itself pefree(event, 0); + return true; } /* }}} */ @@ -2431,33 +2468,35 @@ static void libuv_trigger_event_trigger(zend_async_trigger_event_t *event) /* }}} */ /* {{{ libuv_trigger_event_start */ -static void libuv_trigger_event_start(zend_async_event_t *event) +static bool libuv_trigger_event_start(zend_async_event_t *event) { EVENT_START_PROLOGUE(event); event->loop_ref_count++; ZEND_ASYNC_INCREASE_EVENT_COUNT; + return true; } /* }}} */ /* {{{ libuv_trigger_event_stop */ -static void libuv_trigger_event_stop(zend_async_event_t *event) +static bool libuv_trigger_event_stop(zend_async_event_t *event) { EVENT_STOP_PROLOGUE(event); event->loop_ref_count = 0; ZEND_ASYNC_DECREASE_EVENT_COUNT; + return true; } /* }}} */ /* {{{ libuv_trigger_event_dispose */ -static void libuv_trigger_event_dispose(zend_async_event_t *event) +static bool libuv_trigger_event_dispose(zend_async_event_t *event) { if (ZEND_ASYNC_EVENT_REFCOUNT(event) > 1) { ZEND_ASYNC_EVENT_DEL_REF(event); - return; + return true; } if (event->loop_ref_count > 0) { @@ -2470,6 +2509,7 @@ static void libuv_trigger_event_dispose(zend_async_event_t *event) async_trigger_event_t *trigger = (async_trigger_event_t *) (event); uv_close((uv_handle_t *) &trigger->uv_handle, libuv_close_handle_cb); + return true; } /* }}} */ @@ -2568,7 +2608,7 @@ static void on_connection_event(uv_stream_t *server, int status) /* }}} */ /* {{{ libuv_listen_start */ -static void libuv_listen_start(zend_async_event_t *event) +static bool libuv_listen_start(zend_async_event_t *event) { EVENT_START_PROLOGUE(event); @@ -2579,33 +2619,35 @@ static void libuv_listen_start(zend_async_event_t *event) if (error < 0) { async_throw_error("Failed to start listening: %s", uv_strerror(error)); - return; + return false; } event->loop_ref_count++; ZEND_ASYNC_INCREASE_EVENT_COUNT; + return true; } /* }}} */ /* {{{ libuv_listen_stop */ -static void libuv_listen_stop(zend_async_event_t *event) +static bool libuv_listen_stop(zend_async_event_t *event) { EVENT_STOP_PROLOGUE(event); // uv_listen doesn't have a stop function, we close the handle event->loop_ref_count = 0; ZEND_ASYNC_DECREASE_EVENT_COUNT; + return true; } /* }}} */ /* {{{ libuv_listen_dispose */ -static void libuv_listen_dispose(zend_async_event_t *event) +static bool libuv_listen_dispose(zend_async_event_t *event) { if (ZEND_ASYNC_EVENT_REFCOUNT(event) > 1) { ZEND_ASYNC_EVENT_DEL_REF(event); - return; + return true; } if (event->loop_ref_count > 0) { @@ -2623,6 +2665,7 @@ static void libuv_listen_dispose(zend_async_event_t *event) } uv_close((uv_handle_t *) &listen_event->uv_handle, libuv_close_handle_cb); + return true; } /* }}} */ diff --git a/scheduler.c b/scheduler.c index 889e096..b108770 100644 --- a/scheduler.c +++ b/scheduler.c @@ -607,10 +607,10 @@ static void cancel_queued_coroutines(void) zend_exception_restore_fast(exception, prev_exception); } -void start_graceful_shutdown(void) +bool start_graceful_shutdown(void) { if (ZEND_ASYNC_GRACEFUL_SHUTDOWN) { - return; + return true; } ZEND_ASYNC_GRACEFUL_SHUTDOWN = true; @@ -636,6 +636,7 @@ void start_graceful_shutdown(void) } // After exiting this function, EG(exception) must be 100% clean. + return true; } static void finally_shutdown(void) @@ -740,33 +741,31 @@ static zend_always_inline void stop_waker_events(zend_async_waker_t *waker) /** * The main loop of the scheduler. */ -void async_scheduler_launch(void) +bool async_scheduler_launch(void) { if (ZEND_ASYNC_SCHEDULER) { async_throw_error("The scheduler cannot be started when is already enabled"); - return; + return false; } if (EG(active_fiber)) { async_throw_error("The True Async Scheduler cannot be started from within a Fiber"); - return; + return false; } if (false == zend_async_reactor_is_enabled()) { async_throw_error("The scheduler cannot be started without the Reactor"); - return; + return false; } - ZEND_ASYNC_REACTOR_STARTUP(); - - if (UNEXPECTED(EG(exception) != NULL)) { - return; + if (!ZEND_ASYNC_REACTOR_STARTUP()) { + return false; } fiber_pool_init(); if (UNEXPECTED(EG(exception) != NULL)) { - return; + return false; } // @@ -779,39 +778,38 @@ void async_scheduler_launch(void) if (ZEND_ASYNC_MAIN_SCOPE == NULL) { ZEND_ASYNC_MAIN_SCOPE = ZEND_ASYNC_NEW_SCOPE(NULL); - if (UNEXPECTED(EG(exception) != NULL)) { - return; + if (ZEND_ASYNC_MAIN_SCOPE == NULL) { + return false; } } if (UNEXPECTED(EG(exception) != NULL)) { - return; + return false; } zend_async_scope_t *scope = ZEND_ASYNC_MAIN_SCOPE; async_coroutine_t *main_coroutine = (async_coroutine_t *) ZEND_ASYNC_NEW_COROUTINE(scope); - if (UNEXPECTED(EG(exception) != NULL)) { - return; + if (UNEXPECTED(main_coroutine == NULL)) { + return false; } if (UNEXPECTED(main_coroutine == NULL)) { async_throw_error("Failed to create the main coroutine"); - return; + return false; } zval options; ZVAL_UNDEF(&options); - scope->before_coroutine_enqueue(&main_coroutine->coroutine, scope, &options); - zval_dtor(&options); - - if (UNEXPECTED(EG(exception) != NULL)) { - return; + if (!scope->before_coroutine_enqueue(&main_coroutine->coroutine, scope, &options)) { + zval_dtor(&options); + return false; } + zval_dtor(&options); scope->after_coroutine_enqueue(&main_coroutine->coroutine, scope); if (UNEXPECTED(EG(exception) != NULL)) { - return; + return false; } // Create a new Fiber context for the main coroutine. @@ -835,7 +833,7 @@ void async_scheduler_launch(void) if (UNEXPECTED(zend_hash_index_add_ptr(&ASYNC_G(coroutines), main_coroutine->std.handle, main_coroutine) == NULL)) { async_throw_error("Failed to add the main coroutine to the list"); - return; + return false; } ZEND_ASYNC_INCREASE_COROUTINE_COUNT; @@ -865,31 +863,30 @@ void async_scheduler_launch(void) ASYNC_G(main_vm_stack) = EG(vm_stack); zend_coroutine_t *scheduler_coroutine = ZEND_ASYNC_NEW_COROUTINE(NULL); - if (UNEXPECTED(EG(exception) != NULL)) { - return; + if (UNEXPECTED(scheduler_coroutine == NULL)) { + return false; } if (UNEXPECTED(scheduler_coroutine == NULL)) { async_throw_error("Failed to create the scheduler coroutine"); - return; + return false; } scope = ZEND_ASYNC_NEW_SCOPE(NULL); - if (UNEXPECTED(EG(exception))) { - return; + if (UNEXPECTED(scope == NULL)) { + return false; } ZVAL_UNDEF(&options); - scope->before_coroutine_enqueue(scheduler_coroutine, scope, &options); - zval_dtor(&options); - - if (UNEXPECTED(EG(exception) != NULL)) { - return; + if (!scope->before_coroutine_enqueue(scheduler_coroutine, scope, &options)) { + zval_dtor(&options); + return false; } + zval_dtor(&options); scope->after_coroutine_enqueue(scheduler_coroutine, scope); if (UNEXPECTED(EG(exception) != NULL)) { - return; + return false; } scheduler_coroutine->internal_entry = NULL; @@ -897,10 +894,7 @@ void async_scheduler_launch(void) ZEND_ASYNC_SCHEDULER = scheduler_coroutine; ZEND_ASYNC_ACTIVATE; - zend_async_call_main_coroutine_start_handlers(&main_coroutine->coroutine); - if (UNEXPECTED(EG(exception))) { - return; - } + return zend_async_call_main_coroutine_start_handlers(&main_coroutine->coroutine); } /** @@ -910,15 +904,13 @@ void async_scheduler_launch(void) * This function is needed because the main coroutine runs differently from the others * — its logic cycle is broken. */ -void async_scheduler_main_coroutine_suspend(void) +bool async_scheduler_main_coroutine_suspend(void) { bool do_bailout = false; if (UNEXPECTED(ZEND_ASYNC_SCHEDULER == NULL)) { - async_scheduler_launch(); - - if (UNEXPECTED(EG(exception) != NULL)) { - return; + if (!async_scheduler_launch()) { + return false; } } @@ -996,19 +988,19 @@ void async_scheduler_main_coroutine_suspend(void) } else if (exit_exception != NULL) { async_rethrow_exception(exit_exception); } + + return EG(exception) == NULL; } -void async_scheduler_coroutine_enqueue(zend_coroutine_t *coroutine) +bool async_scheduler_coroutine_enqueue(zend_coroutine_t *coroutine) { /** * Note that the Scheduler is initialized after the first use of suspend, * not at the start of the Zend engine. */ if (UNEXPECTED(coroutine == NULL && ZEND_ASYNC_SCHEDULER == NULL)) { - async_scheduler_launch(); - - if (UNEXPECTED(EG(exception) != NULL)) { - return; + if (!async_scheduler_launch()) { + return false; } coroutine = ZEND_ASYNC_CURRENT_COROUTINE; @@ -1023,7 +1015,7 @@ void async_scheduler_coroutine_enqueue(zend_coroutine_t *coroutine) zend_async_waker_t *waker = zend_async_waker_new(coroutine); if (UNEXPECTED(EG(exception))) { async_throw_error("Failed to create waker for coroutine"); - return; + return false; } coroutine->waker = waker; @@ -1045,6 +1037,8 @@ void async_scheduler_coroutine_enqueue(zend_coroutine_t *coroutine) // stop_waker_events(coroutine->waker); } + + return true; } /** @@ -1110,7 +1104,7 @@ static zend_always_inline void scheduler_next_tick(void) } } -void async_scheduler_coroutine_suspend(void) +bool async_scheduler_coroutine_suspend(void) { // // Before suspending the coroutine, we save the current exception state. @@ -1125,11 +1119,8 @@ void async_scheduler_coroutine_suspend(void) * not at the start of the Zend engine. */ if (UNEXPECTED(ZEND_ASYNC_SCHEDULER == NULL)) { - async_scheduler_launch(); - - if (UNEXPECTED(*exception_ptr)) { - zend_exception_restore_fast(exception_ptr, prev_exception_ptr); - return; + if (!async_scheduler_launch()) { + return false; } } @@ -1154,7 +1145,7 @@ void async_scheduler_coroutine_suspend(void) async_throw_error("The coroutine has no events to wait for"); zend_async_waker_clean(coroutine); zend_exception_restore_fast(exception_ptr, prev_exception_ptr); - return; + return false; } // Before starting the events, we change the status of the Waker. @@ -1186,7 +1177,7 @@ void async_scheduler_coroutine_suspend(void) stop_waker_events(coroutine->waker); zend_async_waker_clean(coroutine); zend_exception_restore_fast(exception_ptr, prev_exception_ptr); - return; + return false; } } @@ -1222,6 +1213,8 @@ void async_scheduler_coroutine_suspend(void) } zend_exception_restore_fast(exception_ptr, prev_exception_ptr); + + return *exception_ptr == NULL; } /////////////////////////////////////////////////////////// diff --git a/scheduler.h b/scheduler.h index ddc89bc..b301f26 100644 --- a/scheduler.h +++ b/scheduler.h @@ -26,17 +26,17 @@ BEGIN_EXTERN_C() void async_scheduler_startup(void); void async_scheduler_shutdown(void); -void start_graceful_shutdown(void); +bool start_graceful_shutdown(void); -void async_scheduler_launch(void); +bool async_scheduler_launch(void); /** * A function that is called when control needs to be transferred from a coroutine to the Scheduler. * In reality, no context switch occurs. * The Scheduler's logic runs directly within the coroutine that called suspend. */ -void async_scheduler_coroutine_suspend(void); -void async_scheduler_main_coroutine_suspend(void); -void async_scheduler_coroutine_enqueue(zend_coroutine_t *coroutine); +bool async_scheduler_coroutine_suspend(void); +bool async_scheduler_main_coroutine_suspend(void); +bool async_scheduler_coroutine_enqueue(zend_coroutine_t *coroutine); /* Fiber context creation */ async_fiber_context_t *async_fiber_context_create(void); diff --git a/scope.c b/scope.c index 68e20e7..078ea27 100644 --- a/scope.c +++ b/scope.c @@ -113,13 +113,14 @@ typedef struct #define SCOPE_CAN_BE_DISPOSED(scope) scope_can_be_disposed(scope, true, true) // Event method forward declarations -static void scope_event_start(zend_async_event_t *event); -static void scope_event_stop(zend_async_event_t *event); -static void scope_add_callback(zend_async_event_t *event, zend_async_event_callback_t *callback); -static void scope_del_callback(zend_async_event_t *event, zend_async_event_callback_t *callback); +static bool scope_event_start(zend_async_event_t *event); +static bool scope_event_stop(zend_async_event_t *event); +static bool scope_add_callback(zend_async_event_t *event, zend_async_event_callback_t *callback); +static bool scope_del_callback(zend_async_event_t *event, zend_async_event_callback_t *callback); static bool scope_replay(zend_async_event_t *event, zend_async_event_callback_t *callback, zval *result, zend_object **exception); static zend_string *scope_info(zend_async_event_t *event); +static bool scope_dispose(zend_async_event_t *scope_event); #define THROW_IF_SCHEDULER_CONTEXT \ if (UNEXPECTED(ZEND_ASYNC_IS_SCHEDULER_CONTEXT)) { \ @@ -661,13 +662,15 @@ METHOD(disposeAfterTimeout) callback->scope = scope_object->scope; callback->scope->scope.event.ref_count++; - timer_event->base.add_callback(&timer_event->base, &callback->callback); - if (UNEXPECTED(EG(exception) != NULL)) { + if (!timer_event->base.add_callback(&timer_event->base, &callback->callback)) { timer_event->base.dispose(&timer_event->base); return; } - timer_event->base.start(&timer_event->base); + if (!timer_event->base.start(&timer_event->base)) { + timer_event->base.dispose(&timer_event->base); + return; + } } METHOD(getChildScopes) @@ -810,11 +813,12 @@ void async_scope_mark_coroutine_zombie(async_coroutine_t *coroutine) } } -static void scope_before_coroutine_enqueue(zend_coroutine_t *coroutine, zend_async_scope_t *zend_scope, zval *result) +static bool scope_before_coroutine_enqueue(zend_coroutine_t *coroutine, zend_async_scope_t *zend_scope, zval *result) { async_scope_t *scope = (async_scope_t *) zend_scope; async_scope_add_coroutine(scope, (async_coroutine_t *) coroutine); + return true; } static void scope_after_coroutine_enqueue(zend_coroutine_t *coroutine, zend_async_scope_t *scope) @@ -998,24 +1002,26 @@ static bool scope_try_to_dispose(zend_async_scope_t *scope) return true; } -static void scope_event_start(zend_async_event_t *event) +static bool scope_event_start(zend_async_event_t *event) { // Empty implementation - scopes don't need explicit start + return true; } -static void scope_event_stop(zend_async_event_t *event) +static bool scope_event_stop(zend_async_event_t *event) { // Empty implementation - scopes don't need explicit stop + return true; } -static void scope_add_callback(zend_async_event_t *event, zend_async_event_callback_t *callback) +static bool scope_add_callback(zend_async_event_t *event, zend_async_event_callback_t *callback) { - zend_async_callbacks_push(event, callback); + return zend_async_callbacks_push(event, callback); } -static void scope_del_callback(zend_async_event_t *event, zend_async_event_callback_t *callback) +static bool scope_del_callback(zend_async_event_t *event, zend_async_event_callback_t *callback) { - zend_async_callbacks_remove(event, callback); + return zend_async_callbacks_remove(event, callback); } static bool @@ -1066,17 +1072,17 @@ static zend_string *scope_info(zend_async_event_t *event) } } -static void scope_dispose(zend_async_event_t *scope_event) +static bool scope_dispose(zend_async_event_t *scope_event) { async_scope_t *scope = (async_scope_t *) scope_event; if (ZEND_ASYNC_SCOPE_IS_DISPOSING(&scope->scope)) { - return; + return true; } if (ZEND_ASYNC_EVENT_REFCOUNT(scope_event) > 1) { ZEND_ASYNC_EVENT_DEL_REF(scope_event); - return; + return true; } if (ZEND_ASYNC_EVENT_REFCOUNT(scope_event) == 1) { @@ -1107,7 +1113,7 @@ static void scope_dispose(zend_async_event_t *scope_event) async_spawn_and_throw(critical_exception, &scope->scope, 0); } ZEND_ASYNC_SCOPE_CLR_DISPOSING(&scope->scope); - return; + return true; } if (scope->scope.parent_scope) { @@ -1158,6 +1164,7 @@ static void scope_dispose(zend_async_event_t *scope_event) if (critical_exception != NULL) { async_rethrow_exception(critical_exception); } + return true; } zend_async_scope_t *async_new_scope(zend_async_scope_t *parent_scope, const bool with_zend_object)