X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fgc.c;h=2071f610b3529476814f9fd747e9669fe8217f8a;hb=f7b9cd57483861ba3b10353d1cc3eb9f1fb26760;hp=3a9db3a1c62518de346ad3b6bbdca7abc4ebd28b;hpb=7f449e7aff8f374d13db7023c7b326dde01b763d;p=mono.git diff --git a/mono/metadata/gc.c b/mono/metadata/gc.c index 3a9db3a1c62..2071f610b35 100644 --- a/mono/metadata/gc.c +++ b/mono/metadata/gc.c @@ -11,19 +11,16 @@ #include #include +#include #include #include #include +#include #include #include #include -#define GC_I_HIDE_POINTERS #include - -#ifndef HIDE_POINTER -#define HIDE_POINTER(v) (v) -#define REVEAL_POINTER(v) (v) -#endif +#include /* for mono_delegate_free_ftnptr () */ typedef struct DomainFinalizationReq { MonoDomain *domain; @@ -37,10 +34,10 @@ extern int __imp_GC_finalize_on_demand; #define GC_finalize_on_demand __imp_GC_finalize_on_demand #endif -static int finalize_slot = -1; - static gboolean gc_disabled = FALSE; +#define mono_finalizer_lock() EnterCriticalSection (&finalizer_mutex) +#define mono_finalizer_unlock() LeaveCriticalSection (&finalizer_mutex) static CRITICAL_SECTION finalizer_mutex; static GSList *domains_to_finalize= NULL; @@ -49,8 +46,7 @@ static MonoThread *gc_thread; static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*)); -#if HAVE_BOEHM_GC -static void finalize_notify (void); +#ifndef HAVE_NULL_GC static HANDLE pending_done_event; static HANDLE shutdown_event; static HANDLE thread_started_event; @@ -67,19 +63,7 @@ run_finalize (void *obj, void *data) MonoObject *o, *o2; o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data)); - if (finalize_slot < 0) { - int i; - MonoClass* obj_class = mono_get_object_class (); - for (i = 0; i < obj_class->vtable_size; ++i) { - MonoMethod *cm = obj_class->vtable [i]; - - if (!strcmp (mono_method_get_name (cm), "Finalize")) { - finalize_slot = i; - break; - } - } - } - +#ifndef HAVE_SGEN_GC mono_domain_lock (o->vtable->domain); o2 = g_hash_table_lookup (o->vtable->domain->finalizable_objects_hash, o); @@ -89,6 +73,7 @@ run_finalize (void *obj, void *data) if (!o2) /* Already finalized somehow */ return; +#endif /* make sure the finalizer is not called again if the object is resurrected */ object_register_finalizer (obj, NULL); @@ -104,13 +89,37 @@ run_finalize (void *obj, void *data) /* Use _internal here, since this thread can enter a doomed appdomain */ mono_domain_set_internal (mono_object_domain (o)); - mono_runtime_invoke (o->vtable->klass->vtable [finalize_slot], o, NULL, &exc); + /* delegates that have a native function pointer allocated are + * registered for finalization, but they don't have a Finalize + * method, because in most cases it's not needed and it's just a waste. + */ + if (o->vtable->klass->delegate) { + MonoDelegate* del = (MonoDelegate*)o; + if (del->delegate_trampoline) + mono_delegate_free_ftnptr ((MonoDelegate*)o); + return; + } + + mono_runtime_invoke (mono_class_get_finalizer (o->vtable->klass), o, NULL, &exc); if (exc) { /* fixme: do something useful */ } } +gpointer +mono_gc_out_of_memory (size_t size) +{ + /* + * we could allocate at program startup some memory that we could release + * back to the system at this point if we're really low on memory (ie, size is + * lower than the memory we set apart) + */ + mono_raise_exception (mono_domain_get ()->out_of_memory_ex); + + return NULL; +} + /* * Some of our objects may point to a different address than the address returned by GC_malloc() * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer. @@ -148,9 +157,19 @@ object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*)) mono_domain_unlock (obj->vtable->domain); GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, callback, GUINT_TO_POINTER (offset), NULL, NULL); +#elif defined(HAVE_SGEN_GC) + mono_gc_register_for_finalization (obj, callback); #endif } +/** + * mono_object_register_finalizer: + * @obj: object to register + * + * Records that object @obj has a finalizer, this will call the + * Finalize method when the garbage collector disposes the object. + * + */ void mono_object_register_finalizer (MonoObject *obj) { @@ -158,11 +177,14 @@ mono_object_register_finalizer (MonoObject *obj) object_register_finalizer (obj, run_finalize); } -/* +/** * mono_domain_finalize: + * @domain: the domain to finalize + * @timeout: msects to wait for the finalization to complete * * Request finalization of all finalizable objects inside @domain. Wait * @timeout msecs for the finalization to complete. + * * Returns: TRUE if succeeded, FALSE if there was a timeout */ @@ -173,16 +195,22 @@ mono_domain_finalize (MonoDomain *domain, guint32 timeout) guint32 res; HANDLE done_event; + if (mono_thread_current () == gc_thread) + /* We are called from inside a finalizer, not much we can do here */ + return FALSE; + + mono_profiler_appdomain_event (domain, MONO_PROFILE_START_UNLOAD); + /* * No need to create another thread 'cause the finalizer thread * is still working and will take care of running the finalizers */ -#if HAVE_BOEHM_GC +#ifndef HAVE_NULL_GC if (gc_disabled) return TRUE; - GC_gcollect (); + mono_gc_collect (mono_gc_max_generation ()); done_event = CreateEvent (NULL, TRUE, FALSE, NULL); @@ -190,14 +218,14 @@ mono_domain_finalize (MonoDomain *domain, guint32 timeout) req->domain = domain; req->done_event = done_event; - EnterCriticalSection (&finalizer_mutex); + mono_finalizer_lock (); domains_to_finalize = g_slist_append (domains_to_finalize, req); - LeaveCriticalSection (&finalizer_mutex); + mono_finalizer_unlock (); /* Tell the finalizer thread to finalize this appdomain */ - finalize_notify (); + mono_gc_finalize_notify (); res = WaitForSingleObjectEx (done_event, timeout, TRUE); @@ -218,11 +246,7 @@ mono_domain_finalize (MonoDomain *domain, guint32 timeout) void ves_icall_System_GC_InternalCollect (int generation) { - MONO_ARCH_SAVE_REGS; - -#if HAVE_BOEHM_GC - GC_gcollect (); -#endif + mono_gc_collect (generation); } gint64 @@ -230,13 +254,9 @@ ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection) { MONO_ARCH_SAVE_REGS; -#if HAVE_BOEHM_GC if (forceCollection) - GC_gcollect (); - return GC_get_heap_size () - GC_get_free_bytes (); -#else - return 0; -#endif + mono_gc_collect (mono_gc_max_generation ()); + return mono_gc_get_used_size (); } void @@ -262,6 +282,13 @@ ves_icall_System_GC_SuppressFinalize (MonoObject *obj) { MONO_ARCH_SAVE_REGS; + /* delegates have no finalizers, but we register them to deal with the + * unmanaged->managed trampoline. We don't let the user suppress it + * otherwise we'd leak it. + */ + if (obj->vtable->klass->delegate) + return; + object_register_finalizer (obj, NULL); } @@ -270,8 +297,8 @@ ves_icall_System_GC_WaitForPendingFinalizers (void) { MONO_ARCH_SAVE_REGS; -#if HAVE_BOEHM_GC - if (!GC_should_invoke_finalizers ()) +#ifndef HAVE_NULL_GC + if (!mono_gc_pending_finalizers ()) return; if (mono_thread_current () == gc_thread) @@ -279,27 +306,17 @@ ves_icall_System_GC_WaitForPendingFinalizers (void) return; ResetEvent (pending_done_event); - finalize_notify (); + mono_gc_finalize_notify (); /* g_print ("Waiting for pending finalizers....\n"); */ WaitForSingleObjectEx (pending_done_event, INFINITE, TRUE); /* g_print ("Done pending....\n"); */ #else #endif } - +#define mono_allocator_lock() EnterCriticalSection (&allocator_section) +#define mono_allocator_unlock() LeaveCriticalSection (&allocator_section) static CRITICAL_SECTION allocator_section; static CRITICAL_SECTION handle_section; -static guint32 next_handle = 0; -static gpointer *gc_handles = NULL; -static guint8 *gc_handle_types = NULL; -static guint32 array_size = 0; - -/* - * The handle type is encoded in the lower two bits of the handle value: - * 0 -> normal - * 1 -> pinned - * 2 -> weak - */ typedef enum { HANDLE_WEAK, @@ -308,206 +325,414 @@ typedef enum { HANDLE_PINNED } HandleType; -/* - * FIXME: make thread safe and reuse the array entries. - */ +static void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj); + MonoObject * ves_icall_System_GCHandle_GetTarget (guint32 handle) { - MonoObject *obj; - gint32 type; - - MONO_ARCH_SAVE_REGS; - - if (gc_handles) { - type = handle & 0x3; - EnterCriticalSection (&handle_section); - g_assert (type == gc_handle_types [handle >> 2]); - obj = gc_handles [handle >> 2]; - LeaveCriticalSection (&handle_section); - if (!obj) - return NULL; - - if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) - return REVEAL_POINTER (obj); - else - return obj; - } - return NULL; + return mono_gchandle_get_target (handle); } +/* + * if type == -1, change the target of the handle, otherwise allocate a new handle. + */ guint32 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type) { - gpointer val = obj; - guint32 h, idx; - - MONO_ARCH_SAVE_REGS; - - EnterCriticalSection (&handle_section); - /* Indexes start from 1 since 0 means the handle is not allocated */ - idx = ++next_handle; - if (idx >= array_size) { - gpointer *new_array; - guint8 *new_type_array; - if (!array_size) - array_size = 16; -#if HAVE_BOEHM_GC - new_array = GC_MALLOC (sizeof (gpointer) * (array_size * 2)); - new_type_array = GC_MALLOC (sizeof (guint8) * (array_size * 2)); -#else - new_array = g_malloc0 (sizeof (gpointer) * (array_size * 2)); - new_type_array = g_malloc0 (sizeof (guint8) * (array_size * 2)); -#endif - if (gc_handles) { - int i; - memcpy (new_array, gc_handles, sizeof (gpointer) * array_size); - memcpy (new_type_array, gc_handle_types, sizeof (guint8) * array_size); - /* need to re-register links for weak refs. test if GC_realloc needs the same */ - for (i = 0; i < array_size; ++i) { -#if 0 /* This breaks the threaded finalizer, by causing segfaults deep - * inside libgc. I assume it will also break without the - * threaded finalizer, just that the stress test (bug 31333) - * deadlocks too early without it. Reverting to the previous - * version here stops the segfault. - */ - if ((gc_handle_types[i] == HANDLE_WEAK) || (gc_handle_types[i] == HANDLE_WEAK_TRACK)) { /* all and only disguised pointers have it set */ -#else - if (((gulong)new_array [i]) & 0x1) { -#endif -#if HAVE_BOEHM_GC - if (gc_handles [i] != (gpointer)-1) - GC_unregister_disappearing_link (&(gc_handles [i])); - if (new_array [i] != (gpointer)-1) - GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(new_array [i]), REVEAL_POINTER (new_array [i])); -#endif - } - } - } - array_size *= 2; -#ifndef HAVE_BOEHM_GC - g_free (gc_handles); - g_free (gc_handle_types); -#endif - gc_handles = new_array; - gc_handle_types = new_type_array; + if (type == -1) { + mono_gchandle_set_target (handle, obj); + /* the handle doesn't change */ + return handle; } - - /* resuse the type from the old target */ - if (type == -1) - type = handle & 0x3; - h = (idx << 2) | type; switch (type) { case HANDLE_WEAK: + return mono_gchandle_new_weakref (obj, FALSE); case HANDLE_WEAK_TRACK: - val = (gpointer)HIDE_POINTER (val); - gc_handles [idx] = val; - gc_handle_types [idx] = type; -#if HAVE_BOEHM_GC - if (gc_handles [idx] != (gpointer)-1) - GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(gc_handles [idx]), obj); -#endif - break; + return mono_gchandle_new_weakref (obj, TRUE); + case HANDLE_NORMAL: + return mono_gchandle_new (obj, FALSE); + case HANDLE_PINNED: + return mono_gchandle_new (obj, TRUE); default: - gc_handles [idx] = val; - gc_handle_types [idx] = type; - break; + g_assert_not_reached (); } - LeaveCriticalSection (&handle_section); - return h; + return 0; } void ves_icall_System_GCHandle_FreeHandle (guint32 handle) { - int idx = handle >> 2; - int type = handle & 0x3; - - MONO_ARCH_SAVE_REGS; - - EnterCriticalSection (&handle_section); - -#ifdef HAVE_BOEHM_GC - g_assert (type == gc_handle_types [idx]); - if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) { - if (gc_handles [idx] != (gpointer)-1) - GC_unregister_disappearing_link (&(gc_handles [idx])); - } -#endif - - gc_handles [idx] = (gpointer)-1; - gc_handle_types [idx] = (guint8)-1; - LeaveCriticalSection (&handle_section); + mono_gchandle_free (handle); } gpointer ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle) { MonoObject *obj; - int type = handle & 0x3; - MONO_ARCH_SAVE_REGS; + obj = mono_gchandle_get_target (handle); + if (obj) { + MonoClass *klass = mono_object_class (obj); + if (klass == mono_defaults.string_class) { + return mono_string_chars ((MonoString*)obj); + } else if (klass->rank) { + return mono_array_addr ((MonoArray*)obj, char, 0); + } else { + /* the C# code will check and throw the exception */ + /* FIXME: missing !klass->blittable test, see bug #61134 */ + if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT) + return (gpointer)-1; + return (char*)obj + sizeof (MonoObject); + } + } + return NULL; +} + +typedef struct { + guint32 *bitmap; + gpointer *entries; + guint32 size; + guint8 type; + guint slot_hint : 24; /* starting slot for search */ + /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */ + /* we alloc this only for weak refs, since we can get the domain directly in the other cases */ + guint16 *domain_ids; +} HandleData; + +/* weak and weak-track arrays will be allocated in malloc memory + */ +static HandleData gc_handles [] = { + {NULL, NULL, 0, HANDLE_WEAK, 0}, + {NULL, NULL, 0, HANDLE_WEAK_TRACK, 0}, + {NULL, NULL, 0, HANDLE_NORMAL, 0}, + {NULL, NULL, 0, HANDLE_PINNED, 0} +}; - if (gc_handles) { - EnterCriticalSection (&handle_section); - obj = gc_handles [handle >> 2]; - g_assert (gc_handle_types [handle >> 2] == type); - LeaveCriticalSection (&handle_section); - if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) { - obj = REVEAL_POINTER (obj); - if (obj == (MonoObject *) -1) - return NULL; +#define lock_handles(handles) EnterCriticalSection (&handle_section) +#define unlock_handles(handles) LeaveCriticalSection (&handle_section) + +static int +find_first_unset (guint32 bitmap) +{ + int i; + for (i = 0; i < 32; ++i) { + if (!(bitmap & (1 << i))) + return i; + } + return -1; +} + +static guint32 +alloc_handle (HandleData *handles, MonoObject *obj) +{ + gint slot, i; + lock_handles (handles); + if (!handles->size) { + handles->size = 32; + if (handles->type > HANDLE_WEAK_TRACK) { + handles->entries = mono_gc_alloc_fixed (sizeof (gpointer) * handles->size, NULL); + } else { + handles->entries = g_malloc0 (sizeof (gpointer) * handles->size); + handles->domain_ids = g_malloc0 (sizeof (guint16) * handles->size); } - if (obj) { - MonoClass *klass = mono_object_class (obj); - if (klass == mono_defaults.string_class) { - return mono_string_chars ((MonoString*)obj); - } else if (klass->rank) { - return mono_array_addr ((MonoArray*)obj, char, 0); - } else { - /* the C# code will check and throw the exception */ - /* FIXME: missing !klass->blittable test, see bug #61134, - * disabled in 1.0 untill the blittable-using code is audited. - if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT) - return (gpointer)-1; */ - return (char*)obj + sizeof (MonoObject); + handles->bitmap = g_malloc0 (handles->size / 8); + } + i = -1; + for (slot = handles->slot_hint; slot < handles->size / 32; ++slot) { + if (handles->bitmap [slot] != 0xffffffff) { + i = find_first_unset (handles->bitmap [slot]); + handles->slot_hint = slot; + break; + } + } + if (i == -1 && handles->slot_hint != 0) { + for (slot = 0; slot < handles->slot_hint; ++slot) { + if (handles->bitmap [slot] != 0xffffffff) { + i = find_first_unset (handles->bitmap [slot]); + handles->slot_hint = slot; + break; } } } - return NULL; + if (i == -1) { + guint32 *new_bitmap; + guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */ + + /* resize and copy the bitmap */ + new_bitmap = g_malloc0 (new_size / 8); + memcpy (new_bitmap, handles->bitmap, handles->size / 8); + g_free (handles->bitmap); + handles->bitmap = new_bitmap; + + /* resize and copy the entries */ + if (handles->type > HANDLE_WEAK_TRACK) { + gpointer *entries; + entries = mono_gc_alloc_fixed (sizeof (gpointer) * new_size, NULL); + memcpy (entries, handles->entries, sizeof (gpointer) * handles->size); + handles->entries = entries; + } else { + gpointer *entries; + guint16 *domain_ids; + domain_ids = g_malloc0 (sizeof (guint16) * new_size); + entries = g_malloc (sizeof (gpointer) * new_size); + /* we disable GC because we could lose some disappearing link updates */ + mono_gc_disable (); + memcpy (entries, handles->entries, sizeof (gpointer) * handles->size); + memset (entries + handles->size, 0, sizeof (gpointer) * handles->size); + memcpy (domain_ids, handles->domain_ids, sizeof (guint16) * handles->size); + for (i = 0; i < handles->size; ++i) { + MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i])); + if (handles->entries [i]) + mono_gc_weak_link_remove (&(handles->entries [i])); + /*g_print ("reg/unreg entry %d of type %d at %p to object %p (%p), was: %p\n", i, handles->type, &(entries [i]), obj, entries [i], handles->entries [i]);*/ + if (obj) { + mono_gc_weak_link_add (&(entries [i]), obj); + } + } + g_free (handles->entries); + g_free (handles->domain_ids); + handles->entries = entries; + handles->domain_ids = domain_ids; + mono_gc_enable (); + } + + /* set i and slot to the next free position */ + i = 0; + slot = (handles->size + 1) / 32; + handles->slot_hint = handles->size + 1; + handles->size = new_size; + } + handles->bitmap [slot] |= 1 << i; + slot = slot * 32 + i; + handles->entries [slot] = obj; + if (handles->type <= HANDLE_WEAK_TRACK) { + if (obj) + mono_gc_weak_link_add (&(handles->entries [slot]), obj); + } + + unlock_handles (handles); + /*g_print ("allocated entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/ + return (slot << 3) | (handles->type + 1); } +/** + * mono_gchandle_new: + * @obj: managed object to get a handle for + * @pinned: whether the object should be pinned + * + * This returns a handle that wraps the object, this is used to keep a + * reference to a managed object from the unmanaged world and preventing the + * object from being disposed. + * + * If @pinned is false the address of the object can not be obtained, if it is + * true the address of the object can be obtained. This will also pin the + * object so it will not be possible by a moving garbage collector to move the + * object. + * + * Returns: a handle that can be used to access the object from + * unmanaged code. + */ guint32 mono_gchandle_new (MonoObject *obj, gboolean pinned) { - return ves_icall_System_GCHandle_GetTargetHandle (obj, 0, pinned? HANDLE_PINNED: HANDLE_NORMAL); + return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj); } +/** + * mono_gchandle_new_weakref: + * @obj: managed object to get a handle for + * @pinned: whether the object should be pinned + * + * This returns a weak handle that wraps the object, this is used to + * keep a reference to a managed object from the unmanaged world. + * Unlike the mono_gchandle_new the object can be reclaimed by the + * garbage collector. In this case the value of the GCHandle will be + * set to zero. + * + * If @pinned is false the address of the object can not be obtained, if it is + * true the address of the object can be obtained. This will also pin the + * object so it will not be possible by a moving garbage collector to move the + * object. + * + * Returns: a handle that can be used to access the object from + * unmanaged code. + */ guint32 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection) { - return ves_icall_System_GCHandle_GetTargetHandle (obj, 0, track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK); + return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj); } -/* This will return NULL for a collected object if using a weakref handle */ +/** + * mono_gchandle_get_target: + * @gchandle: a GCHandle's handle. + * + * The handle was previously created by calling mono_gchandle_new or + * mono_gchandle_new_weakref. + * + * Returns a pointer to the MonoObject represented by the handle or + * NULL for a collected object if using a weakref handle. + */ MonoObject* mono_gchandle_get_target (guint32 gchandle) { - return ves_icall_System_GCHandle_GetTarget (gchandle); + guint slot = gchandle >> 3; + guint type = (gchandle & 7) - 1; + HandleData *handles = &gc_handles [type]; + MonoObject *obj = NULL; + if (type > 3) + return NULL; + lock_handles (handles); + if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) { + if (handles->type <= HANDLE_WEAK_TRACK) { + obj = mono_gc_weak_link_get (&handles->entries [slot]); + } else { + obj = handles->entries [slot]; + } + } else { + /* print a warning? */ + } + unlock_handles (handles); + /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/ + return obj; +} + +static void +mono_gchandle_set_target (guint32 gchandle, MonoObject *obj) +{ + guint slot = gchandle >> 3; + guint type = (gchandle & 7) - 1; + HandleData *handles = &gc_handles [type]; + if (type > 3) + return; + lock_handles (handles); + if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) { + if (handles->type <= HANDLE_WEAK_TRACK) { + if (handles->entries [slot]) + mono_gc_weak_link_remove (&handles->entries [slot]); + if (obj) + mono_gc_weak_link_add (&handles->entries [slot], obj); + } else { + handles->entries [slot] = obj; + } + } else { + /* print a warning? */ + } + /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/ + unlock_handles (handles); +} + +/** + * mono_gchandle_is_in_domain: + * @gchandle: a GCHandle's handle. + * @domain: An application domain. + * + * Returns: true if the object wrapped by the @gchandle belongs to the specific @domain. + */ +gboolean +mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain) +{ + guint slot = gchandle >> 3; + guint type = (gchandle & 7) - 1; + HandleData *handles = &gc_handles [type]; + gboolean result = FALSE; + if (type > 3) + return FALSE; + lock_handles (handles); + if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) { + if (handles->type <= HANDLE_WEAK_TRACK) { + result = domain->domain_id == handles->domain_ids [slot]; + } else { + MonoObject *obj; + obj = handles->entries [slot]; + if (obj == NULL) + result = TRUE; + else + result = domain == mono_object_domain (obj); + } + } else { + /* print a warning? */ + } + unlock_handles (handles); + return result; } +/** + * mono_gchandle_free: + * @gchandle: a GCHandle's handle. + * + * Frees the @gchandle handle. If there are no outstanding + * references, the garbage collector can reclaim the memory of the + * object wrapped. + */ void mono_gchandle_free (guint32 gchandle) { - ves_icall_System_GCHandle_FreeHandle (gchandle); + guint slot = gchandle >> 3; + guint type = (gchandle & 7) - 1; + HandleData *handles = &gc_handles [type]; + if (type > 3) + return; + lock_handles (handles); + if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) { + if (handles->type <= HANDLE_WEAK_TRACK) { + if (handles->entries [slot]) + mono_gc_weak_link_remove (&handles->entries [slot]); + } else { + handles->entries [slot] = NULL; + } + handles->bitmap [slot / 32] &= ~(1 << (slot % 32)); + } else { + /* print a warning? */ + } + /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/ + unlock_handles (handles); } -#if HAVE_BOEHM_GC +/** + * mono_gchandle_free_domain: + * @domain: domain that is unloading + * + * Function used internally to cleanup any GC handle for objects belonging + * to the specified domain during appdomain unload. + */ +void +mono_gchandle_free_domain (MonoDomain *domain) +{ + guint type; + + for (type = 0; type < 3; ++type) { + guint slot; + HandleData *handles = &gc_handles [type]; + lock_handles (handles); + for (slot = 0; slot < handles->size; ++slot) { + if (!(handles->bitmap [slot / 32] & (1 << (slot % 32)))) + continue; + if (type <= HANDLE_WEAK_TRACK) { + if (domain->domain_id == handles->domain_ids [slot]) { + handles->bitmap [slot / 32] &= ~(1 << (slot % 32)); + if (handles->entries [slot]) + mono_gc_weak_link_remove (&handles->entries [slot]); + } + } else { + if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) { + handles->bitmap [slot / 32] &= ~(1 << (slot % 32)); + handles->entries [slot] = NULL; + } + } + } + unlock_handles (handles); + } + +} + +#ifndef HAVE_NULL_GC static HANDLE finalizer_event; static volatile gboolean finished=FALSE; -static void finalize_notify (void) +void +mono_gc_finalize_notify (void) { #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": prodding finalizer"); @@ -531,11 +756,12 @@ collect_objects (gpointer key, gpointer value, gpointer user_data) static void finalize_domain_objects (DomainFinalizationReq *req) { - int i; - GPtrArray *objs; MonoDomain *domain = req->domain; - + +#ifdef HAVE_BOEHM_GC while (g_hash_table_size (domain->finalizable_objects_hash) > 0) { + int i; + GPtrArray *objs; /* * Since the domain is unloading, nobody is allowed to put * new entries into the hash table. But finalize_object might @@ -554,6 +780,20 @@ finalize_domain_objects (DomainFinalizationReq *req) g_ptr_array_free (objs, TRUE); } +#elif defined(HAVE_SGEN_GC) +#define NUM_FOBJECTS 64 + MonoObject *to_finalize [NUM_FOBJECTS]; + int count; + while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) { + int i; + for (i = 0; i < count; ++i) { + run_finalize (to_finalize [i], 0); + } + } +#endif + + /* Process finalizers which are already in the queue */ + mono_gc_invoke_finalizers (); /* printf ("DONE.\n"); */ SetEvent (req->done_event); @@ -572,19 +812,20 @@ static guint32 finalizer_thread (gpointer unused) /* Wait to be notified that there's at least one * finaliser to run */ - WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE); + /* Use alertable=FALSE since we will be asked to exit using the event too */ + WaitForSingleObjectEx (finalizer_event, INFINITE, FALSE); if (domains_to_finalize) { - EnterCriticalSection (&finalizer_mutex); + mono_finalizer_lock (); if (domains_to_finalize) { DomainFinalizationReq *req = domains_to_finalize->data; domains_to_finalize = g_slist_remove (domains_to_finalize, req); - LeaveCriticalSection (&finalizer_mutex); + mono_finalizer_unlock (); finalize_domain_objects (req); } else - LeaveCriticalSection (&finalizer_mutex); + mono_finalizer_unlock (); } #ifdef DEBUG @@ -593,15 +834,8 @@ static guint32 finalizer_thread (gpointer unused) /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup), * before the domain is unloaded. - * - * There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4: - * the 'mem_freed' variable is not initialized when there are no - * objects to finalize, which leads to strange behavior later on. - * The check is necessary to work around that bug. */ - if (GC_should_invoke_finalizers ()) { - GC_invoke_finalizers (); - } + mono_gc_invoke_finalizers (); SetEvent (pending_done_event); } @@ -617,54 +851,19 @@ static guint32 finalizer_thread (gpointer unused) */ #define ENABLE_FINALIZER_THREAD -#ifdef WITH_INCLUDED_LIBGC -/* from threads.c */ -extern void mono_gc_stop_world (void); -extern void mono_gc_start_world (void); -extern void mono_gc_push_all_stacks (void); - -static void mono_gc_lock (void) -{ - EnterCriticalSection (&allocator_section); -} - -static void mono_gc_unlock (void) -{ - LeaveCriticalSection (&allocator_section); -} - -static GCThreadFunctions mono_gc_thread_vtable = { - NULL, - - mono_gc_lock, - mono_gc_unlock, - - mono_gc_stop_world, - NULL, - mono_gc_push_all_stacks, - mono_gc_start_world -}; -#endif /* WITH_INCLUDED_LIBGC */ - -static void -mono_gc_warning (char *msg, GC_word arg) -{ - mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg); -} - -void mono_gc_init (void) +void +mono_gc_init (void) { InitializeCriticalSection (&handle_section); InitializeCriticalSection (&allocator_section); InitializeCriticalSection (&finalizer_mutex); -#ifdef WITH_INCLUDED_LIBGC - gc_thread_vtable = &mono_gc_thread_vtable; -#endif + MONO_GC_REGISTER_ROOT (gc_handles [HANDLE_NORMAL].entries); + MONO_GC_REGISTER_ROOT (gc_handles [HANDLE_PINNED].entries); + + mono_gc_base_init (); - GC_set_warn_proc (mono_gc_warning); - #ifdef ENABLE_FINALIZER_THREAD if (g_getenv ("GC_DONT_GC")) { @@ -680,9 +879,6 @@ void mono_gc_init (void) g_assert_not_reached (); } - GC_finalize_on_demand = 1; - GC_finalizer_notifier = finalize_notify; - mono_thread_create (mono_domain_get (), finalizer_thread, NULL); /* * Wait until the finalizer thread sets gc_thread since its value is needed @@ -702,35 +898,25 @@ void mono_gc_cleanup (void) if (!gc_disabled) { ResetEvent (shutdown_event); finished = TRUE; - finalize_notify (); - /* Finishing the finalizer thread, so wait a little bit... */ - /* MS seems to wait for about 2 seconds */ - if (WaitForSingleObjectEx (shutdown_event, 2000, FALSE) == WAIT_TIMEOUT) { - mono_thread_stop (gc_thread); + if (mono_thread_current () != gc_thread) { + mono_gc_finalize_notify (); + /* Finishing the finalizer thread, so wait a little bit... */ + /* MS seems to wait for about 2 seconds */ + if (WaitForSingleObjectEx (shutdown_event, 2000, FALSE) == WAIT_TIMEOUT) { + mono_thread_stop (gc_thread); + } } + gc_thread = NULL; +#ifdef HAVE_BOEHM_GC + GC_finalizer_notifier = NULL; +#endif } #endif -} -void -mono_gc_disable (void) -{ -#ifdef HAVE_GC_ENABLE - GC_disable (); -#else - g_assert_not_reached (); -#endif -} - -void -mono_gc_enable (void) -{ -#ifdef HAVE_GC_ENABLE - GC_enable (); -#else - g_assert_not_reached (); -#endif + DeleteCriticalSection (&handle_section); + DeleteCriticalSection (&allocator_section); + DeleteCriticalSection (&finalizer_mutex); } #else @@ -745,18 +931,18 @@ void mono_gc_cleanup (void) { } -void -mono_gc_disable (void) -{ -} - -void -mono_gc_enable (void) -{ -} - #endif +/** + * mono_gc_is_finalizer_thread: + * @thread: the thread to test. + * + * In Mono objects are finalized asynchronously on a separate thread. + * This routine tests whether the @thread argument represents the + * finalization thread. + * + * Returns true if @thread is the finalization thread. + */ gboolean mono_gc_is_finalizer_thread (MonoThread *thread) {