@@ -314,25 +314,14 @@ METHOD(onFinally)
314314///////////////////////////////////////////////////////////
315315
316316///////////////////////////////////////////////////////////
317- /// async_coroutine_call_finally_handlers
317+ /// Finally handlers functions
318318///////////////////////////////////////////////////////////
319-
320- // Structure for coroutine finally handlers context
321- typedef struct {
322- async_coroutine_t * coroutine ;
323- zend_object * composite_exception ;
324- } coroutine_finally_handlers_context_t ;
325-
326319static zend_result finally_handlers_iterator_handler (async_iterator_t * iterator , zval * current , zval * key )
327320{
328- coroutine_finally_handlers_context_t * context = (coroutine_finally_handlers_context_t * ) iterator -> extended_data ;
329- async_coroutine_t * coroutine = context -> coroutine ;
321+ finally_handlers_context_t * context = (finally_handlers_context_t * ) iterator -> extended_data ;
330322 zval rv ;
331323 ZVAL_UNDEF (& rv );
332- zval param ;
333- ZVAL_OBJ (& param , & coroutine -> std );
334-
335- call_user_function (NULL , NULL , current , & rv , 1 , & param );
324+ call_user_function (NULL , NULL , current , & rv , context -> params_count , context -> params );
336325 zval_ptr_dtor (& rv );
337326
338327 // Check for exceptions after handler execution
@@ -342,7 +331,7 @@ static zend_result finally_handlers_iterator_handler(async_iterator_t *iterator,
342331 zend_object * current_exception = EG (exception );
343332 GC_ADDREF (current_exception );
344333 zend_clear_exception ();
345-
334+
346335 // Check for graceful/unwind exit exceptions
347336 if (zend_is_graceful_exit (current_exception ) || zend_is_unwind_exit (current_exception )) {
348337 // Release CompositeException if exists
@@ -354,7 +343,7 @@ static zend_result finally_handlers_iterator_handler(async_iterator_t *iterator,
354343 zend_throw_exception_internal (current_exception );
355344 return SUCCESS ;
356345 }
357-
346+
358347 // Handle regular exceptions
359348 if (context -> composite_exception == NULL ) {
360349 context -> composite_exception = current_exception ;
@@ -387,37 +376,59 @@ static void finally_handlers_iterator_dtor(zend_async_iterator_t *zend_iterator)
387376 return ;
388377 }
389378
390- coroutine_finally_handlers_context_t * context = iterator -> extended_data ;
379+ finally_handlers_context_t * context = iterator -> extended_data ;
391380
392381 // Throw CompositeException if any exceptions were collected
393382 if (context -> composite_exception != NULL ) {
394- zend_throw_exception_internal (context -> composite_exception );
383+
384+ async_scope_t * scope = (async_scope_t * ) context -> scope ;
385+
386+ if (ZEND_ASYNC_SCOPE_CATCH (
387+ & scope -> scope ,
388+ & context -> coroutine -> coroutine ,
389+ NULL ,
390+ context -> composite_exception ,
391+ false,
392+ ZEND_ASYNC_SCOPE_IS_DISPOSE_SAFELY (& scope -> scope )
393+ )) {
394+ OBJ_RELEASE (context -> composite_exception );
395+ } else {
396+ zend_throw_exception_internal (context -> composite_exception );
397+ }
398+
395399 context -> composite_exception = NULL ;
396400 }
397401
398- // Release coroutine reference
399- OBJ_RELEASE (& context -> coroutine -> std );
402+ if (context -> dtor != NULL ) {
403+ context -> dtor (context );
404+ context -> dtor = NULL ;
405+ }
400406
401407 // Free the context
402408 efree (context );
403409 iterator -> extended_data = NULL ;
410+
411+ //
412+ // If everything is correct,
413+ // the Scope will destroy itself as soon as the coroutine created within it completes execution.
414+ // Therefore, there's no point in taking additional actions to clean up resources.
415+ //
404416}
405417
406- static void async_coroutine_call_finally_handlers ( async_coroutine_t * coroutine )
418+ bool async_call_finally_handlers ( HashTable * finally_handlers , finally_handlers_context_t * context , int32_t priority )
407419{
408- if (coroutine -> finally_handlers == NULL || zend_hash_num_elements (coroutine -> finally_handlers ) == 0 ) {
409- return ;
420+ if (finally_handlers == NULL || zend_hash_num_elements (finally_handlers ) == 0 ) {
421+ return false ;
410422 }
411423
412424 // Create a special child scope for finally handlers
413- zend_async_scope_t * child_scope = ZEND_ASYNC_NEW_SCOPE (coroutine -> coroutine . scope );
425+ zend_async_scope_t * child_scope = ZEND_ASYNC_NEW_SCOPE (context -> scope );
414426 if (UNEXPECTED (child_scope == NULL )) {
415- return ;
427+ return false ;
416428 }
417429
418430 zval handlers ;
419- ZVAL_ARR (& handlers , coroutine -> finally_handlers );
420- coroutine -> finally_handlers = NULL ;
431+ ZVAL_ARR (& handlers , finally_handlers );
421432
422433 async_iterator_t * iterator = async_iterator_new (
423434 & handlers ,
@@ -426,26 +437,26 @@ static void async_coroutine_call_finally_handlers(async_coroutine_t *coroutine)
426437 finally_handlers_iterator_handler ,
427438 child_scope ,
428439 0 ,
429- 0 ,
440+ priority ,
430441 0
431442 );
432443
433444 zval_ptr_dtor (& handlers );
434445
435446 if (UNEXPECTED (EG (exception ))) {
436- return ;
447+ return false ;
437448 }
438449
439- // Create context for finally handlers
440- coroutine_finally_handlers_context_t * context = ecalloc (1 , sizeof (coroutine_finally_handlers_context_t ));
441- context -> coroutine = coroutine ;
442450 context -> composite_exception = NULL ;
443-
444451 iterator -> extended_data = context ;
445452 iterator -> extended_dtor = finally_handlers_iterator_dtor ;
446- GC_ADDREF ( & coroutine -> std );
453+ async_iterator_run_in_coroutine ( iterator , priority );
447454
448- async_iterator_run_in_coroutine (iterator , 0 );
455+ if (UNEXPECTED (EG (exception ))) {
456+ return false;
457+ }
458+
459+ return true;
449460}
450461
451462///////////////////////////////////////////////////////////
@@ -473,6 +484,34 @@ void async_coroutine_cleanup(zend_fiber_context *context)
473484 OBJ_RELEASE (& coroutine -> std );
474485}
475486
487+ static void finally_context_dtor (finally_handlers_context_t * context )
488+ {
489+ if (context -> coroutine != NULL ) {
490+ // Release the coroutine reference
491+ OBJ_RELEASE (& context -> coroutine -> std );
492+ context -> coroutine = NULL ;
493+ }
494+ }
495+
496+ static zend_always_inline void coroutine_call_finally_handlers (async_coroutine_t * coroutine )
497+ {
498+ HashTable * finally_handlers = coroutine -> finally_handlers ;
499+ coroutine -> finally_handlers = NULL ;
500+ finally_handlers_context_t * finally_context = ecalloc (1 , sizeof (finally_handlers_context_t ));
501+ finally_context -> coroutine = coroutine ;
502+ finally_context -> scope = coroutine -> coroutine .scope ;
503+ finally_context -> dtor = finally_context_dtor ;
504+ finally_context -> params_count = 1 ;
505+ ZVAL_OBJ (& finally_context -> params [0 ], & coroutine -> std );
506+
507+ if (async_call_finally_handlers (finally_handlers , finally_context , 1 )) {
508+ GC_ADDREF (& coroutine -> std ); // Keep reference to coroutine while handlers are running
509+ } else {
510+ efree (finally_context );
511+ zend_array_destroy (finally_handlers );
512+ }
513+ }
514+
476515void async_coroutine_finalize (zend_fiber_transfer * transfer , async_coroutine_t * coroutine )
477516{
478517 ZEND_COROUTINE_SET_FINISHED (& coroutine -> coroutine );
@@ -533,7 +572,10 @@ void async_coroutine_finalize(zend_fiber_transfer *transfer, async_coroutine_t *
533572 }
534573
535574 // Call finally handlers if any
536- async_coroutine_call_finally_handlers (coroutine );
575+ if (coroutine -> finally_handlers != NULL && zend_hash_num_elements (coroutine -> finally_handlers ) > 0 ) {
576+ coroutine_call_finally_handlers (coroutine );
577+ }
578+
537579 zend_async_waker_destroy (& coroutine -> coroutine );
538580
539581 zend_exception_restore ();
@@ -549,10 +591,10 @@ void async_coroutine_finalize(zend_fiber_transfer *transfer, async_coroutine_t *
549591
550592 // Before the exception leads to graceful termination,
551593 // we give one last chance to handle it using Scope handlers.
552- if (exception != NULL
553- && coroutine -> coroutine .scope -> catch_or_cancel (
594+ if (exception != NULL && ZEND_ASYNC_SCOPE_CATCH (
554595 coroutine -> coroutine .scope ,
555596 & coroutine -> coroutine ,
597+ NULL ,
556598 exception ,
557599 false,
558600 ZEND_ASYNC_SCOPE_IS_DISPOSE_SAFELY (coroutine -> coroutine .scope )
0 commit comments