@@ -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. */
271385ZEND_API void zend_shutdown_executor_values (bool fast_shutdown )
272386{
0 commit comments