Skip to content

Commit fa685fb

Browse files
committed
% Major refactoring of Finally Handlers and the exception handling mechanism for Scope.
1 parent b05b35d commit fa685fb

File tree

4 files changed

+223
-234
lines changed

4 files changed

+223
-234
lines changed

coroutine.c

Lines changed: 80 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -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-
326319
static 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+
476515
void 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)

coroutine.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,22 @@ struct _async_coroutine_s {
5050
zend_object std;
5151
};
5252

53+
typedef struct _finally_handlers_context_s finally_handlers_context_t;
54+
55+
// Structure for finally handlers context
56+
struct _finally_handlers_context_s {
57+
union {
58+
void *target;
59+
async_coroutine_t *coroutine;
60+
};
61+
zend_async_scope_t *scope;
62+
HashTable *finally_handlers;
63+
zend_object *composite_exception;
64+
void (*dtor)(finally_handlers_context_t *context);
65+
uint32_t params_count;
66+
zval params[1];
67+
};
68+
5369
void async_register_coroutine_ce(void);
5470
zend_coroutine_t *async_new_coroutine(zend_async_scope_t *scope);
5571
void async_coroutine_cleanup(zend_fiber_context *context);
@@ -62,5 +78,6 @@ bool async_coroutine_context_set(zend_coroutine_t * z_coroutine, zval *key, zval
6278
bool async_coroutine_context_get(zend_coroutine_t * z_coroutine, zval *key, zval *result);
6379
bool async_coroutine_context_has(zend_coroutine_t * z_coroutine, zval *key);
6480
bool async_coroutine_context_delete(zend_coroutine_t * z_coroutine, zval *key);
81+
bool async_call_finally_handlers(HashTable *finally_handlers, finally_handlers_context_t *context, int32_t priority);
6582

6683
#endif //COROUTINE_H

0 commit comments

Comments
 (0)