@@ -520,112 +520,119 @@ void async_coroutine_finalize(zend_fiber_transfer *transfer, async_coroutine_t *
520520 ZEND_COROUTINE_FINISH (& coroutine -> coroutine );
521521 }
522522
523- /* Cleanup switch handlers */
524- zend_coroutine_switch_handlers_destroy (& coroutine -> coroutine );
523+ bool do_bailout = false;
525524
526- // call coroutines handlers
527- zend_object * exception = NULL ;
525+ zend_try {
528526
529- if (EG (exception )) {
530- if (EG (prev_exception )) {
531- zend_exception_set_previous (EG (exception ), EG (prev_exception ));
532- EG (prev_exception ) = NULL ;
533- }
527+ /* Cleanup switch handlers */
528+ zend_coroutine_switch_handlers_destroy (& coroutine -> coroutine );
534529
535- exception = EG ( exception );
536- GC_ADDREF ( exception ) ;
530+ // call coroutines handlers
531+ zend_object * exception = NULL ;
537532
538- zend_clear_exception ();
533+ if (EG (exception )) {
534+ if (EG (prev_exception )) {
535+ zend_exception_set_previous (EG (exception ), EG (prev_exception ));
536+ EG (prev_exception ) = NULL ;
537+ }
539538
540- if (zend_is_graceful_exit (exception ) || zend_is_unwind_exit (exception )) {
541- OBJ_RELEASE (exception );
542- exception = NULL ;
543- }
544- }
539+ exception = EG (exception );
540+ GC_ADDREF (exception );
545541
546- // Hold the exception inside coroutine if it is not NULL.
547- if ( exception != NULL ) {
548- if (coroutine -> coroutine . exception != NULL ) {
549- // If the coroutine already has an exception, we do not overwrite it.
550- // This is to prevent losing the original exception in case of multiple exceptions.
551- zend_exception_set_previous ( exception , coroutine -> coroutine . exception );
542+ zend_clear_exception ();
543+
544+ if (zend_is_graceful_exit ( exception ) || zend_is_unwind_exit ( exception ) ) {
545+ OBJ_RELEASE ( exception );
546+ exception = NULL ;
547+ }
552548 }
553549
554- coroutine -> coroutine . exception = exception ;
555- GC_ADDREF (exception );
556- } else if (coroutine -> coroutine .exception != NULL ) {
557- // If the coroutine has an exception, we keep it.
558- exception = coroutine -> coroutine . exception ;
559- GC_ADDREF ( exception );
560- }
550+ // Hold the exception inside coroutine if it is not NULL.
551+ if (exception != NULL ) {
552+ if (coroutine -> coroutine .exception != NULL ) {
553+ // If the coroutine already has an exception, we do not overwrite it.
554+ // This is to prevent losing the original exception in case of multiple exceptions.
555+ zend_exception_set_previous ( exception , coroutine -> coroutine . exception );
556+ }
561557
562- zend_exception_save ();
563- // Mark second parameter of zend_async_callbacks_notify as ZVAL
564- ZEND_ASYNC_EVENT_SET_ZVAL_RESULT (& coroutine -> coroutine .event );
565- ZEND_COROUTINE_CLR_EXCEPTION_HANDLED (& coroutine -> coroutine );
566- ZEND_ASYNC_CALLBACKS_NOTIFY (& coroutine -> coroutine .event , & coroutine -> coroutine .result , exception );
567- zend_async_callbacks_free (& coroutine -> coroutine .event );
558+ coroutine -> coroutine .exception = exception ;
559+ GC_ADDREF (exception );
560+ } else if (coroutine -> coroutine .exception != NULL ) {
561+ // If the coroutine has an exception, we keep it.
562+ exception = coroutine -> coroutine .exception ;
563+ GC_ADDREF (exception );
564+ }
568565
569- if (coroutine -> coroutine .internal_context != NULL ) {
570- zend_async_coroutine_internal_context_dispose (& coroutine -> coroutine );
571- }
566+ zend_exception_save ();
567+ // Mark second parameter of zend_async_callbacks_notify as ZVAL
568+ ZEND_ASYNC_EVENT_SET_ZVAL_RESULT (& coroutine -> coroutine .event );
569+ ZEND_COROUTINE_CLR_EXCEPTION_HANDLED (& coroutine -> coroutine );
570+ ZEND_ASYNC_CALLBACKS_NOTIFY (& coroutine -> coroutine .event , & coroutine -> coroutine .result , exception );
571+ zend_async_callbacks_free (& coroutine -> coroutine .event );
572+
573+ if (coroutine -> coroutine .internal_context != NULL ) {
574+ zend_async_coroutine_internal_context_dispose (& coroutine -> coroutine );
575+ }
572576
573- // Call finally handlers if any
574- if (coroutine -> finally_handlers != NULL && zend_hash_num_elements (coroutine -> finally_handlers ) > 0 ) {
575- coroutine_call_finally_handlers (coroutine );
576- }
577+ // Call finally handlers if any
578+ if (coroutine -> finally_handlers != NULL && zend_hash_num_elements (coroutine -> finally_handlers ) > 0 ) {
579+ coroutine_call_finally_handlers (coroutine );
580+ }
577581
578- zend_async_waker_destroy (& coroutine -> coroutine );
582+ zend_async_waker_destroy (& coroutine -> coroutine );
579583
580- zend_exception_restore ();
584+ zend_exception_restore ();
581585
582- // If the exception was handled by any handler, we do not propagate it further.
583- // Cancellation-type exceptions are considered handled in all cases and are not propagated further.
584- if (exception != NULL
585- && (ZEND_COROUTINE_IS_EXCEPTION_HANDLED (& coroutine -> coroutine )
586- || instanceof_function (exception -> ce , zend_ce_cancellation_exception ))) {
587- OBJ_RELEASE (exception );
588- exception = NULL ;
589- }
586+ // If the exception was handled by any handler, we do not propagate it further.
587+ // Cancellation-type exceptions are considered handled in all cases and are not propagated further.
588+ if (exception != NULL
589+ && (ZEND_COROUTINE_IS_EXCEPTION_HANDLED (& coroutine -> coroutine )
590+ || instanceof_function (exception -> ce , zend_ce_cancellation_exception ))) {
591+ OBJ_RELEASE (exception );
592+ exception = NULL ;
593+ }
590594
591- // Before the exception leads to graceful termination,
592- // we give one last chance to handle it using Scope handlers.
593- if (exception != NULL && ZEND_ASYNC_SCOPE_CATCH (
594- coroutine -> coroutine .scope ,
595- & coroutine -> coroutine ,
596- NULL ,
597- exception ,
598- false,
599- ZEND_ASYNC_SCOPE_IS_DISPOSE_SAFELY (coroutine -> coroutine .scope )
600- )) {
601- OBJ_RELEASE (exception );
602- exception = NULL ;
603- }
595+ // Before the exception leads to graceful termination,
596+ // we give one last chance to handle it using Scope handlers.
597+ if (exception != NULL && ZEND_ASYNC_SCOPE_CATCH (
598+ coroutine -> coroutine .scope ,
599+ & coroutine -> coroutine ,
600+ NULL ,
601+ exception ,
602+ false,
603+ ZEND_ASYNC_SCOPE_IS_DISPOSE_SAFELY (coroutine -> coroutine .scope )
604+ )) {
605+ OBJ_RELEASE (exception );
606+ exception = NULL ;
607+ }
604608
605- // Notify the async scope that the coroutine has finished.
606- // For the Scheduler, the coroutine's Scope may be undefined.
607- if (EXPECTED (coroutine -> coroutine .scope != NULL )) {
608- async_scope_notify_coroutine_finished (coroutine );
609- coroutine -> coroutine .scope = NULL ;
610- }
609+ // Notify the async scope that the coroutine has finished.
610+ // For the Scheduler, the coroutine's Scope may be undefined.
611+ if (EXPECTED (coroutine -> coroutine .scope != NULL )) {
612+ async_scope_notify_coroutine_finished (coroutine );
613+ coroutine -> coroutine .scope = NULL ;
614+ }
611615
612- // Otherwise, we rethrow the exception.
613- if (exception != NULL ) {
614- zend_throw_exception_internal (exception );
615- }
616+ // Otherwise, we rethrow the exception.
617+ if (exception != NULL ) {
618+ zend_throw_exception_internal (exception );
619+ }
616620
617- if (EG (exception )) {
618- if (!(coroutine -> flags & ZEND_FIBER_FLAG_DESTROYED )
619- || !(zend_is_graceful_exit (EG (exception )) || zend_is_unwind_exit (EG (exception )))
620- ) {
621- coroutine -> flags |= ZEND_FIBER_FLAG_THREW ;
622- transfer -> flags = ZEND_FIBER_TRANSFER_FLAG_ERROR ;
621+ if (EG (exception )) {
622+ if (!(coroutine -> flags & ZEND_FIBER_FLAG_DESTROYED )
623+ || !(zend_is_graceful_exit (EG (exception )) || zend_is_unwind_exit (EG (exception )))
624+ ) {
625+ coroutine -> flags |= ZEND_FIBER_FLAG_THREW ;
626+ transfer -> flags = ZEND_FIBER_TRANSFER_FLAG_ERROR ;
623627
624- ZVAL_OBJ_COPY (& transfer -> value , EG (exception ));
625- }
628+ ZVAL_OBJ_COPY (& transfer -> value , EG (exception ));
629+ }
626630
627- zend_clear_exception ();
628- }
631+ zend_clear_exception ();
632+ }
633+ } zend_catch {
634+ do_bailout = true;
635+ } zend_end_try ();
629636
630637 if (EXPECTED (ZEND_ASYNC_SCHEDULER != & coroutine -> coroutine )) {
631638 // Permanently remove the coroutine from the Scheduler.
@@ -639,6 +646,10 @@ void async_coroutine_finalize(zend_fiber_transfer *transfer, async_coroutine_t *
639646 ZEND_ASYNC_DECREASE_COROUTINE_COUNT
640647 }
641648 }
649+
650+ if (UNEXPECTED (do_bailout )) {
651+ zend_bailout ();
652+ }
642653}
643654
644655/**
@@ -701,7 +712,7 @@ ZEND_STACK_ALIGNED void async_coroutine_execute(zend_fiber_transfer *transfer)
701712 ZEND_COROUTINE_SET_STARTED (& coroutine -> coroutine );
702713
703714 /* Call switch handlers for coroutine entering */
704- if (UNEXPECTED (& coroutine -> coroutine .switch_handlers != NULL )) {
715+ if (UNEXPECTED (coroutine -> coroutine .switch_handlers != NULL )) {
705716 ZEND_COROUTINE_ENTER (& coroutine -> coroutine );
706717 }
707718
@@ -753,18 +764,6 @@ ZEND_STACK_ALIGNED void async_coroutine_execute(zend_fiber_transfer *transfer)
753764 } zend_catch {
754765 coroutine -> flags |= ZEND_FIBER_FLAG_BAILOUT ;
755766 transfer -> flags = ZEND_FIBER_TRANSFER_FLAG_BAILOUT ;
756-
757- if (EXPECTED (ZEND_ASYNC_SCHEDULER != & coroutine -> coroutine )) {
758- // Permanently remove the coroutine from the Scheduler.
759- if (UNEXPECTED (zend_hash_index_del (& ASYNC_G (coroutines ), coroutine -> std .handle ) == FAILURE )) {
760- async_throw_error ("Failed to remove coroutine from the list" );
761- }
762-
763- // Decrease the active coroutine count if the coroutine is not a zombie.
764- if (false == ZEND_COROUTINE_IS_ZOMBIE (& coroutine -> coroutine )) {
765- ZEND_ASYNC_DECREASE_COROUTINE_COUNT
766- }
767- }
768767 } zend_end_try ();
769768
770769 coroutine -> context .cleanup = & async_coroutine_cleanup ;
@@ -1014,6 +1013,7 @@ void async_coroutine_cancel(zend_coroutine_t *zend_coroutine, zend_object *error
10141013 // In any other case, the cancellation exception overrides the existing exception.
10151014 //
10161015 ZEND_ASYNC_WAKER_APPLY_CANCELLATION (waker , error , transfer_error );
1016+ ZEND_ASYNC_DECREASE_COROUTINE_COUNT ;
10171017 async_scheduler_coroutine_enqueue (zend_coroutine );
10181018 return ;
10191019 }
0 commit comments