X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fgc.c;h=1a452c083ee0384a7dedfc9a4dc5d6d06b6c033e;hb=e7d19721c601da32575571c7621d4d28e564cf4b;hp=bcf125022088de9abe6d121ab115e33dd4de222b;hpb=79e23ab784e751d5efe7a9ce654cbcba2e112700;p=mono.git diff --git a/mono/metadata/gc.c b/mono/metadata/gc.c index bcf12502208..1a452c083ee 100644 --- a/mono/metadata/gc.c +++ b/mono/metadata/gc.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include @@ -42,31 +42,34 @@ #include #include #include +#include #ifndef HOST_WIN32 #include #endif typedef struct DomainFinalizationReq { + gint32 ref; MonoDomain *domain; - HANDLE done_event; + MonoCoopSem done; } DomainFinalizationReq; -static gboolean gc_disabled = FALSE; +static gboolean gc_disabled; -static gboolean finalizing_root_domain = FALSE; +static gboolean finalizing_root_domain; -gboolean log_finalizers = FALSE; -gboolean mono_do_not_finalize = FALSE; -gchar **mono_do_not_finalize_class_names = NULL; +gboolean log_finalizers; +gboolean mono_do_not_finalize; +volatile gboolean suspend_finalizers; +gchar **mono_do_not_finalize_class_names ; #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex) #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex) static MonoCoopMutex finalizer_mutex; static MonoCoopMutex reference_queue_mutex; -static GSList *domains_to_finalize= NULL; -static MonoMList *threads_to_finalize = NULL; +static GSList *domains_to_finalize; +static MonoMList *threads_to_finalize; static gboolean finalizer_thread_exited; /* Uses finalizer_mutex */ @@ -74,25 +77,87 @@ static MonoCoopCond exited_cond; static MonoInternalThread *gc_thread; +#ifdef TARGET_WIN32 +static HANDLE pending_done_event; +#else +static gboolean pending_done; +static MonoCoopCond pending_done_cond; +static MonoCoopMutex pending_done_mutex; +#endif + static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*)); static void reference_queue_proccess_all (void); static void mono_reference_queue_cleanup (void); static void reference_queue_clear_for_domain (MonoDomain *domain); -static HANDLE pending_done_event; -static guint32 -guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable) + +static MonoThreadInfoWaitRet +guarded_wait (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable) { - guint32 result; + MonoThreadInfoWaitRet result; MONO_ENTER_GC_SAFE; - result = WaitForSingleObjectEx (handle, timeout, alertable); + result = mono_thread_info_wait_one_handle (thread_handle, timeout, alertable); MONO_EXIT_GC_SAFE; return result; } +typedef struct { + MonoCoopCond *cond; + MonoCoopMutex *mutex; +} BreakCoopAlertableWaitUD; + +static inline void +break_coop_alertable_wait (gpointer user_data) +{ + BreakCoopAlertableWaitUD *ud = (BreakCoopAlertableWaitUD*)user_data; + + mono_coop_mutex_lock (ud->mutex); + mono_coop_cond_signal (ud->cond); + mono_coop_mutex_unlock (ud->mutex); + + g_free (ud); +} + +/* + * coop_cond_timedwait_alertable: + * + * Wait on COND/MUTEX. If ALERTABLE is non-null, the wait can be interrupted. + * In that case, *ALERTABLE will be set to TRUE, and 0 is returned. + */ +static inline gint +coop_cond_timedwait_alertable (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout_ms, gboolean *alertable) +{ + BreakCoopAlertableWaitUD *ud; + int res; + + if (alertable) { + ud = g_new0 (BreakCoopAlertableWaitUD, 1); + ud->cond = cond; + ud->mutex = mutex; + + mono_thread_info_install_interrupt (break_coop_alertable_wait, ud, alertable); + if (*alertable) { + g_free (ud); + return 0; + } + } + res = mono_coop_cond_timedwait (cond, mutex, timeout_ms); + if (alertable) { + mono_thread_info_uninstall_interrupt (alertable); + if (*alertable) + return 0; + else { + /* the interrupt token has not been taken by another + * thread, so it's our responsability to free it up. */ + g_free (ud); + } + } + return res; +} + static gboolean add_thread_to_finalize (MonoInternalThread *thread, MonoError *error) { @@ -105,7 +170,6 @@ add_thread_to_finalize (MonoInternalThread *thread, MonoError *error) return is_ok (error); } -static volatile gboolean suspend_finalizers = FALSE; /* * 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... @@ -263,8 +327,12 @@ mono_gc_run_finalize (void *obj, void *data) if (log_finalizers) g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o); + mono_profiler_gc_finalize_object_begin (o); + runtime_invoke (o, NULL, &exc, NULL); + mono_profiler_gc_finalize_object_end (o); + if (log_finalizers) g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o); @@ -382,9 +450,10 @@ gboolean mono_domain_finalize (MonoDomain *domain, guint32 timeout) { DomainFinalizationReq *req; - guint32 res; - HANDLE done_event; MonoInternalThread *thread = mono_thread_internal_current (); + gint res; + gboolean ret; + gint64 start; #if defined(__native_client__) return FALSE; @@ -408,14 +477,10 @@ 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->ref = 2; req->domain = domain; - req->done_event = done_event; + mono_coop_sem_init (&req->done, 0); if (domain == mono_get_root_domain ()) finalizing_root_domain = TRUE; @@ -430,33 +495,79 @@ mono_domain_finalize (MonoDomain *domain, guint32 timeout) mono_gc_finalize_notify (); if (timeout == -1) - timeout = INFINITE; - - while (TRUE) { - res = guarded_wait (done_event, timeout, TRUE); - /* printf ("WAIT RES: %d.\n", res); */ - - if (res == WAIT_IO_COMPLETION) { - if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0) - return FALSE; - } else if (res == WAIT_TIMEOUT) { - /* We leak the handle here */ - return FALSE; + timeout = MONO_INFINITE_WAIT; + if (timeout != MONO_INFINITE_WAIT) + start = mono_msec_ticks (); + + ret = TRUE; + + for (;;) { + if (timeout == MONO_INFINITE_WAIT) { + res = mono_coop_sem_wait (&req->done, MONO_SEM_FLAGS_ALERTABLE); } else { + gint64 elapsed = mono_msec_ticks () - start; + if (elapsed >= timeout) { + ret = FALSE; + break; + } + + res = mono_coop_sem_timedwait (&req->done, timeout - elapsed, MONO_SEM_FLAGS_ALERTABLE); + } + + if (res == MONO_SEM_TIMEDWAIT_RET_SUCCESS) { break; + } else if (res == MONO_SEM_TIMEDWAIT_RET_ALERTED) { + if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0) { + ret = FALSE; + break; + } + } else if (res == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) { + ret = FALSE; + break; + } else { + g_error ("%s: unknown result %d", __func__, res); } } - CloseHandle (done_event); + if (!ret) { + /* Try removing the req from domains_to_finalize: + * - if it's not found: the domain is being finalized, + * so we the ref count is already decremented + * - if it's found: the domain is not yet being finalized, + * so we can safely decrement the ref */ + + gboolean found; + + mono_finalizer_lock (); + + found = g_slist_index (domains_to_finalize, req) != -1; + if (found) + domains_to_finalize = g_slist_remove (domains_to_finalize, req); + + mono_finalizer_unlock (); + + if (found) { + /* We have to decrement it wherever we + * remove it from domains_to_finalize */ + if (InterlockedDecrement (&req->ref) != 1) + g_error ("%s: req->ref should be 1, as we are the first one to decrement it", __func__); + } + + goto done; + } if (domain == mono_get_root_domain ()) { - mono_threadpool_ms_cleanup (); + mono_threadpool_cleanup (); mono_gc_finalize_threadpool_threads (); } - mono_profiler_appdomain_event (domain, MONO_PROFILE_END_UNLOAD); +done: + if (InterlockedDecrement (&req->ref) == 0) { + mono_coop_sem_destroy (&req->done); + g_free (req); + } - return TRUE; + return ret; } void @@ -528,11 +639,26 @@ ves_icall_System_GC_WaitForPendingFinalizers (void) if (gc_thread == NULL) return; +#ifdef TARGET_WIN32 ResetEvent (pending_done_event); mono_gc_finalize_notify (); /* g_print ("Waiting for pending finalizers....\n"); */ - guarded_wait (pending_done_event, INFINITE, TRUE); + MONO_ENTER_GC_SAFE; + WaitForSingleObjectEx (pending_done_event, INFINITE, TRUE); + MONO_EXIT_GC_SAFE; /* g_print ("Done pending....\n"); */ +#else + gboolean alerted = FALSE; + mono_coop_mutex_lock (&pending_done_mutex); + pending_done = FALSE; + mono_gc_finalize_notify (); + while (!pending_done) { + coop_cond_timedwait_alertable (&pending_done_cond, &pending_done_mutex, MONO_INFINITE_WAIT, &alerted); + if (alerted) + break; + } + mono_coop_mutex_unlock (&pending_done_mutex); +#endif } void @@ -607,7 +733,7 @@ ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle) } 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) + if (mono_class_is_auto_layout (klass)) return (gpointer)-1; return (char*)obj + sizeof (MonoObject); } @@ -622,8 +748,14 @@ mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle) } static MonoCoopSem finalizer_sem; -static volatile gboolean finished=FALSE; +static volatile gboolean finished; +/* + * mono_gc_finalize_notify: + * + * Notify the finalizer thread that finalizers etc. + * are available to be processed. + */ void mono_gc_finalize_notify (void) { @@ -689,15 +821,24 @@ collect_objects (gpointer key, gpointer value, gpointer user_data) * Run the finalizers of all finalizable objects in req->domain. */ static void -finalize_domain_objects (DomainFinalizationReq *req) +finalize_domain_objects (void) { - MonoDomain *domain = req->domain; + DomainFinalizationReq *req = NULL; + MonoDomain *domain; -#if HAVE_SGEN_GC -#define NUM_FOBJECTS 64 - MonoObject *to_finalize [NUM_FOBJECTS]; - int count; -#endif + if (domains_to_finalize) { + mono_finalizer_lock (); + if (domains_to_finalize) { + req = (DomainFinalizationReq *)domains_to_finalize->data; + domains_to_finalize = g_slist_remove (domains_to_finalize, req); + } + mono_finalizer_unlock (); + } + + if (!req) + return; + + domain = req->domain; /* Process finalizers which are already in the queue */ mono_gc_invoke_finalizers (); @@ -724,33 +865,33 @@ finalize_domain_objects (DomainFinalizationReq *req) g_ptr_array_free (objs, TRUE); } #elif defined(HAVE_SGEN_GC) - while (!suspend_finalizers && (count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) { - int i; - for (i = 0; i < count; ++i) { - mono_gc_run_finalize (to_finalize [i], 0); - } - } + mono_gc_finalize_domain (domain); + mono_gc_invoke_finalizers (); #endif /* cleanup the reference queue */ reference_queue_clear_for_domain (domain); /* printf ("DONE.\n"); */ - SetEvent (req->done_event); + mono_coop_sem_post (&req->done); - /* The event is closed in mono_domain_finalize if we get here */ - g_free (req); + if (InterlockedDecrement (&req->ref) == 0) { + /* mono_domain_finalize already returned, and + * doesn't hold a reference to req anymore. */ + mono_coop_sem_destroy (&req->done); + g_free (req); + } } -static guint32 +static gsize WINAPI finalizer_thread (gpointer unused) { MonoError error; + gboolean wait = TRUE; + mono_thread_set_name_internal (mono_thread_internal_current (), mono_string_new (mono_get_root_domain (), "Finalizer"), FALSE, &error); mono_error_assert_ok (&error); - gboolean wait = TRUE; - /* Register a hazard free queue pump callback */ mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big); @@ -776,24 +917,17 @@ finalizer_thread (gpointer unused) mono_attach_maybe_start (); - if (domains_to_finalize) { - mono_finalizer_lock (); - if (domains_to_finalize) { - DomainFinalizationReq *req = (DomainFinalizationReq *)domains_to_finalize->data; - domains_to_finalize = g_slist_remove (domains_to_finalize, req); - mono_finalizer_unlock (); + finalize_domain_objects (); - finalize_domain_objects (req); - } else { - mono_finalizer_unlock (); - } - } + mono_profiler_gc_finalize_begin (); /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup), * before the domain is unloaded. */ mono_gc_invoke_finalizers (); + mono_profiler_gc_finalize_end (); + mono_threads_join_threads (); reference_queue_proccess_all (); @@ -801,11 +935,18 @@ finalizer_thread (gpointer unused) hazard_free_queue_pump (); /* Avoid posting the pending done event until there are pending finalizers */ - if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == 0) { + if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS) { /* Don't wait again at the start of the loop */ wait = FALSE; } else { +#ifdef TARGET_WIN32 SetEvent (pending_done_event); +#else + mono_coop_mutex_lock (&pending_done_mutex); + pending_done = TRUE; + mono_coop_cond_signal (&pending_done_cond); + mono_coop_mutex_unlock (&pending_done_mutex); +#endif } } @@ -847,8 +988,14 @@ mono_gc_init (void) return; } +#ifdef TARGET_WIN32 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL); g_assert (pending_done_event); +#else + mono_coop_cond_init (&pending_done_cond); + mono_coop_mutex_init (&pending_done_mutex); +#endif + mono_coop_cond_init (&exited_cond); mono_coop_sem_init (&finalizer_sem, 0); @@ -870,12 +1017,14 @@ mono_gc_cleanup (void) if (!gc_disabled) { finished = TRUE; if (mono_thread_internal_current () != gc_thread) { + int ret; gint64 start_ticks = mono_msec_ticks (); - gint64 end_ticks = start_ticks + 2000; + gint64 end_ticks = start_ticks + 40000; mono_gc_finalize_notify (); /* Finishing the finalizer thread, so wait a little bit... */ - /* MS seems to wait for about 2 seconds */ + /* MS seems to wait for about 2 seconds per finalizer thread */ + /* and 40 seconds for all finalizers to finish */ while (!finalizer_thread_exited) { gint64 current_ticks = mono_msec_ticks (); guint32 timeout; @@ -891,10 +1040,9 @@ mono_gc_cleanup (void) } if (!finalizer_thread_exited) { - int ret; - /* Set a flag which the finalizer thread can check */ suspend_finalizers = TRUE; + mono_gc_suspend_finalizers (); /* Try to abort the thread, in the hope that it is running managed code */ mono_thread_internal_abort (gc_thread); @@ -902,23 +1050,22 @@ mono_gc_cleanup (void) /* Wait for it to stop */ ret = guarded_wait (gc_thread->handle, 100, TRUE); - if (ret == WAIT_TIMEOUT) { + if (ret == MONO_THREAD_INFO_WAIT_RET_TIMEOUT) { /* * The finalizer thread refused to exit. Make it stop. */ mono_thread_internal_stop (gc_thread); ret = guarded_wait (gc_thread->handle, 100, TRUE); - g_assert (ret != WAIT_TIMEOUT); + g_assert (ret != MONO_THREAD_INFO_WAIT_RET_TIMEOUT); /* The thread can't set this flag */ finalizer_thread_exited = TRUE; } } - int ret; /* Wait for the thread to actually exit */ - ret = guarded_wait (gc_thread->handle, INFINITE, TRUE); - g_assert (ret == WAIT_OBJECT_0); + ret = guarded_wait (gc_thread->handle, MONO_INFINITE_WAIT, TRUE); + g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0); mono_thread_join (GUINT_TO_POINTER (gc_thread->tid)); g_assert (finalizer_thread_exited);