Skip to content

Commit 01931c8

Browse files
committed
* Important fix to the Scheduler logic during graceful shutdown. The coroutine is now properly cancelled and terminates correctly.
1 parent 251b806 commit 01931c8

File tree

3 files changed

+24
-9
lines changed

3 files changed

+24
-9
lines changed

coroutine.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,9 @@ void async_coroutine_finalize(zend_fiber_transfer *transfer, async_coroutine_t *
225225
zend_error(E_CORE_ERROR, "Failed to remove coroutine from the list");
226226
}
227227

228-
// Decrease the active coroutine count if the coroutine is not a zombie.
229-
if (false == ZEND_COROUTINE_IS_ZOMBIE(&coroutine->coroutine)) {
228+
// Decrease the active coroutine count if the coroutine is not a zombie and is started.
229+
if (ZEND_COROUTINE_IS_STARTED(&coroutine->coroutine)
230+
&& false == ZEND_COROUTINE_IS_ZOMBIE(&coroutine->coroutine)) {
230231
ZEND_ASYNC_DECREASE_COROUTINE_COUNT
231232
}
232233
}

libuv_reactor.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,7 @@ static void libuv_process_event_start(zend_async_event_t *event)
790790
char * error_msg = php_win32_error_to_msg((HRESULT) error);
791791
async_throw_error("Failed to associate IO completion port with Job for process: %s", error_msg);
792792
php_win32_error_msg_free(error_msg);
793+
return;
793794
}
794795

795796
event->loop_ref_count++;

scheduler.c

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -172,20 +172,19 @@ static bool execute_next_coroutine(zend_fiber_transfer *transfer)
172172
return false;
173173
}
174174

175-
if (UNEXPECTED(coroutine->waker == NULL)) {
176-
coroutine->event.dispose(&coroutine->event);
177-
return true;
178-
}
179-
180175
zend_async_waker_t * waker = coroutine->waker;
181176

182-
if (UNEXPECTED(waker->status == ZEND_ASYNC_WAKER_IGNORED)) {
177+
if (UNEXPECTED(waker == NULL || waker->status == ZEND_ASYNC_WAKER_IGNORED)) {
183178

184179
//
185180
// This state triggers if the fiber has never been started;
186181
// in this case, it is deallocated differently than usual.
187182
// Finalizing handlers are called. Memory is freed in the correct order!
188183
//
184+
if (ZEND_COROUTINE_IS_CANCELLED(coroutine)) {
185+
async_coroutine_finalize(NULL, async_coroutine);
186+
}
187+
189188
coroutine->event.dispose(&coroutine->event);
190189
return true;
191190
}
@@ -380,6 +379,9 @@ static void cancel_queued_coroutines(void)
380379
if (((async_coroutine_t *) coroutine)->context.status == ZEND_FIBER_STATUS_INIT) {
381380
// No need to cancel the fiber if it has not been started.
382381
coroutine->waker->status = ZEND_ASYNC_WAKER_IGNORED;
382+
ZEND_COROUTINE_SET_CANCELLED(coroutine);
383+
coroutine->exception = cancellation_exception;
384+
GC_ADDREF(cancellation_exception);
383385
} else {
384386
ZEND_ASYNC_CANCEL(coroutine, cancellation_exception, false);
385387
}
@@ -744,7 +746,18 @@ void async_scheduler_coroutine_suspend(zend_fiber_transfer *transfer)
744746
//
745747
if (transfer == NULL && ZEND_ASYNC_CURRENT_COROUTINE != NULL && ZEND_ASYNC_CURRENT_COROUTINE->waker != NULL) {
746748
async_scheduler_start_waker_events(ZEND_ASYNC_CURRENT_COROUTINE->waker);
747-
TRY_HANDLE_SUSPEND_EXCEPTION();
749+
750+
// If an exception occurs during the startup of the Waker object,
751+
// that exception belongs to the current coroutine,
752+
// which means we have the right to immediately return to the point from which we were called.
753+
if (UNEXPECTED(EG(exception) != NULL)) {
754+
// Before returning, We are required to properly destroy the Waker object.
755+
zend_exception_save();
756+
async_scheduler_stop_waker_events(ZEND_ASYNC_CURRENT_COROUTINE->waker);
757+
zend_async_waker_destroy(ZEND_ASYNC_CURRENT_COROUTINE);
758+
zend_exception_restore();
759+
return;
760+
}
748761
}
749762

750763
//

0 commit comments

Comments
 (0)