@@ -63,12 +63,10 @@ async_scope_remove_coroutine(async_scope_t *scope, async_coroutine_t *coroutine)
6363 for (uint32_t i = 0 ; i < vector -> length ; ++ i ) {
6464 if (vector -> data [i ] == coroutine ) {
6565 // Decrement active coroutines count if coroutine was active
66- if (false == ZEND_COROUTINE_IS_ZOMBIE (& coroutine -> coroutine )) {
67- if (scope -> active_coroutines_count > 0 ) {
68- scope -> active_coroutines_count -- ;
69- } else if (scope -> zombie_coroutines_count > 0 ) {
70- scope -> zombie_coroutines_count -- ;
71- }
66+ if (false == ZEND_COROUTINE_IS_ZOMBIE (& coroutine -> coroutine ) && scope -> active_coroutines_count > 0 ) {
67+ scope -> active_coroutines_count -- ;
68+ } else if (scope -> zombie_coroutines_count > 0 ) {
69+ scope -> zombie_coroutines_count -- ;
7270 }
7371
7472 vector -> data [i ] = vector -> data [-- vector -> length ];
@@ -1087,6 +1085,12 @@ static zend_string* scope_info(zend_async_event_t *event)
10871085
10881086static void scope_dispose (zend_async_event_t * scope_event )
10891087{
1088+ async_scope_t * scope = (async_scope_t * ) scope_event ;
1089+
1090+ if (ZEND_ASYNC_SCOPE_IS_DISPOSING (& scope -> scope )) {
1091+ return ;
1092+ }
1093+
10901094 if (ZEND_ASYNC_EVENT_REF (scope_event ) > 1 ) {
10911095 ZEND_ASYNC_EVENT_DEL_REF (scope_event );
10921096 return ;
@@ -1096,16 +1100,31 @@ static void scope_dispose(zend_async_event_t *scope_event)
10961100 ZEND_ASYNC_EVENT_DEL_REF (scope_event );
10971101 }
10981102
1099- async_scope_t * scope = ( async_scope_t * ) scope_event ;
1103+ ZEND_ASYNC_SCOPE_SET_DISPOSING ( & scope -> scope ) ;
11001104
11011105 ZEND_ASSERT (scope -> coroutines .length == 0 && scope -> scope .scopes .length == 0
11021106 && "Scope should be empty before disposal" );
11031107
1108+ zend_object * critical_exception = NULL ;
1109+
1110+ //
1111+ // Notifying subscribers one last time that the scope has been definitively completed.
1112+ //
1113+ ZEND_ASYNC_CALLBACKS_NOTIFY (scope_event , NULL , NULL );
1114+ zend_async_callbacks_free (& scope -> scope .event );
1115+ if (UNEXPECTED (EG (exception ))) {
1116+ critical_exception = zend_exception_merge (critical_exception , true, true);
1117+ }
1118+
11041119 if (scope -> finally_handlers != NULL
11051120 && zend_hash_num_elements (scope -> finally_handlers ) > 0
11061121 && async_scope_call_finally_handlers (scope )) {
11071122 // If finally handlers were called, we don't dispose the scope yet
11081123 ZEND_ASYNC_EVENT_ADD_REF (& scope -> scope .event );
1124+ if (critical_exception ) {
1125+ async_spawn_and_throw (critical_exception , & scope -> scope , 0 );
1126+ }
1127+ ZEND_ASYNC_SCOPE_CLR_DISPOSING (& scope -> scope );
11091128 return ;
11101129 }
11111130
@@ -1153,6 +1172,10 @@ static void scope_dispose(zend_async_event_t *scope_event)
11531172 async_scope_free_coroutines (scope );
11541173 zend_async_scope_free_children (& scope -> scope );
11551174 efree (scope );
1175+
1176+ if (critical_exception != NULL ) {
1177+ async_rethrow_exception (critical_exception );
1178+ }
11561179}
11571180
11581181zend_async_scope_t * async_new_scope (zend_async_scope_t * parent_scope , const bool with_zend_object )
@@ -1181,6 +1204,11 @@ zend_async_scope_t * async_new_scope(zend_async_scope_t * parent_scope, const bo
11811204 scope -> scope .parent_scope = parent_scope ;
11821205 zend_async_event_t * event = & scope -> scope .event ;
11831206
1207+ // Inherit safely disposal flag from parent scope or set it to true if parent scope is NULL
1208+ if (parent_scope == NULL || ZEND_ASYNC_SCOPE_IS_DISPOSE_SAFELY (parent_scope )) {
1209+ ZEND_ASYNC_SCOPE_SET_DISPOSE_SAFELY (& scope -> scope );
1210+ }
1211+
11841212 event -> ref_count = 1 ; // Initialize reference count
11851213
11861214 scope -> scope .before_coroutine_enqueue = scope_before_coroutine_enqueue ;
0 commit comments