Skip to content

Commit 1f109f6

Browse files
committed
+ shutdown_destructors_async Support for concurrent iteration of the global variable table
1 parent ce5c1d7 commit 1f109f6

File tree

5 files changed

+134
-1
lines changed

5 files changed

+134
-1
lines changed

Zend/zend.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1336,7 +1336,7 @@ void zend_call_destructors(void) /* {{{ */
13361336
{
13371337
zend_try {
13381338
#ifdef PHP_ASYNC_API
1339-
async_shutdown_destructors();
1339+
shutdown_destructors_async();
13401340
#else
13411341
shutdown_destructors();
13421342
#endif

Zend/zend_execute.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ ZEND_API extern zend_class_entry *(*zend_autoload)(zend_string *name, zend_strin
4040
void init_executor(void);
4141
void shutdown_executor(void);
4242
void shutdown_destructors(void);
43+
#ifdef PHP_ASYNC_API
44+
void shutdown_destructors_async(void);
45+
#endif
4346
ZEND_API void zend_shutdown_executor_values(bool fast_shutdown);
4447

4548
ZEND_API void zend_init_execute_data(zend_execute_data *execute_data, zend_op_array *op_array, zval *return_value);

Zend/zend_execute_API.c

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,13 @@ void init_executor(void) /* {{{ */
199199
EG(filename_override) = NULL;
200200
EG(lineno_override) = -1;
201201

202+
#ifdef PHP_ASYNC_API
203+
EG(shutdown_context) = (zend_shutdown_context_t) {
204+
.coroutine = NULL,
205+
.idx = 0
206+
};
207+
#endif
208+
202209
zend_max_execution_timer_init();
203210
zend_fiber_init();
204211
zend_weakrefs_init();
@@ -267,6 +274,113 @@ void shutdown_destructors(void) /* {{{ */
267274
}
268275
/* }}} */
269276

277+
#ifdef PHP_ASYNC_API
278+
#include "zend_async_API.h"
279+
280+
static void shutdown_destructors_coroutine_dtor(zend_coroutine_t *coroutine)
281+
{
282+
zend_shutdown_context_t *shutdown_context = &EG(shutdown_context);
283+
284+
if (shutdown_context->coroutine == coroutine) {
285+
shutdown_context->coroutine = NULL;
286+
zend_error(E_CORE_ERROR, "Shutdown destructors coroutine was not finished property");
287+
EG(symbol_table).pDestructor = zend_unclean_zval_ptr_dtor;
288+
shutdown_destructors();
289+
}
290+
}
291+
292+
static bool shutdown_destructors_context_switch_handler(
293+
zend_coroutine_t *coroutine,
294+
bool is_enter,
295+
bool is_finishing
296+
) {
297+
zend_coroutine_t *shutdown_coroutine = ZEND_ASYNC_NEW_COROUTINE(ZEND_ASYNC_MAIN_SCOPE);
298+
299+
shutdown_coroutine->internal_entry = shutdown_destructors_async;
300+
shutdown_coroutine->extended_dispose = shutdown_destructors_coroutine_dtor;
301+
302+
return false;
303+
}
304+
305+
void shutdown_destructors_async(void) /* {{{ */
306+
{
307+
zend_coroutine_t *coroutine = ZEND_ASYNC_CURRENT_COROUTINE;
308+
bool should_continue = false;
309+
310+
zend_shutdown_context_t *shutdown_context = &EG(shutdown_context);
311+
312+
if (coroutine == NULL) {
313+
ZEND_ASYNC_ADD_MAIN_COROUTINE_START_HANDLER(shutdown_destructors_context_switch_handler);
314+
} else {
315+
ZEND_COROUTINE_ADD_SWITCH_HANDLER(coroutine, shutdown_destructors_context_switch_handler);
316+
}
317+
318+
HashTable *symbol_table = &EG(symbol_table);
319+
320+
if (shutdown_context->coroutine == NULL) {
321+
shutdown_context->coroutine = coroutine;
322+
shutdown_context->num_elements = zend_hash_num_elements(symbol_table);
323+
shutdown_context->idx = symbol_table->nNumUsed;
324+
}
325+
326+
if (CG(unclean_shutdown)) {
327+
EG(symbol_table).pDestructor = zend_unclean_zval_ptr_dtor;
328+
}
329+
330+
zend_try {
331+
do {
332+
if (should_continue) {
333+
shutdown_context->num_elements = zend_hash_num_elements(symbol_table);
334+
shutdown_context->idx = symbol_table->nNumUsed;
335+
} else {
336+
should_continue = true;
337+
}
338+
339+
while (shutdown_context->idx > 0) {
340+
341+
shutdown_context->idx--;
342+
343+
Bucket *p = symbol_table->arData + shutdown_context->idx;
344+
345+
if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) {
346+
continue;
347+
}
348+
349+
zval *zv = &p->val;
350+
if (Z_TYPE_P(zv) == IS_INDIRECT) {
351+
zv = Z_INDIRECT_P(zv);
352+
}
353+
354+
if (Z_TYPE_P(zv) == IS_OBJECT && Z_REFCOUNT_P(zv) == 1) {
355+
zend_hash_del_bucket(symbol_table, p);
356+
}
357+
358+
// If the coroutine has changed
359+
if (coroutine != ZEND_ASYNC_CURRENT_COROUTINE) {
360+
should_continue = false;
361+
break;
362+
}
363+
}
364+
365+
if (false == should_continue) {
366+
break;
367+
}
368+
369+
} while (shutdown_context->num_elements != zend_hash_num_elements(symbol_table));
370+
371+
if (should_continue) {
372+
shutdown_context->coroutine = NULL;
373+
zend_objects_store_call_destructors(&EG(objects_store));
374+
}
375+
} zend_catch {
376+
EG(symbol_table).pDestructor = zend_unclean_zval_ptr_dtor;
377+
shutdown_destructors();
378+
zend_bailout();
379+
} zend_end_try();
380+
}
381+
/* }}} */
382+
#endif
383+
270384
/* Free values held by the executor. */
271385
ZEND_API void zend_shutdown_executor_values(bool fast_shutdown)
272386
{

Zend/zend_globals.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,13 @@ struct _zend_compiler_globals {
163163
#endif
164164
};
165165

166+
#ifdef PHP_ASYNC_API
167+
typedef struct {
168+
void *coroutine;
169+
uint32_t num_elements;
170+
uint32_t idx;
171+
} zend_shutdown_context_t;
172+
#endif
166173

167174
struct _zend_executor_globals {
168175
zval uninitialized_zval;
@@ -259,6 +266,11 @@ struct _zend_executor_globals {
259266
const zend_op *opline_before_exception;
260267
zend_op exception_op[3];
261268

269+
#ifdef PHP_ASYNC_API
270+
// Used to track the state of shutdown destructors in coroutines
271+
zend_shutdown_context_t shutdown_context;
272+
#endif
273+
262274
struct _zend_module_entry *current_module;
263275

264276
bool active;

main/main.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1935,6 +1935,10 @@ void php_request_shutdown(void *dummy)
19351935
zend_call_destructors();
19361936
} zend_end_try();
19371937

1938+
#ifdef PHP_ASYNC_API
1939+
ZEND_ASYNC_RUN_SCHEDULER_AFTER_MAIN();
1940+
#endif
1941+
19381942
/* 3. Flush all output buffers */
19391943
zend_try {
19401944
php_output_end_all();

0 commit comments

Comments
 (0)