@@ -199,6 +199,13 @@ void init_executor(void) /* {{{ */
199
199
EG (filename_override ) = NULL ;
200
200
EG (lineno_override ) = -1 ;
201
201
202
+ #ifdef PHP_ASYNC_API
203
+ EG (shutdown_context ) = (zend_shutdown_context_t ) {
204
+ .coroutine = NULL ,
205
+ .idx = 0
206
+ };
207
+ #endif
208
+
202
209
zend_max_execution_timer_init ();
203
210
zend_fiber_init ();
204
211
zend_weakrefs_init ();
@@ -267,6 +274,113 @@ void shutdown_destructors(void) /* {{{ */
267
274
}
268
275
/* }}} */
269
276
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
+
270
384
/* Free values held by the executor. */
271
385
ZEND_API void zend_shutdown_executor_values (bool fast_shutdown )
272
386
{
0 commit comments