Skip to content

Commit 31999c5

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

File tree

4 files changed

+106
-4
lines changed

4 files changed

+106
-4
lines changed

Zend/zend_execute_API.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ void init_executor(void) /* {{{ */
201201

202202
#ifdef PHP_ASYNC_API
203203
EG(shutdown_context) = (zend_shutdown_context_t) {
204-
.coroutine = NULL,
204+
.is_started = NULL,
205205
.idx = 0
206206
};
207207
#endif
@@ -294,8 +294,15 @@ static bool shutdown_destructors_context_switch_handler(
294294
bool is_enter,
295295
bool is_finishing
296296
) {
297-
zend_coroutine_t *shutdown_coroutine = ZEND_ASYNC_NEW_COROUTINE(ZEND_ASYNC_MAIN_SCOPE);
297+
if (is_enter) {
298+
return true;
299+
}
300+
301+
if (is_finishing) {
302+
return false;
303+
}
298304

305+
zend_coroutine_t *shutdown_coroutine = ZEND_ASYNC_SPAWN_WITH_SCOPE_EX(ZEND_ASYNC_MAIN_SCOPE, 1);
299306
shutdown_coroutine->internal_entry = shutdown_destructors_async;
300307
shutdown_coroutine->extended_dispose = shutdown_destructors_coroutine_dtor;
301308

@@ -317,7 +324,8 @@ void shutdown_destructors_async(void) /* {{{ */
317324

318325
HashTable *symbol_table = &EG(symbol_table);
319326

320-
if (shutdown_context->coroutine == NULL) {
327+
if (false == shutdown_context->is_started) {
328+
shutdown_context->is_started = true;
321329
shutdown_context->coroutine = coroutine;
322330
shutdown_context->num_elements = zend_hash_num_elements(symbol_table);
323331
shutdown_context->idx = symbol_table->nNumUsed;
@@ -369,8 +377,9 @@ void shutdown_destructors_async(void) /* {{{ */
369377
} while (shutdown_context->num_elements != zend_hash_num_elements(symbol_table));
370378

371379
if (should_continue) {
380+
shutdown_context->is_started = false;
372381
shutdown_context->coroutine = NULL;
373-
zend_objects_store_call_destructors(&EG(objects_store));
382+
zend_objects_store_call_destructors_async(&EG(objects_store));
374383
}
375384
} zend_catch {
376385
EG(symbol_table).pDestructor = zend_unclean_zval_ptr_dtor;

Zend/zend_globals.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ struct _zend_compiler_globals {
165165

166166
#ifdef PHP_ASYNC_API
167167
typedef struct {
168+
bool is_started;
168169
void *coroutine;
169170
uint32_t num_elements;
170171
uint32_t idx;

Zend/zend_objects_API.c

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include "zend_variables.h"
2424
#include "zend_API.h"
2525
#include "zend_objects_API.h"
26+
27+
#include "zend_async_API.h"
2628
#include "zend_fibers.h"
2729

2830
ZEND_API void ZEND_FASTCALL zend_objects_store_init(zend_objects_store *objects, uint32_t init_size)
@@ -63,6 +65,93 @@ ZEND_API void ZEND_FASTCALL zend_objects_store_call_destructors(zend_objects_sto
6365
}
6466
}
6567

68+
#ifdef PHP_ASYNC_API
69+
static void store_call_destructors_coroutine_dtor(zend_coroutine_t *coroutine)
70+
{
71+
zend_shutdown_context_t *shutdown_context = &EG(shutdown_context);
72+
73+
if (shutdown_context->coroutine == coroutine) {
74+
shutdown_context->coroutine = NULL;
75+
zend_error(E_CORE_ERROR, "Shutdown destructors coroutine was not finished property");
76+
shutdown_destructors();
77+
}
78+
}
79+
80+
static void store_call_destructors_entry(void)
81+
{
82+
zend_objects_store_call_destructors_async(&EG(objects_store));
83+
}
84+
85+
static bool store_call_destructors_context_switch_handler(
86+
zend_coroutine_t *coroutine,
87+
bool is_enter,
88+
bool is_finishing
89+
) {
90+
if (is_enter) {
91+
return true;
92+
}
93+
94+
if (is_finishing) {
95+
return false;
96+
}
97+
98+
zend_coroutine_t *shutdown_coroutine = ZEND_ASYNC_SPAWN_WITH_SCOPE_EX(ZEND_ASYNC_MAIN_SCOPE, 1);
99+
shutdown_coroutine->internal_entry = store_call_destructors_entry;
100+
shutdown_coroutine->extended_dispose = store_call_destructors_coroutine_dtor;
101+
102+
return false;
103+
}
104+
105+
ZEND_API void ZEND_FASTCALL zend_objects_store_call_destructors_async(zend_objects_store *objects)
106+
{
107+
if (objects->top <= 1) {
108+
return;
109+
}
110+
111+
EG(flags) |= EG_FLAGS_OBJECT_STORE_NO_REUSE;
112+
113+
zend_class_entry *coroutine_ce = ZEND_ASYNC_GET_CE(ZEND_ASYNC_CLASS_COROUTINE);
114+
115+
zend_coroutine_t *coroutine = ZEND_ASYNC_CURRENT_COROUTINE;
116+
117+
zend_shutdown_context_t *shutdown_context = &EG(shutdown_context);
118+
119+
if (coroutine == NULL) {
120+
ZEND_ASYNC_ADD_MAIN_COROUTINE_START_HANDLER(store_call_destructors_context_switch_handler);
121+
} else {
122+
ZEND_COROUTINE_ADD_SWITCH_HANDLER(coroutine, store_call_destructors_context_switch_handler);
123+
}
124+
125+
if (false == shutdown_context->is_started) {
126+
shutdown_context->is_started = true;
127+
shutdown_context->coroutine = coroutine;
128+
shutdown_context->idx = 1;
129+
}
130+
131+
for (uint32_t i = shutdown_context->idx; i < objects->top; i++) {
132+
zend_object *obj = objects->object_buckets[i];
133+
if (IS_OBJ_VALID(obj) && obj->ce != coroutine_ce) {
134+
if (!(OBJ_FLAGS(obj) & IS_OBJ_DESTRUCTOR_CALLED)) {
135+
GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED);
136+
137+
if (obj->handlers->dtor_obj != zend_objects_destroy_object || obj->ce->destructor) {
138+
shutdown_context->idx = i;
139+
GC_ADDREF(obj);
140+
obj->handlers->dtor_obj(obj);
141+
GC_DELREF(obj);
142+
143+
if (coroutine != ZEND_ASYNC_CURRENT_COROUTINE) {
144+
return;
145+
}
146+
}
147+
}
148+
}
149+
}
150+
151+
shutdown_context->is_started = false;
152+
}
153+
#endif
154+
66155
ZEND_API void ZEND_FASTCALL zend_objects_store_mark_destructed(zend_objects_store *objects)
67156
{
68157
if (objects->object_buckets && objects->top > 1) {

Zend/zend_objects_API.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ typedef struct _zend_objects_store {
5555
BEGIN_EXTERN_C()
5656
ZEND_API void ZEND_FASTCALL zend_objects_store_init(zend_objects_store *objects, uint32_t init_size);
5757
ZEND_API void ZEND_FASTCALL zend_objects_store_call_destructors(zend_objects_store *objects);
58+
#ifdef PHP_ASYNC_API
59+
ZEND_API void ZEND_FASTCALL zend_objects_store_call_destructors_async(zend_objects_store *objects);
60+
#endif
5861
ZEND_API void ZEND_FASTCALL zend_objects_store_mark_destructed(zend_objects_store *objects);
5962
ZEND_API void ZEND_FASTCALL zend_objects_store_free_object_storage(zend_objects_store *objects, bool fast_shutdown);
6063
ZEND_API void ZEND_FASTCALL zend_objects_store_destroy(zend_objects_store *objects);

0 commit comments

Comments
 (0)