Skip to content

Commit c397718

Browse files
committed
#53: Looking at the completed optimization work, here's a commit description for the changes:
Optimize exception handling in ext/async with fast save/restore operations This commit replaces standard exception save/restore patterns with optimized fast variants throughout the ext/async extension to improve performance during coroutine context switching and async operations. Changes include: * scheduler.c: Optimize 7+ exception handling points including: - TRY_HANDLE_SUSPEND_EXCEPTION macro implementation - cancel_queued_coroutines() function - async_scheduler_coroutine_suspend() function - async_scheduler_dtor() cleanup * coroutine.c: Optimize save/restore pairs in: - async_coroutine_finalize() - finally_handlers_iterator_handler() * scope.c: Optimize scope completion callback exception handling * zend_common.c: Optimize zend_exception_merge() function * exceptions.c: Optimize async_extract_exception() function The optimization uses direct pointer manipulation instead of repeated EG() macro access, following the pattern: zend_object **exception_ptr = &EG(exception); zend_object **prev_exception_ptr = &EG(prev_exception); zend_exception_save_fast(exception_ptr, prev_exception_ptr); // ... code execution ... zend_exception_restore_fast(exception_ptr, prev_exception_ptr); This reduces overhead in exception state management during async operations while maintaining identical exception handling semantics.
1 parent e34d7d1 commit c397718

File tree

4 files changed

+56
-37
lines changed

4 files changed

+56
-37
lines changed

coroutine.c

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,8 @@ void async_coroutine_finalize(async_coroutine_t *coroutine)
500500
}
501501

502502
bool do_bailout = false;
503+
zend_object **exception_ptr = &EG(exception);
504+
zend_object **prev_exception_ptr = &EG(prev_exception);
503505

504506
zend_try
505507
{
@@ -510,13 +512,13 @@ void async_coroutine_finalize(async_coroutine_t *coroutine)
510512
// call coroutines handlers
511513
zend_object *exception = NULL;
512514

513-
if (EG(exception)) {
514-
if (EG(prev_exception)) {
515-
zend_exception_set_previous(EG(exception), EG(prev_exception));
516-
EG(prev_exception) = NULL;
515+
if (UNEXPECTED(*exception_ptr)) {
516+
if (*prev_exception_ptr) {
517+
zend_exception_set_previous(*exception_ptr, *prev_exception_ptr);
518+
*prev_exception_ptr = NULL;
517519
}
518520

519-
exception = EG(exception);
521+
exception = *exception_ptr;
520522
GC_ADDREF(exception);
521523

522524
zend_clear_exception();
@@ -545,7 +547,7 @@ void async_coroutine_finalize(async_coroutine_t *coroutine)
545547
GC_ADDREF(exception);
546548
}
547549

548-
zend_exception_save();
550+
zend_exception_save_fast(exception_ptr, prev_exception_ptr);
549551
// Mark second parameter of zend_async_callbacks_notify as ZVAL
550552
ZEND_ASYNC_EVENT_SET_ZVAL_RESULT(&coroutine->coroutine.event);
551553
ZEND_COROUTINE_CLR_EXCEPTION_HANDLED(&coroutine->coroutine);
@@ -569,7 +571,7 @@ void async_coroutine_finalize(async_coroutine_t *coroutine)
569571
dispose(&coroutine->coroutine);
570572
}
571573

572-
zend_exception_restore();
574+
zend_exception_restore_fast(exception_ptr, prev_exception_ptr);
573575

574576
// If the exception was handled by any handler, we do not propagate it further.
575577
// Cancellation-type exceptions are considered handled in all cases and are not propagated further.
@@ -611,7 +613,7 @@ void async_coroutine_finalize(async_coroutine_t *coroutine)
611613
}
612614
zend_end_try();
613615

614-
if (UNEXPECTED(EG(exception) && (zend_is_graceful_exit(EG(exception)) || zend_is_unwind_exit(EG(exception))))) {
616+
if (UNEXPECTED(*exception_ptr && (zend_is_graceful_exit(*exception_ptr) || zend_is_unwind_exit(*exception_ptr)))) {
615617
zend_clear_exception();
616618
}
617619

@@ -983,11 +985,14 @@ static zend_result finally_handlers_iterator_handler(async_iterator_t *iterator,
983985
zval_ptr_dtor(&rv);
984986
ZVAL_UNDEF(&rv);
985987

988+
zend_object **exception_ptr = &EG(exception);
989+
986990
// Check for exceptions after handler execution
987-
if (EG(exception)) {
988-
zend_exception_save();
989-
zend_exception_restore();
990-
zend_object *current_exception = EG(exception);
991+
if (UNEXPECTED(*exception_ptr)) {
992+
zend_object **prev_exception_ptr = &EG(prev_exception);
993+
zend_exception_save_fast(exception_ptr, prev_exception_ptr);
994+
zend_exception_restore_fast(exception_ptr, prev_exception_ptr);
995+
zend_object *current_exception = *exception_ptr;
991996
GC_ADDREF(current_exception);
992997
zend_clear_exception();
993998

exceptions.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,10 @@ bool async_spawn_and_throw(zend_object *exception, zend_async_scope_t *scope, in
286286
*/
287287
zend_object *async_extract_exception(void)
288288
{
289-
zend_exception_save();
290-
zend_exception_restore();
289+
zend_object **exception_ptr = &EG(exception);
290+
zend_object **prev_exception_ptr = &EG(prev_exception);
291+
zend_exception_save_fast(exception_ptr, prev_exception_ptr);
292+
zend_exception_restore_fast(exception_ptr, prev_exception_ptr);
291293
zend_object *exception = EG(exception);
292294
GC_ADDREF(exception);
293295
zend_clear_exception();

scheduler.c

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ static void fiber_context_cleanup(zend_fiber_context *context);
6464
if (ZEND_ASYNC_GRACEFUL_SHUTDOWN) { \
6565
finally_shutdown(); \
6666
switch_to_scheduler(transfer); \
67-
zend_exception_restore(); \
67+
zend_exception_restore_fast(exception_ptr, prev_exception_ptr); \
6868
return; \
6969
} \
7070
start_graceful_shutdown(); \
@@ -588,7 +588,10 @@ static bool resolve_deadlocks(void)
588588
///////////////////////////////////////////////////////////
589589
static void cancel_queued_coroutines(void)
590590
{
591-
zend_exception_save();
591+
zend_object **exception = &EG(exception);
592+
zend_object **prev_exception = &EG(prev_exception);
593+
594+
zend_exception_save_fast(exception, prev_exception);
592595

593596
// 1. Walk through all coroutines and cancel them if they are suspended.
594597
zval *current;
@@ -614,15 +617,15 @@ static void cancel_queued_coroutines(void)
614617
ZEND_ASYNC_CANCEL(coroutine, cancellation_exception, false);
615618
}
616619

617-
if (EG(exception)) {
618-
zend_exception_save();
620+
if (*exception) {
621+
zend_exception_save_fast(exception, prev_exception);
619622
}
620623
}
621624
ZEND_HASH_FOREACH_END();
622625

623626
OBJ_RELEASE(cancellation_exception);
624627

625-
zend_exception_restore();
628+
zend_exception_restore_fast(exception, prev_exception);
626629
}
627630

628631
void start_graceful_shutdown(void)
@@ -1071,6 +1074,9 @@ static zend_always_inline void scheduler_next_tick(void)
10711074
zend_fiber_transfer *transfer = NULL;
10721075
ZEND_ASYNC_SCHEDULER_CONTEXT = true;
10731076

1077+
zend_object **exception_ptr = &EG(exception);
1078+
zend_object **prev_exception_ptr = &EG(prev_exception);
1079+
10741080
execute_microtasks();
10751081
TRY_HANDLE_SUSPEND_EXCEPTION();
10761082

@@ -1122,7 +1128,10 @@ void async_scheduler_coroutine_suspend(void)
11221128
//
11231129
// Before suspending the coroutine, we save the current exception state.
11241130
//
1125-
zend_exception_save();
1131+
zend_object **exception_ptr = &EG(exception);
1132+
zend_object **prev_exception_ptr = &EG(prev_exception);
1133+
1134+
zend_exception_save_fast(exception_ptr, prev_exception_ptr);
11261135

11271136
/**
11281137
* Note that the Scheduler is initialized after the first use of suspend,
@@ -1131,8 +1140,8 @@ void async_scheduler_coroutine_suspend(void)
11311140
if (UNEXPECTED(ZEND_ASYNC_SCHEDULER == NULL)) {
11321141
async_scheduler_launch();
11331142

1134-
if (UNEXPECTED(EG(exception))) {
1135-
zend_exception_restore();
1143+
if (UNEXPECTED(*exception_ptr)) {
1144+
zend_exception_restore_fast(exception_ptr, prev_exception_ptr);
11361145
return;
11371146
}
11381147
}
@@ -1156,7 +1165,7 @@ void async_scheduler_coroutine_suspend(void)
11561165
if (coroutine->waker->events.nNumOfElements == 0 && not_in_queue) {
11571166
async_throw_error("The coroutine has no events to wait for");
11581167
zend_async_waker_clean(coroutine);
1159-
zend_exception_restore();
1168+
zend_exception_restore_fast(exception_ptr, prev_exception_ptr);
11601169
return;
11611170
}
11621171

@@ -1165,12 +1174,12 @@ void async_scheduler_coroutine_suspend(void)
11651174
// If an exception occurs during the startup of the Waker object,
11661175
// that exception belongs to the current coroutine,
11671176
// which means we have the right to immediately return to the point from which we were called.
1168-
if (UNEXPECTED(EG(exception))) {
1177+
if (UNEXPECTED(*exception_ptr)) {
11691178
// Before returning, We are required to properly destroy the Waker object.
1170-
zend_exception_save();
1179+
zend_exception_save_fast(exception_ptr, prev_exception_ptr);
11711180
stop_waker_events(coroutine->waker);
11721181
zend_async_waker_clean(coroutine);
1173-
zend_exception_restore();
1182+
zend_exception_restore_fast(exception_ptr, prev_exception_ptr);
11741183
return;
11751184
}
11761185

@@ -1208,7 +1217,7 @@ void async_scheduler_coroutine_suspend(void)
12081217
async_rethrow_exception(exception);
12091218
}
12101219

1211-
zend_exception_restore();
1220+
zend_exception_restore_fast(exception_ptr, prev_exception_ptr);
12121221
}
12131222

12141223
///////////////////////////////////////////////////////////

zend_common.c

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -108,16 +108,19 @@ uint32_t zend_current_exception_get_line(void)
108108

109109
zend_object *zend_exception_merge(zend_object *exception, bool to_previous, bool transfer_error)
110110
{
111-
zend_exception_save();
112-
zend_exception_restore();
111+
zend_object **exception_ptr = &EG(exception);
112+
zend_object **prev_exception_ptr = &EG(prev_exception);
113+
114+
zend_exception_save_fast(exception_ptr, prev_exception_ptr);
115+
zend_exception_restore_fast(exception_ptr, prev_exception_ptr);
113116

114117
if (exception == NULL) {
115-
exception = EG(exception);
116-
EG(exception) = NULL;
118+
exception = *exception_ptr;
119+
*exception_ptr = NULL;
117120
return exception;
118121
}
119122

120-
if (EG(exception) == NULL) {
123+
if (*exception_ptr == NULL) {
121124
return exception;
122125
}
123126

@@ -127,12 +130,12 @@ zend_object *zend_exception_merge(zend_object *exception, bool to_previous, bool
127130
if (false == transfer_error) {
128131
GC_ADDREF(exception);
129132
}
130-
zend_exception_set_previous(EG(exception), exception);
131-
exception = EG(exception);
132-
EG(exception) = NULL;
133+
zend_exception_set_previous(*exception_ptr, exception);
134+
exception = *exception_ptr;
135+
*exception_ptr = NULL;
133136
} else {
134-
zend_exception_set_previous(exception, EG(exception));
135-
EG(exception) = NULL;
137+
zend_exception_set_previous(exception, *exception_ptr);
138+
*exception_ptr = NULL;
136139
}
137140

138141
return exception;

0 commit comments

Comments
 (0)