@@ -54,6 +54,7 @@ static gboolean finalizing_root_domain = FALSE;
5454#define mono_finalizer_lock () EnterCriticalSection (&finalizer_mutex)
5555#define mono_finalizer_unlock () LeaveCriticalSection (&finalizer_mutex)
5656static CRITICAL_SECTION finalizer_mutex ;
57+ static CRITICAL_SECTION reference_queue_mutex ;
5758
5859static GSList * domains_to_finalize = NULL ;
5960static MonoMList * threads_to_finalize = NULL ;
@@ -64,6 +65,7 @@ static void object_register_finalizer (MonoObject *obj, void (*callback)(void *,
6465
6566static void mono_gchandle_set_target (guint32 gchandle , MonoObject * obj );
6667
68+ static void reference_queue_proccess_all (void );
6769#ifndef HAVE_NULL_GC
6870static HANDLE pending_done_event ;
6971static HANDLE shutdown_event ;
@@ -1065,6 +1067,8 @@ finalizer_thread (gpointer unused)
10651067 mono_attach_maybe_start ();
10661068#endif
10671069
1070+ reference_queue_proccess_all ();
1071+
10681072 if (domains_to_finalize ) {
10691073 mono_finalizer_lock ();
10701074 if (domains_to_finalize ) {
@@ -1083,6 +1087,7 @@ finalizer_thread (gpointer unused)
10831087 */
10841088 mono_gc_invoke_finalizers ();
10851089
1090+
10861091 SetEvent (pending_done_event );
10871092 }
10881093
@@ -1097,6 +1102,7 @@ mono_gc_init (void)
10971102 InitializeCriticalSection (& allocator_section );
10981103
10991104 InitializeCriticalSection (& finalizer_mutex );
1105+ InitializeCriticalSection (& reference_queue_mutex );
11001106
11011107 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL ].entries );
11021108 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED ].entries );
@@ -1174,6 +1180,7 @@ mono_gc_cleanup (void)
11741180 DeleteCriticalSection (& handle_section );
11751181 DeleteCriticalSection (& allocator_section );
11761182 DeleteCriticalSection (& finalizer_mutex );
1183+ DeleteCriticalSection (& reference_queue_mutex );
11771184}
11781185
11791186#else
@@ -1292,3 +1299,137 @@ mono_gc_alloc_mature (MonoVTable *vtable)
12921299 return mono_object_new_specific (vtable );
12931300}
12941301#endif
1302+
1303+
1304+ static MonoReferenceQueue * ref_queues ;
1305+
1306+ static void
1307+ ref_list_remove_element (RefQueueEntry * * prev , RefQueueEntry * element )
1308+ {
1309+ do {
1310+ /* Guard if head is changed concurrently. */
1311+ while (* prev != element )
1312+ prev = & (* prev )-> next ;
1313+ } while (prev && InterlockedCompareExchangePointer ((void * )prev , element -> next , element ) != element );
1314+ }
1315+
1316+ static void
1317+ ref_list_push (RefQueueEntry * * head , RefQueueEntry * value )
1318+ {
1319+ RefQueueEntry * current ;
1320+ do {
1321+ current = * head ;
1322+ value -> next = current ;
1323+ } while (InterlockedCompareExchangePointer ((void * )head , value , current ) != current );
1324+ }
1325+
1326+ static void
1327+ reference_queue_proccess (MonoReferenceQueue * queue )
1328+ {
1329+ RefQueueEntry * * iter = & queue -> queue ;
1330+ RefQueueEntry * entry ;
1331+ while ((entry = * iter )) {
1332+ if (queue -> should_be_deleted || !mono_gc_weak_link_get (& entry -> dis_link )) {
1333+ ref_list_remove_element (iter , entry );
1334+ mono_gc_weak_link_remove (& entry -> dis_link );
1335+ queue -> callback (entry -> user_data );
1336+ g_free (entry );
1337+ } else {
1338+ iter = & entry -> next ;
1339+ }
1340+ }
1341+ }
1342+
1343+ static void
1344+ reference_queue_proccess_all (void )
1345+ {
1346+ MonoReferenceQueue * * iter ;
1347+ MonoReferenceQueue * queue = ref_queues ;
1348+ for (; queue ; queue = queue -> next )
1349+ reference_queue_proccess (queue );
1350+
1351+ restart :
1352+ EnterCriticalSection (& reference_queue_mutex );
1353+ for (iter = & ref_queues ; * iter ;) {
1354+ queue = * iter ;
1355+ if (!queue -> should_be_deleted ) {
1356+ iter = & queue -> next ;
1357+ continue ;
1358+ }
1359+ if (queue -> queue ) {
1360+ LeaveCriticalSection (& reference_queue_mutex );
1361+ reference_queue_proccess (queue );
1362+ goto restart ;
1363+ }
1364+ * iter = queue -> next ;
1365+ g_free (queue );
1366+ }
1367+ LeaveCriticalSection (& reference_queue_mutex );
1368+ }
1369+
1370+ /**
1371+ * mono_gc_reference_queue_new:
1372+ * @callback callback used when processing dead entries.
1373+ *
1374+ * Create a new reference queue used to process collected objects.
1375+ * A reference queue let you queue the pair (managed object, user data).
1376+ * Once the managed object is collected @callback will be called
1377+ * in the finalizer thread with 'user data' as argument.
1378+ *
1379+ * The callback is called without any locks held.
1380+ */
1381+ MonoReferenceQueue *
1382+ mono_gc_reference_queue_new (mono_reference_queue_callback callback )
1383+ {
1384+ MonoReferenceQueue * res = g_new0 (MonoReferenceQueue , 1 );
1385+ res -> callback = callback ;
1386+
1387+ EnterCriticalSection (& reference_queue_mutex );
1388+ res -> next = ref_queues ;
1389+ ref_queues = res ;
1390+ LeaveCriticalSection (& reference_queue_mutex );
1391+
1392+ return res ;
1393+ }
1394+
1395+ /**
1396+ * mono_gc_reference_queue_add:
1397+ * @queue the queue to add the reference to.
1398+ * @obj the object to be watched for collection
1399+ * @user_data parameter to be passed to the queue callback
1400+ *
1401+ * Queue an object to be watched for collection.
1402+ *
1403+ * @returns false if the queue is scheduled to be freed.
1404+ */
1405+ gboolean
1406+ mono_gc_reference_queue_add (MonoReferenceQueue * queue , MonoObject * obj , void * user_data )
1407+ {
1408+ RefQueueEntry * head ;
1409+ RefQueueEntry * entry ;
1410+ if (queue -> should_be_deleted )
1411+ return FALSE;
1412+
1413+ entry = g_new0 (RefQueueEntry , 1 );
1414+ entry -> user_data = user_data ;
1415+ mono_gc_weak_link_add (& entry -> dis_link , obj , TRUE);
1416+ ref_list_push (& queue -> queue , entry );
1417+ return TRUE;
1418+ }
1419+
1420+ /**
1421+ * mono_gc_reference_queue_free:
1422+ * @queue the queue that should be deleted.
1423+ *
1424+ * This operation signals that @queue should be deleted. This operation is deferred
1425+ * as it happens on the finalizer thread.
1426+ *
1427+ * After this call, no further objects can be queued. It's the responsibility of the
1428+ * caller to make sure that no further attempt to access queue will be made.
1429+ */
1430+ void
1431+ mono_gc_reference_queue_free (MonoReferenceQueue * queue )
1432+ {
1433+ queue -> should_be_deleted = TRUE;
1434+ }
1435+
0 commit comments