X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fgc.c;h=ed68449f044caec15cacefbee3a9d092ab62986a;hb=c8eec53efed03b6539a32c4f0b533f328bc1e42f;hp=00fecb0bd33c658e896e75293f60bc3290b68252;hpb=c4aef31eeea309e6a795c84c098ac8e1a2490340;p=mono.git diff --git a/mono/metadata/gc.c b/mono/metadata/gc.c index 00fecb0bd33..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,23 +38,35 @@ 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; 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; #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... @@ -62,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); @@ -73,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); */ @@ -99,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) { @@ -132,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 } @@ -196,24 +253,28 @@ 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 */ -#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); + 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 (); @@ -222,7 +283,7 @@ mono_domain_finalize (MonoDomain *domain, guint32 timeout) mono_finalizer_unlock (); /* Tell the finalizer thread to finalize this appdomain */ - finalize_notify (); + mono_gc_finalize_notify (); res = WaitForSingleObjectEx (done_event, timeout, TRUE); @@ -233,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 */ @@ -279,16 +346,25 @@ 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; - -#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) @@ -296,13 +372,13 @@ 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; @@ -317,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) { @@ -360,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); @@ -472,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); @@ -552,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. @@ -597,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 { @@ -663,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? */ @@ -675,12 +767,50 @@ mono_gchandle_free (guint32 gchandle) 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"); @@ -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,9 +857,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 */ - GC_invoke_finalizers (); + mono_gc_invoke_finalizers (); /* printf ("DONE.\n"); */ SetEvent (req->done_event); @@ -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,96 +901,35 @@ 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. - * - * 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); } SetEvent (shutdown_event); - 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 - -#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) -{ - mono_allocator_lock (); -} - -static void mono_gc_unlock (void) -{ - mono_allocator_unlock (); -} - -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); + return 0; } -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); - GC_no_dls = TRUE; - - GC_oom_fn = mono_gc_out_of_memory; - - GC_set_warn_proc (mono_gc_warning); -#ifdef ENABLE_FINALIZER_THREAD + mono_gc_base_init (); if (g_getenv ("GC_DONT_GC")) { gc_disabled = TRUE; @@ -862,30 +944,26 @@ 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 * 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; if (mono_thread_current () != gc_thread) { - finalize_notify (); + 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) { @@ -893,15 +971,24 @@ void mono_gc_cleanup (void) } } gc_thread = NULL; +#ifdef HAVE_BOEHM_GC GC_finalizer_notifier = NULL; +#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);