2525#include "scope.h"
2626#include "zend_common.h"
2727#include "zend_exceptions.h"
28+ #include "zend_generators.h"
2829#include "zend_ini.h"
2930
3031#define METHOD (name ) PHP_METHOD(Async_Coroutine, name)
@@ -1182,6 +1183,138 @@ zend_coroutine_t *async_new_coroutine(zend_async_scope_t *scope)
11821183 return & coroutine -> coroutine ;
11831184}
11841185
1186+ static HashTable * async_coroutine_object_gc (zend_object * object , zval * * table , int * num )
1187+ {
1188+ async_coroutine_t * coroutine = (async_coroutine_t * )ZEND_ASYNC_OBJECT_TO_EVENT (object );
1189+ zend_get_gc_buffer * buf = zend_get_gc_buffer_create ();
1190+
1191+ /* Always add basic ZVALs from coroutine structure */
1192+ zend_get_gc_buffer_add_zval (buf , & coroutine -> coroutine .result );
1193+
1194+ /* Add objects that may be present */
1195+ if (coroutine -> coroutine .exception ) {
1196+ zend_get_gc_buffer_add_obj (buf , coroutine -> coroutine .exception );
1197+ }
1198+
1199+ if (coroutine -> deferred_cancellation ) {
1200+ zend_get_gc_buffer_add_obj (buf , coroutine -> deferred_cancellation );
1201+ }
1202+
1203+ /* Add finally handlers if present */
1204+ if (coroutine -> finally_handlers ) {
1205+ zval * val ;
1206+ ZEND_HASH_FOREACH_VAL (coroutine -> finally_handlers , val ) {
1207+ zend_get_gc_buffer_add_zval (buf , val );
1208+ } ZEND_HASH_FOREACH_END ();
1209+ }
1210+
1211+ /* Add internal context HashTable if present */
1212+ if (coroutine -> coroutine .internal_context ) {
1213+ zval * val ;
1214+ ZEND_HASH_FOREACH_VAL (coroutine -> coroutine .internal_context , val ) {
1215+ zend_get_gc_buffer_add_zval (buf , val );
1216+ } ZEND_HASH_FOREACH_END ();
1217+ }
1218+
1219+ /* Add fcall function name and parameters if present */
1220+ if (coroutine -> coroutine .fcall ) {
1221+ zend_get_gc_buffer_add_zval (buf , & coroutine -> coroutine .fcall -> fci .function_name );
1222+
1223+ /* Add function parameters */
1224+ if (coroutine -> coroutine .fcall -> fci .param_count > 0 && coroutine -> coroutine .fcall -> fci .params ) {
1225+ for (uint32_t i = 0 ; i < coroutine -> coroutine .fcall -> fci .param_count ; i ++ ) {
1226+ zend_get_gc_buffer_add_zval (buf , & coroutine -> coroutine .fcall -> fci .params [i ]);
1227+ }
1228+ }
1229+ }
1230+
1231+ /* Add waker-related ZVALs if present */
1232+ if (coroutine -> coroutine .waker ) {
1233+ zend_get_gc_buffer_add_zval (buf , & coroutine -> coroutine .waker -> result );
1234+
1235+ if (coroutine -> coroutine .waker -> error ) {
1236+ zend_get_gc_buffer_add_obj (buf , coroutine -> coroutine .waker -> error );
1237+ }
1238+
1239+ /* Add events HashTable contents */
1240+ zval * event_val ;
1241+ zval zval_object ;
1242+ ZEND_HASH_FOREACH_VAL (& coroutine -> coroutine .waker -> events , event_val ) {
1243+
1244+ zend_async_event_t * event = (zend_async_event_t * ) Z_PTR_P (event_val );
1245+
1246+ if (ZEND_ASYNC_EVENT_IS_REFERENCE (event ) || ZEND_ASYNC_EVENT_IS_ZEND_OBJ (event )) {
1247+ ZVAL_OBJ (& zval_object , ZEND_ASYNC_EVENT_TO_OBJECT (event ));
1248+ zend_get_gc_buffer_add_zval (buf , & zval_object );
1249+ }
1250+ } ZEND_HASH_FOREACH_END ();
1251+
1252+ /* Add triggered events if present */
1253+ if (coroutine -> coroutine .waker -> triggered_events ) {
1254+ ZEND_HASH_FOREACH_VAL (coroutine -> coroutine .waker -> triggered_events , event_val ) {
1255+ zend_get_gc_buffer_add_zval (buf , event_val );
1256+ } ZEND_HASH_FOREACH_END ();
1257+ }
1258+ }
1259+
1260+ /* Add context ZVALs if present */
1261+ if (coroutine -> coroutine .context ) {
1262+ /* Cast to actual context implementation to access HashTables */
1263+ async_context_t * context = (async_context_t * )coroutine -> coroutine .context ;
1264+
1265+ /* Add all values from context->values HashTable */
1266+ zval * val ;
1267+ ZEND_HASH_FOREACH_VAL (& context -> values , val ) {
1268+ zend_get_gc_buffer_add_zval (buf , val );
1269+ } ZEND_HASH_FOREACH_END ();
1270+
1271+ /* Add all object keys from context->keys HashTable */
1272+ ZEND_HASH_FOREACH_VAL (& context -> keys , val ) {
1273+ zend_get_gc_buffer_add_zval (buf , val );
1274+ } ZEND_HASH_FOREACH_END ();
1275+ }
1276+
1277+ /* Check if we should traverse execution stack (similar to fibers) */
1278+ if (coroutine -> context .status != ZEND_FIBER_STATUS_SUSPENDED ||
1279+ !coroutine -> execute_data ) {
1280+ zend_get_gc_buffer_use (buf , table , num );
1281+ return NULL ;
1282+ }
1283+
1284+ /* Traverse execution stack for suspended coroutines */
1285+ HashTable * lastSymTable = NULL ;
1286+ zend_execute_data * ex = coroutine -> execute_data ;
1287+ for (; ex ; ex = ex -> prev_execute_data ) {
1288+ HashTable * symTable ;
1289+ if (ZEND_CALL_INFO (ex ) & ZEND_CALL_GENERATOR ) {
1290+ zend_generator * generator = (zend_generator * )ex -> return_value ;
1291+ if (!(generator -> flags & ZEND_GENERATOR_CURRENTLY_RUNNING )) {
1292+ continue ;
1293+ }
1294+ symTable = zend_generator_frame_gc (buf , generator );
1295+ } else {
1296+ symTable = zend_unfinished_execution_gc_ex (ex ,
1297+ ex -> func && ZEND_USER_CODE (ex -> func -> type ) ? ex -> call : NULL ,
1298+ buf , false);
1299+ }
1300+ if (symTable ) {
1301+ if (lastSymTable ) {
1302+ zval * val ;
1303+ ZEND_HASH_FOREACH_VAL (lastSymTable , val ) {
1304+ if (EXPECTED (Z_TYPE_P (val ) == IS_INDIRECT )) {
1305+ val = Z_INDIRECT_P (val );
1306+ }
1307+ zend_get_gc_buffer_add_zval (buf , val );
1308+ } ZEND_HASH_FOREACH_END ();
1309+ }
1310+ lastSymTable = symTable ;
1311+ }
1312+ }
1313+
1314+ zend_get_gc_buffer_use (buf , table , num );
1315+ return lastSymTable ;
1316+ }
1317+
11851318static zend_object_handlers coroutine_handlers ;
11861319
11871320void async_register_coroutine_ce (void )
@@ -1197,6 +1330,7 @@ void async_register_coroutine_ce(void)
11971330 coroutine_handlers .clone_obj = NULL ;
11981331 coroutine_handlers .dtor_obj = coroutine_object_destroy ;
11991332 coroutine_handlers .free_obj = coroutine_free ;
1333+ coroutine_handlers .get_gc = async_coroutine_object_gc ;
12001334}
12011335
12021336//////////////////////////////////////////////////////////////////////
0 commit comments