#include <mono/utils/mono-tls.h>
#include <mono/utils/atomic.h>
#include <mono/utils/mono-memory-model.h>
+#include <mono/utils/mono-threads-coop.h>
+#include <mono/utils/mono-error-internals.h>
#include <mono/metadata/gc-internals.h>
#include <mono/metadata/reflection-internals.h>
#define LOCK_THREAD(thread) lock_thread((thread))
#define UNLOCK_THREAD(thread) unlock_thread((thread))
-/* Provide this for systems with glib < 2.6 */
-#ifndef G_GSIZE_FORMAT
-# if GLIB_SIZEOF_LONG == 8
-# define G_GSIZE_FORMAT "lu"
-# else
-# define G_GSIZE_FORMAT "u"
-# endif
-#endif
-
typedef struct
{
guint32 (*func)(void *);
}
static MonoThread*
-create_thread_object (MonoDomain *domain, MonoError *error)
+create_thread_object (MonoDomain *domain)
{
+ MonoError error;
MonoVTable *vt = mono_class_vtable (domain, mono_defaults.thread_class);
- MonoThread *t = (MonoThread*)mono_object_new_mature (vt, error);
+ MonoThread *t = (MonoThread*)mono_object_new_mature (vt, &error);
+ /* only possible failure mode is OOM, from which we don't expect to recover. */
+ mono_error_assert_ok (&error);
return t;
}
static MonoThread*
-new_thread_with_internal (MonoDomain *domain, MonoInternalThread *internal, MonoError *error)
+new_thread_with_internal (MonoDomain *domain, MonoInternalThread *internal)
{
MonoThread *thread;
- thread = create_thread_object (domain, error);
- if (!mono_error_ok (error))
- return NULL;
+ thread = create_thread_object (domain);
MONO_OBJECT_SETREF (thread, internal_thread, internal);
}
static MonoInternalThread*
-create_internal_thread (MonoError *error)
+create_internal_thread (void)
{
+ MonoError error;
MonoInternalThread *thread;
MonoVTable *vt;
vt = mono_class_vtable (mono_get_root_domain (), mono_defaults.internal_thread_class);
- thread = (MonoInternalThread*) mono_object_new_mature (vt, error);
- if (!mono_error_ok (error))
- return NULL;
+ thread = (MonoInternalThread*) mono_object_new_mature (vt, &error);
+ /* only possible failure mode is OOM, from which we don't exect to recover */
+ mono_error_assert_ok (&error);
thread->synch_cs = g_new0 (MonoCoopMutex, 1);
mono_coop_mutex_init_recursive (thread->synch_cs);
}
static gboolean
-init_root_domain_thread (MonoInternalThread *thread, MonoThread *candidate, MonoError *error)
+init_root_domain_thread (MonoInternalThread *thread, MonoThread *candidate)
{
MonoDomain *domain = mono_get_root_domain ();
- mono_error_init (error);
if (!candidate || candidate->obj.vtable->domain != domain) {
- candidate = new_thread_with_internal (domain, thread, error);
- return_val_if_nok (error, FALSE);
+ candidate = new_thread_with_internal (domain, thread);
}
set_current_thread_for_domain (domain, thread, candidate);
g_assert (!thread->root_domain_thread);
/* We have to do this here because mono_thread_new_init()
requires that root_domain_thread is set up. */
thread_adjust_static_data (internal);
- init_root_domain_thread (internal, start_info->obj, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ init_root_domain_thread (internal, start_info->obj);
/* This MUST be called before any managed code can be
* executed, as it calls the callback function that (for the
mono_error_init (error);
- thread = create_thread_object (domain, error);
- return_val_if_nok (error, NULL);
+ thread = create_thread_object (domain);
- internal = create_internal_thread (error);
- return_val_if_nok (error, NULL);
+ internal = create_internal_thread ();
MONO_OBJECT_SETREF (thread, internal_thread, internal);
MonoThread *
mono_thread_attach (MonoDomain *domain)
{
- MonoError error;
- MonoThread *thread = mono_thread_attach_full (domain, FALSE, &error);
- mono_error_raise_exception (&error);
+ MonoThread *thread = mono_thread_attach_full (domain, FALSE);
return thread;
}
MonoThread *
-mono_thread_attach_full (MonoDomain *domain, gboolean force_attach, MonoError *error)
+mono_thread_attach_full (MonoDomain *domain, gboolean force_attach)
{
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 (error);
- if (!mono_error_ok (error))
- return NULL;
+ thread = create_internal_thread ();
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, error);
- if (!mono_error_ok (error))
- return NULL;
+ current_thread = new_thread_with_internal (domain, thread);
if (!handle_store (current_thread, force_attach)) {
/* Mono is shutting down, so just wait for the end */
thread_adjust_static_data (thread);
- init_root_domain_thread (thread, current_thread, error);
- return_val_if_nok (error, NULL);
+ init_root_domain_thread (thread, current_thread);
if (domain != mono_get_root_domain ())
set_current_thread_for_domain (domain, thread, current_thread);
void
ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this_obj)
{
- MonoError error;
MonoInternalThread *internal;
- internal = create_internal_thread (&error);
- if (mono_error_set_pending_exception (&error))
- return;
+ internal = create_internal_thread ();
internal->state = ThreadState_Unstarted;
if (alerted) {
MonoException* exc = mono_thread_execute_interruption ();
if (exc) {
- mono_set_pending_exception (exc);
- return;
+ mono_raise_exception (exc);
} else {
// FIXME: !INFINITE
if (ms != INFINITE)
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, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ *current_thread_ptr = new_thread_with_internal (domain, internal);
}
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, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ *current_thread_ptr = new_thread_with_internal (domain, internal);
}
return *current_thread_ptr;
}
mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
mono_error_set_pending_exception (&error);
- return map_native_wait_result_to_managed (ret == WAIT_FAILED);
+ return map_native_wait_result_to_managed (ret);
}
gint32
}
/* Obtain the thread dump of all threads */
-static void
-mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_frames)
+static gboolean
+mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_frames, MonoError *error)
{
- MonoError error;
ThreadDumpUserData ud;
MonoInternalThread *thread_array [128];
MonoDebugSourceLocation *location;
int tindex, nthreads;
- mono_error_init (&error);
+ mono_error_init (error);
*out_threads = NULL;
*out_stack_frames = NULL;
ud.frames = g_new0 (MonoStackFrameInfo, 256);
ud.max_frames = 256;
- *out_threads = mono_array_new_checked (domain, mono_defaults.thread_class, nthreads, &error);
- if (!is_ok (&error))
+ *out_threads = mono_array_new_checked (domain, mono_defaults.thread_class, nthreads, error);
+ if (!is_ok (error))
goto leave;
- *out_stack_frames = mono_array_new_checked (domain, mono_defaults.array_class, nthreads, &error);
- if (!is_ok (&error))
+ *out_stack_frames = mono_array_new_checked (domain, mono_defaults.array_class, nthreads, error);
+ if (!is_ok (error))
goto leave;
for (tindex = 0; tindex < nthreads; ++tindex) {
mono_array_setref_fast (*out_threads, tindex, mono_thread_current_for_thread (thread));
- thread_frames = mono_array_new_checked (domain, mono_defaults.stack_frame_class, ud.nframes, &error);
- if (!is_ok (&error))
+ thread_frames = mono_array_new_checked (domain, mono_defaults.stack_frame_class, ud.nframes, error);
+ if (!is_ok (error))
goto leave;
mono_array_setref_fast (*out_stack_frames, tindex, thread_frames);
for (i = 0; i < ud.nframes; ++i) {
MonoStackFrameInfo *frame = &ud.frames [i];
MonoMethod *method = NULL;
- MonoStackFrame *sf = (MonoStackFrame *)mono_object_new_checked (domain, mono_defaults.stack_frame_class, &error);
- if (!mono_error_ok (&error))
+ MonoStackFrame *sf = (MonoStackFrame *)mono_object_new_checked (domain, mono_defaults.stack_frame_class, error);
+ if (!is_ok (error))
goto leave;
sf->native_offset = frame->native_offset;
if (method) {
sf->method_address = (gsize) frame->ji->code_start;
- MonoReflectionMethod *rm = mono_method_get_object_checked (domain, method, NULL, &error);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ MonoReflectionMethod *rm = mono_method_get_object_checked (domain, method, NULL, error);
+ if (!is_ok (error))
+ goto leave;
MONO_OBJECT_SETREF (sf, method, rm);
location = mono_debug_lookup_source_location (method, frame->native_offset, domain);
leave:
g_free (ud.frames);
- mono_error_raise_exception (&error); /* FIXME don't raise here */
+ return is_ok (error);
}
/**
mono_threads_unlock ();
}
-static void
-mono_special_static_data_free_slot (guint32 offset, guint32 size)
-{
- /* Only ever called for ThreadLocal instances */
- g_assert (ACCESS_SPECIAL_STATIC_OFFSET (offset, type) == SPECIAL_STATIC_OFFSET_TYPE_THREAD);
-
- mono_threads_lock ();
- do_free_special_slot (offset, size);
- mono_threads_unlock ();
-}
-
#ifdef HOST_WIN32
static void CALLBACK dummy_apc (ULONG_PTR param)
{
void
ves_icall_System_Threading_Thread_GetStackTraces (MonoArray **out_threads, MonoArray **out_stack_traces)
{
- mono_threads_get_thread_dump (out_threads, out_stack_traces);
+ MonoError error;
+ mono_threads_get_thread_dump (out_threads, out_stack_traces, &error);
+ mono_error_set_pending_exception (&error);
+}
+
+/*
+ * mono_threads_attach_coop: called by native->managed wrappers
+ *
+ * In non-coop mode:
+ * - @dummy: is NULL
+ * - @return: the original domain which needs to be restored, or NULL.
+ *
+ * In coop mode:
+ * - @dummy: contains the original domain
+ * - @return: a cookie containing current MonoThreadInfo*.
+ */
+gpointer
+mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
+{
+ MonoDomain *orig;
+ gboolean fresh_thread;
+
+ if (!domain) {
+ /* Happens when called from AOTed code which is only used in the root domain. */
+ domain = mono_get_root_domain ();
+ }
+
+ g_assert (domain);
+
+ /* On coop, when we detached, we moved the thread from RUNNING->BLOCKING.
+ * If we try to reattach we do a BLOCKING->RUNNING transition. If the thread
+ * is fresh, mono_thread_attach() will do a STARTING->RUNNING transition so
+ * we're only responsible for making the cookie. */
+ if (mono_threads_is_coop_enabled ()) {
+ MonoThreadInfo *info = mono_thread_info_current_unchecked ();
+ fresh_thread = !info || !mono_thread_info_is_live (info);
+ }
+
+ if (!mono_thread_internal_current ()) {
+ mono_thread_attach_full (domain, FALSE);
+
+ // #678164
+ mono_thread_set_state (mono_thread_internal_current (), ThreadState_Background);
+ }
+
+ orig = mono_domain_get ();
+ if (orig != domain)
+ mono_domain_set (domain, TRUE);
+
+ if (!mono_threads_is_coop_enabled ())
+ return orig != domain ? orig : NULL;
+
+ if (fresh_thread) {
+ *dummy = NULL;
+ /* mono_thread_attach put the thread in RUNNING mode from STARTING, but we need to
+ * return the right cookie. */
+ return mono_threads_enter_gc_unsafe_region_cookie (mono_thread_info_current ());
+ } else {
+ *dummy = orig;
+ /* thread state (BLOCKING|RUNNING) -> RUNNING */
+ return mono_threads_enter_gc_unsafe_region (dummy);
+ }
+}
+
+/*
+ * mono_threads_detach_coop: called by native->managed wrappers
+ *
+ * In non-coop mode:
+ * - @cookie: the original domain which needs to be restored, or NULL.
+ * - @dummy: is NULL
+ *
+ * In coop mode:
+ * - @cookie: contains current MonoThreadInfo* if it was in BLOCKING mode, NULL otherwise
+ * - @dummy: contains the original domain
+ */
+void
+mono_threads_detach_coop (gpointer cookie, gpointer *dummy)
+{
+ MonoDomain *domain, *orig;
+
+ if (!mono_threads_is_coop_enabled ()) {
+ orig = (MonoDomain*) cookie;
+ if (orig)
+ mono_domain_set (orig, TRUE);
+ } else {
+ orig = (MonoDomain*) *dummy;
+
+ domain = mono_domain_get ();
+ g_assert (domain);
+
+ /* it won't do anything if cookie is NULL
+ * thread state RUNNING -> (RUNNING|BLOCKING) */
+ mono_threads_exit_gc_unsafe_region (cookie, dummy);
+
+ if (orig != domain) {
+ if (!orig)
+ mono_domain_unset ();
+ else
+ mono_domain_set (orig, TRUE);
+ }
+ }
}