X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fgc.c;h=ed68449f044caec15cacefbee3a9d092ab62986a;hb=c8eec53efed03b6539a32c4f0b533f328bc1e42f;hp=7dc5de072b24db2f7f0cc1c7ae22b22e4bff921f;hpb=496dfbf9ec0fd3143e5dd560a863d916e56a52b8;p=mono.git diff --git a/mono/metadata/gc.c b/mono/metadata/gc.c index 7dc5de072b2..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,6 +76,7 @@ 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 @@ -78,11 +94,22 @@ run_finalize (void *obj, void *data) /* 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); */ @@ -100,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) { @@ -133,28 +187,28 @@ 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) @@ -199,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 @@ -213,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 (); @@ -236,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 */ @@ -282,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; @@ -303,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; @@ -320,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) { @@ -363,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); @@ -556,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. @@ -749,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) { @@ -772,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 (); @@ -783,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 (); @@ -803,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. */ @@ -821,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) { @@ -844,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; @@ -865,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; @@ -892,8 +976,6 @@ void mono_gc_cleanup (void) #endif } -#endif - DeleteCriticalSection (&handle_section); DeleteCriticalSection (&allocator_section); DeleteCriticalSection (&finalizer_mutex); @@ -901,7 +983,12 @@ void mono_gc_cleanup (void) #else -/* no Boehm GC support. */ +/* Null GC dummy functions */ +void +mono_gc_finalize_notify (void) +{ +} + void mono_gc_init (void) { InitializeCriticalSection (&handle_section);