Merge pull request #2721 from ludovic-henry/fix-mono_ms_ticks
[mono.git] / mono / metadata / gc.c
index 077251c7b1e7b95e0ec13895562a79111a735f90..2cff646db1771c099780ffccbf618800d049a270 100644 (file)
@@ -6,6 +6,7 @@
  * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com)
  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
  * Copyright 2012 Xamarin Inc (http://www.xamarin.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
  */
 
 #include <config.h>
@@ -39,6 +40,7 @@
 #include <mono/utils/mono-threads.h>
 #include <mono/utils/atomic.h>
 #include <mono/utils/mono-coop-semaphore.h>
+#include <mono/utils/hazard-pointer.h>
 
 #ifndef HOST_WIN32
 #include <pthread.h>
@@ -451,6 +453,8 @@ mono_domain_finalize (MonoDomain *domain, guint32 timeout)
                mono_gc_finalize_threadpool_threads ();
        }
 
+       mono_profiler_appdomain_event (domain, MONO_PROFILE_END_UNLOAD);
+
        return TRUE;
 }
 
@@ -638,6 +642,41 @@ mono_gc_finalize_notify (void)
        mono_coop_sem_post (&finalizer_sem);
 }
 
+/*
+This is the number of entries allowed in the hazard free queue before
+we explicitly cycle the finalizer thread to trigger pumping the queue.
+
+It was picked empirically by running the corlib test suite in a stress
+scenario where all hazard entries are queued.
+
+In this extreme scenario we double the number of times we cycle the finalizer
+thread compared to just GC calls.
+
+Entries are usually in the order of 100's of bytes each, so we're limiting
+floating garbage to be in the order of a dozen kb.
+*/
+static gboolean finalizer_thread_pulsed;
+#define HAZARD_QUEUE_OVERFLOW_SIZE 20
+
+static void
+hazard_free_queue_is_too_big (size_t size)
+{
+       if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
+               return;
+
+       if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
+               return;
+
+       mono_gc_finalize_notify ();
+}
+
+static void
+hazard_free_queue_pump (void)
+{
+       mono_thread_hazardous_try_free_all ();
+       finalizer_thread_pulsed = FALSE;
+}
+
 #ifdef HAVE_BOEHM_GC
 
 static void
@@ -711,8 +750,15 @@ finalize_domain_objects (DomainFinalizationReq *req)
 static guint32
 finalizer_thread (gpointer unused)
 {
+       MonoError error;
+       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);
+
        while (!finished) {
                /* Wait to be notified that there's at least one
                 * finaliser to run
@@ -757,6 +803,8 @@ finalizer_thread (gpointer unused)
 
                reference_queue_proccess_all ();
 
+               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) {
                        /* Don't wait again at the start of the loop */
@@ -780,8 +828,9 @@ static
 void
 mono_gc_init_finalizer_thread (void)
 {
-       gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
-       ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
+       MonoError error;
+       gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0, &error);
+       mono_error_assert_ok (&error);
 }
 
 void