X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fgc.c;h=ed68449f044caec15cacefbee3a9d092ab62986a;hb=c8eec53efed03b6539a32c4f0b533f328bc1e42f;hp=c636cbf2dd47256f52e907e7aaad6f2111f01c85;hpb=096265478e6e4145c90250a5bf78c0c179ee50af;p=mono.git diff --git a/mono/metadata/gc.c b/mono/metadata/gc.c index c636cbf2dd4..ed68449f044 100644 --- a/mono/metadata/gc.c +++ b/mono/metadata/gc.c @@ -18,8 +18,10 @@ #include #include #include +#include +#include #include -#include +#include #include /* for mono_delegate_free_ftnptr () */ typedef struct DomainFinalizationReq { @@ -36,11 +38,14 @@ extern int __imp_GC_finalize_on_demand; static gboolean gc_disabled = FALSE; +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 GSList *domains_to_finalize= NULL; +static MonoMList *threads_to_finalize = NULL; static MonoThread *gc_thread; @@ -52,6 +57,16 @@ static HANDLE shutdown_event; static HANDLE thread_started_event; #endif +static void +add_thread_to_finalize (MonoThread *thread) +{ + mono_finalizer_lock (); + if (!threads_to_finalize) + MONO_GC_REGISTER_ROOT (threads_to_finalize); + threads_to_finalize = mono_mlist_append (threads_to_finalize, (MonoObject*)thread); + mono_finalizer_unlock (); +} + /* * actually, we might want to queue the finalize requests in a separate thread, * but we need to be careful about the execution domain of the thread... @@ -61,8 +76,10 @@ run_finalize (void *obj, void *data) { MonoObject *exc = NULL; MonoObject *o, *o2; + MonoMethod* finalizer = NULL; o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data)); +#ifndef HAVE_SGEN_GC mono_domain_lock (o->vtable->domain); o2 = g_hash_table_lookup (o->vtable->domain->finalizable_objects_hash, o); @@ -72,15 +89,27 @@ 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); - if (o->vtable->klass == mono_get_thread_class ()) - if (mono_gc_is_finalizer_thread ((MonoThread*)o)) + if (o->vtable->klass == mono_get_thread_class ()) { + MonoThread *t = (MonoThread*)o; + + if (mono_gc_is_finalizer_thread (t)) /* Avoid finalizing ourselves */ return; + if (t->threadpool_thread && finalizing_root_domain) { + /* Don't finalize threadpool threads when + shutting down - they're finalized when the + threadpool shuts down. */ + add_thread_to_finalize (t); + return; + } + } + /* speedup later... and use a timeout */ /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */ @@ -98,13 +127,40 @@ run_finalize (void *obj, void *data) return; } - mono_runtime_invoke (mono_class_get_finalizer (o->vtable->klass), o, NULL, &exc); + finalizer = mono_class_get_finalizer (o->vtable->klass); + + /* If object has a CCW but has no finalizer, it was only + * registered for finalization in order to free the CCW. + * Else it needs the regular finalizer run. + * FIXME: what to do about ressurection and suppression + * of finalizer on object with CCW. + */ + if (mono_marshal_free_ccw (o) && !finalizer) + return; + + mono_runtime_invoke (finalizer, o, NULL, &exc); if (exc) { /* fixme: do something useful */ } } +void +mono_gc_finalize_threadpool_threads (void) +{ + while (threads_to_finalize) { + MonoThread *thread = (MonoThread*) mono_mlist_get_data (threads_to_finalize); + + /* Force finalization of the thread. */ + thread->threadpool_thread = FALSE; + mono_object_register_finalizer ((MonoObject*)thread); + + run_finalize (thread, NULL); + + threads_to_finalize = mono_mlist_next (threads_to_finalize); + } +} + gpointer mono_gc_out_of_memory (size_t size) { @@ -131,30 +187,32 @@ object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*)) { #if HAVE_BOEHM_GC guint offset = 0; + MonoDomain *domain = obj->vtable->domain; #ifndef GC_DEBUG /* This assertion is not valid when GC_DEBUG is defined */ g_assert (GC_base (obj) == (char*)obj - offset); #endif - if (mono_domain_is_unloading (obj->vtable->domain) && (callback != NULL)) + if (mono_domain_is_unloading (domain) && (callback != NULL)) /* * Can't register finalizers in a dying appdomain, since they * could be invoked after the appdomain has been unloaded. */ return; - mono_domain_lock (obj->vtable->domain); + mono_domain_lock (domain); if (callback) - g_hash_table_insert (obj->vtable->domain->finalizable_objects_hash, obj, - obj); + g_hash_table_insert (domain->finalizable_objects_hash, obj, obj); else - g_hash_table_remove (obj->vtable->domain->finalizable_objects_hash, obj); + g_hash_table_remove (domain->finalizable_objects_hash, obj); - mono_domain_unlock (obj->vtable->domain); + mono_domain_unlock (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 } @@ -195,8 +253,6 @@ mono_domain_finalize (MonoDomain *domain, guint32 timeout) /* 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 @@ -209,10 +265,16 @@ mono_domain_finalize (MonoDomain *domain, guint32 timeout) mono_gc_collect (mono_gc_max_generation ()); done_event = CreateEvent (NULL, TRUE, FALSE, NULL); + if (done_event == NULL) { + return FALSE; + } req = g_new0 (DomainFinalizationReq, 1); req->domain = domain; req->done_event = done_event; + + if (domain == mono_get_root_domain ()) + finalizing_root_domain = TRUE; mono_finalizer_lock (); @@ -232,6 +294,12 @@ mono_domain_finalize (MonoDomain *domain, guint32 timeout) } CloseHandle (done_event); + + if (domain == mono_get_root_domain ()) { + mono_thread_pool_cleanup (); + mono_gc_finalize_threadpool_threads (); + } + return TRUE; #else /* We don't support domain finalization without a GC */ @@ -278,14 +346,23 @@ 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; + + /* FIXME: Need to handle case where obj has COM Callable Wrapper + * generated for it that needs cleaned up, but user wants to suppress + * their derived object finalizer. */ + object_register_finalizer (obj, NULL); } void ves_icall_System_GC_WaitForPendingFinalizers (void) { - MONO_ARCH_SAVE_REGS; - #ifndef HAVE_NULL_GC if (!mono_gc_pending_finalizers ()) return; @@ -299,9 +376,9 @@ ves_icall_System_GC_WaitForPendingFinalizers (void) /* 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; @@ -316,6 +393,8 @@ typedef enum { static void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj); +static HandleType mono_gchandle_get_type (guint32 gchandle); + MonoObject * ves_icall_System_GCHandle_GetTarget (guint32 handle) { @@ -359,6 +438,8 @@ ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle) { MonoObject *obj; + if (mono_gchandle_get_type (handle) != HANDLE_PINNED) + return (gpointer)-2; obj = mono_gchandle_get_target (handle); if (obj) { MonoClass *klass = mono_object_class (obj); @@ -471,7 +552,8 @@ alloc_handle (HandleData *handles, MonoObject *obj) 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])); - mono_gc_weak_link_remove (&(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); @@ -551,6 +633,14 @@ mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection) return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj); } +static HandleType +mono_gchandle_get_type (guint32 gchandle) +{ + guint type = (gchandle & 7) - 1; + + return type; +} + /** * mono_gchandle_get_target: * @gchandle: a GCHandle's handle. @@ -596,7 +686,8 @@ mono_gchandle_set_target (guint32 gchandle, MonoObject *obj) lock_handles (handles); if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) { if (handles->type <= HANDLE_WEAK_TRACK) { - mono_gc_weak_link_remove (&handles->entries [slot]); + if (handles->entries [slot]) + mono_gc_weak_link_remove (&handles->entries [slot]); if (obj) mono_gc_weak_link_add (&handles->entries [slot], obj); } else { @@ -662,10 +753,12 @@ mono_gchandle_free (guint32 gchandle) return; lock_handles (handles); if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) { - if (handles->type <= HANDLE_WEAK_TRACK) - mono_gc_weak_link_remove (&handles->entries [slot]); - else + 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? */ @@ -674,6 +767,43 @@ mono_gchandle_free (guint32 gchandle) unlock_handles (handles); } +/** + * 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; @@ -704,19 +834,19 @@ 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 * remove entries from the hash table, so we make a copy. */ objs = g_ptr_array_new (); - g_hash_table_foreach (domain->finalizable_objects_hash, - collect_objects, objs); + g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs); /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */ for (i = 0; i < objs->len; ++i) { @@ -727,6 +857,17 @@ 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 (); @@ -738,17 +879,19 @@ finalize_domain_objects (DomainFinalizationReq *req) g_free (req); } -static guint32 finalizer_thread (gpointer unused) +static guint32 +finalizer_thread (gpointer unused) { gc_thread = mono_thread_current (); SetEvent (thread_started_event); - while(!finished) { + while (!finished) { /* 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) { mono_finalizer_lock (); @@ -758,15 +901,11 @@ static guint32 finalizer_thread (gpointer unused) mono_finalizer_unlock (); finalize_domain_objects (req); - } - else + } else { mono_finalizer_unlock (); + } } -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": invoking finalizers"); -#endif - /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup), * before the domain is unloaded. */ @@ -776,16 +915,9 @@ static guint32 finalizer_thread (gpointer unused) } SetEvent (shutdown_event); - return(0); + return 0; } -/* - * Enable or disable the separate finalizer thread. - * It's currently disabled because it still requires some - * work in the rest of the runtime. - */ -#define ENABLE_FINALIZER_THREAD - void mono_gc_init (void) { @@ -799,8 +931,6 @@ mono_gc_init (void) mono_gc_base_init (); -#ifdef ENABLE_FINALIZER_THREAD - if (g_getenv ("GC_DONT_GC")) { gc_disabled = TRUE; return; @@ -820,16 +950,15 @@ mono_gc_init (void) * by mono_thread_attach () */ WaitForSingleObjectEx (thread_started_event, INFINITE, FALSE); -#endif } -void mono_gc_cleanup (void) +void +mono_gc_cleanup (void) { #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": cleaning up finalizer"); #endif -#ifdef ENABLE_FINALIZER_THREAD if (!gc_disabled) { ResetEvent (shutdown_event); finished = TRUE; @@ -847,12 +976,19 @@ void mono_gc_cleanup (void) #endif } -#endif + DeleteCriticalSection (&handle_section); + DeleteCriticalSection (&allocator_section); + DeleteCriticalSection (&finalizer_mutex); } #else -/* no Boehm GC support. */ +/* Null GC dummy functions */ +void +mono_gc_finalize_notify (void) +{ +} + void mono_gc_init (void) { InitializeCriticalSection (&handle_section);