* 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>
#include <mono/utils/mono-time.h>
#include <mono/utils/dtrace.h>
#include <mono/utils/mono-threads.h>
+#include <mono/utils/mono-threads-coop.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>
static MonoInternalThread *gc_thread;
-static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*), MonoError *error);
+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);
{
guint32 result;
- MONO_PREPARE_BLOCKING;
+ MONO_ENTER_GC_SAFE;
result = WaitForSingleObjectEx (handle, timeout, alertable);
- MONO_FINISH_BLOCKING;
+ MONO_EXIT_GC_SAFE;
return result;
}
-static void
-add_thread_to_finalize (MonoInternalThread *thread)
+static gboolean
+add_thread_to_finalize (MonoInternalThread *thread, MonoError *error)
{
+ mono_error_init (error);
mono_finalizer_lock ();
if (!threads_to_finalize)
MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize, MONO_ROOT_SOURCE_FINALIZER_QUEUE, "finalizable threads list");
- threads_to_finalize = mono_mlist_append (threads_to_finalize, (MonoObject*)thread);
+ threads_to_finalize = mono_mlist_append_checked (threads_to_finalize, (MonoObject*)thread, error);
mono_finalizer_unlock ();
+ return is_ok (error);
}
static gboolean suspend_finalizers = FALSE;
#endif
/* make sure the finalizer is not called again if the object is resurrected */
- object_register_finalizer ((MonoObject *)obj, NULL, &error);
- mono_error_assert_ok (&error); /* FIXME don't swallow the error */
+ object_register_finalizer ((MonoObject *)obj, NULL);
if (log_finalizers)
g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
/* Don't finalize threadpool threads when
shutting down - they're finalized when the
threadpool shuts down. */
- add_thread_to_finalize (t);
+ if (!add_thread_to_finalize (t, &error))
+ goto unhandled_error;
return;
}
}
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);
- domain->finalize_runtime_invoke = mono_compile_method (invoke);
+ domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, &error);
+ mono_error_assert_ok (&error); /* expect this not to fail */
}
runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
mono_runtime_class_init_full (o->vtable, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ if (!is_ok (&error))
+ goto unhandled_error;
if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
if (log_finalizers)
g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
+unhandled_error:
+ if (!is_ok (&error))
+ exc = (MonoObject*)mono_error_convert_to_exception (&error);
if (exc)
mono_thread_internal_unhandled_exception (exc);
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, &error);
- mono_error_assert_ok (&error); /* FIXME don't swallow the error */
+ mono_object_register_finalizer ((MonoObject*)thread);
mono_gc_run_finalize (thread, NULL);
* since that, too, can cause the underlying pointer to be offset.
*/
static void
-object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*), MonoError *error)
+object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
{
MonoDomain *domain;
- mono_error_init (error);
-
- if (obj == NULL) {
- mono_error_set_argument_null (error, "obj", "");
- return;
- }
+ g_assert (obj != NULL);
domain = obj->vtable->domain;
*
*/
void
-mono_object_register_finalizer (MonoObject *obj, MonoError *error)
+mono_object_register_finalizer (MonoObject *obj)
{
/* 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, error);
+ object_register_finalizer (obj, mono_gc_run_finalize);
}
/**
mono_gc_finalize_threadpool_threads ();
}
+ mono_profiler_appdomain_event (domain, MONO_PROFILE_END_UNLOAD);
+
return TRUE;
}
void
ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
{
- MonoError error;
-
MONO_CHECK_ARG_NULL (obj,);
- object_register_finalizer (obj, mono_gc_run_finalize, &error);
- mono_error_set_pending_exception (&error);
+ object_register_finalizer (obj, mono_gc_run_finalize);
}
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
* generated for it that needs cleaned up, but user wants to suppress
* their derived object finalizer. */
- object_register_finalizer (obj, NULL, &error);
- mono_error_set_pending_exception (&error);
+ object_register_finalizer (obj, NULL);
}
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
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
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 */
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
finished = TRUE;
if (mono_thread_internal_current () != gc_thread) {
gboolean timed_out = FALSE;
- guint32 start_ticks = mono_msec_ticks ();
- guint32 end_ticks = start_ticks + 2000;
+ gint64 start_ticks = mono_msec_ticks ();
+ gint64 end_ticks = start_ticks + 2000;
mono_gc_finalize_notify ();
/* Finishing the finalizer thread, so wait a little bit... */
/* MS seems to wait for about 2 seconds */
while (!finalizer_thread_exited) {
- guint32 current_ticks = mono_msec_ticks ();
+ gint64 current_ticks = mono_msec_ticks ();
guint32 timeout;
if (current_ticks >= end_ticks)
gboolean
mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
{
- MonoError error;
RefQueueEntry *entry;
if (queue->should_be_deleted)
return FALSE;
+ g_assert (obj != NULL);
+
entry = g_new0 (RefQueueEntry, 1);
entry->user_data = user_data;
entry->domain = mono_object_domain (obj);
entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
- mono_object_register_finalizer (obj, &error);
- mono_error_assert_ok (&error);
+ mono_object_register_finalizer (obj);
ref_list_push (&queue->queue, entry);
return TRUE;