[utils] Small header sanitization. networking.h now include config.h
[mono-project.git] / mono / metadata / gc.c
blob921898a444205a0219307176555414c0c18597fc
1 /*
2 * metadata/gc.c: GC icalls.
4 * Author: Paolo Molaro <lupus@ximian.com>
6 * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com)
7 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
8 * Copyright 2012 Xamarin Inc (http://www.xamarin.com)
9 */
11 #include <config.h>
12 #include <glib.h>
13 #include <string.h>
14 #include <errno.h>
16 #include <mono/metadata/gc-internal.h>
17 #include <mono/metadata/mono-gc.h>
18 #include <mono/metadata/threads.h>
19 #include <mono/metadata/tabledefs.h>
20 #include <mono/metadata/exception.h>
21 #include <mono/metadata/profiler-private.h>
22 #include <mono/metadata/domain-internals.h>
23 #include <mono/metadata/class-internals.h>
24 #include <mono/metadata/metadata-internals.h>
25 #include <mono/metadata/mono-mlist.h>
26 #include <mono/metadata/threadpool.h>
27 #include <mono/metadata/threadpool-internals.h>
28 #include <mono/metadata/threads-types.h>
29 #include <mono/metadata/sgen-conf.h>
30 #include <mono/utils/mono-logger-internal.h>
31 #include <mono/metadata/gc-internal.h>
32 #include <mono/metadata/marshal.h> /* for mono_delegate_free_ftnptr () */
33 #include <mono/metadata/attach.h>
34 #include <mono/metadata/console-io.h>
35 #include <mono/utils/mono-semaphore.h>
36 #include <mono/utils/mono-memory-model.h>
37 #include <mono/utils/mono-counters.h>
38 #include <mono/utils/dtrace.h>
39 #include <mono/utils/mono-threads.h>
40 #include <mono/utils/atomic.h>
42 #ifndef HOST_WIN32
43 #include <pthread.h>
44 #endif
46 typedef struct DomainFinalizationReq {
47 MonoDomain *domain;
48 HANDLE done_event;
49 } DomainFinalizationReq;
51 #ifdef PLATFORM_WINCE /* FIXME: add accessors to gc.dll API */
52 extern void (*__imp_GC_finalizer_notifier)(void);
53 #define GC_finalizer_notifier __imp_GC_finalizer_notifier
54 extern int __imp_GC_finalize_on_demand;
55 #define GC_finalize_on_demand __imp_GC_finalize_on_demand
56 #endif
58 static gboolean gc_disabled = FALSE;
60 static gboolean finalizing_root_domain = FALSE;
62 #define mono_finalizer_lock() mono_mutex_lock (&finalizer_mutex)
63 #define mono_finalizer_unlock() mono_mutex_unlock (&finalizer_mutex)
64 static mono_mutex_t finalizer_mutex;
65 static mono_mutex_t reference_queue_mutex;
67 static GSList *domains_to_finalize= NULL;
68 static MonoMList *threads_to_finalize = NULL;
70 static MonoInternalThread *gc_thread;
72 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
74 static void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj);
76 static void reference_queue_proccess_all (void);
77 static void mono_reference_queue_cleanup (void);
78 static void reference_queue_clear_for_domain (MonoDomain *domain);
79 #ifndef HAVE_NULL_GC
80 static HANDLE pending_done_event;
81 static HANDLE shutdown_event;
82 #endif
84 GCStats gc_stats;
86 static void
87 add_thread_to_finalize (MonoInternalThread *thread)
89 mono_finalizer_lock ();
90 if (!threads_to_finalize)
91 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize);
92 threads_to_finalize = mono_mlist_append (threads_to_finalize, (MonoObject*)thread);
93 mono_finalizer_unlock ();
96 static gboolean suspend_finalizers = FALSE;
97 /*
98 * actually, we might want to queue the finalize requests in a separate thread,
99 * but we need to be careful about the execution domain of the thread...
101 void
102 mono_gc_run_finalize (void *obj, void *data)
104 MonoObject *exc = NULL;
105 MonoObject *o;
106 #ifndef HAVE_SGEN_GC
107 MonoObject *o2;
108 #endif
109 MonoMethod* finalizer = NULL;
110 MonoDomain *caller_domain = mono_domain_get ();
111 MonoDomain *domain;
112 RuntimeInvokeFunction runtime_invoke;
114 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
116 if (suspend_finalizers)
117 return;
119 domain = o->vtable->domain;
121 #ifndef HAVE_SGEN_GC
122 mono_domain_finalizers_lock (domain);
124 o2 = g_hash_table_lookup (domain->finalizable_objects_hash, o);
126 mono_domain_finalizers_unlock (domain);
128 if (!o2)
129 /* Already finalized somehow */
130 return;
131 #endif
133 /* make sure the finalizer is not called again if the object is resurrected */
134 object_register_finalizer (obj, NULL);
136 if (o->vtable->klass == mono_defaults.internal_thread_class) {
137 MonoInternalThread *t = (MonoInternalThread*)o;
139 if (mono_gc_is_finalizer_internal_thread (t))
140 /* Avoid finalizing ourselves */
141 return;
143 if (t->threadpool_thread && finalizing_root_domain) {
144 /* Don't finalize threadpool threads when
145 shutting down - they're finalized when the
146 threadpool shuts down. */
147 add_thread_to_finalize (t);
148 return;
152 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
154 * These can't be finalized during unloading/shutdown, since that would
155 * free the native code which can still be referenced by other
156 * finalizers.
157 * FIXME: This is not perfect, objects dying at the same time as
158 * dynamic methods can still reference them even when !shutdown.
160 return;
163 if (mono_runtime_get_no_exec ())
164 return;
166 /* speedup later... and use a timeout */
167 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
169 /* Use _internal here, since this thread can enter a doomed appdomain */
170 mono_domain_set_internal (mono_object_domain (o));
172 /* delegates that have a native function pointer allocated are
173 * registered for finalization, but they don't have a Finalize
174 * method, because in most cases it's not needed and it's just a waste.
176 if (o->vtable->klass->delegate) {
177 MonoDelegate* del = (MonoDelegate*)o;
178 if (del->delegate_trampoline)
179 mono_delegate_free_ftnptr ((MonoDelegate*)o);
180 mono_domain_set_internal (caller_domain);
181 return;
184 finalizer = mono_class_get_finalizer (o->vtable->klass);
186 #ifndef DISABLE_COM
187 /* If object has a CCW but has no finalizer, it was only
188 * registered for finalization in order to free the CCW.
189 * Else it needs the regular finalizer run.
190 * FIXME: what to do about ressurection and suppression
191 * of finalizer on object with CCW.
193 if (mono_marshal_free_ccw (o) && !finalizer) {
194 mono_domain_set_internal (caller_domain);
195 return;
197 #endif
200 * To avoid the locking plus the other overhead of mono_runtime_invoke (),
201 * create and precompile a wrapper which calls the finalize method using
202 * a CALLVIRT.
204 if (!domain->finalize_runtime_invoke) {
205 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
207 domain->finalize_runtime_invoke = mono_compile_method (invoke);
210 runtime_invoke = domain->finalize_runtime_invoke;
212 mono_runtime_class_init (o->vtable);
214 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
215 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
216 o->vtable->klass->name_space, o->vtable->klass->name);
219 runtime_invoke (o, NULL, &exc, NULL);
221 if (exc)
222 mono_internal_thread_unhandled_exception (exc);
224 mono_domain_set_internal (caller_domain);
227 void
228 mono_gc_finalize_threadpool_threads (void)
230 while (threads_to_finalize) {
231 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
233 /* Force finalization of the thread. */
234 thread->threadpool_thread = FALSE;
235 mono_object_register_finalizer ((MonoObject*)thread);
237 mono_gc_run_finalize (thread, NULL);
239 threads_to_finalize = mono_mlist_next (threads_to_finalize);
243 gpointer
244 mono_gc_out_of_memory (size_t size)
247 * we could allocate at program startup some memory that we could release
248 * back to the system at this point if we're really low on memory (ie, size is
249 * lower than the memory we set apart)
251 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
253 return NULL;
257 * Some of our objects may point to a different address than the address returned by GC_malloc()
258 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
259 * This also means that in the callback we need to adjust the pointer to get back the real
260 * MonoObject*.
261 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
262 * since that, too, can cause the underlying pointer to be offset.
264 static void
265 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
267 MonoDomain *domain;
269 if (obj == NULL)
270 mono_raise_exception (mono_get_exception_argument_null ("obj"));
272 domain = obj->vtable->domain;
274 #if HAVE_BOEHM_GC
275 if (mono_domain_is_unloading (domain) && (callback != NULL))
277 * Can't register finalizers in a dying appdomain, since they
278 * could be invoked after the appdomain has been unloaded.
280 return;
282 mono_domain_finalizers_lock (domain);
284 if (callback)
285 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
286 else
287 g_hash_table_remove (domain->finalizable_objects_hash, obj);
289 mono_domain_finalizers_unlock (domain);
291 mono_gc_register_for_finalization (obj, callback);
292 #elif defined(HAVE_SGEN_GC)
294 * If we register finalizers for domains that are unloading we might
295 * end up running them while or after the domain is being cleared, so
296 * the objects will not be valid anymore.
298 if (!mono_domain_is_unloading (domain))
299 mono_gc_register_for_finalization (obj, callback);
300 #endif
304 * mono_object_register_finalizer:
305 * @obj: object to register
307 * Records that object @obj has a finalizer, this will call the
308 * Finalize method when the garbage collector disposes the object.
311 void
312 mono_object_register_finalizer (MonoObject *obj)
314 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
315 object_register_finalizer (obj, mono_gc_run_finalize);
319 * mono_domain_finalize:
320 * @domain: the domain to finalize
321 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
323 * Request finalization of all finalizable objects inside @domain. Wait
324 * @timeout msecs for the finalization to complete.
326 * Returns: TRUE if succeeded, FALSE if there was a timeout
329 gboolean
330 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
332 DomainFinalizationReq *req;
333 guint32 res;
334 HANDLE done_event;
335 MonoInternalThread *thread = mono_thread_internal_current ();
337 #if defined(__native_client__)
338 return FALSE;
339 #endif
341 if (mono_thread_internal_current () == gc_thread)
342 /* We are called from inside a finalizer, not much we can do here */
343 return FALSE;
346 * No need to create another thread 'cause the finalizer thread
347 * is still working and will take care of running the finalizers
350 #ifndef HAVE_NULL_GC
351 if (gc_disabled)
352 return TRUE;
354 mono_gc_collect (mono_gc_max_generation ());
356 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
357 if (done_event == NULL) {
358 return FALSE;
361 req = g_new0 (DomainFinalizationReq, 1);
362 req->domain = domain;
363 req->done_event = done_event;
365 if (domain == mono_get_root_domain ())
366 finalizing_root_domain = TRUE;
368 mono_finalizer_lock ();
370 domains_to_finalize = g_slist_append (domains_to_finalize, req);
372 mono_finalizer_unlock ();
374 /* Tell the finalizer thread to finalize this appdomain */
375 mono_gc_finalize_notify ();
377 if (timeout == -1)
378 timeout = INFINITE;
380 while (TRUE) {
381 res = WaitForSingleObjectEx (done_event, timeout, TRUE);
382 /* printf ("WAIT RES: %d.\n", res); */
384 if (res == WAIT_IO_COMPLETION) {
385 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
386 return FALSE;
387 } else if (res == WAIT_TIMEOUT) {
388 /* We leak the handle here */
389 return FALSE;
390 } else {
391 break;
395 CloseHandle (done_event);
397 if (domain == mono_get_root_domain ()) {
398 mono_thread_pool_cleanup ();
399 mono_gc_finalize_threadpool_threads ();
402 return TRUE;
403 #else
404 /* We don't support domain finalization without a GC */
405 return FALSE;
406 #endif
409 void
410 ves_icall_System_GC_InternalCollect (int generation)
412 mono_gc_collect (generation);
415 gint64
416 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
418 MONO_ARCH_SAVE_REGS;
420 if (forceCollection)
421 mono_gc_collect (mono_gc_max_generation ());
422 return mono_gc_get_used_size ();
425 void
426 ves_icall_System_GC_KeepAlive (MonoObject *obj)
428 MONO_ARCH_SAVE_REGS;
431 * Does nothing.
435 void
436 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
438 if (!obj)
439 mono_raise_exception (mono_get_exception_argument_null ("obj"));
441 object_register_finalizer (obj, mono_gc_run_finalize);
444 void
445 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
447 if (!obj)
448 mono_raise_exception (mono_get_exception_argument_null ("obj"));
450 /* delegates have no finalizers, but we register them to deal with the
451 * unmanaged->managed trampoline. We don't let the user suppress it
452 * otherwise we'd leak it.
454 if (obj->vtable->klass->delegate)
455 return;
457 /* FIXME: Need to handle case where obj has COM Callable Wrapper
458 * generated for it that needs cleaned up, but user wants to suppress
459 * their derived object finalizer. */
461 object_register_finalizer (obj, NULL);
464 void
465 ves_icall_System_GC_WaitForPendingFinalizers (void)
467 #ifndef HAVE_NULL_GC
468 if (!mono_gc_pending_finalizers ())
469 return;
471 if (mono_thread_internal_current () == gc_thread)
472 /* Avoid deadlocks */
473 return;
476 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
477 be the one responsible for starting it up.
479 if (gc_thread == NULL)
480 return;
482 ResetEvent (pending_done_event);
483 mono_gc_finalize_notify ();
484 /* g_print ("Waiting for pending finalizers....\n"); */
485 WaitForSingleObjectEx (pending_done_event, INFINITE, TRUE);
486 /* g_print ("Done pending....\n"); */
487 #endif
490 void
491 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
493 #ifdef HAVE_SGEN_GC
494 if (!mono_gc_ephemeron_array_add (array))
495 mono_raise_exception (mono_object_domain (array)->out_of_memory_ex);
496 #endif
499 MonoObject*
500 ves_icall_System_GC_get_ephemeron_tombstone (void)
502 return mono_domain_get ()->ephemeron_tombstone;
505 #define mono_allocator_lock() mono_mutex_lock (&allocator_section)
506 #define mono_allocator_unlock() mono_mutex_unlock (&allocator_section)
507 static mono_mutex_t allocator_section;
508 static mono_mutex_t handle_section;
510 typedef enum {
511 HANDLE_WEAK,
512 HANDLE_WEAK_TRACK,
513 HANDLE_NORMAL,
514 HANDLE_PINNED
515 } HandleType;
517 static HandleType mono_gchandle_get_type (guint32 gchandle);
519 MonoObject *
520 ves_icall_System_GCHandle_GetTarget (guint32 handle)
522 return mono_gchandle_get_target (handle);
526 * if type == -1, change the target of the handle, otherwise allocate a new handle.
528 guint32
529 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
531 if (type == -1) {
532 mono_gchandle_set_target (handle, obj);
533 /* the handle doesn't change */
534 return handle;
536 switch (type) {
537 case HANDLE_WEAK:
538 return mono_gchandle_new_weakref (obj, FALSE);
539 case HANDLE_WEAK_TRACK:
540 return mono_gchandle_new_weakref (obj, TRUE);
541 case HANDLE_NORMAL:
542 return mono_gchandle_new (obj, FALSE);
543 case HANDLE_PINNED:
544 return mono_gchandle_new (obj, TRUE);
545 default:
546 g_assert_not_reached ();
548 return 0;
551 void
552 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
554 mono_gchandle_free (handle);
557 gpointer
558 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
560 MonoObject *obj;
562 if (mono_gchandle_get_type (handle) != HANDLE_PINNED)
563 return (gpointer)-2;
564 obj = mono_gchandle_get_target (handle);
565 if (obj) {
566 MonoClass *klass = mono_object_class (obj);
567 if (klass == mono_defaults.string_class) {
568 return mono_string_chars ((MonoString*)obj);
569 } else if (klass->rank) {
570 return mono_array_addr ((MonoArray*)obj, char, 0);
571 } else {
572 /* the C# code will check and throw the exception */
573 /* FIXME: missing !klass->blittable test, see bug #61134 */
574 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
575 return (gpointer)-1;
576 return (char*)obj + sizeof (MonoObject);
579 return NULL;
582 MonoBoolean
583 ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag)
585 return mono_gc_set_allow_synchronous_major (flag);
588 typedef struct {
589 guint32 *bitmap;
590 gpointer *entries;
591 guint32 size;
592 guint8 type;
593 guint slot_hint : 24; /* starting slot for search */
594 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
595 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
596 guint16 *domain_ids;
597 } HandleData;
599 /* weak and weak-track arrays will be allocated in malloc memory
601 static HandleData gc_handles [] = {
602 {NULL, NULL, 0, HANDLE_WEAK, 0},
603 {NULL, NULL, 0, HANDLE_WEAK_TRACK, 0},
604 {NULL, NULL, 0, HANDLE_NORMAL, 0},
605 {NULL, NULL, 0, HANDLE_PINNED, 0}
608 #define lock_handles(handles) mono_mutex_lock (&handle_section)
609 #define unlock_handles(handles) mono_mutex_unlock (&handle_section)
611 static int
612 find_first_unset (guint32 bitmap)
614 int i;
615 for (i = 0; i < 32; ++i) {
616 if (!(bitmap & (1 << i)))
617 return i;
619 return -1;
622 static void*
623 make_root_descr_all_refs (int numbits, gboolean pinned)
625 #ifdef HAVE_SGEN_GC
626 if (pinned)
627 return NULL;
628 #endif
629 return mono_gc_make_root_descr_all_refs (numbits);
632 static guint32
633 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
635 gint slot, i;
636 guint32 res;
637 lock_handles (handles);
638 if (!handles->size) {
639 handles->size = 32;
640 if (handles->type > HANDLE_WEAK_TRACK) {
641 handles->entries = mono_gc_alloc_fixed (sizeof (gpointer) * handles->size, make_root_descr_all_refs (handles->size, handles->type == HANDLE_PINNED));
642 } else {
643 handles->entries = g_malloc0 (sizeof (gpointer) * handles->size);
644 handles->domain_ids = g_malloc0 (sizeof (guint16) * handles->size);
646 handles->bitmap = g_malloc0 (handles->size / 8);
648 i = -1;
649 for (slot = handles->slot_hint; slot < handles->size / 32; ++slot) {
650 if (handles->bitmap [slot] != 0xffffffff) {
651 i = find_first_unset (handles->bitmap [slot]);
652 handles->slot_hint = slot;
653 break;
656 if (i == -1 && handles->slot_hint != 0) {
657 for (slot = 0; slot < handles->slot_hint; ++slot) {
658 if (handles->bitmap [slot] != 0xffffffff) {
659 i = find_first_unset (handles->bitmap [slot]);
660 handles->slot_hint = slot;
661 break;
665 if (i == -1) {
666 guint32 *new_bitmap;
667 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
669 /* resize and copy the bitmap */
670 new_bitmap = g_malloc0 (new_size / 8);
671 memcpy (new_bitmap, handles->bitmap, handles->size / 8);
672 g_free (handles->bitmap);
673 handles->bitmap = new_bitmap;
675 /* resize and copy the entries */
676 if (handles->type > HANDLE_WEAK_TRACK) {
677 gpointer *entries;
679 entries = mono_gc_alloc_fixed (sizeof (gpointer) * new_size, make_root_descr_all_refs (new_size, handles->type == HANDLE_PINNED));
680 mono_gc_memmove_aligned (entries, handles->entries, sizeof (gpointer) * handles->size);
682 mono_gc_free_fixed (handles->entries);
683 handles->entries = entries;
684 } else {
685 gpointer *entries;
686 guint16 *domain_ids;
687 domain_ids = g_malloc0 (sizeof (guint16) * new_size);
688 entries = g_malloc0 (sizeof (gpointer) * new_size);
689 memcpy (domain_ids, handles->domain_ids, sizeof (guint16) * handles->size);
690 for (i = 0; i < handles->size; ++i) {
691 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
692 if (obj) {
693 mono_gc_weak_link_add (&(entries [i]), obj, track);
694 mono_gc_weak_link_remove (&(handles->entries [i]), track);
695 } else {
696 g_assert (!handles->entries [i]);
699 g_free (handles->entries);
700 g_free (handles->domain_ids);
701 handles->entries = entries;
702 handles->domain_ids = domain_ids;
705 /* set i and slot to the next free position */
706 i = 0;
707 slot = (handles->size + 1) / 32;
708 handles->slot_hint = handles->size + 1;
709 handles->size = new_size;
711 handles->bitmap [slot] |= 1 << i;
712 slot = slot * 32 + i;
713 handles->entries [slot] = NULL;
714 if (handles->type <= HANDLE_WEAK_TRACK) {
715 /*FIXME, what to use when obj == null?*/
716 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
717 if (obj)
718 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
719 } else {
720 handles->entries [slot] = obj;
723 #ifndef DISABLE_PERFCOUNTERS
724 mono_perfcounters->gc_num_handles++;
725 #endif
726 unlock_handles (handles);
727 /*g_print ("allocated entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
728 res = (slot << 3) | (handles->type + 1);
729 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
730 return res;
734 * mono_gchandle_new:
735 * @obj: managed object to get a handle for
736 * @pinned: whether the object should be pinned
738 * This returns a handle that wraps the object, this is used to keep a
739 * reference to a managed object from the unmanaged world and preventing the
740 * object from being disposed.
742 * If @pinned is false the address of the object can not be obtained, if it is
743 * true the address of the object can be obtained. This will also pin the
744 * object so it will not be possible by a moving garbage collector to move the
745 * object.
747 * Returns: a handle that can be used to access the object from
748 * unmanaged code.
750 guint32
751 mono_gchandle_new (MonoObject *obj, gboolean pinned)
753 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
757 * mono_gchandle_new_weakref:
758 * @obj: managed object to get a handle for
759 * @pinned: whether the object should be pinned
761 * This returns a weak handle that wraps the object, this is used to
762 * keep a reference to a managed object from the unmanaged world.
763 * Unlike the mono_gchandle_new the object can be reclaimed by the
764 * garbage collector. In this case the value of the GCHandle will be
765 * set to zero.
767 * If @pinned is false the address of the object can not be obtained, if it is
768 * true the address of the object can be obtained. This will also pin the
769 * object so it will not be possible by a moving garbage collector to move the
770 * object.
772 * Returns: a handle that can be used to access the object from
773 * unmanaged code.
775 guint32
776 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
778 guint32 handle = alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
780 return handle;
783 static HandleType
784 mono_gchandle_get_type (guint32 gchandle)
786 guint type = (gchandle & 7) - 1;
788 return type;
792 * mono_gchandle_get_target:
793 * @gchandle: a GCHandle's handle.
795 * The handle was previously created by calling mono_gchandle_new or
796 * mono_gchandle_new_weakref.
798 * Returns a pointer to the MonoObject represented by the handle or
799 * NULL for a collected object if using a weakref handle.
801 MonoObject*
802 mono_gchandle_get_target (guint32 gchandle)
804 guint slot = gchandle >> 3;
805 guint type = (gchandle & 7) - 1;
806 HandleData *handles = &gc_handles [type];
807 MonoObject *obj = NULL;
808 if (type > 3)
809 return NULL;
810 lock_handles (handles);
811 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
812 if (handles->type <= HANDLE_WEAK_TRACK) {
813 obj = mono_gc_weak_link_get (&handles->entries [slot]);
814 } else {
815 obj = handles->entries [slot];
817 } else {
818 /* print a warning? */
820 unlock_handles (handles);
821 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
822 return obj;
825 static void
826 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
828 guint slot = gchandle >> 3;
829 guint type = (gchandle & 7) - 1;
830 HandleData *handles = &gc_handles [type];
831 MonoObject *old_obj = NULL;
833 if (type > 3)
834 return;
835 lock_handles (handles);
836 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
837 if (handles->type <= HANDLE_WEAK_TRACK) {
838 old_obj = handles->entries [slot];
839 if (handles->entries [slot])
840 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
841 if (obj)
842 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
843 /*FIXME, what to use when obj == null?*/
844 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
845 } else {
846 handles->entries [slot] = obj;
848 } else {
849 /* print a warning? */
851 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
852 unlock_handles (handles);
856 * mono_gchandle_is_in_domain:
857 * @gchandle: a GCHandle's handle.
858 * @domain: An application domain.
860 * Returns: true if the object wrapped by the @gchandle belongs to the specific @domain.
862 gboolean
863 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
865 guint slot = gchandle >> 3;
866 guint type = (gchandle & 7) - 1;
867 HandleData *handles = &gc_handles [type];
868 gboolean result = FALSE;
869 if (type > 3)
870 return FALSE;
871 lock_handles (handles);
872 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
873 if (handles->type <= HANDLE_WEAK_TRACK) {
874 result = domain->domain_id == handles->domain_ids [slot];
875 } else {
876 MonoObject *obj;
877 obj = handles->entries [slot];
878 if (obj == NULL)
879 result = TRUE;
880 else
881 result = domain == mono_object_domain (obj);
883 } else {
884 /* print a warning? */
886 unlock_handles (handles);
887 return result;
891 * mono_gchandle_free:
892 * @gchandle: a GCHandle's handle.
894 * Frees the @gchandle handle. If there are no outstanding
895 * references, the garbage collector can reclaim the memory of the
896 * object wrapped.
898 void
899 mono_gchandle_free (guint32 gchandle)
901 guint slot = gchandle >> 3;
902 guint type = (gchandle & 7) - 1;
903 HandleData *handles = &gc_handles [type];
904 if (type > 3)
905 return;
907 lock_handles (handles);
908 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
909 if (handles->type <= HANDLE_WEAK_TRACK) {
910 if (handles->entries [slot])
911 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
912 } else {
913 handles->entries [slot] = NULL;
915 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
916 } else {
917 /* print a warning? */
919 #ifndef DISABLE_PERFCOUNTERS
920 mono_perfcounters->gc_num_handles--;
921 #endif
922 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
923 unlock_handles (handles);
924 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
928 * mono_gchandle_free_domain:
929 * @domain: domain that is unloading
931 * Function used internally to cleanup any GC handle for objects belonging
932 * to the specified domain during appdomain unload.
934 void
935 mono_gchandle_free_domain (MonoDomain *domain)
937 guint type;
939 for (type = 0; type < 3; ++type) {
940 guint slot;
941 HandleData *handles = &gc_handles [type];
942 lock_handles (handles);
943 for (slot = 0; slot < handles->size; ++slot) {
944 if (!(handles->bitmap [slot / 32] & (1 << (slot % 32))))
945 continue;
946 if (type <= HANDLE_WEAK_TRACK) {
947 if (domain->domain_id == handles->domain_ids [slot]) {
948 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
949 if (handles->entries [slot])
950 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
952 } else {
953 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
954 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
955 handles->entries [slot] = NULL;
959 unlock_handles (handles);
964 MonoBoolean
965 GCHandle_CheckCurrentDomain (guint32 gchandle)
967 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
970 #ifndef HAVE_NULL_GC
972 #ifdef MONO_HAS_SEMAPHORES
973 static MonoSemType finalizer_sem;
974 #endif
975 static HANDLE finalizer_event;
976 static volatile gboolean finished=FALSE;
978 void
979 mono_gc_finalize_notify (void)
981 #ifdef DEBUG
982 g_message ( "%s: prodding finalizer", __func__);
983 #endif
985 #ifdef MONO_HAS_SEMAPHORES
986 MONO_SEM_POST (&finalizer_sem);
987 #else
988 SetEvent (finalizer_event);
989 #endif
992 #ifdef HAVE_BOEHM_GC
994 static void
995 collect_objects (gpointer key, gpointer value, gpointer user_data)
997 GPtrArray *arr = (GPtrArray*)user_data;
998 g_ptr_array_add (arr, key);
1001 #endif
1004 * finalize_domain_objects:
1006 * Run the finalizers of all finalizable objects in req->domain.
1008 static void
1009 finalize_domain_objects (DomainFinalizationReq *req)
1011 MonoDomain *domain = req->domain;
1013 #if HAVE_SGEN_GC
1014 #define NUM_FOBJECTS 64
1015 MonoObject *to_finalize [NUM_FOBJECTS];
1016 int count;
1017 #endif
1019 /* Process finalizers which are already in the queue */
1020 mono_gc_invoke_finalizers ();
1022 #ifdef HAVE_BOEHM_GC
1023 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
1024 int i;
1025 GPtrArray *objs;
1027 * Since the domain is unloading, nobody is allowed to put
1028 * new entries into the hash table. But finalize_object might
1029 * remove entries from the hash table, so we make a copy.
1031 objs = g_ptr_array_new ();
1032 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
1033 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
1035 for (i = 0; i < objs->len; ++i) {
1036 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
1037 /* FIXME: Avoid finalizing threads, etc */
1038 mono_gc_run_finalize (o, 0);
1041 g_ptr_array_free (objs, TRUE);
1043 #elif defined(HAVE_SGEN_GC)
1044 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
1045 int i;
1046 for (i = 0; i < count; ++i) {
1047 mono_gc_run_finalize (to_finalize [i], 0);
1050 #endif
1052 /* cleanup the reference queue */
1053 reference_queue_clear_for_domain (domain);
1055 /* printf ("DONE.\n"); */
1056 SetEvent (req->done_event);
1058 /* The event is closed in mono_domain_finalize if we get here */
1059 g_free (req);
1062 static guint32
1063 finalizer_thread (gpointer unused)
1065 gboolean wait = TRUE;
1067 while (!finished) {
1068 /* Wait to be notified that there's at least one
1069 * finaliser to run
1072 g_assert (mono_domain_get () == mono_get_root_domain ());
1074 if (wait) {
1075 /* An alertable wait is required so this thread can be suspended on windows */
1076 #ifdef MONO_HAS_SEMAPHORES
1077 MONO_SEM_WAIT_ALERTABLE (&finalizer_sem, TRUE);
1078 #else
1079 WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
1080 #endif
1082 wait = TRUE;
1084 mono_threads_perform_thread_dump ();
1086 mono_console_handle_async_ops ();
1088 #ifndef DISABLE_ATTACH
1089 mono_attach_maybe_start ();
1090 #endif
1092 if (domains_to_finalize) {
1093 mono_finalizer_lock ();
1094 if (domains_to_finalize) {
1095 DomainFinalizationReq *req = domains_to_finalize->data;
1096 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
1097 mono_finalizer_unlock ();
1099 finalize_domain_objects (req);
1100 } else {
1101 mono_finalizer_unlock ();
1105 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
1106 * before the domain is unloaded.
1108 mono_gc_invoke_finalizers ();
1110 mono_threads_join_threads ();
1112 reference_queue_proccess_all ();
1114 #ifdef MONO_HAS_SEMAPHORES
1115 /* Avoid posting the pending done event until there are pending finalizers */
1116 if (MONO_SEM_TIMEDWAIT (&finalizer_sem, 0) == 0)
1117 /* Don't wait again at the start of the loop */
1118 wait = FALSE;
1119 else
1120 SetEvent (pending_done_event);
1121 #else
1122 SetEvent (pending_done_event);
1123 #endif
1126 SetEvent (shutdown_event);
1127 return 0;
1130 #ifndef LAZY_GC_THREAD_CREATION
1131 static
1132 #endif
1133 void
1134 mono_gc_init_finalizer_thread (void)
1136 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
1137 ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
1140 void
1141 mono_gc_init (void)
1143 mono_mutex_init_recursive (&handle_section);
1144 mono_mutex_init_recursive (&allocator_section);
1146 mono_mutex_init_recursive (&finalizer_mutex);
1147 mono_mutex_init_recursive (&reference_queue_mutex);
1149 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries);
1150 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries);
1152 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
1153 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
1154 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
1155 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
1156 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
1158 mono_gc_base_init ();
1160 if (mono_gc_is_disabled ()) {
1161 gc_disabled = TRUE;
1162 return;
1165 finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
1166 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
1167 shutdown_event = CreateEvent (NULL, TRUE, FALSE, NULL);
1168 if (finalizer_event == NULL || pending_done_event == NULL || shutdown_event == NULL) {
1169 g_assert_not_reached ();
1171 #ifdef MONO_HAS_SEMAPHORES
1172 MONO_SEM_INIT (&finalizer_sem, 0);
1173 #endif
1175 #ifndef LAZY_GC_THREAD_CREATION
1176 mono_gc_init_finalizer_thread ();
1177 #endif
1180 void
1181 mono_gc_cleanup (void)
1183 #ifdef DEBUG
1184 g_message ("%s: cleaning up finalizer", __func__);
1185 #endif
1187 if (!gc_disabled) {
1188 ResetEvent (shutdown_event);
1189 finished = TRUE;
1190 if (mono_thread_internal_current () != gc_thread) {
1191 gboolean timed_out = FALSE;
1193 mono_gc_finalize_notify ();
1194 /* Finishing the finalizer thread, so wait a little bit... */
1195 /* MS seems to wait for about 2 seconds */
1196 if (WaitForSingleObjectEx (shutdown_event, 2000, FALSE) == WAIT_TIMEOUT) {
1197 int ret;
1199 /* Set a flag which the finalizer thread can check */
1200 suspend_finalizers = TRUE;
1202 /* Try to abort the thread, in the hope that it is running managed code */
1203 mono_thread_internal_stop (gc_thread);
1205 /* Wait for it to stop */
1206 ret = WaitForSingleObjectEx (gc_thread->handle, 100, TRUE);
1208 if (ret == WAIT_TIMEOUT) {
1210 * The finalizer thread refused to die. There is not much we
1211 * can do here, since the runtime is shutting down so the
1212 * state the finalizer thread depends on will vanish.
1214 g_warning ("Shutting down finalizer thread timed out.");
1215 timed_out = TRUE;
1219 if (!timed_out) {
1220 int ret;
1222 /* Wait for the thread to actually exit */
1223 ret = WaitForSingleObjectEx (gc_thread->handle, INFINITE, TRUE);
1224 g_assert (ret == WAIT_OBJECT_0);
1226 mono_thread_join (MONO_UINT_TO_NATIVE_THREAD_ID (gc_thread->tid));
1229 gc_thread = NULL;
1230 #ifdef HAVE_BOEHM_GC
1231 GC_finalizer_notifier = NULL;
1232 #endif
1235 mono_reference_queue_cleanup ();
1237 mono_mutex_destroy (&handle_section);
1238 mono_mutex_destroy (&allocator_section);
1239 mono_mutex_destroy (&finalizer_mutex);
1240 mono_mutex_destroy (&reference_queue_mutex);
1243 #else
1245 /* Null GC dummy functions */
1246 void
1247 mono_gc_finalize_notify (void)
1251 void mono_gc_init (void)
1253 mono_mutex_init_recursive (&handle_section);
1256 void mono_gc_cleanup (void)
1260 #endif
1262 gboolean
1263 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1265 return thread == gc_thread;
1269 * mono_gc_is_finalizer_thread:
1270 * @thread: the thread to test.
1272 * In Mono objects are finalized asynchronously on a separate thread.
1273 * This routine tests whether the @thread argument represents the
1274 * finalization thread.
1276 * Returns true if @thread is the finalization thread.
1278 gboolean
1279 mono_gc_is_finalizer_thread (MonoThread *thread)
1281 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1284 #if defined(__MACH__)
1285 static pthread_t mach_exception_thread;
1287 void
1288 mono_gc_register_mach_exception_thread (pthread_t thread)
1290 mach_exception_thread = thread;
1293 pthread_t
1294 mono_gc_get_mach_exception_thread (void)
1296 return mach_exception_thread;
1298 #endif
1301 * mono_gc_parse_environment_string_extract_number:
1303 * @str: points to the first digit of the number
1304 * @out: pointer to the variable that will receive the value
1306 * Tries to extract a number from the passed string, taking in to account m, k
1307 * and g suffixes
1309 * Returns true if passing was successful
1311 gboolean
1312 mono_gc_parse_environment_string_extract_number (const char *str, size_t *out)
1314 char *endptr;
1315 int len = strlen (str), shift = 0;
1316 size_t val;
1317 gboolean is_suffix = FALSE;
1318 char suffix;
1320 if (!len)
1321 return FALSE;
1323 suffix = str [len - 1];
1325 switch (suffix) {
1326 case 'g':
1327 case 'G':
1328 shift += 10;
1329 case 'm':
1330 case 'M':
1331 shift += 10;
1332 case 'k':
1333 case 'K':
1334 shift += 10;
1335 is_suffix = TRUE;
1336 break;
1337 default:
1338 if (!isdigit (suffix))
1339 return FALSE;
1340 break;
1343 errno = 0;
1344 val = strtol (str, &endptr, 10);
1346 if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
1347 || (errno != 0 && val == 0) || (endptr == str))
1348 return FALSE;
1350 if (is_suffix) {
1351 size_t unshifted;
1353 if (val < 0) /* negative numbers cannot be suffixed */
1354 return FALSE;
1355 if (*(endptr + 1)) /* Invalid string. */
1356 return FALSE;
1358 unshifted = (size_t)val;
1359 val <<= shift;
1360 if (val < 0) /* overflow */
1361 return FALSE;
1362 if (((size_t)val >> shift) != unshifted) /* value too large */
1363 return FALSE;
1366 *out = val;
1367 return TRUE;
1370 #ifndef HAVE_SGEN_GC
1371 void*
1372 mono_gc_alloc_mature (MonoVTable *vtable)
1374 return mono_object_new_specific (vtable);
1376 #endif
1379 static MonoReferenceQueue *ref_queues;
1381 static void
1382 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1384 do {
1385 /* Guard if head is changed concurrently. */
1386 while (*prev != element)
1387 prev = &(*prev)->next;
1388 } while (prev && InterlockedCompareExchangePointer ((void*)prev, element->next, element) != element);
1391 static void
1392 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1394 RefQueueEntry *current;
1395 do {
1396 current = *head;
1397 value->next = current;
1398 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1399 } while (InterlockedCompareExchangePointer ((void*)head, value, current) != current);
1402 static void
1403 reference_queue_proccess (MonoReferenceQueue *queue)
1405 RefQueueEntry **iter = &queue->queue;
1406 RefQueueEntry *entry;
1407 while ((entry = *iter)) {
1408 #ifdef HAVE_SGEN_GC
1409 if (queue->should_be_deleted || !mono_gc_weak_link_get (&entry->dis_link)) {
1410 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1411 #else
1412 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1413 mono_gchandle_free ((guint32)entry->gchandle);
1414 #endif
1415 ref_list_remove_element (iter, entry);
1416 queue->callback (entry->user_data);
1417 g_free (entry);
1418 } else {
1419 iter = &entry->next;
1424 static void
1425 reference_queue_proccess_all (void)
1427 MonoReferenceQueue **iter;
1428 MonoReferenceQueue *queue = ref_queues;
1429 for (; queue; queue = queue->next)
1430 reference_queue_proccess (queue);
1432 restart:
1433 mono_mutex_lock (&reference_queue_mutex);
1434 for (iter = &ref_queues; *iter;) {
1435 queue = *iter;
1436 if (!queue->should_be_deleted) {
1437 iter = &queue->next;
1438 continue;
1440 if (queue->queue) {
1441 mono_mutex_unlock (&reference_queue_mutex);
1442 reference_queue_proccess (queue);
1443 goto restart;
1445 *iter = queue->next;
1446 g_free (queue);
1448 mono_mutex_unlock (&reference_queue_mutex);
1451 static void
1452 mono_reference_queue_cleanup (void)
1454 MonoReferenceQueue *queue = ref_queues;
1455 for (; queue; queue = queue->next)
1456 queue->should_be_deleted = TRUE;
1457 reference_queue_proccess_all ();
1460 static void
1461 reference_queue_clear_for_domain (MonoDomain *domain)
1463 MonoReferenceQueue *queue = ref_queues;
1464 for (; queue; queue = queue->next) {
1465 RefQueueEntry **iter = &queue->queue;
1466 RefQueueEntry *entry;
1467 while ((entry = *iter)) {
1468 if (entry->domain == domain) {
1469 #ifdef HAVE_SGEN_GC
1470 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1471 #else
1472 mono_gchandle_free ((guint32)entry->gchandle);
1473 #endif
1474 ref_list_remove_element (iter, entry);
1475 queue->callback (entry->user_data);
1476 g_free (entry);
1477 } else {
1478 iter = &entry->next;
1484 * mono_gc_reference_queue_new:
1485 * @callback callback used when processing collected entries.
1487 * Create a new reference queue used to process collected objects.
1488 * A reference queue let you add a pair of (managed object, user data)
1489 * using the mono_gc_reference_queue_add method.
1491 * Once the managed object is collected @callback will be called
1492 * in the finalizer thread with 'user data' as argument.
1494 * The callback is called from the finalizer thread without any locks held.
1495 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1496 * will be invoked.
1498 * @returns the new queue.
1500 MonoReferenceQueue*
1501 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1503 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1504 res->callback = callback;
1506 mono_mutex_lock (&reference_queue_mutex);
1507 res->next = ref_queues;
1508 ref_queues = res;
1509 mono_mutex_unlock (&reference_queue_mutex);
1511 return res;
1515 * mono_gc_reference_queue_add:
1516 * @queue the queue to add the reference to.
1517 * @obj the object to be watched for collection
1518 * @user_data parameter to be passed to the queue callback
1520 * Queue an object to be watched for collection, when the @obj is
1521 * collected, the callback that was registered for the @queue will
1522 * be invoked with @user_data as argument.
1524 * @returns false if the queue is scheduled to be freed.
1526 gboolean
1527 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1529 RefQueueEntry *entry;
1530 if (queue->should_be_deleted)
1531 return FALSE;
1533 entry = g_new0 (RefQueueEntry, 1);
1534 entry->user_data = user_data;
1535 entry->domain = mono_object_domain (obj);
1537 #ifdef HAVE_SGEN_GC
1538 mono_gc_weak_link_add (&entry->dis_link, obj, TRUE);
1539 #else
1540 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1541 mono_object_register_finalizer (obj);
1542 #endif
1544 ref_list_push (&queue->queue, entry);
1545 return TRUE;
1549 * mono_gc_reference_queue_free:
1550 * @queue the queue that should be freed.
1552 * This operation signals that @queue should be freed. This operation is deferred
1553 * as it happens on the finalizer thread.
1555 * After this call, no further objects can be queued. It's the responsibility of the
1556 * caller to make sure that no further attempt to access queue will be made.
1558 void
1559 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1561 queue->should_be_deleted = TRUE;