diff --git a/mono/metadata/gc-internal.h b/mono/metadata/gc-internal.h index f80180ec06c2e..8f43c6c3e8a53 100644 --- a/mono/metadata/gc-internal.h +++ b/mono/metadata/gc-internal.h @@ -198,5 +198,72 @@ void *mono_gc_scan_object (void *obj) MONO_INTERNAL; /* Return the bitmap encoded by a descriptor */ gsize* mono_gc_get_bitmap_for_descr (void *descr, int *numbits) MONO_INTERNAL; +/* Return the suspend signal number used by the GC to suspend threads, + or -1 if not applicable. */ +int mono_gc_get_suspend_signal (void) MONO_INTERNAL; + +/* + * Return a human readable description of the GC in malloc-ed memory. + */ +char* mono_gc_get_description (void) MONO_INTERNAL; + +/* + * Configure the GC to desktop mode + */ +void mono_gc_set_desktop_mode (void) MONO_INTERNAL; + +/* + * Return whenever this GC can move objects + */ +gboolean mono_gc_is_moving (void) MONO_INTERNAL; + +typedef void* (*MonoGCLockedCallbackFunc) (void *data); + +void* mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data) MONO_INTERNAL; + +int mono_gc_get_los_limit (void) MONO_INTERNAL; + +guint8* mono_gc_get_card_table (int *shift_bits, gpointer *card_mask) MONO_INTERNAL; + +void* mono_gc_get_nursery (int *shift_bits, size_t *size) MONO_INTERNAL; + +/* + * Return whenever GC is disabled + */ +gboolean mono_gc_is_disabled (void) MONO_INTERNAL; + +#if defined(__MACH__) +void mono_gc_register_mach_exception_thread (pthread_t thread) MONO_INTERNAL; +pthread_t mono_gc_get_mach_exception_thread (void) MONO_INTERNAL; +#endif + +gboolean mono_gc_parse_environment_string_extract_number (const char *str, glong *out) MONO_INTERNAL; + +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__ */ diff --git a/mono/metadata/gc.c b/mono/metadata/gc.c index edbd47dfd18f8..81e784fcff5d8 100644 --- a/mono/metadata/gc.c +++ b/mono/metadata/gc.c @@ -52,6 +52,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; @@ -62,6 +63,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; @@ -1029,6 +1031,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) { @@ -1047,6 +1051,7 @@ finalizer_thread (gpointer unused) */ mono_gc_invoke_finalizers (); + SetEvent (pending_done_event); } @@ -1065,6 +1070,7 @@ mono_gc_init (void) InitializeCriticalSection (&allocator_section); InitializeCriticalSection (&finalizer_mutex); + InitializeCriticalSection (&reference_queue_mutex); MONO_GC_REGISTER_ROOT (gc_handles [HANDLE_NORMAL].entries); MONO_GC_REGISTER_ROOT (gc_handles [HANDLE_PINNED].entries); @@ -1141,6 +1147,7 @@ mono_gc_cleanup (void) DeleteCriticalSection (&handle_section); DeleteCriticalSection (&allocator_section); DeleteCriticalSection (&finalizer_mutex); + DeleteCriticalSection (&reference_queue_mutex); } #else @@ -1177,3 +1184,136 @@ mono_gc_is_finalizer_thread (MonoThread *thread) { return thread == gc_thread; } + + +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; +}