Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Implement a reference queue API.

	* gc.c: A reference queue allows one to queue
	callbcks for when objects are collected.
	It allows for safe cleanup of objects that can
	only be done when it is effectively collected.
	The major difference with regular finalization
	is that the collector makes sure the object
	was collected - and can't be resurrected.

	* gc-internal.h: Export entrypoints for the
	new API.
  • Loading branch information...
commit 8eb1189099e02372fd45ca1c67230eccf1edddc0 1 parent 7424fef
@kumpera kumpera authored
Showing with 164 additions and 0 deletions.
  1. +23 −0 mono/metadata/gc-internal.h
  2. +141 −0 mono/metadata/gc.c
View
23 mono/metadata/gc-internal.h
@@ -335,5 +335,28 @@ gboolean mono_gc_precise_stack_mark_enabled (void) MONO_INTERNAL;
FILE *mono_gc_get_logfile (void) MONO_INTERNAL;
+typedef void (*mono_reference_queue_callback) (void *user_data);
+
+typedef struct _MonoReferenceQueue MonoReferenceQueue;
+typedef struct _RefQueueEntry RefQueueEntry;
+
+struct _RefQueueEntry {
+ void *dis_link;
+ void *user_data;
+ RefQueueEntry *next;
+};
+
+struct _MonoReferenceQueue {
+ RefQueueEntry *queue;
+ mono_reference_queue_callback callback;
+ MonoReferenceQueue *next;
+ gboolean should_be_deleted;
+};
+
+MonoReferenceQueue* mono_gc_reference_queue_new (mono_reference_queue_callback callback) MONO_INTERNAL;
+void mono_gc_reference_queue_free (MonoReferenceQueue *queue) MONO_INTERNAL;
+gboolean mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data) MONO_INTERNAL;
+
+
#endif /* __MONO_METADATA_GC_INTERNAL_H__ */
View
141 mono/metadata/gc.c
@@ -54,6 +54,7 @@ static gboolean finalizing_root_domain = FALSE;
#define mono_finalizer_lock() EnterCriticalSection (&finalizer_mutex)
#define mono_finalizer_unlock() LeaveCriticalSection (&finalizer_mutex)
static CRITICAL_SECTION finalizer_mutex;
+static CRITICAL_SECTION reference_queue_mutex;
static GSList *domains_to_finalize= NULL;
static MonoMList *threads_to_finalize = NULL;
@@ -64,6 +65,7 @@ static void object_register_finalizer (MonoObject *obj, void (*callback)(void *,
static void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj);
+static void reference_queue_proccess_all (void);
#ifndef HAVE_NULL_GC
static HANDLE pending_done_event;
static HANDLE shutdown_event;
@@ -1065,6 +1067,8 @@ finalizer_thread (gpointer unused)
mono_attach_maybe_start ();
#endif
+ reference_queue_proccess_all ();
+
if (domains_to_finalize) {
mono_finalizer_lock ();
if (domains_to_finalize) {
@@ -1083,6 +1087,7 @@ finalizer_thread (gpointer unused)
*/
mono_gc_invoke_finalizers ();
+
SetEvent (pending_done_event);
}
@@ -1097,6 +1102,7 @@ mono_gc_init (void)
InitializeCriticalSection (&allocator_section);
InitializeCriticalSection (&finalizer_mutex);
+ InitializeCriticalSection (&reference_queue_mutex);
MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries);
MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries);
@@ -1174,6 +1180,7 @@ mono_gc_cleanup (void)
DeleteCriticalSection (&handle_section);
DeleteCriticalSection (&allocator_section);
DeleteCriticalSection (&finalizer_mutex);
+ DeleteCriticalSection (&reference_queue_mutex);
}
#else
@@ -1292,3 +1299,137 @@ mono_gc_alloc_mature (MonoVTable *vtable)
return mono_object_new_specific (vtable);
}
#endif
+
+
+static MonoReferenceQueue *ref_queues;
+
+static void
+ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
+{
+ do {
+ /* Guard if head is changed concurrently. */
+ while (*prev != element)
+ prev = &(*prev)->next;
+ } while (prev && InterlockedCompareExchangePointer ((void*)prev, element->next, element) != element);
+}
+
+static void
+ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
+{
+ RefQueueEntry *current;
+ do {
+ current = *head;
+ value->next = current;
+ } while (InterlockedCompareExchangePointer ((void*)head, value, current) != current);
+}
+
+static void
+reference_queue_proccess (MonoReferenceQueue *queue)
+{
+ RefQueueEntry **iter = &queue->queue;
+ RefQueueEntry *entry;
+ while ((entry = *iter)) {
+ if (queue->should_be_deleted || !mono_gc_weak_link_get (&entry->dis_link)) {
+ ref_list_remove_element (iter, entry);
+ mono_gc_weak_link_remove (&entry->dis_link);
+ queue->callback (entry->user_data);
+ g_free (entry);
+ } else {
+ iter = &entry->next;
+ }
+ }
+}
+
+static void
+reference_queue_proccess_all (void)
+{
+ MonoReferenceQueue **iter;
+ MonoReferenceQueue *queue = ref_queues;
+ for (; queue; queue = queue->next)
+ reference_queue_proccess (queue);
+
+restart:
+ EnterCriticalSection (&reference_queue_mutex);
+ for (iter = &ref_queues; *iter;) {
+ queue = *iter;
+ if (!queue->should_be_deleted) {
+ iter = &queue->next;
+ continue;
+ }
+ if (queue->queue) {
+ LeaveCriticalSection (&reference_queue_mutex);
+ reference_queue_proccess (queue);
+ goto restart;
+ }
+ *iter = queue->next;
+ g_free (queue);
+ }
+ LeaveCriticalSection (&reference_queue_mutex);
+}
+
+/**
+ * mono_gc_reference_queue_new:
+ * @callback callback used when processing dead entries.
+ *
+ * Create a new reference queue used to process collected objects.
+ * A reference queue let you queue the pair (managed object, user data).
+ * Once the managed object is collected @callback will be called
+ * in the finalizer thread with 'user data' as argument.
+ *
+ * The callback is called without any locks held.
+ */
+MonoReferenceQueue*
+mono_gc_reference_queue_new (mono_reference_queue_callback callback)
+{
+ MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
+ res->callback = callback;
+
+ EnterCriticalSection (&reference_queue_mutex);
+ res->next = ref_queues;
+ ref_queues = res;
+ LeaveCriticalSection (&reference_queue_mutex);
+
+ return res;
+}
+
+/**
+ * mono_gc_reference_queue_add:
+ * @queue the queue to add the reference to.
+ * @obj the object to be watched for collection
+ * @user_data parameter to be passed to the queue callback
+ *
+ * Queue an object to be watched for collection.
+ *
+ * @returns false if the queue is scheduled to be freed.
+ */
+gboolean
+mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
+{
+ RefQueueEntry *head;
+ RefQueueEntry *entry;
+ if (queue->should_be_deleted)
+ return FALSE;
+
+ entry = g_new0 (RefQueueEntry, 1);
+ entry->user_data = user_data;
+ mono_gc_weak_link_add (&entry->dis_link, obj, TRUE);
+ ref_list_push (&queue->queue, entry);
+ return TRUE;
+}
+
+/**
+ * mono_gc_reference_queue_free:
+ * @queue the queue that should be deleted.
+ *
+ * This operation signals that @queue should be deleted. This operation is deferred
+ * as it happens on the finalizer thread.
+ *
+ * After this call, no further objects can be queued. It's the responsibility of the
+ * caller to make sure that no further attempt to access queue will be made.
+ */
+void
+mono_gc_reference_queue_free (MonoReferenceQueue *queue)
+{
+ queue->should_be_deleted = TRUE;
+}
+
Please sign in to comment.
Something went wrong with that request. Please try again.