X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fgc.c;h=2071f610b3529476814f9fd747e9669fe8217f8a;hb=f7b9cd57483861ba3b10353d1cc3eb9f1fb26760;hp=4bee4b445acedefe8d097ed2c9f12a81aa15136e;hpb=2a8259225695032220537b3c90a99d7a2686f214;p=mono.git diff --git a/mono/metadata/gc.c b/mono/metadata/gc.c index 4bee4b445ac..2071f610b35 100644 --- a/mono/metadata/gc.c +++ b/mono/metadata/gc.c @@ -36,6 +36,8 @@ extern int __imp_GC_finalize_on_demand; 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; @@ -44,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; @@ -62,6 +63,7 @@ run_finalize (void *obj, void *data) MonoObject *o, *o2; 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); @@ -71,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); @@ -154,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) { @@ -164,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 */ @@ -190,11 +206,11 @@ mono_domain_finalize (MonoDomain *domain, guint32 timeout) * 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); @@ -202,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); @@ -266,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); } @@ -274,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) @@ -283,14 +306,15 @@ 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; @@ -458,7 +482,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); @@ -481,7 +506,8 @@ alloc_handle (HandleData *handles, MonoObject *obj) slot = slot * 32 + i; handles->entries [slot] = obj; if (handles->type <= HANDLE_WEAK_TRACK) { - mono_gc_weak_link_add (&(handles->entries [slot]), obj); + if (obj) + mono_gc_weak_link_add (&(handles->entries [slot]), obj); } unlock_handles (handles); @@ -489,19 +515,64 @@ alloc_handle (HandleData *handles, MonoObject *obj) 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 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 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) { @@ -537,8 +608,10 @@ 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]); - mono_gc_weak_link_add (&handles->entries [slot], obj); + 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; } @@ -549,6 +622,13 @@ mono_gchandle_set_target (guint32 gchandle, MonoObject *obj) 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) { @@ -577,6 +657,14 @@ mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain) 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) { @@ -587,9 +675,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]); - handles->entries [slot] = NULL; + 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? */ @@ -598,12 +689,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"); @@ -627,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 @@ -650,9 +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 */ - GC_invoke_finalizers (); + mono_gc_invoke_finalizers (); /* printf ("DONE.\n"); */ SetEvent (req->done_event); @@ -671,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 @@ -692,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); } @@ -716,59 +851,18 @@ 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); - GC_no_dls = TRUE; - GC_oom_fn = mono_gc_out_of_memory; - - GC_set_warn_proc (mono_gc_warning); + mono_gc_base_init (); #ifdef ENABLE_FINALIZER_THREAD @@ -785,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 @@ -808,7 +899,7 @@ void mono_gc_cleanup (void) 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) { @@ -816,10 +907,16 @@ 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 @@ -836,6 +933,16 @@ void mono_gc_cleanup (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) {