#define mono_contexts_unlock() LeaveCriticalSection (&contexts_mutex)
static CRITICAL_SECTION contexts_mutex;
+/* Controls access to the 'joinable_threads' hash table */
+#define joinable_threads_lock() EnterCriticalSection (&joinable_threads_mutex)
+#define joinable_threads_unlock() LeaveCriticalSection (&joinable_threads_mutex)
+static CRITICAL_SECTION joinable_threads_mutex;
+
/* Holds current status of static data heap */
static StaticDataInfo thread_static_info;
static StaticDataInfo context_static_info;
return offset;
}
+static inline MonoNativeThreadId
+thread_get_tid (MonoInternalThread *thread)
+{
+ /* We store the tid as a guint64 to keep the object layout constant between platforms */
+ return MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid);
+}
+
/* handle_store() and handle_remove() manage the array of threads that
* still need to be waited for when the main thread exits.
*
return ret;
}
+static void ensure_synch_cs_set (MonoInternalThread *thread)
+{
+ CRITICAL_SECTION *synch_cs;
+
+ if (thread->synch_cs != NULL) {
+ return;
+ }
+
+ synch_cs = g_new0 (CRITICAL_SECTION, 1);
+ InitializeCriticalSection (synch_cs);
+
+ if (InterlockedCompareExchangePointer ((gpointer *)&thread->synch_cs,
+ synch_cs, NULL) != NULL) {
+ /* Another thread must have installed this CS */
+ DeleteCriticalSection (synch_cs);
+ g_free (synch_cs);
+ }
+}
+
static inline void
lock_thread (MonoInternalThread *thread)
{
+ if (!thread->synch_cs)
+ ensure_synch_cs_set (thread);
+
g_assert (thread->synch_cs);
EnterCriticalSection (thread->synch_cs);
}
mono_array_set (thread->cached_culture_info, MonoObject*, i, NULL);
}
+ /*
+ * thread->synch_cs can be NULL if this was called after
+ * ves_icall_System_Threading_InternalThread_Thread_free_internal.
+ * This can happen only during shutdown.
+ * The shutting_down flag is not always set, so we can't assert on it.
+ */
if (thread->synch_cs)
LOCK_THREAD (thread);
- else
- g_assert (shutting_down);
thread->state |= ThreadState_Stopped;
thread->state &= ~ThreadState_Background;
*/
mono_profiler_thread_start (tid);
+ /* if the name was set before starting, we didn't invoke the profiler callback */
+ if (internal->name && (internal->flags & MONO_THREAD_FLAG_NAME_SET)) {
+ char *tname = g_utf16_to_utf8 (internal->name, -1, NULL, NULL, NULL);
+ mono_profiler_thread_name (internal->tid, tname);
+ g_free (tname);
+ }
/* start_func is set only for unmanaged start functions */
if (start_func) {
start_func (start_arg);
thread_cleanup (internal);
+ internal->tid = 0;
+
/* Remove the reference to the thread object in the TLS data,
* so the thread object can be finalized. This won't be
* reached if the thread threw an uncaught exception, so those
MonoNativeThreadId tid;
guint32 create_flags;
+ /*
+ * Join joinable threads to prevent running out of threads since the finalizer
+ * thread might be blocked/backlogged.
+ */
+ mono_threads_join_threads ();
+
mono_threads_lock ();
if (shutting_down) {
g_free (start_info);
thread = create_internal_thread ();
- thread_handle = GetCurrentThread ();
+ thread_handle = mono_thread_info_open_handle ();
g_assert (thread_handle);
tid=GetCurrentThreadId ();
- /*
- * The handle returned by GetCurrentThread () is a pseudo handle, so it can't be used to
- * refer to the thread from other threads for things like aborting.
- */
- DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle,
- THREAD_ALL_ACCESS, TRUE, 0);
-
thread->handle=thread_handle;
thread->tid=tid;
#ifdef PLATFORM_ANDROID
}
void
-mono_thread_detach (MonoThread *thread)
+mono_thread_detach_internal (MonoInternalThread *thread)
{
g_return_if_fail (thread != NULL);
- THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->internal_thread->tid));
+ THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
- thread_cleanup (thread->internal_thread);
+ thread_cleanup (thread);
SET_CURRENT_OBJECT (NULL);
mono_domain_unset ();
*/
}
+void
+mono_thread_detach (MonoThread *thread)
+{
+ if (thread)
+ mono_thread_detach_internal (thread->internal_thread);
+}
+
void
mono_thread_exit ()
{
/* we could add a callback here for embedders to use. */
if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread))
exit (mono_environment_exitcode_get ());
- ExitThread (-1);
+ mono_thread_info_exit ();
}
void
}
/*
- * This is called from the finalizer of the internal thread object. Since threads keep a reference to their
- * thread object while running, by the time this function is called, the thread has already exited/detached,
- * i.e. thread_cleanup () has ran.
+ * This is called from the finalizer of the internal thread object.
*/
void
ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThread *this, HANDLE thread)
{
THREAD_DEBUG (g_message ("%s: Closing thread %p, handle %p", __func__, this, thread));
+ /*
+ * Since threads keep a reference to their thread object while running, by the time this function is called,
+ * the thread has already exited/detached, i.e. thread_cleanup () has ran. The exception is during shutdown,
+ * when thread_cleanup () can be called after this.
+ */
if (thread)
CloseHandle (thread);
UNLOCK_THREAD (this_obj);
- if (this_obj->name) {
+ if (this_obj->name && this_obj->tid) {
char *tname = mono_string_to_utf8 (name);
mono_profiler_thread_name (this_obj->tid, tname);
+ mono_thread_info_set_name (thread_get_tid (this_obj), tname);
mono_free (tname);
}
}
return arr;
copy = mono_array_new (domain, mono_defaults.byte_class, arr->max_length);
- mono_gc_memmove (mono_array_addr (copy, guint8, 0), mono_array_addr (arr, guint8, 0), arr->max_length);
+ memmove (mono_array_addr (copy, guint8, 0), mono_array_addr (arr, guint8, 0), arr->max_length);
return copy;
}
continue;
/* Re-calculate ms according to the time passed */
- diff_ms = (mono_100ns_ticks () - start) / 10000;
+ diff_ms = (gint32)((mono_100ns_ticks () - start) / 10000);
if (diff_ms >= ms) {
ret = WAIT_TIMEOUT;
break;
/* Do this WaitSleepJoin check before creating objects */
mono_thread_current_check_pending_interrupt ();
- numhandles = mono_array_length(mono_handles);
+ /* We fail in managed if the array has more than 64 elements */
+ numhandles = (guint32)mono_array_length(mono_handles);
handles = g_new0(HANDLE, numhandles);
for(i = 0; i < numhandles; i++) {
gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
{
HANDLE handles [MAXIMUM_WAIT_OBJECTS];
- guint32 numhandles;
+ uintptr_t numhandles;
guint32 ret;
guint32 i;
MonoObject *waitHandle;
*/
static void signal_thread_state_change (MonoInternalThread *thread)
{
+#ifndef HOST_WIN32
+ gpointer wait_handle;
+#endif
+
if (thread == mono_thread_internal_current ()) {
/* Do it synchronously */
MonoException *exc = mono_thread_request_interruption (FALSE);
}
#ifdef HOST_WIN32
- QueueUserAPC ((PAPCFUNC)interruption_request_apc, thread->handle, NULL);
+ QueueUserAPC ((PAPCFUNC)interruption_request_apc, thread->handle, (ULONG_PTR)NULL);
#else
- /* fixme: store the state somewhere */
- mono_thread_kill (thread, mono_thread_get_abort_signal ());
-
/*
* This will cause waits to be broken.
* It will also prevent the thread from entering a wait, so if the thread returns
* functions in the io-layer until the signal handler calls QueueUserAPC which will
* make it return.
*/
- wapi_interrupt_thread (thread->handle);
+ wait_handle = wapi_prepare_interrupt_thread (thread->handle);
+
+ /* fixme: store the state somewhere */
+ mono_thread_kill (thread, mono_thread_get_abort_signal ());
+
+ wapi_finish_interrupt_thread (wait_handle);
#endif /* HOST_WIN32 */
}
InitializeCriticalSection(&threads_mutex);
InitializeCriticalSection(&interlocked_mutex);
InitializeCriticalSection(&contexts_mutex);
+ InitializeCriticalSection(&joinable_threads_mutex);
background_change_event = CreateEvent (NULL, TRUE, FALSE, NULL);
g_assert(background_change_event != NULL);
void mono_thread_cleanup (void)
{
#if !defined(HOST_WIN32) && !defined(RUN_IN_SUBTHREAD)
+ MonoThreadInfo *info;
+
/* The main thread must abandon any held mutexes (particularly
* important for named mutexes as they are shared across
* processes, see bug 74680.) This will happen when the
* thread exits, but if it's not running in a subthread it
* won't exit in time.
*/
- /* Using non-w32 API is a nasty kludge, but I couldn't find
- * anything in the documentation that would let me do this
- * here yet still be safe to call on windows.
- */
- _wapi_thread_signal_self (mono_environment_exitcode_get ());
+ info = mono_thread_info_current ();
+ wapi_thread_handle_set_exited (info->handle, mono_environment_exitcode_get ());
#endif
#if 0
/*
* On !win32, when the thread handle becomes signalled, it just means the thread has exited user code,
* it can still run io-layer etc. code. So wait for it to really exit.
+ * FIXME: This won't join threads which are not in the joinable_hash yet.
*/
mono_thread_join ((gpointer)tid);
return;
}
- handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
+ handle = mono_threads_open_thread_handle (thread->handle, (MonoNativeThreadId)thread->tid);
if (handle == NULL) {
THREAD_DEBUG (g_message ("%s: ignoring unopenable thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
return;
if (thread->tid != self && (thread->state & ThreadState_Background) != 0 &&
!(thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)) {
- handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
+ handle = mono_threads_open_thread_handle (thread->handle, (MonoNativeThreadId)thread->tid);
if (handle == NULL)
return FALSE;
mono_domain_unset ();
/* Wake up other threads potentially waiting for us */
- ExitThread (0);
+ mono_thread_info_exit ();
} else {
shutting_down = TRUE;
return;
if (wait->num<MAXIMUM_WAIT_OBJECTS) {
- handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
+ handle = mono_threads_open_thread_handle (thread->handle, (MonoNativeThreadId)thread->tid);
if (handle == NULL)
return;
HANDLE handle;
if (wait->num<MAXIMUM_WAIT_OBJECTS) {
- handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
+ handle = mono_threads_open_thread_handle (thread->handle, (MonoNativeThreadId)thread->tid);
if (handle == NULL)
return;
#endif
mono_get_eh_callbacks ()->mono_walk_stack_with_state (print_stack_frame_to_string, &info->suspend_state, MONO_UNWIND_SIGNAL_SAFE, text);
- mono_thread_info_resume (mono_thread_info_get_tid (info));
+ mono_thread_info_finish_suspend_and_resume (info);
fprintf (stdout, "%s", text->str);
/* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread->tid, domain->friendly_name); */
if(data->wait.num<MAXIMUM_WAIT_OBJECTS) {
- HANDLE handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
+ HANDLE handle = mono_threads_open_thread_handle (thread->handle, (MonoNativeThreadId)thread->tid);
if (handle == NULL)
return;
data->wait.handles [data->wait.num] = handle;
return NULL;
}
+#if SIZEOF_VOID_P == 4
+#define ONE_P 1
+#else
+#define ONE_P 1ll
+#endif
+
static void
update_tls_reference_bitmap (guint32 offset, uintptr_t *bitmap, int numbits)
{
offset /= sizeof (gpointer);
/* offset is now the bitmap offset */
for (i = 0; i < numbits; ++i) {
- if (bitmap [i / sizeof (uintptr_t)] & (1L << (i & (sizeof (uintptr_t) * 8 -1))))
- rb [(offset + i) / (sizeof (uintptr_t) * 8)] |= (1L << ((offset + i) & (sizeof (uintptr_t) * 8 -1)));
+ if (bitmap [i / sizeof (uintptr_t)] & (ONE_P << (i & (sizeof (uintptr_t) * 8 -1))))
+ rb [(offset + i) / (sizeof (uintptr_t) * 8)] |= (ONE_P << ((offset + i) & (sizeof (uintptr_t) * 8 -1)));
}
}
if (!thread->static_data || !thread->static_data [idx])
return;
ptr = ((char*) thread->static_data [idx]) + (data->offset & 0xffffff);
- mono_gc_bzero (ptr, data->size);
+ mono_gc_bzero_atomic (ptr, data->size);
}
static void
/* MonoThread::interruption_requested can only be changed with atomics */
if (InterlockedCompareExchange (&thread->interruption_requested, FALSE, TRUE)) {
/* this will consume pending APC calls */
+#ifdef HOST_WIN32
WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
+#endif
InterlockedDecrement (&thread_interruption_requested);
#ifndef HOST_WIN32
/* Clear the interrupted flag of the thread so it can wait again */
or similar */
/* Our implementation of this function ignores the func argument */
#ifdef HOST_WIN32
- QueueUserAPC ((PAPCFUNC)dummy_apc, thread->handle, NULL);
+ QueueUserAPC ((PAPCFUNC)dummy_apc, thread->handle, (ULONG_PTR)NULL);
#else
- wapi_thread_interrupt_self ();
+ wapi_self_interrupt ();
#endif
return NULL;
}
}
if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (&info->suspend_state)) {
- mono_thread_info_resume (mono_thread_info_get_tid (info));
+ mono_thread_info_finish_suspend_and_resume (info);
return;
}
/*someone is already interrupting it*/
if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1) {
- mono_thread_info_resume (mono_thread_info_get_tid (info));
+ mono_thread_info_finish_suspend_and_resume (info);
return;
}
InterlockedIncrement (&thread_interruption_requested);
/*Set the thread to call */
if (install_async_abort)
mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
- mono_thread_info_resume (mono_thread_info_get_tid (info));
+ mono_thread_info_finish_suspend_and_resume (info);
} else {
- gpointer interrupt_handle;
/*
* This will cause waits to be broken.
* It will also prevent the thread from entering a wait, so if the thread returns
* make it return.
*/
#ifndef HOST_WIN32
+ gpointer interrupt_handle;
interrupt_handle = wapi_prepare_interrupt_thread (thread->handle);
#endif
- mono_thread_info_resume (mono_thread_info_get_tid (info));
+ mono_thread_info_finish_suspend_and_resume (info);
#ifndef HOST_WIN32
wapi_finish_interrupt_thread (interrupt_handle);
#endif
}
static void
-transition_to_suspended (MonoInternalThread *thread)
+transition_to_suspended (MonoInternalThread *thread, MonoThreadInfo *info)
{
if ((thread->state & ThreadState_SuspendRequested) == 0) {
g_assert (0); /*FIXME we should not reach this */
/*Make sure we balance the suspend count.*/
- mono_thread_info_resume ((MonoNativeThreadId)(gpointer)(gsize)thread->tid);
+ if (info)
+ mono_thread_info_finish_suspend_and_resume (info);
} else {
thread->state &= ~ThreadState_SuspendRequested;
thread->state |= ThreadState_Suspended;
- mono_thread_info_finish_suspend ();
+ if (info)
+ mono_thread_info_finish_suspend (info);
}
UNLOCK_THREAD (thread);
}
LOCK_THREAD (thread);
if (thread == mono_thread_internal_current ()) {
- transition_to_suspended (thread);
+ transition_to_suspended (thread, NULL);
mono_thread_info_self_suspend ();
} else {
MonoThreadInfo *info;
running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
if (running_managed && !protected_wrapper) {
- transition_to_suspended (thread);
+ transition_to_suspended (thread, info);
} else {
+#ifndef HOST_WIN32
gpointer interrupt_handle;
+#endif
if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 0)
InterlockedIncrement (&thread_interruption_requested);
if (interrupt)
interrupt_handle = wapi_prepare_interrupt_thread (thread->handle);
#endif
- mono_thread_info_resume (mono_thread_info_get_tid (info));
+ mono_thread_info_finish_suspend_and_resume (info);
#ifndef HOST_WIN32
if (interrupt)
wapi_finish_interrupt_thread (interrupt_handle);
return;
}
- transition_to_suspended (thread);
+ transition_to_suspended (thread, NULL);
mono_thread_info_self_suspend ();
}
* 2fd16f60/r114307. So we collect them and join them when
* we have time (in he finalizer thread).
*/
- mono_threads_lock ();
+ joinable_threads_lock ();
if (!joinable_threads)
joinable_threads = g_hash_table_new (NULL, NULL);
g_hash_table_insert (joinable_threads, tid, tid);
joinable_thread_count ++;
- mono_threads_unlock ();
+ joinable_threads_unlock ();
mono_gc_finalize_notify ();
#endif
return;
while (TRUE) {
- mono_threads_lock ();
+ joinable_threads_lock ();
found = FALSE;
if (g_hash_table_size (joinable_threads)) {
g_hash_table_iter_init (&iter, joinable_threads);
joinable_thread_count --;
found = TRUE;
}
- mono_threads_unlock ();
+ joinable_threads_unlock ();
if (found) {
if (thread != pthread_self ())
/* This shouldn't block */
{
#ifndef HOST_WIN32
pthread_t thread;
+ gboolean found = FALSE;
- mono_threads_lock ();
+ joinable_threads_lock ();
if (!joinable_threads)
joinable_threads = g_hash_table_new (NULL, NULL);
if (g_hash_table_lookup (joinable_threads, tid)) {
g_hash_table_remove (joinable_threads, tid);
joinable_thread_count --;
+ found = TRUE;
}
- mono_threads_unlock ();
+ joinable_threads_unlock ();
+ if (!found)
+ return;
thread = (pthread_t)tid;
pthread_join (thread, NULL);
#endif