#include <mono/utils/mono-memory-model.h>
#include <mono/metadata/gc-internals.h>
+#include <mono/metadata/reflection-internals.h>
#ifdef HAVE_SIGNAL_H
#include <signal.h>
*/
static GHashTable *contexts = NULL;
+/* Cleanup queue for contexts. */
+static MonoReferenceQueue *context_queue;
+
/*
* Threads which are starting up and they are not in the 'threads' hash yet.
* When handle_store is called for a thread, it will be removed from this hash table.
static void suspend_thread_internal (MonoInternalThread *thread, gboolean interrupt);
static void self_suspend_internal (MonoInternalThread *thread);
-static MonoException* mono_thread_execute_interruption ();
+static MonoException* mono_thread_execute_interruption (void);
static void ref_stack_destroy (gpointer rs);
/* Spin lock for InterlockedXXX 64 bit functions */
}
static MonoThread*
-create_thread_object (MonoDomain *domain)
+create_thread_object (MonoDomain *domain, MonoError *error)
{
MonoVTable *vt = mono_class_vtable (domain, mono_defaults.thread_class);
- return (MonoThread*)mono_gc_alloc_mature (vt);
+ MonoThread *t = (MonoThread*)mono_object_new_mature (vt, error);
+ return t;
}
static MonoThread*
-new_thread_with_internal (MonoDomain *domain, MonoInternalThread *internal)
+new_thread_with_internal (MonoDomain *domain, MonoInternalThread *internal, MonoError *error)
{
- MonoThread *thread = create_thread_object (domain);
+ MonoThread *thread;
+
+ thread = create_thread_object (domain, error);
+ if (!mono_error_ok (error))
+ return NULL;
+
MONO_OBJECT_SETREF (thread, internal_thread, internal);
+
return thread;
}
static MonoInternalThread*
-create_internal_thread (void)
+create_internal_thread (MonoError *error)
{
MonoInternalThread *thread;
MonoVTable *vt;
vt = mono_class_vtable (mono_get_root_domain (), mono_defaults.internal_thread_class);
- thread = (MonoInternalThread*)mono_gc_alloc_mature (vt);
+ thread = (MonoInternalThread*) mono_object_new_mature (vt, error);
+ if (!mono_error_ok (error))
+ return NULL;
thread->synch_cs = g_new0 (MonoCoopMutex, 1);
mono_coop_mutex_init_recursive (thread->synch_cs);
static void
init_root_domain_thread (MonoInternalThread *thread, MonoThread *candidate)
{
+ MonoError error;
MonoDomain *domain = mono_get_root_domain ();
- if (!candidate || candidate->obj.vtable->domain != domain)
- candidate = new_thread_with_internal (domain, thread);
+ if (!candidate || candidate->obj.vtable->domain != domain) {
+ candidate = new_thread_with_internal (domain, thread, &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
+ }
set_current_thread_for_domain (domain, thread, candidate);
g_assert (!thread->root_domain_thread);
MONO_OBJECT_SETREF (thread, root_domain_thread, candidate);
MonoInternalThread*
mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gboolean threadpool_thread, guint32 stack_size)
{
+ MonoError error;
MonoThread *thread;
MonoInternalThread *internal;
StartInfo *start_info;
gboolean res;
- thread = create_thread_object (domain);
- internal = create_internal_thread ();
+ thread = create_thread_object (domain, &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
+
+ internal = create_internal_thread (&error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
+
MONO_OBJECT_SETREF (thread, internal_thread, internal);
start_info = g_new0 (StartInfo, 1);
MonoThread *
mono_thread_attach (MonoDomain *domain)
{
- return mono_thread_attach_full (domain, FALSE);
+ MonoError error;
+ MonoThread *thread = mono_thread_attach_full (domain, FALSE, &error);
+ mono_error_raise_exception (&error);
+
+ return thread;
}
MonoThread *
-mono_thread_attach_full (MonoDomain *domain, gboolean force_attach)
+mono_thread_attach_full (MonoDomain *domain, gboolean force_attach, MonoError *error)
{
MonoThreadInfo *info;
MonoInternalThread *thread;
HANDLE thread_handle;
MonoNativeThreadId tid;
+ mono_error_init (error);
+
if ((thread = mono_thread_internal_current ())) {
if (domain != mono_domain_get ())
mono_domain_set (domain, TRUE);
g_error ("Thread %"G_GSIZE_FORMAT" calling into managed code is not registered with the GC. On UNIX, this can be fixed by #include-ing <gc.h> before <pthread.h> in the file containing the thread creation code.", mono_native_thread_id_get ());
}
- thread = create_internal_thread ();
+ thread = create_internal_thread (error);
+ if (!mono_error_ok (error))
+ return NULL;
thread_handle = mono_thread_info_open_handle ();
g_assert (thread_handle);
thread->thread_info = info;
thread->small_id = info->small_id;
- current_thread = new_thread_with_internal (domain, thread);
+ current_thread = new_thread_with_internal (domain, thread, error);
+ if (!mono_error_ok (error))
+ return NULL;
if (!handle_store (current_thread, force_attach)) {
/* Mono is shutting down, so just wait for the end */
void
ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this_obj)
{
- MonoInternalThread *internal = create_internal_thread ();
+ MonoError error;
+ MonoInternalThread *internal;
+
+ internal = create_internal_thread (&error);
+ mono_error_raise_exception (&error);
internal->state = ThreadState_Unstarted;
return res;
}
+/*
+ * mono_thread_get_name_utf8:
+ *
+ * Return the name of the thread in UTF-8.
+ * Return NULL if the thread has no name.
+ * The returned memory is owned by the caller.
+ */
+char *
+mono_thread_get_name_utf8 (MonoThread *thread)
+{
+ if (thread == NULL)
+ return NULL;
+
+ MonoInternalThread *internal = thread->internal_thread;
+ if (internal == NULL)
+ return NULL;
+
+ LOCK_THREAD (internal);
+
+ char *tname = g_utf16_to_utf8 (internal->name, internal->name_len, NULL, NULL, NULL);
+
+ UNLOCK_THREAD (internal);
+
+ return tname;
+}
+
+/*
+ * mono_thread_get_managed_id:
+ *
+ * Return the Thread.ManagedThreadId value of `thread`.
+ * Returns -1 if `thread` is NULL.
+ */
+int32_t
+mono_thread_get_managed_id (MonoThread *thread)
+{
+ if (thread == NULL)
+ return -1;
+
+ MonoInternalThread *internal = thread->internal_thread;
+ if (internal == NULL)
+ return -1;
+
+ int32_t id = internal->managed_id;
+
+ return id;
+}
+
MonoString*
ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThread *this_obj)
{
+ MonoError error;
MonoString* str;
+ mono_error_init (&error);
+
LOCK_THREAD (this_obj);
if (!this_obj->name)
str = NULL;
else
- str = mono_string_new_utf16 (mono_domain_get (), this_obj->name, this_obj->name_len);
+ str = mono_string_new_utf16_checked (mono_domain_get (), this_obj->name, this_obj->name_len, &error);
UNLOCK_THREAD (this_obj);
+
+ mono_error_raise_exception (&error);
return str;
}
mono_thread_set_name_internal (this_obj, name, TRUE);
}
+/*
+ * ves_icall_System_Threading_Thread_GetPriority_internal:
+ * @param this_obj: The MonoInternalThread on which to operate.
+ *
+ * Gets the priority of the given thread.
+ * @return: The priority of the given thread.
+ */
int
ves_icall_System_Threading_Thread_GetPriority (MonoThread *this_obj)
{
- return ThreadPriority_Lowest;
+ gint32 priority;
+ MonoInternalThread *internal = this_obj->internal_thread;
+
+ LOCK_THREAD (internal);
+ priority = GetThreadPriority (internal->handle) + 2;
+ UNLOCK_THREAD (internal);
+ return priority;
}
+/*
+ * ves_icall_System_Threading_Thread_SetPriority_internal:
+ * @param this_obj: The MonoInternalThread on which to operate.
+ * @param priority: The priority to set.
+ *
+ * Sets the priority of the given thread.
+ */
void
ves_icall_System_Threading_Thread_SetPriority (MonoThread *this_obj, int priority)
{
+ MonoInternalThread *internal = this_obj->internal_thread;
+
+ LOCK_THREAD (internal);
+ SetThreadPriority (internal->handle, priority - 2);
+ UNLOCK_THREAD (internal);
}
/* If the array is already in the requested domain, we just return it,
MonoThread *
mono_thread_current (void)
{
+ MonoError error;
MonoDomain *domain = mono_domain_get ();
MonoInternalThread *internal = mono_thread_internal_current ();
MonoThread **current_thread_ptr;
if (!*current_thread_ptr) {
g_assert (domain != mono_get_root_domain ());
- *current_thread_ptr = new_thread_with_internal (domain, internal);
+ *current_thread_ptr = new_thread_with_internal (domain, internal, &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
}
return *current_thread_ptr;
}
static MonoThread *
mono_thread_current_for_thread (MonoInternalThread *internal)
{
+ MonoError error;
MonoDomain *domain = mono_domain_get ();
MonoThread **current_thread_ptr;
if (!*current_thread_ptr) {
g_assert (domain != mono_get_root_domain ());
- *current_thread_ptr = new_thread_with_internal (domain, internal);
+ *current_thread_ptr = new_thread_with_internal (domain, internal, &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
}
return *current_thread_ptr;
}
}
thread->state |= ThreadState_SuspendRequested;
-
- UNLOCK_THREAD (thread);
-
suspend_thread_internal (thread, FALSE);
return TRUE;
}
gint8
ves_icall_System_Threading_Thread_VolatileRead1 (void *ptr)
{
- gint8 tmp;
- mono_atomic_load_acquire (tmp, gint8, (volatile gint8 *) ptr);
+ gint8 tmp = *(volatile gint8 *)ptr;
+ mono_memory_barrier ();
return tmp;
}
gint16
ves_icall_System_Threading_Thread_VolatileRead2 (void *ptr)
{
- gint16 tmp;
- mono_atomic_load_acquire (tmp, gint16, (volatile gint16 *) ptr);
+ gint16 tmp = *(volatile gint16 *)ptr;
+ mono_memory_barrier ();
return tmp;
}
gint32
ves_icall_System_Threading_Thread_VolatileRead4 (void *ptr)
{
- gint32 tmp;
- mono_atomic_load_acquire (tmp, gint32, (volatile gint32 *) ptr);
+ gint32 tmp = *(volatile gint32 *)ptr;
+ mono_memory_barrier ();
return tmp;
}
gint64
ves_icall_System_Threading_Thread_VolatileRead8 (void *ptr)
{
- gint64 tmp;
- mono_atomic_load_acquire (tmp, gint64, (volatile gint64 *) ptr);
+ gint64 tmp = *(volatile gint64 *)ptr;
+ mono_memory_barrier ();
return tmp;
}
void *
ves_icall_System_Threading_Thread_VolatileReadIntPtr (void *ptr)
{
- volatile void *tmp;
- mono_atomic_load_acquire (tmp, volatile void *, (volatile void **) ptr);
+ volatile void *tmp = *(volatile void **)ptr;
+ mono_memory_barrier ();
return (void *) tmp;
}
void *
ves_icall_System_Threading_Thread_VolatileReadObject (void *ptr)
{
- volatile MonoObject *tmp;
- mono_atomic_load_acquire (tmp, volatile MonoObject *, (volatile MonoObject **) ptr);
+ volatile MonoObject *tmp = *(volatile MonoObject **)ptr;
+ mono_memory_barrier ();
return (MonoObject *) tmp;
}
double
ves_icall_System_Threading_Thread_VolatileReadDouble (void *ptr)
{
- double tmp;
- mono_atomic_load_acquire (tmp, double, (volatile double *) ptr);
+ double tmp = *(volatile double *)ptr;
+ mono_memory_barrier ();
return tmp;
}
float
ves_icall_System_Threading_Thread_VolatileReadFloat (void *ptr)
{
- float tmp;
- mono_atomic_load_acquire (tmp, float, (volatile float *) ptr);
+ float tmp = *(volatile float *)ptr;
+ mono_memory_barrier ();
return tmp;
}
void
ves_icall_System_Threading_Thread_VolatileWrite1 (void *ptr, gint8 value)
{
- mono_atomic_store_release ((volatile gint8 *) ptr, value);
+ mono_memory_barrier ();
+ *(volatile gint8 *)ptr = value;
}
void
ves_icall_System_Threading_Thread_VolatileWrite2 (void *ptr, gint16 value)
{
- mono_atomic_store_release ((volatile gint16 *) ptr, value);
+ mono_memory_barrier ();
+ *(volatile gint16 *)ptr = value;
}
void
ves_icall_System_Threading_Thread_VolatileWrite4 (void *ptr, gint32 value)
{
- mono_atomic_store_release ((volatile gint32 *) ptr, value);
+ mono_memory_barrier ();
+ *(volatile gint32 *)ptr = value;
}
void
ves_icall_System_Threading_Thread_VolatileWrite8 (void *ptr, gint64 value)
{
- mono_atomic_store_release ((volatile gint64 *) ptr, value);
+ mono_memory_barrier ();
+ *(volatile gint64 *)ptr = value;
}
void
ves_icall_System_Threading_Thread_VolatileWriteIntPtr (void *ptr, void *value)
{
- mono_atomic_store_release ((volatile void **) ptr, value);
+ mono_memory_barrier ();
+ *(volatile void **)ptr = value;
}
void
ves_icall_System_Threading_Thread_VolatileWriteObject (void *ptr, MonoObject *value)
{
- mono_gc_wbarrier_generic_store_atomic (ptr, value);
+ mono_memory_barrier ();
+ mono_gc_wbarrier_generic_store (ptr, value);
}
void
ves_icall_System_Threading_Thread_VolatileWriteDouble (void *ptr, double value)
{
- mono_atomic_store_release ((volatile double *) ptr, value);
+ mono_memory_barrier ();
+ *(volatile double *)ptr = value;
}
void
ves_icall_System_Threading_Thread_VolatileWriteFloat (void *ptr, float value)
{
- mono_atomic_store_release ((volatile float *) ptr, value);
+ mono_memory_barrier ();
+ *(volatile float *)ptr = value;
}
void
mono_gc_wbarrier_generic_store_atomic (ptr, value);
}
+static void
+free_context (void *user_data)
+{
+ ContextStaticData *data = user_data;
+
+ mono_threads_lock ();
+
+ /*
+ * There is no guarantee that, by the point this reference queue callback
+ * has been invoked, the GC handle associated with the object will fail to
+ * resolve as one might expect. So if we don't free and remove the GC
+ * handle here, free_context_static_data_helper () could end up resolving
+ * a GC handle to an actually-dead context which would contain a pointer
+ * to an already-freed static data segment, resulting in a crash when
+ * accessing it.
+ */
+ g_hash_table_remove (contexts, GUINT_TO_POINTER (data->gc_handle));
+
+ mono_threads_unlock ();
+
+ mono_gchandle_free (data->gc_handle);
+ mono_free_static_data (data->static_data);
+ g_free (data);
+}
+
void
ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppContext *ctx)
{
if (!contexts)
contexts = g_hash_table_new (NULL, NULL);
- context_adjust_static_data (ctx);
+ if (!context_queue)
+ context_queue = mono_gc_reference_queue_new (free_context);
+
gpointer gch = GUINT_TO_POINTER (mono_gchandle_new_weakref (&ctx->obj, FALSE));
g_hash_table_insert (contexts, gch, gch);
+ /*
+ * We use this intermediate structure to contain a duplicate pointer to
+ * the static data because we can't rely on being able to resolve the GC
+ * handle in the reference queue callback.
+ */
+ ContextStaticData *data = g_new0 (ContextStaticData, 1);
+ data->gc_handle = GPOINTER_TO_UINT (gch);
+ ctx->data = data;
+
+ context_adjust_static_data (ctx);
+ mono_gc_reference_queue_add (context_queue, &ctx->obj, data);
+
mono_threads_unlock ();
mono_profiler_context_loaded (ctx);
/*
* NOTE: Since finalizers are unreliable for the purposes of ensuring
* cleanup in exceptional circumstances, we don't actually do any
- * cleanup work here. We instead do this when we iterate the `contexts`
- * hash table. The only purpose of this finalizer, at the moment, is to
- * notify the profiler.
+ * cleanup work here. We instead do this via a reference queue.
*/
//g_print ("Releasing context %d in domain %d\n", ctx->context_id, ctx->domain_id);
thread->state &= ~ThreadState_AbortRequested;
thread->state |= ThreadState_SuspendRequested;
-
- UNLOCK_THREAD (thread);
-
/* Signal the thread to suspend */
suspend_thread_internal (thread, TRUE);
}
static void
mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_frames)
{
+ MonoError error;
+
ThreadDumpUserData ud;
MonoInternalThread *thread_array [128];
MonoDomain *domain = mono_domain_get ();
MonoDebugSourceLocation *location;
int tindex, nthreads;
+ mono_error_init (&error);
+
*out_threads = NULL;
*out_stack_frames = NULL;
for (i = 0; i < ud.nframes; ++i) {
MonoStackFrameInfo *frame = &ud.frames [i];
MonoMethod *method = NULL;
- MonoStackFrame *sf = (MonoStackFrame *)mono_object_new (domain, mono_defaults.stack_frame_class);
+ MonoStackFrame *sf = (MonoStackFrame *)mono_object_new_checked (domain, mono_defaults.stack_frame_class, &error);
+ if (!mono_error_ok (&error))
+ goto leave;
sf->native_offset = frame->native_offset;
if (method) {
sf->method_address = (gsize) frame->ji->code_start;
- MONO_OBJECT_SETREF (sf, method, mono_method_get_object (domain, method, NULL));
+ MonoReflectionMethod *rm = mono_method_get_object_checked (domain, method, NULL, &error);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MONO_OBJECT_SETREF (sf, method, rm);
location = mono_debug_lookup_source_location (method, frame->native_offset, domain);
if (location) {
}
}
+leave:
g_free (ud.frames);
+ mono_error_raise_exception (&error); /* FIXME don't raise here */
}
/**
if (context_static_info.offset || context_static_info.idx > 0) {
guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (context_static_info.idx, context_static_info.offset, 0);
mono_alloc_static_data (&ctx->static_data, offset, FALSE);
+ ctx->data->static_data = ctx->static_data;
}
}
/*
* LOCKING: requires that threads_mutex is held
*/
-static gboolean
+static void
alloc_context_static_data_helper (gpointer key, gpointer value, gpointer user)
{
- uint32_t gch = GPOINTER_TO_INT (key);
- MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (gch);
+ MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (GPOINTER_TO_INT (key));
- if (!ctx) {
- mono_gchandle_free (gch);
- return TRUE; // Remove this key/value pair
- }
+ if (!ctx)
+ return;
guint32 offset = GPOINTER_TO_UINT (user);
mono_alloc_static_data (&ctx->static_data, offset, FALSE);
-
- return FALSE; // Don't remove it
+ ctx->data->static_data = ctx->static_data;
}
static StaticDataFreeList*
mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
} else {
if (contexts != NULL)
- g_hash_table_foreach_remove (contexts, alloc_context_static_data_helper, GUINT_TO_POINTER (offset));
+ g_hash_table_foreach (contexts, alloc_context_static_data_helper, GUINT_TO_POINTER (offset));
ACCESS_SPECIAL_STATIC_OFFSET (offset, type) = SPECIAL_STATIC_OFFSET_TYPE_CONTEXT;
}
/*
* LOCKING: requires that threads_mutex is held
*/
-static gboolean
+static void
free_context_static_data_helper (gpointer key, gpointer value, gpointer user)
{
- uint32_t gch = GPOINTER_TO_INT (key);
- MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (gch);
+ MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (GPOINTER_TO_INT (key));
- if (!ctx) {
- mono_gchandle_free (gch);
- return TRUE; // Remove this key/value pair
- }
+ if (!ctx)
+ return;
OffsetSize *data = (OffsetSize *)user;
int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index);
char *ptr;
if (!ctx->static_data || !ctx->static_data [idx])
- return FALSE; // Don't remove this key/value pair
+ return;
ptr = ((char*) ctx->static_data [idx]) + off;
mono_gc_bzero_atomic (ptr, data->size);
-
- return FALSE; // Don't remove this key/value pair
}
static void
mono_g_hash_table_foreach (threads, free_thread_static_data_helper, &data);
} else {
if (contexts != NULL)
- g_hash_table_foreach_remove (contexts, free_context_static_data_helper, &data);
+ g_hash_table_foreach (contexts, free_context_static_data_helper, &data);
}
if (!mono_runtime_is_shutting_down ()) {
return mono_thread_interruption_checkpoint_request (TRUE);
}
-/*
- * Performs the interruption of the current thread, if one has been requested.
- * Throw the exception which needs to be thrown, if any.
- */
-void
-mono_thread_force_interruption_checkpoint (void)
-{
- MonoException *ex;
-
- ex = mono_thread_interruption_checkpoint_request (TRUE);
- if (ex)
- mono_raise_exception (ex);
-}
-
/*
* mono_thread_get_and_clear_pending_exception:
*
static void
suspend_thread_internal (MonoInternalThread *thread, gboolean interrupt)
{
- LOCK_THREAD (thread);
if (thread == mono_thread_internal_current ()) {
mono_thread_info_begin_self_suspend ();
//XXX replace this with better named functions