Merge pull request #2698 from esdrubal/iosxmlarray
[mono.git] / mono / metadata / gc.c
index 61fa63033c93fb0c3f0fad8e1c93388afdc22bd1..3e56e77a821782527dfb4c03ba07f5076dc0ffb2 100644 (file)
 #include <mono/metadata/marshal.h> /* for mono_delegate_free_ftnptr () */
 #include <mono/metadata/attach.h>
 #include <mono/metadata/console-io.h>
-#include <mono/utils/mono-semaphore.h>
+#include <mono/utils/mono-os-semaphore.h>
 #include <mono/utils/mono-memory-model.h>
 #include <mono/utils/mono-counters.h>
 #include <mono/utils/mono-time.h>
 #include <mono/utils/dtrace.h>
 #include <mono/utils/mono-threads.h>
 #include <mono/utils/atomic.h>
+#include <mono/utils/mono-coop-semaphore.h>
 
 #ifndef HOST_WIN32
 #include <pthread.h>
@@ -56,21 +57,21 @@ gboolean log_finalizers = FALSE;
 gboolean mono_do_not_finalize = FALSE;
 gchar **mono_do_not_finalize_class_names = NULL;
 
-#define mono_finalizer_lock() mono_mutex_lock (&finalizer_mutex)
-#define mono_finalizer_unlock() mono_mutex_unlock (&finalizer_mutex)
-static mono_mutex_t finalizer_mutex;
-static mono_mutex_t reference_queue_mutex;
+#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 gboolean finalizer_thread_exited;
 /* Uses finalizer_mutex */
-static mono_cond_t exited_cond;
+static MonoCoopCond exited_cond;
 
 static MonoInternalThread *gc_thread;
 
-static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
+static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*), MonoError *error);
 
 static void reference_queue_proccess_all (void);
 static void mono_reference_queue_cleanup (void);
@@ -107,6 +108,7 @@ static gboolean suspend_finalizers = FALSE;
 void
 mono_gc_run_finalize (void *obj, void *data)
 {
+       MonoError error;
        MonoObject *exc = NULL;
        MonoObject *o;
 #ifndef HAVE_SGEN_GC
@@ -150,7 +152,7 @@ mono_gc_run_finalize (void *obj, void *data)
 #ifndef HAVE_SGEN_GC
        mono_domain_finalizers_lock (domain);
 
-       o2 = g_hash_table_lookup (domain->finalizable_objects_hash, o);
+       o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
 
        mono_domain_finalizers_unlock (domain);
 
@@ -160,7 +162,8 @@ mono_gc_run_finalize (void *obj, void *data)
 #endif
 
        /* make sure the finalizer is not called again if the object is resurrected */
-       object_register_finalizer (obj, NULL);
+       object_register_finalizer ((MonoObject *)obj, NULL, &error);
+       mono_error_assert_ok (&error); /* FIXME don't swallow the error */
 
        if (log_finalizers)
                g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
@@ -227,7 +230,7 @@ mono_gc_run_finalize (void *obj, void *data)
        }
 
        /* 
-        * To avoid the locking plus the other overhead of mono_runtime_invoke (),
+        * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
         * create and precompile a wrapper which calls the finalize method using
         * a CALLVIRT.
         */
@@ -235,14 +238,15 @@ mono_gc_run_finalize (void *obj, void *data)
                g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
 
        if (!domain->finalize_runtime_invoke) {
-               MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE, FALSE);
+               MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
 
                domain->finalize_runtime_invoke = mono_compile_method (invoke);
        }
 
-       runtime_invoke = domain->finalize_runtime_invoke;
+       runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
 
-       mono_runtime_class_init (o->vtable);
+       mono_runtime_class_init_full (o->vtable, &error);
+       mono_error_raise_exception (&error); /* FIXME don't raise here */
 
        if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
                MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
@@ -266,12 +270,14 @@ mono_gc_run_finalize (void *obj, void *data)
 void
 mono_gc_finalize_threadpool_threads (void)
 {
+       MonoError error;
        while (threads_to_finalize) {
                MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
 
                /* Force finalization of the thread. */
                thread->threadpool_thread = FALSE;
-               mono_object_register_finalizer ((MonoObject*)thread);
+               mono_object_register_finalizer ((MonoObject*)thread, &error);
+               mono_error_assert_ok (&error); /* FIXME don't swallow the error */
 
                mono_gc_run_finalize (thread, NULL);
 
@@ -301,12 +307,16 @@ mono_gc_out_of_memory (size_t size)
  * since that, too, can cause the underlying pointer to be offset.
  */
 static void
-object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
+object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*), MonoError *error)
 {
        MonoDomain *domain;
 
-       if (obj == NULL)
-               mono_raise_exception (mono_get_exception_argument_null ("obj"));
+       mono_error_init (error);
+
+       if (obj == NULL) {
+               mono_error_set_argument_null (error, "obj", "");
+               return;
+       }
 
        domain = obj->vtable->domain;
 
@@ -334,11 +344,8 @@ object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
         * end up running them while or after the domain is being cleared, so
         * the objects will not be valid anymore.
         */
-       if (!mono_domain_is_unloading (domain)) {
-               MONO_TRY_BLOCKING;
+       if (!mono_domain_is_unloading (domain))
                mono_gc_register_for_finalization (obj, callback);
-               MONO_FINISH_TRY_BLOCKING;
-       }
 #endif
 }
 
@@ -351,10 +358,10 @@ object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
  * 
  */
 void
-mono_object_register_finalizer (MonoObject *obj)
+mono_object_register_finalizer (MonoObject *obj, MonoError *error)
 {
        /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
-       object_register_finalizer (obj, mono_gc_run_finalize);
+       object_register_finalizer (obj, mono_gc_run_finalize, error);
 }
 
 /**
@@ -472,14 +479,19 @@ ves_icall_System_GC_KeepAlive (MonoObject *obj)
 void
 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
 {
+       MonoError error;
+
        MONO_CHECK_ARG_NULL (obj,);
 
-       object_register_finalizer (obj, mono_gc_run_finalize);
+       object_register_finalizer (obj, mono_gc_run_finalize, &error);
+       mono_error_set_pending_exception (&error);
 }
 
 void
 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
 {
+       MonoError error;
+
        MONO_CHECK_ARG_NULL (obj,);
 
        /* delegates have no finalizers, but we register them to deal with the
@@ -493,7 +505,8 @@ ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
         * generated for it that needs cleaned up, but user wants to suppress
         * their derived object finalizer. */
 
-       object_register_finalizer (obj, NULL);
+       object_register_finalizer (obj, NULL, &error);
+       mono_error_set_pending_exception (&error);
 }
 
 void
@@ -540,10 +553,6 @@ ves_icall_System_GC_get_ephemeron_tombstone (void)
        return mono_domain_get ()->ephemeron_tombstone;
 }
 
-#define mono_allocator_lock() mono_mutex_lock (&allocator_section)
-#define mono_allocator_unlock() mono_mutex_unlock (&allocator_section)
-static mono_mutex_t allocator_section;
-
 MonoObject *
 ves_icall_System_GCHandle_GetTarget (guint32 handle)
 {
@@ -613,10 +622,7 @@ mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
        return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
 }
 
-#ifdef MONO_HAS_SEMAPHORES
-static MonoSemType finalizer_sem;
-#endif
-static HANDLE finalizer_event;
+static MonoCoopSem finalizer_sem;
 static volatile gboolean finished=FALSE;
 
 void
@@ -629,11 +635,7 @@ mono_gc_finalize_notify (void)
        if (mono_gc_is_null ())
                return;
 
-#ifdef MONO_HAS_SEMAPHORES
-       MONO_SEM_POST (&finalizer_sem);
-#else
-       SetEvent (finalizer_event);
-#endif
+       mono_coop_sem_post (&finalizer_sem);
 }
 
 #ifdef HAVE_BOEHM_GC
@@ -718,18 +720,13 @@ finalizer_thread (gpointer unused)
 
                g_assert (mono_domain_get () == mono_get_root_domain ());
                mono_gc_set_skip_thread (TRUE);
-               MONO_PREPARE_BLOCKING;
 
                if (wait) {
-               /* An alertable wait is required so this thread can be suspended on windows */
-#ifdef MONO_HAS_SEMAPHORES
-                       MONO_SEM_WAIT_ALERTABLE (&finalizer_sem, TRUE);
-#else
-                       WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
-#endif
+                       /* An alertable wait is required so this thread can be suspended on windows */
+                       mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
                }
                wait = TRUE;
-               MONO_FINISH_BLOCKING;
+
                mono_gc_set_skip_thread (FALSE);
 
                mono_threads_perform_thread_dump ();
@@ -741,7 +738,7 @@ finalizer_thread (gpointer unused)
                if (domains_to_finalize) {
                        mono_finalizer_lock ();
                        if (domains_to_finalize) {
-                               DomainFinalizationReq *req = domains_to_finalize->data;
+                               DomainFinalizationReq *req = (DomainFinalizationReq *)domains_to_finalize->data;
                                domains_to_finalize = g_slist_remove (domains_to_finalize, req);
                                mono_finalizer_unlock ();
 
@@ -760,21 +757,18 @@ finalizer_thread (gpointer unused)
 
                reference_queue_proccess_all ();
 
-#ifdef MONO_HAS_SEMAPHORES
                /* Avoid posting the pending done event until there are pending finalizers */
-               if (MONO_SEM_TIMEDWAIT (&finalizer_sem, 0) == 0)
+               if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == 0) {
                        /* Don't wait again at the start of the loop */
                        wait = FALSE;
-               else
-                       SetEvent (pending_done_event);
-#else
+               } else {
                        SetEvent (pending_done_event);
-#endif
+               }
        }
 
        mono_finalizer_lock ();
        finalizer_thread_exited = TRUE;
-       mono_cond_signal (&exited_cond);
+       mono_coop_cond_signal (&exited_cond);
        mono_finalizer_unlock ();
 
        return 0;
@@ -793,10 +787,8 @@ mono_gc_init_finalizer_thread (void)
 void
 mono_gc_init (void)
 {
-       mono_mutex_init_recursive (&allocator_section);
-
-       mono_mutex_init_recursive (&finalizer_mutex);
-       mono_mutex_init_recursive (&reference_queue_mutex);
+       mono_coop_mutex_init_recursive (&finalizer_mutex);
+       mono_coop_mutex_init_recursive (&reference_queue_mutex);
 
        mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
        mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
@@ -810,15 +802,11 @@ mono_gc_init (void)
                gc_disabled = TRUE;
                return;
        }
-       
-       finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
-       g_assert (finalizer_event);
+
        pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
        g_assert (pending_done_event);
-       mono_cond_init (&exited_cond, 0);
-#ifdef MONO_HAS_SEMAPHORES
-       MONO_SEM_INIT (&finalizer_sem, 0);
-#endif
+       mono_coop_cond_init (&exited_cond);
+       mono_coop_sem_init (&finalizer_sem, 0);
 
 #ifndef LAZY_GC_THREAD_CREATION
        mono_gc_init_finalizer_thread ();
@@ -853,12 +841,10 @@ mono_gc_cleanup (void)
                                        break;
                                else
                                        timeout = end_ticks - current_ticks;
-                               MONO_PREPARE_BLOCKING;
                                mono_finalizer_lock ();
                                if (!finalizer_thread_exited)
-                                       mono_cond_timedwait_ms (&exited_cond, &finalizer_mutex, timeout);
+                                       mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout);
                                mono_finalizer_unlock ();
-                               MONO_FINISH_BLOCKING;
                        }
 
                        if (!finalizer_thread_exited) {
@@ -893,6 +879,7 @@ mono_gc_cleanup (void)
 
                                mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
                        }
+                       g_assert (finalizer_thread_exited);
                }
                gc_thread = NULL;
                mono_gc_base_cleanup ();
@@ -900,9 +887,8 @@ mono_gc_cleanup (void)
 
        mono_reference_queue_cleanup ();
 
-       mono_mutex_destroy (&allocator_section);
-       mono_mutex_destroy (&finalizer_mutex);
-       mono_mutex_destroy (&reference_queue_mutex);
+       mono_coop_mutex_destroy (&finalizer_mutex);
+       mono_coop_mutex_destroy (&reference_queue_mutex);
 }
 
 gboolean
@@ -919,7 +905,7 @@ mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
  * This routine tests whether the @thread argument represents the
  * finalization thread.
  * 
- * Returns true if @thread is the finalization thread.
+ * Returns: TRUE if @thread is the finalization thread.
  */
 gboolean
 mono_gc_is_finalizer_thread (MonoThread *thread)
@@ -943,15 +929,6 @@ mono_gc_get_mach_exception_thread (void)
 }
 #endif
 
-#ifndef HAVE_SGEN_GC
-void*
-mono_gc_alloc_mature (MonoVTable *vtable)
-{
-       return mono_object_new_specific (vtable);
-}
-#endif
-
-
 static MonoReferenceQueue *ref_queues;
 
 static void
@@ -961,7 +938,7 @@ ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
                /* Guard if head is changed concurrently. */
                while (*prev != element)
                        prev = &(*prev)->next;
-       } while (prev && InterlockedCompareExchangePointer ((void*)prev, element->next, element) != element);
+       } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
 }
 
 static void
@@ -972,7 +949,7 @@ ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
                current = *head;
                value->next = current;
                STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
-       } while (InterlockedCompareExchangePointer ((void*)head, value, current) != current);
+       } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
 }
 
 static void
@@ -1001,7 +978,7 @@ reference_queue_proccess_all (void)
                reference_queue_proccess (queue);
 
 restart:
-       mono_mutex_lock (&reference_queue_mutex);
+       mono_coop_mutex_lock (&reference_queue_mutex);
        for (iter = &ref_queues; *iter;) {
                queue = *iter;
                if (!queue->should_be_deleted) {
@@ -1009,14 +986,14 @@ restart:
                        continue;
                }
                if (queue->queue) {
-                       mono_mutex_unlock (&reference_queue_mutex);
+                       mono_coop_mutex_unlock (&reference_queue_mutex);
                        reference_queue_proccess (queue);
                        goto restart;
                }
                *iter = queue->next;
                g_free (queue);
        }
-       mono_mutex_unlock (&reference_queue_mutex);
+       mono_coop_mutex_unlock (&reference_queue_mutex);
 }
 
 static void
@@ -1070,10 +1047,10 @@ mono_gc_reference_queue_new (mono_reference_queue_callback callback)
        MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
        res->callback = callback;
 
-       mono_mutex_lock (&reference_queue_mutex);
+       mono_coop_mutex_lock (&reference_queue_mutex);
        res->next = ref_queues;
        ref_queues = res;
-       mono_mutex_unlock (&reference_queue_mutex);
+       mono_coop_mutex_unlock (&reference_queue_mutex);
 
        return res;
 }
@@ -1093,6 +1070,7 @@ mono_gc_reference_queue_new (mono_reference_queue_callback callback)
 gboolean
 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
 {
+       MonoError error;
        RefQueueEntry *entry;
        if (queue->should_be_deleted)
                return FALSE;
@@ -1102,7 +1080,8 @@ mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *u
        entry->domain = mono_object_domain (obj);
 
        entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
-       mono_object_register_finalizer (obj);
+       mono_object_register_finalizer (obj, &error);
+       mono_error_assert_ok (&error);
 
        ref_list_push (&queue->queue, entry);
        return TRUE;