[win32] Fix some compilation warnings (#15956)
[mono-project.git] / mono / metadata / threads.c
blob1f11f490e720448448dc5d3542307089e128c250
1 /**
2 * \file
3 * Thread support internal calls
5 * Author:
6 * Dick Porter (dick@ximian.com)
7 * Paolo Molaro (lupus@ximian.com)
8 * Patrik Torstensson (patrik.torstensson@labs2.com)
10 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
11 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
12 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
13 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
16 #include <config.h>
18 #include <glib.h>
19 #include <string.h>
21 #include <mono/metadata/object.h>
22 #include <mono/metadata/domain-internals.h>
23 #include <mono/metadata/profiler-private.h>
24 #include <mono/metadata/threads.h>
25 #include <mono/metadata/threads-types.h>
26 #include <mono/metadata/exception.h>
27 #include <mono/metadata/environment.h>
28 #include <mono/metadata/monitor.h>
29 #include <mono/metadata/mono-hash-internals.h>
30 #include <mono/metadata/gc-internals.h>
31 #include <mono/metadata/marshal.h>
32 #include <mono/metadata/runtime.h>
33 #include <mono/metadata/object-internals.h>
34 #include <mono/metadata/debug-internals.h>
35 #include <mono/utils/monobitset.h>
36 #include <mono/utils/mono-compiler.h>
37 #include <mono/utils/mono-mmap.h>
38 #include <mono/utils/mono-membar.h>
39 #include <mono/utils/mono-time.h>
40 #include <mono/utils/mono-threads.h>
41 #include <mono/utils/mono-threads-coop.h>
42 #include <mono/utils/mono-tls.h>
43 #include <mono/utils/atomic.h>
44 #include <mono/utils/mono-memory-model.h>
45 #include <mono/utils/mono-error-internals.h>
46 #include <mono/utils/os-event.h>
47 #include <mono/utils/mono-threads-debug.h>
48 #include <mono/utils/unlocked.h>
49 #include <mono/metadata/w32handle.h>
50 #include <mono/metadata/w32event.h>
51 #include <mono/metadata/w32mutex.h>
53 #include <mono/metadata/reflection-internals.h>
54 #include <mono/metadata/abi-details.h>
55 #include <mono/metadata/w32error.h>
56 #include <mono/utils/w32api.h>
57 #include <mono/utils/mono-os-wait.h>
58 #include <mono/metadata/exception-internals.h>
59 #include <mono/utils/mono-state.h>
60 #include <mono/metadata/w32subset.h>
62 #ifdef HAVE_SYS_WAIT_H
63 #include <sys/wait.h>
64 #endif
66 #ifdef HAVE_SIGNAL_H
67 #include <signal.h>
68 #endif
70 #if defined(HOST_WIN32)
71 #include <objbase.h>
72 #include <sys/timeb.h>
73 extern gboolean
74 mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle);
75 #endif
77 #if defined(HOST_FUCHSIA)
78 #include <zircon/syscalls.h>
79 #endif
81 #if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
82 #define USE_TKILL_ON_ANDROID 1
83 #endif
85 #ifdef HOST_ANDROID
86 #include <errno.h>
88 #ifdef USE_TKILL_ON_ANDROID
89 extern int tkill (pid_t tid, int signal);
90 #endif
91 #endif
93 #include "icall-decl.h"
95 /*#define THREAD_DEBUG(a) do { a; } while (0)*/
96 #define THREAD_DEBUG(a)
97 /*#define THREAD_WAIT_DEBUG(a) do { a; } while (0)*/
98 #define THREAD_WAIT_DEBUG(a)
99 /*#define LIBGC_DEBUG(a) do { a; } while (0)*/
100 #define LIBGC_DEBUG(a)
102 #define SPIN_TRYLOCK(i) (mono_atomic_cas_i32 (&(i), 1, 0) == 0)
103 #define SPIN_LOCK(i) do { \
104 if (SPIN_TRYLOCK (i)) \
105 break; \
106 } while (1)
108 #define SPIN_UNLOCK(i) i = 0
110 #define LOCK_THREAD(thread) lock_thread((thread))
111 #define UNLOCK_THREAD(thread) unlock_thread((thread))
113 typedef union {
114 gint32 ival;
115 gfloat fval;
116 } IntFloatUnion;
118 typedef union {
119 gint64 ival;
120 gdouble fval;
121 } LongDoubleUnion;
123 typedef struct _StaticDataFreeList StaticDataFreeList;
124 struct _StaticDataFreeList {
125 StaticDataFreeList *next;
126 guint32 offset;
127 guint32 size;
130 typedef struct {
131 int idx;
132 int offset;
133 StaticDataFreeList *freelist;
134 } StaticDataInfo;
136 /* Controls access to the 'threads' hash table */
137 static void mono_threads_lock (void);
138 static void mono_threads_unlock (void);
139 static MonoCoopMutex threads_mutex;
141 /* Controls access to the 'joinable_threads' hash table */
142 #define joinable_threads_lock() mono_coop_mutex_lock (&joinable_threads_mutex)
143 #define joinable_threads_unlock() mono_coop_mutex_unlock (&joinable_threads_mutex)
144 static MonoCoopMutex joinable_threads_mutex;
146 /* Holds current status of static data heap */
147 static StaticDataInfo thread_static_info;
148 static StaticDataInfo context_static_info;
150 /* The hash of existing threads (key is thread ID, value is
151 * MonoInternalThread*) that need joining before exit
153 static MonoGHashTable *threads=NULL;
155 /* List of app context GC handles.
156 * Added to from mono_threads_register_app_context ().
158 static GHashTable *contexts = NULL;
160 /* Cleanup queue for contexts. */
161 static MonoReferenceQueue *context_queue;
164 * Threads which are starting up and they are not in the 'threads' hash yet.
165 * When mono_thread_attach_internal is called for a thread, it will be removed from this hash table.
166 * Protected by mono_threads_lock ().
168 static MonoGHashTable *threads_starting_up = NULL;
170 /* Contains tids */
171 /* Protected by the threads lock */
172 static GHashTable *joinable_threads;
173 static gint32 joinable_thread_count;
175 /* mono_threads_join_threads will take threads from joinable_threads list and wait for them. */
176 /* When this happens, the tid is not on the list anymore so mono_thread_join assumes the thread has complete */
177 /* and will return back to the caller. This could cause a race since caller of join assumes thread has completed */
178 /* and on some OS it could cause errors. Keeping the tid's currently pending a native thread join call */
179 /* in a separate table (only affecting callers interested in this internal join detail) and look at that table in mono_thread_join */
180 /* will close this race. */
181 static GHashTable *pending_native_thread_join_calls;
182 static MonoCoopCond pending_native_thread_join_calls_event;
184 static GHashTable *pending_joinable_threads;
185 static gint32 pending_joinable_thread_count;
187 static MonoCoopCond zero_pending_joinable_thread_event;
189 static void threads_add_pending_joinable_runtime_thread (MonoThreadInfo *mono_thread_info);
190 static gboolean threads_wait_pending_joinable_threads (uint32_t timeout);
191 static gchar* thread_dump_dir = NULL;
193 #define SET_CURRENT_OBJECT mono_tls_set_thread
194 #define GET_CURRENT_OBJECT mono_tls_get_thread
196 /* function called at thread start */
197 static MonoThreadStartCB mono_thread_start_cb = NULL;
199 /* function called at thread attach */
200 static MonoThreadAttachCB mono_thread_attach_cb = NULL;
202 /* function called at thread cleanup */
203 static MonoThreadCleanupFunc mono_thread_cleanup_fn = NULL;
205 /* The default stack size for each thread */
206 static guint32 default_stacksize = 0;
207 #define default_stacksize_for_thread(thread) ((thread)->stack_size? (thread)->stack_size: default_stacksize)
209 static void context_adjust_static_data (MonoAppContextHandle ctx);
210 static void mono_free_static_data (gpointer* static_data);
211 static void mono_init_static_data_info (StaticDataInfo *static_data);
212 static guint32 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align);
213 static gboolean mono_thread_resume (MonoInternalThread* thread);
214 static void async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort);
215 static void self_abort_internal (MonoError *error);
216 static void async_suspend_internal (MonoInternalThread *thread, gboolean interrupt);
217 static void self_suspend_internal (void);
219 static gboolean
220 mono_thread_set_interruption_requested_flags (MonoInternalThread *thread, gboolean sync);
222 MONO_COLD void
223 mono_set_pending_exception_handle (MonoExceptionHandle exc);
225 static MonoException*
226 mono_thread_execute_interruption_ptr (void);
228 static void
229 mono_thread_execute_interruption_void (void);
231 static gboolean
232 mono_thread_execute_interruption (MonoExceptionHandle *pexc);
234 static void ref_stack_destroy (gpointer rs);
236 #if SIZEOF_VOID_P == 4
237 /* Spin lock for unaligned InterlockedXXX 64 bit functions on 32bit platforms. */
238 #define mono_interlocked_lock() mono_os_mutex_lock (&interlocked_mutex)
239 #define mono_interlocked_unlock() mono_os_mutex_unlock (&interlocked_mutex)
240 static mono_mutex_t interlocked_mutex;
241 #endif
243 /* global count of thread interruptions requested */
244 static gint32 thread_interruption_requested = 0;
246 /* Event signaled when a thread changes its background mode */
247 static MonoOSEvent background_change_event;
249 static gboolean shutting_down = FALSE;
251 static gint32 managed_thread_id_counter = 0;
253 static void
254 mono_threads_lock (void)
256 mono_locks_coop_acquire (&threads_mutex, ThreadsLock);
259 static void
260 mono_threads_unlock (void)
262 mono_locks_coop_release (&threads_mutex, ThreadsLock);
266 static guint32
267 get_next_managed_thread_id (void)
269 return mono_atomic_inc_i32 (&managed_thread_id_counter);
273 * We separate interruptions/exceptions into either sync (they can be processed anytime,
274 * normally as soon as they are set, and are set by the same thread) and async (they can't
275 * be processed inside abort protected blocks and are normally set by other threads). We
276 * can have both a pending sync and async interruption. In this case, the sync exception is
277 * processed first. Since we clean sync flag first, mono_thread_execute_interruption must
278 * also handle all sync type exceptions before the async type exceptions.
280 enum {
281 INTERRUPT_SYNC_REQUESTED_BIT = 0x1,
282 INTERRUPT_ASYNC_REQUESTED_BIT = 0x2,
283 INTERRUPT_REQUESTED_MASK = 0x3,
284 ABORT_PROT_BLOCK_SHIFT = 2,
285 ABORT_PROT_BLOCK_BITS = 8,
286 ABORT_PROT_BLOCK_MASK = (((1 << ABORT_PROT_BLOCK_BITS) - 1) << ABORT_PROT_BLOCK_SHIFT)
289 static int
290 mono_thread_get_abort_prot_block_count (MonoInternalThread *thread)
292 gsize state = thread->thread_state;
293 return (state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT;
296 gboolean
297 mono_threads_is_current_thread_in_protected_block (void)
299 MonoInternalThread *thread = mono_thread_internal_current ();
301 return mono_thread_get_abort_prot_block_count (thread) > 0;
304 void
305 mono_threads_begin_abort_protected_block (void)
307 MonoInternalThread *thread = mono_thread_internal_current ();
308 gsize old_state, new_state;
309 int new_val;
310 do {
311 old_state = thread->thread_state;
313 new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) + 1;
314 //bounds check abort_prot_count
315 g_assert (new_val > 0);
316 g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
318 new_state = old_state + (1 << ABORT_PROT_BLOCK_SHIFT);
319 } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
321 /* Defer async request since we won't be able to process until exiting the block */
322 if (new_val == 1 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT)) {
323 mono_atomic_dec_i32 (&thread_interruption_requested);
324 THREADS_INTERRUPT_DEBUG ("[%d] begin abort protected block old_state %ld new_state %ld, defer tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
325 if (thread_interruption_requested < 0)
326 g_warning ("bad thread_interruption_requested state");
327 } else {
328 THREADS_INTERRUPT_DEBUG ("[%d] begin abort protected block old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
332 static gboolean
333 mono_thread_state_has_interruption (gsize state)
335 /* pending exception, self abort */
336 if (state & INTERRUPT_SYNC_REQUESTED_BIT)
337 return TRUE;
339 /* abort, interruption, suspend */
340 if ((state & INTERRUPT_ASYNC_REQUESTED_BIT) && !(state & ABORT_PROT_BLOCK_MASK))
341 return TRUE;
343 return FALSE;
346 gboolean
347 mono_threads_end_abort_protected_block (void)
349 MonoInternalThread *thread = mono_thread_internal_current ();
350 gsize old_state, new_state;
351 int new_val;
352 do {
353 old_state = thread->thread_state;
355 //bounds check abort_prot_count
356 new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) - 1;
357 g_assert (new_val >= 0);
358 g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
360 new_state = old_state - (1 << ABORT_PROT_BLOCK_SHIFT);
361 } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
363 if (new_val == 0 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT)) {
364 mono_atomic_inc_i32 (&thread_interruption_requested);
365 THREADS_INTERRUPT_DEBUG ("[%d] end abort protected block old_state %ld new_state %ld, restore tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
366 } else {
367 THREADS_INTERRUPT_DEBUG ("[%d] end abort protected block old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
370 return mono_thread_state_has_interruption (new_state);
373 static gboolean
374 mono_thread_get_interruption_requested (MonoInternalThread *thread)
376 gsize state = thread->thread_state;
378 return mono_thread_state_has_interruption (state);
382 * Returns TRUE is there was a state change
383 * We clear a single interruption request, sync has priority.
385 static gboolean
386 mono_thread_clear_interruption_requested (MonoInternalThread *thread)
388 gsize old_state, new_state;
389 do {
390 old_state = thread->thread_state;
392 // no interruption to process
393 if (!(old_state & INTERRUPT_SYNC_REQUESTED_BIT) &&
394 (!(old_state & INTERRUPT_ASYNC_REQUESTED_BIT) || (old_state & ABORT_PROT_BLOCK_MASK)))
395 return FALSE;
397 if (old_state & INTERRUPT_SYNC_REQUESTED_BIT)
398 new_state = old_state & ~INTERRUPT_SYNC_REQUESTED_BIT;
399 else
400 new_state = old_state & ~INTERRUPT_ASYNC_REQUESTED_BIT;
401 } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
403 mono_atomic_dec_i32 (&thread_interruption_requested);
404 THREADS_INTERRUPT_DEBUG ("[%d] clear interruption old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
405 if (thread_interruption_requested < 0)
406 g_warning ("bad thread_interruption_requested state");
407 return TRUE;
410 static gboolean
411 mono_thread_clear_interruption_requested_handle (MonoInternalThreadHandle thread)
413 // Internal threads are pinned so shallow coop/handle.
414 return mono_thread_clear_interruption_requested (mono_internal_thread_handle_ptr (thread));
417 /* Returns TRUE is there was a state change and the interruption can be processed */
418 static gboolean
419 mono_thread_set_interruption_requested (MonoInternalThread *thread)
421 //always force when the current thread is doing it to itself.
422 gboolean sync = thread == mono_thread_internal_current ();
423 /* Normally synchronous interruptions can bypass abort protection. */
424 return mono_thread_set_interruption_requested_flags (thread, sync);
427 /* Returns TRUE if there was a state change and the interruption can be
428 * processed. This variant defers a self abort when inside an abort protected
429 * block. Normally this should only be done when a thread has received an
430 * outside indication that it should abort. (For example when the JIT sets a
431 * flag in an finally block.)
434 static gboolean
435 mono_thread_set_self_interruption_respect_abort_prot (void)
437 MonoInternalThread *thread = mono_thread_internal_current ();
438 /* N.B. Sets the ASYNC_REQUESTED_BIT for current this thread,
439 * which is unusual. */
440 return mono_thread_set_interruption_requested_flags (thread, FALSE);
443 /* Returns TRUE if there was a state change and the interruption can be processed. */
444 static gboolean
445 mono_thread_set_interruption_requested_flags (MonoInternalThread *thread, gboolean sync)
447 gsize old_state, new_state;
448 do {
449 old_state = thread->thread_state;
451 //Already set
452 if ((sync && (old_state & INTERRUPT_SYNC_REQUESTED_BIT)) ||
453 (!sync && (old_state & INTERRUPT_ASYNC_REQUESTED_BIT)))
454 return FALSE;
456 if (sync)
457 new_state = old_state | INTERRUPT_SYNC_REQUESTED_BIT;
458 else
459 new_state = old_state | INTERRUPT_ASYNC_REQUESTED_BIT;
460 } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
462 if (sync || !(new_state & ABORT_PROT_BLOCK_MASK)) {
463 mono_atomic_inc_i32 (&thread_interruption_requested);
464 THREADS_INTERRUPT_DEBUG ("[%d] set interruption on [%d] old_state %ld new_state %ld, tir %d\n", mono_thread_internal_current ()->small_id, thread->small_id, old_state, new_state, thread_interruption_requested);
465 } else {
466 THREADS_INTERRUPT_DEBUG ("[%d] set interruption on [%d] old_state %ld new_state %ld, tir deferred %d\n", mono_thread_internal_current ()->small_id, thread->small_id, old_state, new_state, thread_interruption_requested);
469 return sync || !(new_state & ABORT_PROT_BLOCK_MASK);
472 static inline MonoNativeThreadId
473 thread_get_tid (MonoInternalThread *thread)
475 /* We store the tid as a guint64 to keep the object layout constant between platforms */
476 return MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid);
479 static void
480 free_synch_cs (MonoCoopMutex *synch_cs)
482 g_assert (synch_cs);
483 mono_coop_mutex_destroy (synch_cs);
484 g_free (synch_cs);
487 static void
488 free_longlived_thread_data (void *user_data)
490 MonoLongLivedThreadData *lltd = (MonoLongLivedThreadData*)user_data;
491 free_synch_cs (lltd->synch_cs);
493 g_free (lltd);
496 static void
497 init_longlived_thread_data (MonoLongLivedThreadData *lltd)
499 mono_refcount_init (lltd, free_longlived_thread_data);
500 mono_refcount_inc (lltd);
501 /* Initial refcount is 2: decremented once by
502 * mono_thread_detach_internal and once by the MonoInternalThread
503 * finalizer - whichever one happens later will deallocate. */
505 lltd->synch_cs = g_new0 (MonoCoopMutex, 1);
506 mono_coop_mutex_init_recursive (lltd->synch_cs);
508 mono_memory_barrier ();
511 static void
512 dec_longlived_thread_data (MonoLongLivedThreadData *lltd)
514 mono_refcount_dec (lltd);
517 static inline void
518 lock_thread (MonoInternalThread *thread)
520 g_assert (thread->longlived);
521 g_assert (thread->longlived->synch_cs);
523 mono_coop_mutex_lock (thread->longlived->synch_cs);
526 static inline void
527 unlock_thread (MonoInternalThread *thread)
529 mono_coop_mutex_unlock (thread->longlived->synch_cs);
532 static void
533 lock_thread_handle (MonoInternalThreadHandle thread)
535 lock_thread (mono_internal_thread_handle_ptr (thread));
538 static void
539 unlock_thread_handle (MonoInternalThreadHandle thread)
541 unlock_thread (mono_internal_thread_handle_ptr (thread));
544 static inline gboolean
545 is_appdomainunloaded_exception (MonoClass *klass)
547 #ifdef ENABLE_NETCORE
548 return FALSE;
549 #else
550 return klass == mono_class_get_appdomain_unloaded_exception_class ();
551 #endif
554 static inline gboolean
555 is_threadabort_exception (MonoClass *klass)
557 return klass == mono_defaults.threadabortexception_class;
561 * A special static data offset (guint32) consists of 3 parts:
563 * [0] 6-bit index into the array of chunks.
564 * [6] 25-bit offset into the array.
565 * [31] Bit indicating thread or context static.
568 typedef union {
569 struct {
570 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
571 guint32 type : 1;
572 guint32 offset : 25;
573 guint32 index : 6;
574 #else
575 guint32 index : 6;
576 guint32 offset : 25;
577 guint32 type : 1;
578 #endif
579 } fields;
580 guint32 raw;
581 } SpecialStaticOffset;
583 #define SPECIAL_STATIC_OFFSET_TYPE_THREAD 0
584 #define SPECIAL_STATIC_OFFSET_TYPE_CONTEXT 1
586 static guint32
587 MAKE_SPECIAL_STATIC_OFFSET (guint32 index, guint32 offset, guint32 type)
589 SpecialStaticOffset special_static_offset;
590 memset (&special_static_offset, 0, sizeof (special_static_offset));
591 special_static_offset.fields.index = index;
592 special_static_offset.fields.offset = offset;
593 special_static_offset.fields.type = type;
594 return special_static_offset.raw;
597 #define ACCESS_SPECIAL_STATIC_OFFSET(x,f) \
598 (((SpecialStaticOffset *) &(x))->fields.f)
600 static gpointer
601 get_thread_static_data (MonoInternalThread *thread, guint32 offset)
603 g_assert (ACCESS_SPECIAL_STATIC_OFFSET (offset, type) == SPECIAL_STATIC_OFFSET_TYPE_THREAD);
605 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
606 int off = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
608 return ((char *) thread->static_data [idx]) + off;
611 static gpointer
612 get_context_static_data (MonoAppContext *ctx, guint32 offset)
614 g_assert (ACCESS_SPECIAL_STATIC_OFFSET (offset, type) == SPECIAL_STATIC_OFFSET_TYPE_CONTEXT);
616 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
617 int off = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
619 return ((char *) ctx->static_data [idx]) + off;
622 static MonoThread**
623 get_current_thread_ptr_for_domain (MonoDomain *domain, MonoInternalThread *thread)
625 static MonoClassField *current_thread_field = NULL;
627 guint32 offset;
629 if (!current_thread_field) {
630 current_thread_field = mono_class_get_field_from_name_full (mono_defaults.thread_class, "current_thread", NULL);
631 g_assert (current_thread_field);
634 ERROR_DECL (thread_vt_error);
635 mono_class_vtable_checked (domain, mono_defaults.thread_class, thread_vt_error);
636 mono_error_assert_ok (thread_vt_error);
637 mono_domain_lock (domain);
638 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, current_thread_field));
639 mono_domain_unlock (domain);
640 g_assert (offset);
642 return (MonoThread **)get_thread_static_data (thread, offset);
645 static void
646 set_current_thread_for_domain (MonoDomain *domain, MonoInternalThread *thread, MonoThread *current)
648 #ifndef ENABLE_NETCORE
649 MonoThread **current_thread_ptr = get_current_thread_ptr_for_domain (domain, thread);
651 g_assert (current->obj.vtable->domain == domain);
653 g_assert (!*current_thread_ptr);
654 *current_thread_ptr = current;
655 #endif
658 static MonoThread*
659 create_thread_object (MonoDomain *domain, MonoInternalThread *internal)
661 #ifdef ENABLE_NETCORE
662 MONO_OBJECT_SETREF_INTERNAL (internal, internal_thread, internal);
663 return internal;
664 #else
665 MonoThread *thread;
666 MonoVTable *vtable;
667 ERROR_DECL (error);
669 vtable = mono_class_vtable_checked (domain, mono_defaults.thread_class, error);
670 mono_error_assert_ok (error);
672 thread = (MonoThread*)mono_object_new_mature (vtable, error);
673 /* only possible failure mode is OOM, from which we don't expect to recover. */
674 mono_error_assert_ok (error);
676 MONO_OBJECT_SETREF_INTERNAL (thread, internal_thread, internal);
678 return thread;
679 #endif
682 static void
683 init_internal_thread_object (MonoInternalThread *thread)
685 thread->longlived = g_new0 (MonoLongLivedThreadData, 1);
686 init_longlived_thread_data (thread->longlived);
688 thread->apartment_state = ThreadApartmentState_Unknown;
689 thread->managed_id = get_next_managed_thread_id ();
690 if (mono_gc_is_moving ()) {
691 thread->thread_pinning_ref = thread;
692 MONO_GC_REGISTER_ROOT_PINNING (thread->thread_pinning_ref, MONO_ROOT_SOURCE_THREADING, NULL, "Thread Pinning Reference");
695 thread->priority = MONO_THREAD_PRIORITY_NORMAL;
697 thread->suspended = g_new0 (MonoOSEvent, 1);
698 mono_os_event_init (thread->suspended, TRUE);
701 static MonoInternalThread*
702 create_internal_thread_object (void)
704 ERROR_DECL (error);
705 MonoInternalThread *thread;
706 MonoVTable *vt;
708 vt = mono_class_vtable_checked (mono_get_root_domain (), mono_defaults.internal_thread_class, error);
709 mono_error_assert_ok (error);
710 thread = (MonoInternalThread*) mono_object_new_mature (vt, error);
711 /* only possible failure mode is OOM, from which we don't exect to recover */
712 mono_error_assert_ok (error);
714 init_internal_thread_object (thread);
716 return thread;
719 static void
720 mono_thread_internal_set_priority (MonoInternalThread *internal, MonoThreadPriority priority)
722 g_assert (internal);
724 g_assert (priority >= MONO_THREAD_PRIORITY_LOWEST);
725 g_assert (priority <= MONO_THREAD_PRIORITY_HIGHEST);
726 g_assert (MONO_THREAD_PRIORITY_LOWEST < MONO_THREAD_PRIORITY_HIGHEST);
728 #ifdef HOST_WIN32
729 BOOL res;
730 DWORD last_error;
732 g_assert (internal->native_handle);
734 MONO_ENTER_GC_SAFE;
735 res = SetThreadPriority (internal->native_handle, (int)priority - 2);
736 last_error = GetLastError ();
737 MONO_EXIT_GC_SAFE;
738 if (!res)
739 g_error ("%s: SetThreadPriority failed, error %d", __func__, last_error);
740 #elif defined(HOST_FUCHSIA)
741 int z_priority;
743 if (priority == MONO_THREAD_PRIORITY_LOWEST)
744 z_priority = ZX_PRIORITY_LOWEST;
745 else if (priority == MONO_THREAD_PRIORITY_BELOW_NORMAL)
746 z_priority = ZX_PRIORITY_LOW;
747 else if (priority == MONO_THREAD_PRIORITY_NORMAL)
748 z_priority = ZX_PRIORITY_DEFAULT;
749 else if (priority == MONO_THREAD_PRIORITY_ABOVE_NORMAL)
750 z_priority = ZX_PRIORITY_HIGH;
751 else if (priority == MONO_THREAD_PRIORITY_HIGHEST)
752 z_priority = ZX_PRIORITY_HIGHEST;
753 else
754 return;
757 // When this API becomes available on an arbitrary thread, we can use it,
758 // not available on current Zircon
760 #else /* !HOST_WIN32 and not HOST_FUCHSIA */
761 pthread_t tid;
762 int policy;
763 struct sched_param param;
764 gint res;
766 tid = thread_get_tid (internal);
768 MONO_ENTER_GC_SAFE;
769 res = pthread_getschedparam (tid, &policy, &param);
770 MONO_EXIT_GC_SAFE;
771 if (res != 0)
772 g_error ("%s: pthread_getschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
774 #ifdef _POSIX_PRIORITY_SCHEDULING
775 int max, min;
777 /* Necessary to get valid priority range */
779 MONO_ENTER_GC_SAFE;
780 #if defined(__PASE__)
781 /* only priorities allowed by IBM i */
782 min = PRIORITY_MIN;
783 max = PRIORITY_MAX;
784 #else
785 min = sched_get_priority_min (policy);
786 max = sched_get_priority_max (policy);
787 #endif
788 MONO_EXIT_GC_SAFE;
790 if (max > 0 && min >= 0 && max > min) {
791 double srange, drange, sposition, dposition;
792 srange = MONO_THREAD_PRIORITY_HIGHEST - MONO_THREAD_PRIORITY_LOWEST;
793 drange = max - min;
794 sposition = priority - MONO_THREAD_PRIORITY_LOWEST;
795 dposition = (sposition / srange) * drange;
796 param.sched_priority = (int)(dposition + min);
797 } else
798 #endif
800 switch (policy) {
801 case SCHED_FIFO:
802 case SCHED_RR:
803 param.sched_priority = 50;
804 break;
805 #ifdef SCHED_BATCH
806 case SCHED_BATCH:
807 #endif
808 case SCHED_OTHER:
809 param.sched_priority = 0;
810 break;
811 default:
812 g_warning ("%s: unknown policy %d", __func__, policy);
813 return;
817 MONO_ENTER_GC_SAFE;
818 #if defined(__PASE__)
819 /* only scheduling param allowed by IBM i */
820 res = pthread_setschedparam (tid, SCHED_OTHER, &param);
821 #else
822 res = pthread_setschedparam (tid, policy, &param);
823 #endif
824 MONO_EXIT_GC_SAFE;
825 if (res != 0) {
826 if (res == EPERM) {
827 #if !defined(_AIX)
828 /* AIX doesn't like doing this and will spam this every time;
829 * weirdly, i doesn't complain
831 g_warning ("%s: pthread_setschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
832 #endif
833 return;
835 g_error ("%s: pthread_setschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
837 #endif /* HOST_WIN32 */
840 static void
841 mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, void *alloc_key, gboolean threadlocal);
843 static gboolean
844 mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean force_domain)
846 MonoThreadInfo *info;
847 MonoInternalThread *internal;
848 MonoDomain *domain, *root_domain;
849 guint32 gchandle;
851 g_assert (thread);
853 info = mono_thread_info_current ();
854 g_assert (info);
856 internal = thread->internal_thread;
857 g_assert (internal);
859 /* It is needed to store the MonoInternalThread on the MonoThreadInfo, because of the following case:
860 * - the MonoInternalThread TLS key is destroyed: set it to NULL
861 * - the MonoThreadInfo TLS key is destroyed: calls mono_thread_info_detach
862 * - it calls MonoThreadInfoCallbacks.thread_detach
863 * - mono_thread_internal_current returns NULL -> fails to detach the MonoInternalThread. */
864 mono_thread_info_set_internal_thread_gchandle (info, mono_gchandle_new_internal ((MonoObject*) internal, FALSE));
866 internal->handle = mono_threads_open_thread_handle (info->handle);
867 internal->native_handle = MONO_NATIVE_THREAD_HANDLE_TO_GPOINTER (mono_threads_open_native_thread_handle (info->native_handle));
868 internal->tid = MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ());
869 internal->thread_info = info;
870 internal->small_id = info->small_id;
872 THREAD_DEBUG (g_message ("%s: (%" G_GSIZE_FORMAT ") Setting current_object_key to %p", __func__, mono_native_thread_id_get (), internal));
874 SET_CURRENT_OBJECT (internal);
876 domain = mono_object_domain (thread);
878 mono_thread_push_appdomain_ref (domain);
879 if (!mono_domain_set_fast (domain, force_domain)) {
880 mono_thread_pop_appdomain_ref ();
881 goto fail;
884 mono_threads_lock ();
886 if (shutting_down && !force_attach) {
887 mono_threads_unlock ();
888 mono_thread_pop_appdomain_ref ();
889 goto fail;
892 if (threads_starting_up)
893 mono_g_hash_table_remove (threads_starting_up, thread);
895 if (!threads) {
896 threads = mono_g_hash_table_new_type_internal (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_THREADING, NULL, "Thread Table");
899 /* We don't need to duplicate thread->handle, because it is
900 * only closed when the thread object is finalized by the GC. */
901 mono_g_hash_table_insert_internal (threads, (gpointer)(gsize)(internal->tid), internal);
903 /* We have to do this here because mono_thread_start_cb
904 * requires that root_domain_thread is set up. */
905 if (thread_static_info.offset || thread_static_info.idx > 0) {
906 /* get the current allocated size */
907 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (thread_static_info.idx, thread_static_info.offset, 0);
908 mono_alloc_static_data (&internal->static_data, offset, (void *) MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid), TRUE);
911 mono_threads_unlock ();
913 root_domain = mono_get_root_domain ();
915 g_assert (!internal->root_domain_thread);
916 if (domain != root_domain)
917 MONO_OBJECT_SETREF_INTERNAL (internal, root_domain_thread, create_thread_object (root_domain, internal));
918 else
919 MONO_OBJECT_SETREF_INTERNAL (internal, root_domain_thread, thread);
921 if (domain != root_domain)
922 set_current_thread_for_domain (root_domain, internal, internal->root_domain_thread);
924 set_current_thread_for_domain (domain, internal, thread);
926 THREAD_DEBUG (g_message ("%s: Attached thread ID %" G_GSIZE_FORMAT " (handle %p)", __func__, internal->tid, internal->handle));
928 return TRUE;
930 fail:
931 mono_threads_lock ();
932 if (threads_starting_up)
933 mono_g_hash_table_remove (threads_starting_up, thread);
934 mono_threads_unlock ();
936 if (!mono_thread_info_try_get_internal_thread_gchandle (info, &gchandle))
937 g_error ("%s: failed to get gchandle, info %p", __func__, info);
939 mono_gchandle_free_internal (gchandle);
941 mono_thread_info_unset_internal_thread_gchandle (info);
943 SET_CURRENT_OBJECT(NULL);
945 return FALSE;
948 static void
949 mono_thread_detach_internal (MonoInternalThread *thread)
951 MonoThreadInfo *info;
952 MonoInternalThread *value;
953 gboolean removed;
954 guint32 gchandle;
956 g_assert (mono_thread_internal_is_current (thread));
958 g_assert (thread != NULL);
959 SET_CURRENT_OBJECT (thread);
961 info = thread->thread_info;
962 g_assert (info);
964 THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%" G_GSIZE_FORMAT ")", __func__, thread, (gsize)thread->tid));
966 MONO_PROFILER_RAISE (thread_stopping, (thread->tid));
969 * Prevent race condition between thread shutdown and runtime shutdown.
970 * Including all runtime threads in the pending joinable count will make
971 * sure shutdown will wait for it to get onto the joinable thread list before
972 * critical resources have been cleanup (like GC memory). Threads getting onto
973 * the joinable thread list should just about to exit and not blocking a potential
974 * join call. Owner of threads attached to the runtime but not identified as runtime
975 * threads needs to make sure thread detach calls won't race with runtime shutdown.
977 threads_add_pending_joinable_runtime_thread (info);
979 #ifndef HOST_WIN32
980 mono_w32mutex_abandon (thread);
981 #endif
983 mono_gchandle_free_internal (thread->abort_state_handle);
984 thread->abort_state_handle = 0;
986 thread->abort_exc = NULL;
987 thread->current_appcontext = NULL;
989 LOCK_THREAD (thread);
991 thread->state |= ThreadState_Stopped;
992 thread->state &= ~ThreadState_Background;
994 UNLOCK_THREAD (thread);
997 An interruption request has leaked to cleanup. Adjust the global counter.
999 This can happen is the abort source thread finds the abortee (this) thread
1000 in unmanaged code. If this thread never trips back to managed code or check
1001 the local flag it will be left set and positively unbalance the global counter.
1003 Leaving the counter unbalanced will cause a performance degradation since all threads
1004 will now keep checking their local flags all the time.
1006 mono_thread_clear_interruption_requested (thread);
1008 mono_threads_lock ();
1010 g_assert (threads);
1012 if (!mono_g_hash_table_lookup_extended (threads, (gpointer)thread->tid, NULL, (gpointer*) &value)) {
1013 g_error ("%s: thread %p (tid: %p) should not have been removed yet from threads", __func__, thread, thread->tid);
1014 } else if (thread != value) {
1015 /* We have to check whether the thread object for the tid is still the same in the table because the
1016 * thread might have been destroyed and the tid reused in the meantime, in which case the tid would be in
1017 * the table, but with another thread object. */
1018 g_error ("%s: thread %p (tid: %p) do not match with value %p (tid: %p)", __func__, thread, thread->tid, value, value->tid);
1021 removed = mono_g_hash_table_remove (threads, (gpointer)thread->tid);
1022 g_assert (removed);
1024 mono_threads_unlock ();
1026 /* Don't close the handle here, wait for the object finalizer
1027 * to do it. Otherwise, the following race condition applies:
1029 * 1) Thread exits (and mono_thread_detach_internal() closes the handle)
1031 * 2) Some other handle is reassigned the same slot
1033 * 3) Another thread tries to join the first thread, and
1034 * blocks waiting for the reassigned handle to be signalled
1035 * (which might never happen). This is possible, because the
1036 * thread calling Join() still has a reference to the first
1037 * thread's object.
1040 mono_release_type_locks (thread);
1042 MONO_PROFILER_RAISE (thread_stopped, (thread->tid));
1043 MONO_PROFILER_RAISE (gc_root_unregister, ((const mono_byte*)(info->stack_start_limit)));
1044 MONO_PROFILER_RAISE (gc_root_unregister, ((const mono_byte*)(info->handle_stack)));
1047 * This will signal async signal handlers that the thread has exited.
1048 * The profiler callback needs this to be set, so it cannot be done earlier.
1050 mono_domain_unset ();
1051 mono_memory_barrier ();
1053 mono_thread_pop_appdomain_ref ();
1055 mono_free_static_data (thread->static_data);
1056 thread->static_data = NULL;
1057 ref_stack_destroy (thread->appdomain_refs);
1058 thread->appdomain_refs = NULL;
1060 g_assert (thread->suspended);
1061 mono_os_event_destroy (thread->suspended);
1062 g_free (thread->suspended);
1063 thread->suspended = NULL;
1065 if (mono_thread_cleanup_fn)
1066 mono_thread_cleanup_fn (thread_get_tid (thread));
1068 mono_memory_barrier ();
1070 if (mono_gc_is_moving ()) {
1071 MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref);
1072 thread->thread_pinning_ref = NULL;
1075 /* There is no more any guarantee that `thread` is alive */
1076 mono_memory_barrier ();
1078 SET_CURRENT_OBJECT (NULL);
1079 mono_domain_unset ();
1081 if (!mono_thread_info_try_get_internal_thread_gchandle (info, &gchandle))
1082 g_error ("%s: failed to get gchandle, info = %p", __func__, info);
1084 mono_gchandle_free_internal (gchandle);
1086 mono_thread_info_unset_internal_thread_gchandle (info);
1088 /* Possibly free synch_cs, if the finalizer for InternalThread already
1089 * ran also. */
1090 dec_longlived_thread_data (thread->longlived);
1092 MONO_PROFILER_RAISE (thread_exited, (thread->tid));
1094 /* Don't need to close the handle to this thread, even though we took a
1095 * reference in mono_thread_attach (), because the GC will do it
1096 * when the Thread object is finalised.
1100 typedef struct {
1101 gint32 ref;
1102 MonoThread *thread;
1103 MonoObject *start_delegate;
1104 MonoObject *start_delegate_arg;
1105 MonoThreadStart start_func;
1106 gpointer start_func_arg;
1107 gboolean force_attach;
1108 gboolean failed;
1109 MonoCoopSem registered;
1110 } StartInfo;
1112 static void
1113 fire_attach_profiler_events (MonoNativeThreadId tid)
1115 MONO_PROFILER_RAISE (thread_started, ((uintptr_t) tid));
1117 MonoThreadInfo *info = mono_thread_info_current ();
1119 MONO_PROFILER_RAISE (gc_root_register, (
1120 (const mono_byte*)(info->stack_start_limit),
1121 (char *) info->stack_end - (char *) info->stack_start_limit,
1122 MONO_ROOT_SOURCE_STACK,
1123 (void *) tid,
1124 "Thread Stack"));
1126 // The handle stack is a pseudo-root similar to the finalizer queues.
1127 MONO_PROFILER_RAISE (gc_root_register, (
1128 (const mono_byte*)info->handle_stack,
1130 MONO_ROOT_SOURCE_HANDLE,
1131 (void *) tid,
1132 "Handle Stack"));
1135 static guint32 WINAPI
1136 start_wrapper_internal (StartInfo *start_info, gsize *stack_ptr)
1138 ERROR_DECL (error);
1139 MonoThreadStart start_func;
1140 void *start_func_arg;
1141 gsize tid;
1143 * We don't create a local to hold start_info->thread, so hopefully it won't get pinned during a
1144 * GC stack walk.
1146 MonoThread *thread;
1147 MonoInternalThread *internal;
1148 MonoObject *start_delegate;
1149 MonoObject *start_delegate_arg;
1151 thread = start_info->thread;
1152 internal = thread->internal_thread;
1154 THREAD_DEBUG (g_message ("%s: (%" G_GSIZE_FORMAT ") Start wrapper", __func__, mono_native_thread_id_get ()));
1156 if (!mono_thread_attach_internal (thread, start_info->force_attach, FALSE)) {
1157 start_info->failed = TRUE;
1159 mono_coop_sem_post (&start_info->registered);
1161 if (mono_atomic_dec_i32 (&start_info->ref) == 0) {
1162 mono_coop_sem_destroy (&start_info->registered);
1163 g_free (start_info);
1166 return 0;
1169 mono_thread_internal_set_priority (internal, (MonoThreadPriority)internal->priority);
1171 tid = internal->tid;
1173 start_delegate = start_info->start_delegate;
1174 start_delegate_arg = start_info->start_delegate_arg;
1175 start_func = start_info->start_func;
1176 start_func_arg = start_info->start_func_arg;
1178 /* This MUST be called before any managed code can be
1179 * executed, as it calls the callback function that (for the
1180 * jit) sets the lmf marker.
1183 if (mono_thread_start_cb)
1184 mono_thread_start_cb (tid, stack_ptr, (gpointer)start_func);
1186 /* On 2.0 profile (and higher), set explicitly since state might have been
1187 Unknown */
1188 if (internal->apartment_state == ThreadApartmentState_Unknown)
1189 internal->apartment_state = ThreadApartmentState_MTA;
1191 mono_thread_init_apartment_state ();
1193 /* Let the thread that called Start() know we're ready */
1194 mono_coop_sem_post (&start_info->registered);
1196 if (mono_atomic_dec_i32 (&start_info->ref) == 0) {
1197 mono_coop_sem_destroy (&start_info->registered);
1198 g_free (start_info);
1201 /* start_info is not valid anymore */
1202 start_info = NULL;
1205 * Call this after calling start_notify, since the profiler callback might want
1206 * to lock the thread, and the lock is held by thread_start () which waits for
1207 * start_notify.
1209 fire_attach_profiler_events ((MonoNativeThreadId) tid);
1211 /* if the name was set before starting, we didn't invoke the profiler callback */
1212 if (internal->name) {
1213 char *tname = g_utf16_to_utf8 (internal->name, internal->name_len, NULL, NULL, NULL);
1214 MONO_PROFILER_RAISE (thread_name, (internal->tid, tname));
1215 mono_native_thread_set_name (MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid), tname);
1216 g_free (tname);
1219 /* start_func is set only for unmanaged start functions */
1220 if (start_func) {
1221 start_func (start_func_arg);
1222 } else {
1223 #ifdef ENABLE_NETCORE
1224 static MonoMethod *cb;
1226 /* Call a callback in the RuntimeThread class */
1227 g_assert (start_delegate == NULL);
1228 if (!cb) {
1229 cb = mono_class_get_method_from_name_checked (internal->obj.vtable->klass, "StartCallback", 0, 0, error);
1230 g_assert (cb);
1231 mono_error_assert_ok (error);
1233 mono_runtime_invoke_checked (cb, internal, NULL, error);
1234 #else
1235 void *args [1];
1237 g_assert (start_delegate != NULL);
1239 /* we may want to handle the exception here. See comment below on unhandled exceptions */
1240 args [0] = (gpointer) start_delegate_arg;
1241 mono_runtime_delegate_invoke_checked (start_delegate, args, error);
1242 #endif
1244 if (!mono_error_ok (error)) {
1245 MonoException *ex = mono_error_convert_to_exception (error);
1247 g_assert (ex != NULL);
1248 MonoClass *klass = mono_object_class (ex);
1249 if ((mono_runtime_unhandled_exception_policy_get () != MONO_UNHANDLED_POLICY_LEGACY) &&
1250 !is_threadabort_exception (klass)) {
1251 mono_unhandled_exception_internal (&ex->object);
1252 mono_invoke_unhandled_exception_hook (&ex->object);
1253 g_assert_not_reached ();
1255 } else {
1256 mono_error_cleanup (error);
1260 /* If the thread calls ExitThread at all, this remaining code
1261 * will not be executed, but the main thread will eventually
1262 * call mono_thread_detach_internal() on this thread's behalf.
1265 THREAD_DEBUG (g_message ("%s: (%" G_GSIZE_FORMAT ") Start wrapper terminating", __func__, mono_native_thread_id_get ()));
1267 /* Do any cleanup needed for apartment state. This
1268 * cannot be done in mono_thread_detach_internal since
1269 * mono_thread_detach_internal could be called for a
1270 * thread other than the current thread.
1271 * mono_thread_cleanup_apartment_state cleans up apartment
1272 * for the current thead */
1273 mono_thread_cleanup_apartment_state ();
1275 mono_thread_detach_internal (internal);
1277 return 0;
1280 static mono_thread_start_return_t WINAPI
1281 start_wrapper (gpointer data)
1283 StartInfo *start_info;
1284 MonoThreadInfo *info;
1285 gsize res;
1287 start_info = (StartInfo*) data;
1288 g_assert (start_info);
1290 info = mono_thread_info_attach ();
1291 info->runtime_thread = TRUE;
1293 /* Run the actual main function of the thread */
1294 res = start_wrapper_internal (start_info, (gsize*)info->stack_end);
1296 mono_thread_info_exit (res);
1298 g_assert_not_reached ();
1302 * create_thread:
1304 * Common thread creation code.
1305 * LOCKING: Acquires the threads lock.
1307 static gboolean
1308 create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *start_delegate, MonoThreadStart start_func, gpointer start_func_arg,
1309 MonoThreadCreateFlags flags, MonoError *error)
1311 StartInfo *start_info = NULL;
1312 MonoNativeThreadId tid;
1313 gboolean ret;
1314 gsize stack_set_size;
1316 if (start_delegate)
1317 g_assert (!start_func && !start_func_arg);
1318 if (start_func)
1319 g_assert (!start_delegate);
1321 if (flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL) {
1322 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER));
1323 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE));
1325 if (flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER) {
1326 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL));
1327 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE));
1331 * Join joinable threads to prevent running out of threads since the finalizer
1332 * thread might be blocked/backlogged.
1334 mono_threads_join_threads ();
1336 error_init (error);
1338 mono_threads_lock ();
1339 if (shutting_down && !(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE)) {
1340 mono_threads_unlock ();
1341 mono_error_set_execution_engine (error, "Couldn't create thread. Runtime is shutting down.");
1342 return FALSE;
1344 if (threads_starting_up == NULL) {
1345 threads_starting_up = mono_g_hash_table_new_type_internal (NULL, NULL, MONO_HASH_KEY_VALUE_GC, MONO_ROOT_SOURCE_THREADING, NULL, "Thread Starting Table");
1347 mono_g_hash_table_insert_internal (threads_starting_up, thread, thread);
1348 mono_threads_unlock ();
1350 internal->threadpool_thread = flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL;
1351 if (internal->threadpool_thread)
1352 mono_thread_set_state (internal, ThreadState_Background);
1354 internal->debugger_thread = flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER;
1356 start_info = g_new0 (StartInfo, 1);
1357 start_info->ref = 2;
1358 start_info->thread = thread;
1359 start_info->start_delegate = start_delegate;
1360 start_info->start_delegate_arg = thread->start_obj;
1361 start_info->start_func = start_func;
1362 start_info->start_func_arg = start_func_arg;
1363 start_info->force_attach = flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE;
1364 start_info->failed = FALSE;
1365 mono_coop_sem_init (&start_info->registered, 0);
1367 if (flags != MONO_THREAD_CREATE_FLAGS_SMALL_STACK)
1368 stack_set_size = default_stacksize_for_thread (internal);
1369 else
1370 stack_set_size = 0;
1372 if (!mono_thread_platform_create_thread (start_wrapper, start_info, &stack_set_size, &tid)) {
1373 /* The thread couldn't be created, so set an exception */
1374 mono_threads_lock ();
1375 mono_g_hash_table_remove (threads_starting_up, thread);
1376 mono_threads_unlock ();
1377 mono_error_set_execution_engine (error, "Couldn't create thread. Error 0x%x", mono_w32error_get_last());
1378 /* ref is not going to be decremented in start_wrapper_internal */
1379 mono_atomic_dec_i32 (&start_info->ref);
1380 ret = FALSE;
1381 goto done;
1384 internal->stack_size = (int) stack_set_size;
1386 THREAD_DEBUG (g_message ("%s: (%" G_GSIZE_FORMAT ") Launching thread %p (%" G_GSIZE_FORMAT ")", __func__, mono_native_thread_id_get (), internal, (gsize)internal->tid));
1389 * Wait for the thread to set up its TLS data etc, so
1390 * theres no potential race condition if someone tries
1391 * to look up the data believing the thread has
1392 * started
1395 mono_coop_sem_wait (&start_info->registered, MONO_SEM_FLAGS_NONE);
1397 THREAD_DEBUG (g_message ("%s: (%" G_GSIZE_FORMAT ") Done launching thread %p (%" G_GSIZE_FORMAT ")", __func__, mono_native_thread_id_get (), internal, (gsize)internal->tid));
1399 ret = !start_info->failed;
1401 done:
1402 if (mono_atomic_dec_i32 (&start_info->ref) == 0) {
1403 mono_coop_sem_destroy (&start_info->registered);
1404 g_free (start_info);
1407 return ret;
1411 * mono_thread_new_init:
1413 void
1414 mono_thread_new_init (intptr_t tid, gpointer stack_start, gpointer func)
1416 if (mono_thread_start_cb) {
1417 mono_thread_start_cb (tid, stack_start, func);
1422 * mono_threads_set_default_stacksize:
1424 void
1425 mono_threads_set_default_stacksize (guint32 stacksize)
1427 default_stacksize = stacksize;
1431 * mono_threads_get_default_stacksize:
1433 guint32
1434 mono_threads_get_default_stacksize (void)
1436 return default_stacksize;
1440 * mono_thread_create_internal:
1442 * ARG should not be a GC reference.
1444 MonoInternalThread*
1445 mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, MonoThreadCreateFlags flags, MonoError *error)
1447 MonoThread *thread;
1448 MonoInternalThread *internal;
1449 gboolean res;
1451 error_init (error);
1453 internal = create_internal_thread_object ();
1455 thread = create_thread_object (domain, internal);
1457 LOCK_THREAD (internal);
1459 res = create_thread (thread, internal, NULL, (MonoThreadStart) func, arg, flags, error);
1460 (void)res;
1462 UNLOCK_THREAD (internal);
1464 return_val_if_nok (error, NULL);
1465 return internal;
1468 MonoInternalThreadHandle
1469 mono_thread_create_internal_handle (MonoDomain *domain, gpointer func, gpointer arg, MonoThreadCreateFlags flags, MonoError *error)
1471 // FIXME invert
1472 return MONO_HANDLE_NEW (MonoInternalThread, mono_thread_create_internal (domain, func, arg, flags, error));
1476 * mono_thread_create:
1478 void
1479 mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
1481 MONO_ENTER_GC_UNSAFE;
1482 ERROR_DECL (error);
1483 if (!mono_thread_create_checked (domain, func, arg, error))
1484 mono_error_cleanup (error);
1485 MONO_EXIT_GC_UNSAFE;
1488 gboolean
1489 mono_thread_create_checked (MonoDomain *domain, gpointer func, gpointer arg, MonoError *error)
1491 return (NULL != mono_thread_create_internal (domain, func, arg, MONO_THREAD_CREATE_FLAGS_NONE, error));
1495 * mono_thread_attach:
1497 MonoThread *
1498 mono_thread_attach (MonoDomain *domain)
1500 MonoInternalThread *internal;
1501 MonoThread *thread;
1502 MonoThreadInfo *info;
1503 MonoNativeThreadId tid;
1505 if (mono_thread_internal_current_is_attached ()) {
1506 if (domain != mono_domain_get ())
1507 mono_domain_set_fast (domain, TRUE);
1508 /* Already attached */
1509 return mono_thread_current ();
1512 info = mono_thread_info_attach ();
1513 g_assert (info);
1515 tid=mono_native_thread_id_get ();
1517 if (mono_runtime_get_no_exec ())
1518 return NULL;
1520 internal = create_internal_thread_object ();
1522 thread = create_thread_object (domain, internal);
1524 if (!mono_thread_attach_internal (thread, FALSE, TRUE)) {
1525 /* Mono is shutting down, so just wait for the end */
1526 for (;;)
1527 mono_thread_info_sleep (10000, NULL);
1530 THREAD_DEBUG (g_message ("%s: Attached thread ID %" G_GSIZE_FORMAT " (handle %p)", __func__, tid, internal->handle));
1532 if (mono_thread_attach_cb)
1533 mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), info->stack_end);
1535 fire_attach_profiler_events (tid);
1537 return thread;
1541 * mono_thread_detach:
1543 void
1544 mono_thread_detach (MonoThread *thread)
1546 if (thread)
1547 mono_thread_detach_internal (thread->internal_thread);
1551 * mono_thread_detach_if_exiting:
1553 * Detach the current thread from the runtime if it is exiting, i.e. it is running pthread dtors.
1554 * This should be used at the end of embedding code which calls into managed code, and which
1555 * can be called from pthread dtors, like <code>dealloc:</code> implementations in Objective-C.
1557 mono_bool
1558 mono_thread_detach_if_exiting (void)
1560 if (mono_thread_info_is_exiting ()) {
1561 MonoInternalThread *thread;
1563 thread = mono_thread_internal_current ();
1564 if (thread) {
1565 // Switch to GC Unsafe thread state before detaching;
1566 // don't expect to undo this switch, hence unbalanced.
1567 gpointer dummy;
1568 (void) mono_threads_enter_gc_unsafe_region_unbalanced (&dummy);
1570 mono_thread_detach_internal (thread);
1571 mono_thread_info_detach ();
1572 return TRUE;
1575 return FALSE;
1578 gboolean
1579 mono_thread_internal_current_is_attached (void)
1581 MonoInternalThread *internal;
1583 internal = GET_CURRENT_OBJECT ();
1584 if (!internal)
1585 return FALSE;
1587 return TRUE;
1591 * mono_thread_exit:
1593 void
1594 mono_thread_exit (void)
1596 MonoInternalThread *thread = mono_thread_internal_current ();
1598 THREAD_DEBUG (g_message ("%s: mono_thread_exit for %p (%" G_GSIZE_FORMAT ")", __func__, thread, (gsize)thread->tid));
1600 mono_thread_detach_internal (thread);
1602 /* we could add a callback here for embedders to use. */
1603 if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread))
1604 exit (mono_environment_exitcode_get ());
1606 mono_thread_info_exit (0);
1609 static void
1610 mono_thread_construct_internal (MonoThreadObjectHandle this_obj_handle)
1612 MonoInternalThread * const internal = create_internal_thread_object ();
1614 internal->state = ThreadState_Unstarted;
1616 int const thread_gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, this_obj_handle), TRUE);
1618 MonoThreadObject *this_obj = MONO_HANDLE_RAW (this_obj_handle);
1620 mono_atomic_cas_ptr ((volatile gpointer *)&this_obj->internal_thread, internal, NULL);
1622 mono_gchandle_free_internal (thread_gchandle);
1625 #ifndef ENABLE_NETCORE
1626 void
1627 ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThreadObjectHandle this_obj_handle, MonoError *error)
1629 mono_thread_construct_internal (this_obj_handle);
1631 #endif
1633 MonoThreadObjectHandle
1634 ves_icall_System_Threading_Thread_GetCurrentThread (MonoError *error)
1636 return MONO_HANDLE_NEW (MonoThreadObject, mono_thread_current ());
1639 static MonoInternalThread*
1640 thread_handle_to_internal_ptr (MonoThreadObjectHandle thread_handle)
1642 return MONO_HANDLE_GETVAL(thread_handle, internal_thread); // InternalThreads are always pinned.
1645 static void
1646 mono_error_set_exception_thread_state (MonoError *error, const char *exception_message)
1648 mono_error_set_generic_error (error, "System.Threading", "ThreadStateException", "%s", exception_message);
1651 static void
1652 mono_error_set_exception_thread_not_started_or_dead (MonoError *error)
1654 mono_error_set_exception_thread_state (error, "Thread has not been started, or is dead.");
1657 #ifndef ENABLE_NETCORE
1658 MonoBoolean
1659 ves_icall_System_Threading_Thread_Thread_internal (MonoThreadObjectHandle thread_handle, MonoObjectHandle start_handle, MonoError *error)
1661 MonoInternalThread *internal;
1662 gboolean res;
1663 MonoThread *this_obj = MONO_HANDLE_RAW (thread_handle);
1664 MonoObject *start = MONO_HANDLE_RAW (start_handle);
1666 THREAD_DEBUG (g_message("%s: Trying to start a new thread: this (%p) start (%p)", __func__, this_obj, start));
1668 internal = thread_handle_to_internal_ptr (thread_handle);
1670 if (!internal) {
1671 mono_thread_construct_internal (thread_handle);
1672 internal = thread_handle_to_internal_ptr (thread_handle);
1673 g_assert (internal);
1676 LOCK_THREAD (internal);
1678 if ((internal->state & ThreadState_Unstarted) == 0) {
1679 UNLOCK_THREAD (internal);
1680 mono_error_set_exception_thread_state (error, "Thread has already been started.");
1681 return FALSE;
1684 if ((internal->state & ThreadState_Aborted) != 0) {
1685 UNLOCK_THREAD (internal);
1686 return TRUE;
1689 res = create_thread (this_obj, internal, start, NULL, NULL, MONO_THREAD_CREATE_FLAGS_NONE, error);
1690 if (!res) {
1691 UNLOCK_THREAD (internal);
1692 return FALSE;
1695 internal->state &= ~ThreadState_Unstarted;
1697 THREAD_DEBUG (g_message ("%s: Started thread ID %" G_GSIZE_FORMAT " (handle %p)", __func__, tid, thread));
1699 UNLOCK_THREAD (internal);
1700 return TRUE;
1702 #endif
1705 * This is called from the finalizer of the internal thread object.
1707 void
1708 ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThreadHandle this_obj_handle, MonoError *error)
1710 MonoInternalThread *this_obj = mono_internal_thread_handle_ptr (this_obj_handle);
1711 THREAD_DEBUG (g_message ("%s: Closing thread %p, handle %p", __func__, this_obj, this_obj->handle));
1714 * Since threads keep a reference to their thread object while running, by
1715 * the time this function is called, the thread has already exited/detached,
1716 * i.e. mono_thread_detach_internal () has ran. The exception is during
1717 * shutdown, when mono_thread_detach_internal () can be called after this.
1719 if (this_obj->handle) {
1720 mono_threads_close_thread_handle (this_obj->handle);
1721 this_obj->handle = NULL;
1724 mono_threads_close_native_thread_handle (MONO_GPOINTER_TO_NATIVE_THREAD_HANDLE (this_obj->native_handle));
1725 this_obj->native_handle = NULL;
1727 /* Possibly free synch_cs, if the thread already detached also. */
1728 dec_longlived_thread_data (this_obj->longlived);
1731 if (this_obj->name) {
1732 void *name = this_obj->name;
1733 this_obj->name = NULL;
1734 g_free (name);
1738 void
1739 ves_icall_System_Threading_Thread_Sleep_internal (gint32 ms, MonoError *error)
1741 THREAD_DEBUG (g_message ("%s: Sleeping for %d ms", __func__, ms));
1743 if (mono_thread_current_check_pending_interrupt ())
1744 return;
1746 MonoInternalThread * const thread = mono_thread_internal_current ();
1748 HANDLE_LOOP_PREPARE;
1750 while (TRUE) {
1751 gboolean alerted = FALSE;
1753 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1755 (void)mono_thread_info_sleep (ms, &alerted);
1757 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1759 if (!alerted)
1760 return;
1762 SETUP_ICALL_FRAME;
1764 MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, NULL);
1766 const gboolean interrupt = mono_thread_execute_interruption (&exc);
1768 if (interrupt)
1769 mono_set_pending_exception_handle (exc);
1771 CLEAR_ICALL_FRAME;
1773 if (interrupt)
1774 return;
1775 if (ms == MONO_INFINITE_WAIT) // FIXME: !MONO_INFINITE_WAIT
1776 continue;
1777 return;
1781 void
1782 ves_icall_System_Threading_Thread_SpinWait_nop (MonoError *error)
1786 #ifndef ENABLE_NETCORE
1787 gint32
1788 ves_icall_System_Threading_Thread_GetDomainID (MonoError *error)
1790 return mono_domain_get()->domain_id;
1792 #endif
1795 * mono_thread_get_name_utf8:
1796 * \returns the name of the thread in UTF-8.
1797 * Return NULL if the thread has no name.
1798 * The returned memory is owned by the caller (g_free it).
1800 char *
1801 mono_thread_get_name_utf8 (MonoThread *thread)
1803 if (thread == NULL)
1804 return NULL;
1806 MonoInternalThread *internal = thread->internal_thread;
1807 if (internal == NULL)
1808 return NULL;
1810 LOCK_THREAD (internal);
1812 char *tname = g_utf16_to_utf8 (internal->name, internal->name_len, NULL, NULL, NULL);
1814 UNLOCK_THREAD (internal);
1816 return tname;
1820 * mono_thread_get_managed_id:
1821 * \returns the \c Thread.ManagedThreadId value of \p thread.
1822 * Returns \c -1 if \p thread is NULL.
1824 int32_t
1825 mono_thread_get_managed_id (MonoThread *thread)
1827 if (thread == NULL)
1828 return -1;
1830 MonoInternalThread *internal = thread->internal_thread;
1831 if (internal == NULL)
1832 return -1;
1834 int32_t id = internal->managed_id;
1836 return id;
1839 #ifndef ENABLE_NETCORE
1840 MonoStringHandle
1841 ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThreadHandle thread_handle, MonoError *error)
1843 // InternalThreads are always pinned, so shallowly coop-handleize.
1844 MonoInternalThread * const this_obj = mono_internal_thread_handle_ptr (thread_handle);
1846 MonoStringHandle str = MONO_HANDLE_NEW (MonoString, NULL);
1848 LOCK_THREAD (this_obj);
1850 if (this_obj->name)
1851 MONO_HANDLE_ASSIGN (str, mono_string_new_utf16_handle (mono_domain_get (), this_obj->name, this_obj->name_len, error));
1853 UNLOCK_THREAD (this_obj);
1855 return str;
1857 #endif
1859 void
1860 mono_thread_set_name_internal (MonoInternalThread *this_obj,
1861 MonoString *name,
1862 MonoSetThreadNameFlags flags, MonoError *error)
1864 MonoNativeThreadId tid = 0;
1866 LOCK_THREAD (this_obj);
1868 if (flags & MonoSetThreadNameFlag_Reset) {
1869 this_obj->flags &= ~MONO_THREAD_FLAG_NAME_SET;
1870 } else if (this_obj->flags & MONO_THREAD_FLAG_NAME_SET) {
1871 UNLOCK_THREAD (this_obj);
1873 mono_error_set_invalid_operation (error, "%s", "Thread.Name can only be set once.");
1874 return;
1876 if (this_obj->name) {
1877 g_free (this_obj->name);
1878 this_obj->name_len = 0;
1880 if (name) {
1881 this_obj->name = g_memdup (mono_string_chars_internal (name), mono_string_length_internal (name) * sizeof (gunichar2));
1882 this_obj->name_len = mono_string_length_internal (name);
1884 if (flags & MonoSetThreadNameFlag_Permanent)
1885 this_obj->flags |= MONO_THREAD_FLAG_NAME_SET;
1887 else
1888 this_obj->name = NULL;
1890 if (!(this_obj->state & ThreadState_Stopped))
1891 tid = thread_get_tid (this_obj);
1893 UNLOCK_THREAD (this_obj);
1895 if (this_obj->name && tid) {
1896 char *tname = mono_string_to_utf8_checked_internal (name, error);
1897 return_if_nok (error);
1898 MONO_PROFILER_RAISE (thread_name, ((uintptr_t)tid, tname));
1899 mono_native_thread_set_name (tid, tname);
1900 mono_free (tname);
1903 mono_thread_set_name_windows (this_obj->native_handle, name ? mono_string_chars_internal (name) : NULL);
1906 void
1907 ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj, MonoString *name)
1909 ERROR_DECL (error);
1910 mono_thread_set_name_internal (this_obj, name, MonoSetThreadNameFlag_Permanent, error);
1911 mono_error_set_pending_exception (error);
1914 #ifndef ENABLE_NETCORE
1916 * ves_icall_System_Threading_Thread_GetPriority_internal:
1917 * @param this_obj: The MonoInternalThread on which to operate.
1919 * Gets the priority of the given thread.
1920 * @return: The priority of the given thread.
1923 ves_icall_System_Threading_Thread_GetPriority (MonoThreadObjectHandle this_obj, MonoError *error)
1925 gint32 priority;
1927 MonoInternalThread *internal = thread_handle_to_internal_ptr (this_obj);
1929 LOCK_THREAD (internal);
1930 priority = internal->priority;
1931 UNLOCK_THREAD (internal);
1933 return priority;
1935 #endif
1938 * ves_icall_System_Threading_Thread_SetPriority_internal:
1939 * @param this_obj: The MonoInternalThread on which to operate.
1940 * @param priority: The priority to set.
1942 * Sets the priority of the given thread.
1944 void
1945 ves_icall_System_Threading_Thread_SetPriority (MonoThreadObjectHandle this_obj, int priority, MonoError *error)
1947 MonoInternalThread *internal = thread_handle_to_internal_ptr (this_obj);
1949 LOCK_THREAD (internal);
1950 internal->priority = priority;
1951 if (internal->thread_info != NULL)
1952 mono_thread_internal_set_priority (internal, (MonoThreadPriority)priority);
1953 UNLOCK_THREAD (internal);
1956 /* If the array is already in the requested domain, we just return it,
1957 otherwise we return a copy in that domain. */
1958 static MonoArrayHandle
1959 byte_array_to_domain (MonoArrayHandle arr, MonoDomain *domain, MonoError *error)
1961 HANDLE_FUNCTION_ENTER ()
1963 if (MONO_HANDLE_IS_NULL (arr))
1964 return MONO_HANDLE_NEW (MonoArray, NULL);
1966 if (MONO_HANDLE_DOMAIN (arr) == domain)
1967 return arr;
1969 size_t const size = mono_array_handle_length (arr);
1971 // Capture arrays into common representation for repetitious code.
1972 // These two variables could also be an array of size 2 and
1973 // repitition implemented with a loop.
1974 struct {
1975 MonoArrayHandle handle;
1976 gpointer p;
1977 guint gchandle;
1979 source = { arr },
1980 dest = { mono_array_new_handle (domain, mono_defaults.byte_class, size, error) };
1981 goto_if_nok (error, exit);
1983 // Pin both arrays.
1984 source.p = mono_array_handle_pin_with_size (source.handle, size, 0, &source.gchandle);
1985 dest.p = mono_array_handle_pin_with_size (dest.handle, size, 0, &dest.gchandle);
1987 memmove (dest.p, source.p, size);
1988 exit:
1989 // Unpin both arrays.
1990 mono_gchandle_free_internal (source.gchandle);
1991 mono_gchandle_free_internal (dest.gchandle);
1993 HANDLE_FUNCTION_RETURN_REF (MonoArray, dest.handle)
1996 #ifndef ENABLE_NETCORE
1997 MonoArrayHandle
1998 ves_icall_System_Threading_Thread_ByteArrayToRootDomain (MonoArrayHandle arr, MonoError *error)
2000 return byte_array_to_domain (arr, mono_get_root_domain (), error);
2003 MonoArrayHandle
2004 ves_icall_System_Threading_Thread_ByteArrayToCurrentDomain (MonoArrayHandle arr, MonoError *error)
2006 return byte_array_to_domain (arr, mono_domain_get (), error);
2008 #endif
2011 * mono_thread_current:
2013 MonoThread *
2014 mono_thread_current (void)
2016 #ifdef ENABLE_NETCORE
2017 return mono_thread_internal_current ();
2018 #else
2019 MonoDomain *domain = mono_domain_get ();
2020 MonoInternalThread *internal = mono_thread_internal_current ();
2021 MonoThread **current_thread_ptr;
2023 g_assert (internal);
2024 current_thread_ptr = get_current_thread_ptr_for_domain (domain, internal);
2026 if (!*current_thread_ptr) {
2027 g_assert (domain != mono_get_root_domain ());
2028 *current_thread_ptr = create_thread_object (domain, internal);
2030 return *current_thread_ptr;
2031 #endif
2034 static MonoThreadObjectHandle
2035 mono_thread_current_handle (void)
2037 return MONO_HANDLE_NEW (MonoThreadObject, mono_thread_current ());
2040 /* Return the thread object belonging to INTERNAL in the current domain */
2041 static MonoThread *
2042 mono_thread_current_for_thread (MonoInternalThread *internal)
2044 #ifdef ENABLE_NETCORE
2045 return mono_thread_internal_current ();
2046 #else
2047 MonoDomain *domain = mono_domain_get ();
2048 MonoThread **current_thread_ptr;
2050 g_assert (internal);
2051 current_thread_ptr = get_current_thread_ptr_for_domain (domain, internal);
2053 if (!*current_thread_ptr) {
2054 g_assert (domain != mono_get_root_domain ());
2055 *current_thread_ptr = create_thread_object (domain, internal);
2057 return *current_thread_ptr;
2058 #endif
2061 MonoInternalThread*
2062 mono_thread_internal_current (void)
2064 MonoInternalThread *res = GET_CURRENT_OBJECT ();
2065 THREAD_DEBUG (g_message ("%s: returning %p", __func__, res));
2066 return res;
2069 MonoInternalThreadHandle
2070 mono_thread_internal_current_handle (void)
2072 return MONO_HANDLE_NEW (MonoInternalThread, mono_thread_internal_current ());
2075 static MonoThreadInfoWaitRet
2076 mono_join_uninterrupted (MonoThreadHandle* thread_to_join, gint32 ms, MonoError *error)
2078 MonoThreadInfoWaitRet ret;
2079 gint32 wait = ms;
2081 const gint64 start = (ms == -1) ? 0 : mono_msec_ticks ();
2082 while (TRUE) {
2083 MONO_ENTER_GC_SAFE;
2084 ret = mono_thread_info_wait_one_handle (thread_to_join, wait, TRUE);
2085 MONO_EXIT_GC_SAFE;
2087 if (ret != MONO_THREAD_INFO_WAIT_RET_ALERTED)
2088 return ret;
2090 MonoException *exc = mono_thread_execute_interruption_ptr ();
2091 if (exc) {
2092 mono_error_set_exception_instance (error, exc);
2093 return ret;
2096 if (ms == -1)
2097 continue;
2099 /* Re-calculate ms according to the time passed */
2100 const gint32 diff_ms = (gint32)(mono_msec_ticks () - start);
2101 if (diff_ms >= ms) {
2102 ret = MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
2103 return ret;
2105 wait = ms - diff_ms;
2108 return ret;
2111 MonoBoolean
2112 ves_icall_System_Threading_Thread_Join_internal (MonoThreadObjectHandle thread_handle, int ms, MonoError *error)
2114 if (mono_thread_current_check_pending_interrupt ())
2115 return FALSE;
2117 // Internal threads are pinned so shallow coop/handle.
2118 MonoInternalThread * const thread = thread_handle_to_internal_ptr (thread_handle);
2119 MonoThreadHandle *handle = thread->handle;
2120 MonoInternalThread *cur_thread = mono_thread_internal_current ();
2121 gboolean ret = FALSE;
2123 LOCK_THREAD (thread);
2125 if ((thread->state & ThreadState_Unstarted) != 0) {
2126 UNLOCK_THREAD (thread);
2128 mono_error_set_exception_thread_state (error, "Thread has not been started.");
2129 return FALSE;
2132 UNLOCK_THREAD (thread);
2134 if (ms == -1)
2135 ms = MONO_INFINITE_WAIT;
2136 THREAD_DEBUG (g_message ("%s: joining thread handle %p, %d ms", __func__, handle, ms));
2138 mono_thread_set_state (cur_thread, ThreadState_WaitSleepJoin);
2140 ret = mono_join_uninterrupted (handle, ms, error);
2142 mono_thread_clr_state (cur_thread, ThreadState_WaitSleepJoin);
2144 if (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0) {
2145 THREAD_DEBUG (g_message ("%s: join successful", __func__));
2147 mono_error_assert_ok (error);
2149 /* Wait for the thread to really exit */
2150 MonoNativeThreadId tid = thread_get_tid (thread);
2151 mono_thread_join ((gpointer)(gsize)tid);
2153 return TRUE;
2156 THREAD_DEBUG (g_message ("%s: join failed", __func__));
2158 return FALSE;
2161 #define MANAGED_WAIT_FAILED 0x7fffffff
2163 static gint32
2164 map_native_wait_result_to_managed (MonoW32HandleWaitRet val, gsize numobjects)
2166 if (val >= MONO_W32HANDLE_WAIT_RET_SUCCESS_0 && val < MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + numobjects) {
2167 return WAIT_OBJECT_0 + (val - MONO_W32HANDLE_WAIT_RET_SUCCESS_0);
2168 } else if (val >= MONO_W32HANDLE_WAIT_RET_ABANDONED_0 && val < MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + numobjects) {
2169 return WAIT_ABANDONED_0 + (val - MONO_W32HANDLE_WAIT_RET_ABANDONED_0);
2170 } else if (val == MONO_W32HANDLE_WAIT_RET_ALERTED) {
2171 return WAIT_IO_COMPLETION;
2172 } else if (val == MONO_W32HANDLE_WAIT_RET_TIMEOUT) {
2173 return WAIT_TIMEOUT;
2174 } else if (val == MONO_W32HANDLE_WAIT_RET_TOO_MANY_POSTS) {
2175 return WAIT_TOO_MANY_POSTS;
2176 } else if (val == MONO_W32HANDLE_WAIT_RET_NOT_OWNED_BY_CALLER) {
2177 return WAIT_NOT_OWNED_BY_CALLER;
2178 } else if (val == MONO_W32HANDLE_WAIT_RET_FAILED) {
2179 /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
2180 return MANAGED_WAIT_FAILED;
2181 } else {
2182 g_error ("%s: unknown val value %d", __func__, val);
2186 gint32
2187 ves_icall_System_Threading_WaitHandle_Wait_internal (gpointer *handles, gint32 numhandles, MonoBoolean waitall, gint32 timeout, MonoError *error)
2189 /* Do this WaitSleepJoin check before creating objects */
2190 if (mono_thread_current_check_pending_interrupt ())
2191 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
2193 MonoInternalThread * const thread = mono_thread_internal_current ();
2195 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
2197 gint64 start = 0;
2199 if (timeout == -1)
2200 timeout = MONO_INFINITE_WAIT;
2201 if (timeout != MONO_INFINITE_WAIT)
2202 start = mono_msec_ticks ();
2204 guint32 timeoutLeft = timeout;
2206 MonoW32HandleWaitRet ret;
2208 HANDLE_LOOP_PREPARE;
2210 for (;;) {
2212 /* mono_w32handle_wait_multiple optimizes the case for numhandles == 1 */
2213 ret = mono_w32handle_wait_multiple (handles, numhandles, waitall, timeoutLeft, TRUE, error);
2215 if (ret != MONO_W32HANDLE_WAIT_RET_ALERTED)
2216 break;
2218 SETUP_ICALL_FRAME;
2220 MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, NULL);
2222 const gboolean interrupt = mono_thread_execute_interruption (&exc);
2224 if (interrupt)
2225 mono_error_set_exception_handle (error, exc);
2227 CLEAR_ICALL_FRAME;
2229 if (interrupt)
2230 break;
2232 if (timeout != MONO_INFINITE_WAIT) {
2233 gint64 const elapsed = mono_msec_ticks () - start;
2234 if (elapsed >= timeout) {
2235 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
2236 break;
2239 timeoutLeft = timeout - elapsed;
2243 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
2245 return map_native_wait_result_to_managed (ret, numhandles);
2248 #if HAVE_API_SUPPORT_WIN32_SIGNAL_OBJECT_AND_WAIT
2249 gint32
2250 ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (gpointer toSignal, gpointer toWait, gint32 ms, MonoError *error)
2252 MonoW32HandleWaitRet ret;
2253 MonoInternalThread *thread = mono_thread_internal_current ();
2255 if (ms == -1)
2256 ms = MONO_INFINITE_WAIT;
2258 if (mono_thread_current_check_pending_interrupt ())
2259 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
2261 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
2263 ret = mono_w32handle_signal_and_wait (toSignal, toWait, ms, TRUE);
2265 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
2267 return map_native_wait_result_to_managed (ret, 1);
2270 #endif
2272 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
2274 return mono_atomic_inc_i32 (location);
2277 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
2279 #if SIZEOF_VOID_P == 4
2280 if (G_UNLIKELY ((size_t)location & 0x7)) {
2281 gint64 ret;
2282 mono_interlocked_lock ();
2283 (*location)++;
2284 ret = *location;
2285 mono_interlocked_unlock ();
2286 return ret;
2288 #endif
2289 return mono_atomic_inc_i64 (location);
2292 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
2294 return mono_atomic_dec_i32(location);
2297 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
2299 #if SIZEOF_VOID_P == 4
2300 if (G_UNLIKELY ((size_t)location & 0x7)) {
2301 gint64 ret;
2302 mono_interlocked_lock ();
2303 (*location)--;
2304 ret = *location;
2305 mono_interlocked_unlock ();
2306 return ret;
2308 #endif
2309 return mono_atomic_dec_i64 (location);
2312 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location, gint32 value)
2314 return mono_atomic_xchg_i32(location, value);
2317 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location, MonoObject *value)
2319 MonoObject *res;
2320 res = (MonoObject *) mono_atomic_xchg_ptr((gpointer *) location, value);
2321 mono_gc_wbarrier_generic_nostore_internal (location);
2322 return res;
2325 gpointer ves_icall_System_Threading_Interlocked_Exchange_IntPtr (gpointer *location, gpointer value)
2327 return mono_atomic_xchg_ptr(location, value);
2330 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location, gfloat value)
2332 IntFloatUnion val, ret;
2334 val.fval = value;
2335 ret.ival = mono_atomic_xchg_i32((gint32 *) location, val.ival);
2337 return ret.fval;
2340 gint64
2341 ves_icall_System_Threading_Interlocked_Exchange_Long (gint64 *location, gint64 value)
2343 #if SIZEOF_VOID_P == 4
2344 if (G_UNLIKELY ((size_t)location & 0x7)) {
2345 gint64 ret;
2346 mono_interlocked_lock ();
2347 ret = *location;
2348 *location = value;
2349 mono_interlocked_unlock ();
2350 return ret;
2352 #endif
2353 return mono_atomic_xchg_i64 (location, value);
2356 gdouble
2357 ves_icall_System_Threading_Interlocked_Exchange_Double (gdouble *location, gdouble value)
2359 LongDoubleUnion val, ret;
2361 val.fval = value;
2362 ret.ival = (gint64)mono_atomic_xchg_i64((gint64 *) location, val.ival);
2364 return ret.fval;
2367 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location, gint32 value, gint32 comparand)
2369 return mono_atomic_cas_i32(location, value, comparand);
2372 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int_Success(gint32 *location, gint32 value, gint32 comparand, MonoBoolean *success)
2374 gint32 r = mono_atomic_cas_i32(location, value, comparand);
2375 *success = r == comparand;
2376 return r;
2379 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location, MonoObject *value, MonoObject *comparand)
2381 MonoObject *res;
2382 res = (MonoObject *) mono_atomic_cas_ptr((gpointer *) location, value, comparand);
2383 mono_gc_wbarrier_generic_nostore_internal (location);
2384 return res;
2387 gpointer ves_icall_System_Threading_Interlocked_CompareExchange_IntPtr(gpointer *location, gpointer value, gpointer comparand)
2389 return mono_atomic_cas_ptr(location, value, comparand);
2392 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location, gfloat value, gfloat comparand)
2394 IntFloatUnion val, ret, cmp;
2396 val.fval = value;
2397 cmp.fval = comparand;
2398 ret.ival = mono_atomic_cas_i32((gint32 *) location, val.ival, cmp.ival);
2400 return ret.fval;
2403 gdouble
2404 ves_icall_System_Threading_Interlocked_CompareExchange_Double (gdouble *location, gdouble value, gdouble comparand)
2406 #if SIZEOF_VOID_P == 8
2407 LongDoubleUnion val, comp, ret;
2409 val.fval = value;
2410 comp.fval = comparand;
2411 ret.ival = (gint64)mono_atomic_cas_ptr((gpointer *) location, (gpointer)val.ival, (gpointer)comp.ival);
2413 return ret.fval;
2414 #else
2415 gdouble old;
2417 mono_interlocked_lock ();
2418 old = *location;
2419 if (old == comparand)
2420 *location = value;
2421 mono_interlocked_unlock ();
2423 return old;
2424 #endif
2427 gint64
2428 ves_icall_System_Threading_Interlocked_CompareExchange_Long (gint64 *location, gint64 value, gint64 comparand)
2430 #if SIZEOF_VOID_P == 4
2431 if (G_UNLIKELY ((size_t)location & 0x7)) {
2432 gint64 old;
2433 mono_interlocked_lock ();
2434 old = *location;
2435 if (old == comparand)
2436 *location = value;
2437 mono_interlocked_unlock ();
2438 return old;
2440 #endif
2441 return mono_atomic_cas_i64 (location, value, comparand);
2444 MonoObject*
2445 ves_icall_System_Threading_Interlocked_CompareExchange_T (MonoObject **location, MonoObject *value, MonoObject *comparand)
2447 MonoObject *res;
2448 res = (MonoObject *)mono_atomic_cas_ptr ((volatile gpointer *)location, value, comparand);
2449 mono_gc_wbarrier_generic_nostore_internal (location);
2450 return res;
2453 MonoObject*
2454 ves_icall_System_Threading_Interlocked_Exchange_T (MonoObject **location, MonoObject *value)
2456 MonoObject *res;
2457 MONO_CHECK_NULL (location, NULL);
2458 res = (MonoObject *)mono_atomic_xchg_ptr ((volatile gpointer *)location, value);
2459 mono_gc_wbarrier_generic_nostore_internal (location);
2460 return res;
2463 gint32
2464 ves_icall_System_Threading_Interlocked_Add_Int (gint32 *location, gint32 value)
2466 return mono_atomic_add_i32 (location, value);
2469 gint64
2470 ves_icall_System_Threading_Interlocked_Add_Long (gint64 *location, gint64 value)
2472 #if SIZEOF_VOID_P == 4
2473 if (G_UNLIKELY ((size_t)location & 0x7)) {
2474 gint64 ret;
2475 mono_interlocked_lock ();
2476 *location += value;
2477 ret = *location;
2478 mono_interlocked_unlock ();
2479 return ret;
2481 #endif
2482 return mono_atomic_add_i64 (location, value);
2485 gint64
2486 ves_icall_System_Threading_Interlocked_Read_Long (gint64 *location)
2488 #if SIZEOF_VOID_P == 4
2489 if (G_UNLIKELY ((size_t)location & 0x7)) {
2490 gint64 ret;
2491 mono_interlocked_lock ();
2492 ret = *location;
2493 mono_interlocked_unlock ();
2494 return ret;
2496 #endif
2497 return mono_atomic_load_i64 (location);
2500 void
2501 ves_icall_System_Threading_Interlocked_MemoryBarrierProcessWide (void)
2503 mono_memory_barrier_process_wide ();
2506 void
2507 ves_icall_System_Threading_Thread_MemoryBarrier (void)
2509 mono_memory_barrier ();
2512 void
2513 ves_icall_System_Threading_Thread_ClrState (MonoInternalThreadHandle this_obj, guint32 state, MonoError *error)
2515 // InternalThreads are always pinned, so shallowly coop-handleize.
2516 mono_thread_clr_state (mono_internal_thread_handle_ptr (this_obj), (MonoThreadState)state);
2519 void
2520 ves_icall_System_Threading_Thread_SetState (MonoInternalThreadHandle thread_handle, guint32 state, MonoError *error)
2522 // InternalThreads are always pinned, so shallowly coop-handleize.
2523 mono_thread_set_state (mono_internal_thread_handle_ptr (thread_handle), (MonoThreadState)state);
2526 guint32
2527 ves_icall_System_Threading_Thread_GetState (MonoInternalThreadHandle thread_handle, MonoError *error)
2529 // InternalThreads are always pinned, so shallowly coop-handleize.
2530 MonoInternalThread *this_obj = mono_internal_thread_handle_ptr (thread_handle);
2532 guint32 state;
2534 LOCK_THREAD (this_obj);
2536 state = this_obj->state;
2538 UNLOCK_THREAD (this_obj);
2540 return state;
2543 void
2544 ves_icall_System_Threading_Thread_Interrupt_internal (MonoThreadObjectHandle thread_handle, MonoError *error)
2546 // Internal threads are pinned so shallow coop/handle.
2547 MonoInternalThread * const thread = thread_handle_to_internal_ptr (thread_handle);
2548 MonoInternalThread * const current = mono_thread_internal_current ();
2550 LOCK_THREAD (thread);
2552 thread->thread_interrupt_requested = TRUE;
2553 gboolean const throw_ = current != thread && (thread->state & ThreadState_WaitSleepJoin);
2555 UNLOCK_THREAD (thread);
2557 if (throw_)
2558 async_abort_internal (thread, FALSE);
2562 * mono_thread_current_check_pending_interrupt:
2563 * Checks if there's a interruption request and set the pending exception if so.
2564 * \returns true if a pending exception was set
2566 gboolean
2567 mono_thread_current_check_pending_interrupt (void)
2569 MonoInternalThread *thread = mono_thread_internal_current ();
2570 gboolean throw_ = FALSE;
2572 LOCK_THREAD (thread);
2574 if (thread->thread_interrupt_requested) {
2575 throw_ = TRUE;
2576 thread->thread_interrupt_requested = FALSE;
2579 UNLOCK_THREAD (thread);
2581 if (throw_) {
2582 ERROR_DECL (error);
2583 mono_error_set_thread_interrupted (error);
2584 mono_error_set_pending_exception (error);
2586 return throw_;
2589 static gboolean
2590 request_thread_abort (MonoInternalThread *thread, MonoObjectHandle *state, gboolean appdomain_unload)
2591 // state is a pointer to a handle in order to be optional,
2592 // and be passed unspecified from functions not using handles.
2593 // When raw pointers is gone, it need not be a pointer,
2594 // though this would still work efficiently.
2596 LOCK_THREAD (thread);
2598 /* With self abort we always throw a new exception */
2599 if (thread == mono_thread_internal_current ())
2600 thread->abort_exc = NULL;
2602 if (thread->state & (ThreadState_AbortRequested | ThreadState_Stopped))
2604 UNLOCK_THREAD (thread);
2605 return FALSE;
2608 if ((thread->state & ThreadState_Unstarted) != 0) {
2609 thread->state |= ThreadState_Aborted;
2610 UNLOCK_THREAD (thread);
2611 return FALSE;
2614 thread->state |= ThreadState_AbortRequested;
2615 if (appdomain_unload)
2616 thread->flags |= MONO_THREAD_FLAG_APPDOMAIN_ABORT;
2617 else
2618 thread->flags &= ~MONO_THREAD_FLAG_APPDOMAIN_ABORT;
2620 mono_gchandle_free_internal (thread->abort_state_handle);
2621 thread->abort_state_handle = 0;
2624 if (state && !MONO_HANDLE_IS_NULL (*state)) {
2625 thread->abort_state_handle = mono_gchandle_from_handle (*state, FALSE);
2626 g_assert (thread->abort_state_handle);
2629 thread->abort_exc = NULL;
2631 THREAD_DEBUG (g_message ("%s: (%" G_GSIZE_FORMAT ") Abort requested for %p (%" G_GSIZE_FORMAT ")", __func__, mono_native_thread_id_get (), thread, (gsize)thread->tid));
2633 /* During shutdown, we can't wait for other threads */
2634 if (!shutting_down)
2635 /* Make sure the thread is awake */
2636 mono_thread_resume (thread);
2638 UNLOCK_THREAD (thread);
2639 return TRUE;
2642 #ifndef ENABLE_NETCORE
2643 void
2644 ves_icall_System_Threading_Thread_Abort (MonoInternalThreadHandle thread_handle, MonoObjectHandle state, MonoError *error)
2646 // InternalThreads are always pinned, so shallowly coop-handleize.
2647 MonoInternalThread * const thread = mono_internal_thread_handle_ptr (thread_handle);
2648 gboolean is_self = thread == mono_thread_internal_current ();
2650 /* For self aborts we always process the abort */
2651 if (!request_thread_abort (thread, &state, FALSE) && !is_self)
2652 return;
2654 if (is_self) {
2655 self_abort_internal (error);
2656 } else {
2657 async_abort_internal (thread, TRUE);
2660 #endif
2663 * mono_thread_internal_abort:
2664 * Request thread \p thread to be aborted.
2665 * \p thread MUST NOT be the current thread.
2667 void
2668 mono_thread_internal_abort (MonoInternalThread *thread, gboolean appdomain_unload)
2670 g_assert (thread != mono_thread_internal_current ());
2672 if (!request_thread_abort (thread, NULL, appdomain_unload))
2673 return;
2674 async_abort_internal (thread, TRUE);
2677 #ifndef ENABLE_NETCORE
2678 void
2679 ves_icall_System_Threading_Thread_ResetAbort (MonoThreadObjectHandle this_obj, MonoError *error)
2681 MonoInternalThread *thread = mono_thread_internal_current ();
2682 gboolean was_aborting, is_domain_abort;
2684 LOCK_THREAD (thread);
2685 was_aborting = thread->state & ThreadState_AbortRequested;
2686 is_domain_abort = thread->flags & MONO_THREAD_FLAG_APPDOMAIN_ABORT;
2688 if (was_aborting && !is_domain_abort)
2689 thread->state &= ~ThreadState_AbortRequested;
2690 UNLOCK_THREAD (thread);
2692 if (!was_aborting) {
2693 mono_error_set_exception_thread_state (error, "Unable to reset abort because no abort was requested");
2694 return;
2695 } else if (is_domain_abort) {
2696 /* Silently ignore abort resets in unloading appdomains */
2697 return;
2700 mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
2701 thread->abort_exc = NULL;
2702 mono_gchandle_free_internal (thread->abort_state_handle);
2703 /* This is actually not necessary - the handle
2704 only counts if the exception is set */
2705 thread->abort_state_handle = 0;
2707 #endif
2709 void
2710 mono_thread_internal_reset_abort (MonoInternalThread *thread)
2712 LOCK_THREAD (thread);
2714 thread->state &= ~ThreadState_AbortRequested;
2716 if (thread->abort_exc) {
2717 mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
2718 thread->abort_exc = NULL;
2719 mono_gchandle_free_internal (thread->abort_state_handle);
2720 /* This is actually not necessary - the handle
2721 only counts if the exception is set */
2722 thread->abort_state_handle = 0;
2725 UNLOCK_THREAD (thread);
2728 #ifndef ENABLE_NETCORE
2729 MonoObjectHandle
2730 ves_icall_System_Threading_Thread_GetAbortExceptionState (MonoThreadObjectHandle this_obj, MonoError *error)
2732 MonoInternalThread *thread = thread_handle_to_internal_ptr (this_obj);
2734 if (!thread->abort_state_handle)
2735 return NULL_HANDLE; // No state. No error.
2737 // Convert gc handle to coop handle.
2738 MonoObjectHandle state = mono_gchandle_get_target_handle (thread->abort_state_handle);
2739 g_assert (MONO_HANDLE_BOOL (state));
2741 MonoDomain *domain = mono_domain_get ();
2742 if (MONO_HANDLE_DOMAIN (state) == domain)
2743 return state; // No need to cross domain, return state directly.
2745 // Attempt move state cross-domain.
2746 MonoObjectHandle deserialized = mono_object_xdomain_representation (state, domain, error);
2748 // If deserialized is null, there must be an error, and vice versa.
2749 g_assert (is_ok (error) == MONO_HANDLE_BOOL (deserialized));
2751 if (MONO_HANDLE_BOOL (deserialized))
2752 return deserialized; // Cross-domain serialization succeeded. Return it.
2754 // Wrap error in InvalidOperationException.
2755 ERROR_DECL (error_creating_exception);
2756 MonoExceptionHandle invalid_op_exc = mono_exception_new_invalid_operation (
2757 "Thread.ExceptionState cannot access an ExceptionState from a different AppDomain", error_creating_exception);
2758 mono_error_assert_ok (error_creating_exception);
2759 g_assert (!is_ok (error) && 1);
2760 MONO_HANDLE_SET (invalid_op_exc, inner_ex, mono_error_convert_to_exception_handle (error));
2761 error_init_reuse (error);
2762 mono_error_set_exception_handle (error, invalid_op_exc);
2763 g_assert (!is_ok (error) && 2);
2765 // There is state, but we failed to return it.
2766 return NULL_HANDLE;
2768 #endif
2770 static gboolean
2771 mono_thread_suspend (MonoInternalThread *thread)
2773 LOCK_THREAD (thread);
2775 if (thread->state & (ThreadState_Unstarted | ThreadState_Aborted | ThreadState_Stopped))
2777 UNLOCK_THREAD (thread);
2778 return FALSE;
2781 if (thread->state & (ThreadState_Suspended | ThreadState_SuspendRequested | ThreadState_AbortRequested))
2783 UNLOCK_THREAD (thread);
2784 return TRUE;
2787 thread->state |= ThreadState_SuspendRequested;
2788 MONO_ENTER_GC_SAFE;
2789 mono_os_event_reset (thread->suspended);
2790 MONO_EXIT_GC_SAFE;
2792 if (thread == mono_thread_internal_current ()) {
2793 /* calls UNLOCK_THREAD (thread) */
2794 self_suspend_internal ();
2795 } else {
2796 /* calls UNLOCK_THREAD (thread) */
2797 async_suspend_internal (thread, FALSE);
2800 return TRUE;
2803 #ifndef ENABLE_NETCORE
2804 void
2805 ves_icall_System_Threading_Thread_Suspend (MonoThreadObjectHandle this_obj, MonoError *error)
2807 if (!mono_thread_suspend (thread_handle_to_internal_ptr (this_obj)))
2808 mono_error_set_exception_thread_not_started_or_dead (error);
2811 #endif
2813 /* LOCKING: LOCK_THREAD(thread) must be held */
2814 static gboolean
2815 mono_thread_resume (MonoInternalThread *thread)
2817 if ((thread->state & ThreadState_SuspendRequested) != 0) {
2818 // g_async_safe_printf ("RESUME (1) thread %p\n", thread_get_tid (thread));
2819 thread->state &= ~ThreadState_SuspendRequested;
2820 MONO_ENTER_GC_SAFE;
2821 mono_os_event_set (thread->suspended);
2822 MONO_EXIT_GC_SAFE;
2823 return TRUE;
2826 if ((thread->state & ThreadState_Suspended) == 0 ||
2827 (thread->state & ThreadState_Unstarted) != 0 ||
2828 (thread->state & ThreadState_Aborted) != 0 ||
2829 (thread->state & ThreadState_Stopped) != 0)
2831 // g_async_safe_printf ("RESUME (2) thread %p\n", thread_get_tid (thread));
2832 return FALSE;
2835 // g_async_safe_printf ("RESUME (3) thread %p\n", thread_get_tid (thread));
2837 MONO_ENTER_GC_SAFE;
2838 mono_os_event_set (thread->suspended);
2839 MONO_EXIT_GC_SAFE;
2841 if (!thread->self_suspended) {
2842 UNLOCK_THREAD (thread);
2844 /* Awake the thread */
2845 if (!mono_thread_info_resume (thread_get_tid (thread)))
2846 return FALSE;
2848 LOCK_THREAD (thread);
2851 thread->state &= ~ThreadState_Suspended;
2853 return TRUE;
2856 #ifndef ENABLE_NETCORE
2857 void
2858 ves_icall_System_Threading_Thread_Resume (MonoThreadObjectHandle thread_handle, MonoError *error)
2860 // Internal threads are pinned so shallow coop/handle.
2861 MonoInternalThread * const internal_thread = thread_handle_to_internal_ptr (thread_handle);
2862 gboolean exception = FALSE;
2864 if (!internal_thread) {
2865 exception = TRUE;
2866 } else {
2867 LOCK_THREAD (internal_thread);
2868 if (!mono_thread_resume (internal_thread))
2869 exception = TRUE;
2870 UNLOCK_THREAD (internal_thread);
2873 if (exception)
2874 mono_error_set_exception_thread_not_started_or_dead (error);
2876 #endif
2878 gboolean
2879 mono_threads_is_critical_method (MonoMethod *method)
2881 switch (method->wrapper_type) {
2882 case MONO_WRAPPER_RUNTIME_INVOKE:
2883 case MONO_WRAPPER_XDOMAIN_INVOKE:
2884 case MONO_WRAPPER_XDOMAIN_DISPATCH:
2885 return TRUE;
2887 return FALSE;
2890 static gboolean
2891 find_wrapper (MonoMethod *m, gint no, gint ilo, gboolean managed, gpointer data)
2893 if (managed)
2894 return TRUE;
2896 if (mono_threads_is_critical_method (m)) {
2897 *((gboolean*)data) = TRUE;
2898 return TRUE;
2900 return FALSE;
2903 static gboolean
2904 is_running_protected_wrapper (void)
2906 gboolean found = FALSE;
2907 mono_stack_walk (find_wrapper, &found);
2908 return found;
2912 * mono_thread_stop:
2914 void
2915 mono_thread_stop (MonoThread *thread)
2917 MonoInternalThread *internal = thread->internal_thread;
2919 if (!request_thread_abort (internal, NULL, FALSE))
2920 return;
2922 if (internal == mono_thread_internal_current ()) {
2923 ERROR_DECL (error);
2924 self_abort_internal (error);
2926 This function is part of the embeding API and has no way to return the exception
2927 to be thrown. So what we do is keep the old behavior and raise the exception.
2929 mono_error_raise_exception_deprecated (error); /* OK to throw, see note */
2930 } else {
2931 async_abort_internal (internal, TRUE);
2935 gint8
2936 ves_icall_System_Threading_Thread_VolatileRead1 (void *ptr)
2938 gint8 tmp = *(volatile gint8 *)ptr;
2939 mono_memory_barrier ();
2940 return tmp;
2943 gint16
2944 ves_icall_System_Threading_Thread_VolatileRead2 (void *ptr)
2946 gint16 tmp = *(volatile gint16 *)ptr;
2947 mono_memory_barrier ();
2948 return tmp;
2951 gint32
2952 ves_icall_System_Threading_Thread_VolatileRead4 (void *ptr)
2954 gint32 tmp = *(volatile gint32 *)ptr;
2955 mono_memory_barrier ();
2956 return tmp;
2959 gint64
2960 ves_icall_System_Threading_Thread_VolatileRead8 (void *ptr)
2962 gint64 tmp = *(volatile gint64 *)ptr;
2963 mono_memory_barrier ();
2964 return tmp;
2967 void *
2968 ves_icall_System_Threading_Thread_VolatileReadIntPtr (void *ptr)
2970 volatile void *tmp = *(volatile void **)ptr;
2971 mono_memory_barrier ();
2972 return (void *) tmp;
2975 void *
2976 ves_icall_System_Threading_Thread_VolatileReadObject (void *ptr)
2978 volatile MonoObject *tmp = *(volatile MonoObject **)ptr;
2979 mono_memory_barrier ();
2980 return (MonoObject *) tmp;
2983 double
2984 ves_icall_System_Threading_Thread_VolatileReadDouble (void *ptr)
2986 double tmp = *(volatile double *)ptr;
2987 mono_memory_barrier ();
2988 return tmp;
2991 float
2992 ves_icall_System_Threading_Thread_VolatileReadFloat (void *ptr)
2994 float tmp = *(volatile float *)ptr;
2995 mono_memory_barrier ();
2996 return tmp;
2999 gint64
3000 ves_icall_System_Threading_Volatile_Read8 (void *ptr)
3002 #if SIZEOF_VOID_P == 4
3003 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
3004 gint64 val;
3005 mono_interlocked_lock ();
3006 val = *(gint64*)ptr;
3007 mono_interlocked_unlock ();
3008 return val;
3010 #endif
3011 return mono_atomic_load_i64 ((volatile gint64 *)ptr);
3014 guint64
3015 ves_icall_System_Threading_Volatile_ReadU8 (void *ptr)
3017 #if SIZEOF_VOID_P == 4
3018 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
3019 guint64 val;
3020 mono_interlocked_lock ();
3021 val = *(guint64*)ptr;
3022 mono_interlocked_unlock ();
3023 return val;
3025 #endif
3026 return (guint64)mono_atomic_load_i64 ((volatile gint64 *)ptr);
3029 double
3030 ves_icall_System_Threading_Volatile_ReadDouble (void *ptr)
3032 LongDoubleUnion u;
3034 #if SIZEOF_VOID_P == 4
3035 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
3036 double val;
3037 mono_interlocked_lock ();
3038 val = *(double*)ptr;
3039 mono_interlocked_unlock ();
3040 return val;
3042 #endif
3044 u.ival = mono_atomic_load_i64 ((volatile gint64 *)ptr);
3046 return u.fval;
3049 void
3050 ves_icall_System_Threading_Thread_VolatileWrite1 (void *ptr, gint8 value)
3052 mono_memory_barrier ();
3053 *(volatile gint8 *)ptr = value;
3056 void
3057 ves_icall_System_Threading_Thread_VolatileWrite2 (void *ptr, gint16 value)
3059 mono_memory_barrier ();
3060 *(volatile gint16 *)ptr = value;
3063 void
3064 ves_icall_System_Threading_Thread_VolatileWrite4 (void *ptr, gint32 value)
3066 mono_memory_barrier ();
3067 *(volatile gint32 *)ptr = value;
3070 void
3071 ves_icall_System_Threading_Thread_VolatileWrite8 (void *ptr, gint64 value)
3073 mono_memory_barrier ();
3074 *(volatile gint64 *)ptr = value;
3077 void
3078 ves_icall_System_Threading_Thread_VolatileWriteIntPtr (void *ptr, void *value)
3080 mono_memory_barrier ();
3081 *(volatile void **)ptr = value;
3084 void
3085 ves_icall_System_Threading_Thread_VolatileWriteObject (void *ptr, MonoObject *value)
3087 mono_memory_barrier ();
3088 mono_gc_wbarrier_generic_store_internal (ptr, value);
3091 void
3092 ves_icall_System_Threading_Thread_VolatileWriteDouble (void *ptr, double value)
3094 mono_memory_barrier ();
3095 *(volatile double *)ptr = value;
3098 void
3099 ves_icall_System_Threading_Thread_VolatileWriteFloat (void *ptr, float value)
3101 mono_memory_barrier ();
3102 *(volatile float *)ptr = value;
3105 void
3106 ves_icall_System_Threading_Volatile_Write8 (void *ptr, gint64 value)
3108 #if SIZEOF_VOID_P == 4
3109 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
3110 mono_interlocked_lock ();
3111 *(gint64*)ptr = value;
3112 mono_interlocked_unlock ();
3113 return;
3115 #endif
3117 mono_atomic_store_i64 ((volatile gint64 *)ptr, value);
3120 void
3121 ves_icall_System_Threading_Volatile_WriteU8 (void *ptr, guint64 value)
3123 #if SIZEOF_VOID_P == 4
3124 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
3125 mono_interlocked_lock ();
3126 *(guint64*)ptr = value;
3127 mono_interlocked_unlock ();
3128 return;
3130 #endif
3132 mono_atomic_store_i64 ((volatile gint64 *)ptr, (gint64)value);
3135 void
3136 ves_icall_System_Threading_Volatile_WriteDouble (void *ptr, double value)
3138 LongDoubleUnion u;
3140 #if SIZEOF_VOID_P == 4
3141 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
3142 mono_interlocked_lock ();
3143 *(double*)ptr = value;
3144 mono_interlocked_unlock ();
3145 return;
3147 #endif
3149 u.fval = value;
3151 mono_atomic_store_i64 ((volatile gint64 *)ptr, u.ival);
3154 static void
3155 free_context (void *user_data)
3157 ContextStaticData *data = (ContextStaticData*)user_data;
3159 mono_threads_lock ();
3162 * There is no guarantee that, by the point this reference queue callback
3163 * has been invoked, the GC handle associated with the object will fail to
3164 * resolve as one might expect. So if we don't free and remove the GC
3165 * handle here, free_context_static_data_helper () could end up resolving
3166 * a GC handle to an actually-dead context which would contain a pointer
3167 * to an already-freed static data segment, resulting in a crash when
3168 * accessing it.
3170 g_hash_table_remove (contexts, GUINT_TO_POINTER (data->gc_handle));
3172 mono_threads_unlock ();
3174 mono_gchandle_free_internal (data->gc_handle);
3175 mono_free_static_data (data->static_data);
3176 g_free (data);
3179 void
3180 mono_threads_register_app_context (MonoAppContextHandle ctx, MonoError *error)
3182 error_init (error);
3183 mono_threads_lock ();
3185 //g_print ("Registering context %d in domain %d\n", ctx->context_id, ctx->domain_id);
3187 if (!contexts)
3188 contexts = g_hash_table_new (NULL, NULL);
3190 if (!context_queue)
3191 context_queue = mono_gc_reference_queue_new_internal (free_context);
3193 gpointer gch = GUINT_TO_POINTER (mono_gchandle_new_weakref_from_handle (MONO_HANDLE_CAST (MonoObject, ctx)));
3194 g_hash_table_insert (contexts, gch, gch);
3197 * We use this intermediate structure to contain a duplicate pointer to
3198 * the static data because we can't rely on being able to resolve the GC
3199 * handle in the reference queue callback.
3201 ContextStaticData *data = g_new0 (ContextStaticData, 1);
3202 data->gc_handle = GPOINTER_TO_UINT (gch);
3203 MONO_HANDLE_SETVAL (ctx, data, ContextStaticData*, data);
3205 context_adjust_static_data (ctx);
3206 mono_gc_reference_queue_add_handle (context_queue, ctx, data);
3208 mono_threads_unlock ();
3210 MONO_PROFILER_RAISE (context_loaded, (MONO_HANDLE_RAW (ctx)));
3213 #ifndef ENABLE_NETCORE
3214 void
3215 ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppContextHandle ctx, MonoError *error)
3217 mono_threads_register_app_context (ctx, error);
3219 #endif
3221 void
3222 mono_threads_release_app_context (MonoAppContext* ctx, MonoError *error)
3225 * NOTE: Since finalizers are unreliable for the purposes of ensuring
3226 * cleanup in exceptional circumstances, we don't actually do any
3227 * cleanup work here. We instead do this via a reference queue.
3230 //g_print ("Releasing context %d in domain %d\n", ctx->context_id, ctx->domain_id);
3232 MONO_PROFILER_RAISE (context_unloaded, (ctx));
3235 #ifndef ENABLE_NETCORE
3236 void
3237 ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContextHandle ctx, MonoError *error)
3239 mono_threads_release_app_context (MONO_HANDLE_RAW (ctx), error); /* FIXME use handles in mono_threads_release_app_context */
3241 #endif
3243 void mono_thread_init (MonoThreadStartCB start_cb,
3244 MonoThreadAttachCB attach_cb)
3246 mono_coop_mutex_init_recursive (&threads_mutex);
3248 #if SIZEOF_VOID_P == 4
3249 mono_os_mutex_init (&interlocked_mutex);
3250 #endif
3251 mono_coop_mutex_init_recursive(&joinable_threads_mutex);
3253 mono_os_event_init (&background_change_event, FALSE);
3255 mono_coop_cond_init (&pending_native_thread_join_calls_event);
3256 mono_coop_cond_init (&zero_pending_joinable_thread_event);
3258 mono_init_static_data_info (&thread_static_info);
3259 mono_init_static_data_info (&context_static_info);
3261 mono_thread_start_cb = start_cb;
3262 mono_thread_attach_cb = attach_cb;
3266 static gpointer
3267 thread_attach (MonoThreadInfo *info)
3269 return mono_gc_thread_attach (info);
3272 static void
3273 thread_detach (MonoThreadInfo *info)
3275 MonoInternalThread *internal;
3276 guint32 gchandle;
3278 /* If a delegate is passed to native code and invoked on a thread we dont
3279 * know about, marshal will register it with mono_threads_attach_coop, but
3280 * we have no way of knowing when that thread goes away. SGen has a TSD
3281 * so we assume that if the domain is still registered, we can detach
3282 * the thread */
3284 g_assert (info);
3285 g_assert (mono_thread_info_is_current (info));
3287 if (!mono_thread_info_try_get_internal_thread_gchandle (info, &gchandle))
3288 return;
3290 internal = (MonoInternalThread*) mono_gchandle_get_target_internal (gchandle);
3291 g_assert (internal);
3293 mono_thread_detach_internal (internal);
3296 static void
3297 thread_detach_with_lock (MonoThreadInfo *info)
3299 mono_gc_thread_detach_with_lock (info);
3302 static gboolean
3303 thread_in_critical_region (MonoThreadInfo *info)
3305 return mono_gc_thread_in_critical_region (info);
3308 static gboolean
3309 ip_in_critical_region (MonoDomain *domain, gpointer ip)
3311 MonoJitInfo *ji;
3312 MonoMethod *method;
3315 * We pass false for 'try_aot' so this becomes async safe.
3316 * It won't find aot methods whose jit info is not yet loaded,
3317 * so we preload their jit info in the JIT.
3319 ji = mono_jit_info_table_find_internal (domain, ip, FALSE, FALSE);
3320 if (!ji)
3321 return FALSE;
3323 method = mono_jit_info_get_method (ji);
3324 g_assert (method);
3326 return mono_gc_is_critical_method (method);
3329 static void
3330 thread_flags_changing (MonoThreadInfoFlags old, MonoThreadInfoFlags new_)
3332 mono_gc_skip_thread_changing (!!(new_ & MONO_THREAD_INFO_FLAGS_NO_GC));
3335 static void
3336 thread_flags_changed (MonoThreadInfoFlags old, MonoThreadInfoFlags new_)
3338 mono_gc_skip_thread_changed (!!(new_ & MONO_THREAD_INFO_FLAGS_NO_GC));
3341 void
3342 mono_thread_callbacks_init (void)
3344 MonoThreadInfoCallbacks cb;
3346 memset (&cb, 0, sizeof(cb));
3347 cb.thread_attach = thread_attach;
3348 cb.thread_detach = thread_detach;
3349 cb.thread_detach_with_lock = thread_detach_with_lock;
3350 cb.ip_in_critical_region = ip_in_critical_region;
3351 cb.thread_in_critical_region = thread_in_critical_region;
3352 cb.thread_flags_changing = thread_flags_changing;
3353 cb.thread_flags_changed = thread_flags_changed;
3354 mono_thread_info_callbacks_init (&cb);
3358 * mono_thread_cleanup:
3360 void
3361 mono_thread_cleanup (void)
3363 /* Wait for pending threads to park on joinable threads list */
3364 /* NOTE, waiting on this should be extremely rare and will only happen */
3365 /* under certain specific conditions. */
3366 gboolean wait_result = threads_wait_pending_joinable_threads (2000);
3367 if (!wait_result)
3368 g_warning ("Waiting on threads to park on joinable thread list timed out.");
3370 mono_threads_join_threads ();
3372 #if !defined(HOST_WIN32)
3373 /* The main thread must abandon any held mutexes (particularly
3374 * important for named mutexes as they are shared across
3375 * processes, see bug 74680.) This will happen when the
3376 * thread exits, but if it's not running in a subthread it
3377 * won't exit in time.
3379 if (!mono_runtime_get_no_exec ())
3380 mono_w32mutex_abandon (mono_thread_internal_current ());
3381 #endif
3383 #if 0
3384 /* This stuff needs more testing, it seems one of these
3385 * critical sections can be locked when mono_thread_cleanup is
3386 * called.
3388 mono_coop_mutex_destroy (&threads_mutex);
3389 mono_os_mutex_destroy (&interlocked_mutex);
3390 mono_os_mutex_destroy (&delayed_free_table_mutex);
3391 mono_os_mutex_destroy (&small_id_mutex);
3392 mono_coop_cond_destroy (&zero_pending_joinable_thread_event);
3393 mono_coop_cond_destroy (&pending_native_thread_join_calls_event);
3394 mono_os_event_destroy (&background_change_event);
3395 #endif
3398 void
3399 mono_threads_install_cleanup (MonoThreadCleanupFunc func)
3401 mono_thread_cleanup_fn = func;
3405 * mono_thread_set_manage_callback:
3407 void
3408 mono_thread_set_manage_callback (MonoThread *thread, MonoThreadManageCallback func)
3410 thread->internal_thread->manage_callback = func;
3413 G_GNUC_UNUSED
3414 static void print_tids (gpointer key, gpointer value, gpointer user)
3416 /* GPOINTER_TO_UINT breaks horribly if sizeof(void *) >
3417 * sizeof(uint) and a cast to uint would overflow
3419 /* Older versions of glib don't have G_GSIZE_FORMAT, so just
3420 * print this as a pointer.
3422 g_message ("Waiting for: %p", key);
3425 struct wait_data
3427 MonoThreadHandle *handles[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
3428 MonoInternalThread *threads[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
3429 guint32 num;
3432 static void
3433 wait_for_tids (struct wait_data *wait, guint32 timeout, gboolean check_state_change)
3435 guint32 i;
3436 MonoThreadInfoWaitRet ret;
3438 THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num));
3440 /* Add the thread state change event, so it wakes
3441 * up if a thread changes to background mode. */
3443 MONO_ENTER_GC_SAFE;
3444 if (check_state_change)
3445 ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, &background_change_event, FALSE, timeout, TRUE);
3446 else
3447 ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, NULL, TRUE, timeout, TRUE);
3448 MONO_EXIT_GC_SAFE;
3450 if (ret == MONO_THREAD_INFO_WAIT_RET_FAILED) {
3451 /* See the comment in build_wait_tids() */
3452 THREAD_DEBUG (g_message ("%s: Wait failed", __func__));
3453 return;
3456 for( i = 0; i < wait->num; i++)
3457 mono_threads_close_thread_handle (wait->handles [i]);
3459 if (ret >= MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 && ret < (MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + wait->num)) {
3460 MonoInternalThread *internal;
3462 internal = wait->threads [ret - MONO_THREAD_INFO_WAIT_RET_SUCCESS_0];
3464 mono_threads_lock ();
3465 if (mono_g_hash_table_lookup (threads, (gpointer) internal->tid) == internal)
3466 g_error ("%s: failed to call mono_thread_detach_internal on thread %p, InternalThread: %p", __func__, internal->tid, internal);
3467 mono_threads_unlock ();
3471 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
3473 struct wait_data *wait=(struct wait_data *)user;
3475 if(wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS - 1) {
3476 MonoInternalThread *thread=(MonoInternalThread *)value;
3478 /* Ignore background threads, we abort them later */
3479 /* Do not lock here since it is not needed and the caller holds threads_lock */
3480 if (thread->state & ThreadState_Background) {
3481 THREAD_DEBUG (g_message ("%s: ignoring background thread %" G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3482 return; /* just leave, ignore */
3485 if (mono_gc_is_finalizer_internal_thread (thread)) {
3486 THREAD_DEBUG (g_message ("%s: ignoring finalizer thread %" G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3487 return;
3490 if (thread == mono_thread_internal_current ()) {
3491 THREAD_DEBUG (g_message ("%s: ignoring current thread %" G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3492 return;
3495 if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread)) {
3496 THREAD_DEBUG (g_message ("%s: ignoring main thread %" G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3497 return;
3500 if (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE) {
3501 THREAD_DEBUG (g_message ("%s: ignoring thread %" G_GSIZE_FORMAT "with DONT_MANAGE flag set.", __func__, (gsize)thread->tid));
3502 return;
3505 THREAD_DEBUG (g_message ("%s: Invoking mono_thread_manage callback on thread %p", __func__, thread));
3506 if ((thread->manage_callback == NULL) || (thread->manage_callback (thread->root_domain_thread) == TRUE)) {
3507 wait->handles[wait->num]=mono_threads_open_thread_handle (thread->handle);
3508 wait->threads[wait->num]=thread;
3509 wait->num++;
3511 THREAD_DEBUG (g_message ("%s: adding thread %" G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3512 } else {
3513 THREAD_DEBUG (g_message ("%s: ignoring (because of callback) thread %" G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3517 } else {
3518 /* Just ignore the rest, we can't do anything with
3519 * them yet
3524 static void
3525 abort_threads (gpointer key, gpointer value, gpointer user)
3527 struct wait_data *wait=(struct wait_data *)user;
3528 MonoNativeThreadId self = mono_native_thread_id_get ();
3529 MonoInternalThread *thread = (MonoInternalThread *)value;
3531 if (wait->num >= MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS)
3532 return;
3534 if (mono_native_thread_id_equals (thread_get_tid (thread), self))
3535 return;
3536 if (mono_gc_is_finalizer_internal_thread (thread))
3537 return;
3539 if ((thread->flags & MONO_THREAD_FLAG_DONT_MANAGE))
3540 return;
3542 wait->handles[wait->num] = mono_threads_open_thread_handle (thread->handle);
3543 wait->threads[wait->num] = thread;
3544 wait->num++;
3546 THREAD_DEBUG (g_print ("%s: Aborting id: %" G_GSIZE_FORMAT "\n", __func__, (gsize)thread->tid));
3547 mono_thread_internal_abort (thread, FALSE);
3550 /**
3551 * mono_threads_set_shutting_down:
3553 * Is called by a thread that wants to shut down Mono. If the runtime is already
3554 * shutting down, the calling thread is suspended/stopped, and this function never
3555 * returns.
3557 void
3558 mono_threads_set_shutting_down (void)
3560 MonoInternalThread *current_thread = mono_thread_internal_current ();
3562 mono_threads_lock ();
3564 if (shutting_down) {
3565 mono_threads_unlock ();
3567 /* Make sure we're properly suspended/stopped */
3569 LOCK_THREAD (current_thread);
3571 if (current_thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
3572 UNLOCK_THREAD (current_thread);
3573 mono_thread_execute_interruption_void ();
3574 } else {
3575 UNLOCK_THREAD (current_thread);
3578 /*since we're killing the thread, detach it.*/
3579 mono_thread_detach_internal (current_thread);
3581 /* Wake up other threads potentially waiting for us */
3582 mono_thread_info_exit (0);
3583 } else {
3584 shutting_down = TRUE;
3586 /* Not really a background state change, but this will
3587 * interrupt the main thread if it is waiting for all
3588 * the other threads.
3590 MONO_ENTER_GC_SAFE;
3591 mono_os_event_set (&background_change_event);
3592 MONO_EXIT_GC_SAFE;
3594 mono_threads_unlock ();
3599 * mono_thread_manage:
3601 void
3602 mono_thread_manage (void)
3604 struct wait_data wait_data;
3605 struct wait_data *wait = &wait_data;
3607 memset (wait, 0, sizeof (struct wait_data));
3608 /* join each thread that's still running */
3609 THREAD_DEBUG (g_message ("%s: Joining each running thread...", __func__));
3611 mono_threads_lock ();
3612 if(threads==NULL) {
3613 THREAD_DEBUG (g_message("%s: No threads", __func__));
3614 mono_threads_unlock ();
3615 return;
3618 mono_threads_unlock ();
3620 do {
3621 mono_threads_lock ();
3622 if (shutting_down) {
3623 /* somebody else is shutting down */
3624 mono_threads_unlock ();
3625 break;
3627 THREAD_DEBUG (g_message ("%s: There are %d threads to join", __func__, mono_g_hash_table_size (threads));
3628 mono_g_hash_table_foreach (threads, print_tids, NULL));
3630 MONO_ENTER_GC_SAFE;
3631 mono_os_event_reset (&background_change_event);
3632 MONO_EXIT_GC_SAFE;
3633 wait->num=0;
3634 /* We must zero all InternalThread pointers to avoid making the GC unhappy. */
3635 memset (wait->threads, 0, sizeof (wait->threads));
3636 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
3637 mono_threads_unlock ();
3638 if (wait->num > 0)
3639 /* Something to wait for */
3640 wait_for_tids (wait, MONO_INFINITE_WAIT, TRUE);
3641 THREAD_DEBUG (g_message ("%s: I have %d threads after waiting.", __func__, wait->num));
3642 } while(wait->num>0);
3644 /* Mono is shutting down, so just wait for the end */
3645 if (!mono_runtime_try_shutdown ()) {
3646 /*FIXME mono_thread_suspend probably should call mono_thread_execute_interruption when self interrupting. */
3647 mono_thread_suspend (mono_thread_internal_current ());
3648 mono_thread_execute_interruption_void ();
3651 #ifndef ENABLE_NETCORE
3653 * Under netcore, we don't abort any threads, just exit.
3654 * This is not a problem since we don't do runtime cleanup either.
3657 * Remove everything but the finalizer thread and self.
3658 * Also abort all the background threads
3659 * */
3660 do {
3661 mono_threads_lock ();
3663 wait->num = 0;
3664 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
3665 memset (wait->threads, 0, sizeof (wait->threads));
3666 mono_g_hash_table_foreach (threads, abort_threads, wait);
3668 mono_threads_unlock ();
3670 THREAD_DEBUG (g_message ("%s: wait->num is now %d", __func__, wait->num));
3671 if (wait->num > 0) {
3672 /* Something to wait for */
3673 wait_for_tids (wait, MONO_INFINITE_WAIT, FALSE);
3675 } while (wait->num > 0);
3676 #endif
3679 * give the subthreads a chance to really quit (this is mainly needed
3680 * to get correct user and system times from getrusage/wait/time(1)).
3681 * This could be removed if we avoid pthread_detach() and use pthread_join().
3683 mono_thread_info_yield ();
3686 static void
3687 collect_threads_for_suspend (gpointer key, gpointer value, gpointer user_data)
3689 MonoInternalThread *thread = (MonoInternalThread*)value;
3690 struct wait_data *wait = (struct wait_data*)user_data;
3693 * We try to exclude threads early, to avoid running into the MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
3694 * limitation.
3695 * This needs no locking.
3697 if ((thread->state & ThreadState_Suspended) != 0 ||
3698 (thread->state & ThreadState_Stopped) != 0)
3699 return;
3701 if (wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
3702 wait->handles [wait->num] = mono_threads_open_thread_handle (thread->handle);
3703 wait->threads [wait->num] = thread;
3704 wait->num++;
3709 * mono_thread_suspend_all_other_threads:
3711 * Suspend all managed threads except the finalizer thread and this thread. It is
3712 * not possible to resume them later.
3714 void mono_thread_suspend_all_other_threads (void)
3716 struct wait_data wait_data;
3717 struct wait_data *wait = &wait_data;
3718 int i;
3719 MonoNativeThreadId self = mono_native_thread_id_get ();
3720 guint32 eventidx = 0;
3721 gboolean starting, finished;
3723 memset (wait, 0, sizeof (struct wait_data));
3725 * The other threads could be in an arbitrary state at this point, i.e.
3726 * they could be starting up, shutting down etc. This means that there could be
3727 * threads which are not even in the threads hash table yet.
3731 * First we set a barrier which will be checked by all threads before they
3732 * are added to the threads hash table, and they will exit if the flag is set.
3733 * This ensures that no threads could be added to the hash later.
3734 * We will use shutting_down as the barrier for now.
3736 g_assert (shutting_down);
3739 * We make multiple calls to WaitForMultipleObjects since:
3740 * - we can only wait for MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS threads
3741 * - some threads could exit without becoming suspended
3743 finished = FALSE;
3744 while (!finished) {
3746 * Make a copy of the hashtable since we can't do anything with
3747 * threads while threads_mutex is held.
3749 wait->num = 0;
3750 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
3751 memset (wait->threads, 0, sizeof (wait->threads));
3752 mono_threads_lock ();
3753 mono_g_hash_table_foreach (threads, collect_threads_for_suspend, wait);
3754 mono_threads_unlock ();
3756 eventidx = 0;
3757 /* Get the suspended events that we'll be waiting for */
3758 for (i = 0; i < wait->num; ++i) {
3759 MonoInternalThread *thread = wait->threads [i];
3761 if (mono_native_thread_id_equals (thread_get_tid (thread), self)
3762 || mono_gc_is_finalizer_internal_thread (thread)
3763 || (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)
3765 mono_threads_close_thread_handle (wait->handles [i]);
3766 wait->threads [i] = NULL;
3767 continue;
3770 LOCK_THREAD (thread);
3772 if (thread->state & (ThreadState_Suspended | ThreadState_Stopped)) {
3773 UNLOCK_THREAD (thread);
3774 mono_threads_close_thread_handle (wait->handles [i]);
3775 wait->threads [i] = NULL;
3776 continue;
3779 ++eventidx;
3781 /* Convert abort requests into suspend requests */
3782 if ((thread->state & ThreadState_AbortRequested) != 0)
3783 thread->state &= ~ThreadState_AbortRequested;
3785 thread->state |= ThreadState_SuspendRequested;
3786 MONO_ENTER_GC_SAFE;
3787 mono_os_event_reset (thread->suspended);
3788 MONO_EXIT_GC_SAFE;
3790 /* Signal the thread to suspend + calls UNLOCK_THREAD (thread) */
3791 async_suspend_internal (thread, TRUE);
3793 mono_threads_close_thread_handle (wait->handles [i]);
3794 wait->threads [i] = NULL;
3796 if (eventidx <= 0) {
3798 * If there are threads which are starting up, we wait until they
3799 * are suspended when they try to register in the threads hash.
3800 * This is guaranteed to finish, since the threads which can create new
3801 * threads get suspended after a while.
3802 * FIXME: The finalizer thread can still create new threads.
3804 mono_threads_lock ();
3805 if (threads_starting_up)
3806 starting = mono_g_hash_table_size (threads_starting_up) > 0;
3807 else
3808 starting = FALSE;
3809 mono_threads_unlock ();
3810 if (starting)
3811 mono_thread_info_sleep (100, NULL);
3812 else
3813 finished = TRUE;
3818 typedef struct {
3819 MonoInternalThread *thread;
3820 MonoStackFrameInfo *frames;
3821 int nframes, max_frames;
3822 int nthreads, max_threads;
3823 MonoInternalThread **threads;
3824 } ThreadDumpUserData;
3826 static gboolean thread_dump_requested;
3828 /* This needs to be async safe */
3829 static gboolean
3830 collect_frame (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
3832 ThreadDumpUserData *ud = (ThreadDumpUserData *)data;
3834 if (ud->nframes < ud->max_frames) {
3835 memcpy (&ud->frames [ud->nframes], frame, sizeof (MonoStackFrameInfo));
3836 ud->nframes ++;
3839 return FALSE;
3842 /* This needs to be async safe */
3843 static SuspendThreadResult
3844 get_thread_dump (MonoThreadInfo *info, gpointer ud)
3846 ThreadDumpUserData *user_data = (ThreadDumpUserData *)ud;
3847 MonoInternalThread *thread = user_data->thread;
3849 #if 0
3850 /* This no longer works with remote unwinding */
3851 g_string_append_printf (text, " tid=0x%p this=0x%p ", (gpointer)(gsize)thread->tid, thread);
3852 mono_thread_internal_describe (thread, text);
3853 g_string_append (text, "\n");
3854 #endif
3856 if (thread == mono_thread_internal_current ())
3857 mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (collect_frame, NULL, MONO_UNWIND_SIGNAL_SAFE, ud);
3858 else
3859 mono_get_eh_callbacks ()->mono_walk_stack_with_state (collect_frame, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, ud);
3861 return MonoResumeThread;
3864 typedef struct {
3865 int nthreads, max_threads;
3867 guint32 *threads;
3868 } CollectThreadsUserData;
3870 typedef struct {
3871 int nthreads, max_threads;
3872 MonoNativeThreadId *threads;
3873 } CollectThreadIdsUserData;
3875 static void
3876 collect_thread (gpointer key, gpointer value, gpointer user)
3878 CollectThreadsUserData *ud = (CollectThreadsUserData *)user;
3879 MonoInternalThread *thread = (MonoInternalThread *)value;
3881 if (ud->nthreads < ud->max_threads)
3882 ud->threads [ud->nthreads ++] = mono_gchandle_new_internal (&thread->obj, TRUE);
3886 * Collect running threads into the THREADS array.
3887 * THREADS should be an array allocated on the stack.
3889 static int
3890 collect_threads (guint32 *thread_handles, int max_threads)
3892 CollectThreadsUserData ud;
3894 mono_memory_barrier ();
3895 if (!threads)
3896 return 0;
3898 memset (&ud, 0, sizeof (ud));
3899 /* This array contains refs, but its on the stack, so its ok */
3900 ud.threads = thread_handles;
3901 ud.max_threads = max_threads;
3903 mono_threads_lock ();
3904 mono_g_hash_table_foreach (threads, collect_thread, &ud);
3905 mono_threads_unlock ();
3907 return ud.nthreads;
3910 void
3911 mono_gstring_append_thread_name (GString* text, MonoInternalThread* thread)
3913 // FIXME Conversion here is temporary. thread->name will change to UTF8.
3914 char* const name = thread->name
3915 ? g_utf16_to_utf8 (thread->name, thread->name_len, NULL, NULL, NULL)
3916 : NULL;
3917 g_string_append (text, "\n\"");
3918 g_string_append (text, name ? name :
3919 thread->threadpool_thread ? "<threadpool thread>" :
3920 "<unnamed thread>");
3921 g_string_append (text, "\"");
3922 g_free (name);
3925 static void
3926 dump_thread (MonoInternalThread *thread, ThreadDumpUserData *ud, FILE* output_file)
3928 GString* text = g_string_new (0);
3929 int i;
3931 ud->thread = thread;
3932 ud->nframes = 0;
3934 /* Collect frames for the thread */
3935 if (thread == mono_thread_internal_current ()) {
3936 get_thread_dump (mono_thread_info_current (), ud);
3937 } else {
3938 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, get_thread_dump, ud);
3942 * Do all the non async-safe work outside of get_thread_dump.
3944 mono_gstring_append_thread_name (text, thread);
3946 for (i = 0; i < ud->nframes; ++i) {
3947 MonoStackFrameInfo *frame = &ud->frames [i];
3948 MonoMethod *method = NULL;
3950 if (frame->type == FRAME_TYPE_MANAGED)
3951 method = mono_jit_info_get_method (frame->ji);
3953 if (method) {
3954 gchar *location = mono_debug_print_stack_frame (method, frame->native_offset, frame->domain);
3955 g_string_append_printf (text, " %s\n", location);
3956 g_free (location);
3957 } else {
3958 g_string_append_printf (text, " at <unknown> <0x%05x>\n", frame->native_offset);
3962 g_fprintf (output_file, "%s", text->str);
3964 #if PLATFORM_WIN32 && TARGET_WIN32 && _DEBUG
3965 OutputDebugStringA(text->str);
3966 #endif
3968 g_string_free (text, TRUE);
3969 fflush (output_file);
3972 static void
3973 mono_get_time_of_day (struct timeval *tv) {
3974 #ifdef WIN32
3975 struct _timeb time;
3976 _ftime(&time);
3977 tv->tv_sec = time.time;
3978 tv->tv_usec = time.millitm * 1000;
3979 #else
3980 if (gettimeofday (tv, NULL) == -1) {
3981 g_error ("gettimeofday() failed; errno is %d (%s)", errno, strerror (errno));
3983 #endif
3986 static void
3987 mono_local_time (const struct timeval *tv, struct tm *tm) {
3988 #ifdef HAVE_LOCALTIME_R
3989 localtime_r(&tv->tv_sec, tm);
3990 #else
3991 time_t const tv_sec = tv->tv_sec; // Copy due to Win32/Posix contradiction.
3992 *tm = *localtime (&tv_sec);
3993 #endif
3996 void
3997 mono_threads_perform_thread_dump (void)
3999 FILE* output_file = NULL;
4000 ThreadDumpUserData ud;
4001 guint32 thread_array [128];
4002 int tindex, nthreads;
4004 if (!thread_dump_requested)
4005 return;
4007 if (thread_dump_dir != NULL) {
4008 GString* path = g_string_new (0);
4009 char time_str[80];
4010 struct timeval tv;
4011 long ms;
4012 struct tm tod;
4013 mono_get_time_of_day (&tv);
4014 mono_local_time(&tv, &tod);
4015 strftime(time_str, sizeof(time_str), MONO_STRFTIME_F "_" MONO_STRFTIME_T, &tod);
4016 ms = tv.tv_usec / 1000;
4017 g_string_append_printf (path, "%s/%s.%03ld.tdump", thread_dump_dir, time_str, ms);
4018 output_file = fopen (path->str, "w");
4019 g_string_free (path, TRUE);
4021 if (output_file == NULL) {
4022 g_print ("Full thread dump:\n");
4025 /* Make a copy of the threads hash to avoid doing work inside threads_lock () */
4026 nthreads = collect_threads (thread_array, 128);
4028 memset (&ud, 0, sizeof (ud));
4029 ud.frames = g_new0 (MonoStackFrameInfo, 256);
4030 ud.max_frames = 256;
4032 for (tindex = 0; tindex < nthreads; ++tindex) {
4033 guint32 handle = thread_array [tindex];
4034 MonoInternalThread *thread = (MonoInternalThread *) mono_gchandle_get_target_internal (handle);
4035 dump_thread (thread, &ud, output_file != NULL ? output_file : stdout);
4036 mono_gchandle_free_internal (handle);
4039 if (output_file != NULL) {
4040 fclose (output_file);
4042 g_free (ud.frames);
4044 thread_dump_requested = FALSE;
4047 /* Obtain the thread dump of all threads */
4048 static gboolean
4049 mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_frames, MonoError *error)
4052 ThreadDumpUserData ud;
4053 guint32 thread_array [128];
4054 MonoDomain *domain = mono_domain_get ();
4055 MonoDebugSourceLocation *location;
4056 int tindex, nthreads;
4058 error_init (error);
4060 *out_threads = NULL;
4061 *out_stack_frames = NULL;
4063 /* Make a copy of the threads hash to avoid doing work inside threads_lock () */
4064 nthreads = collect_threads (thread_array, 128);
4066 memset (&ud, 0, sizeof (ud));
4067 ud.frames = g_new0 (MonoStackFrameInfo, 256);
4068 ud.max_frames = 256;
4070 *out_threads = mono_array_new_checked (domain, mono_defaults.thread_class, nthreads, error);
4071 goto_if_nok (error, leave);
4072 *out_stack_frames = mono_array_new_checked (domain, mono_defaults.array_class, nthreads, error);
4073 goto_if_nok (error, leave);
4075 for (tindex = 0; tindex < nthreads; ++tindex) {
4076 guint32 handle = thread_array [tindex];
4077 MonoInternalThread *thread = (MonoInternalThread *) mono_gchandle_get_target_internal (handle);
4079 MonoArray *thread_frames;
4080 int i;
4082 ud.thread = thread;
4083 ud.nframes = 0;
4085 /* Collect frames for the thread */
4086 if (thread == mono_thread_internal_current ()) {
4087 get_thread_dump (mono_thread_info_current (), &ud);
4088 } else {
4089 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, get_thread_dump, &ud);
4092 mono_array_setref_fast (*out_threads, tindex, mono_thread_current_for_thread (thread));
4094 thread_frames = mono_array_new_checked (domain, mono_defaults.stack_frame_class, ud.nframes, error);
4095 goto_if_nok (error, leave);
4096 mono_array_setref_fast (*out_stack_frames, tindex, thread_frames);
4098 for (i = 0; i < ud.nframes; ++i) {
4099 MonoStackFrameInfo *frame = &ud.frames [i];
4100 MonoMethod *method = NULL;
4101 MonoStackFrame *sf = (MonoStackFrame *)mono_object_new_checked (domain, mono_defaults.stack_frame_class, error);
4102 goto_if_nok (error, leave);
4104 sf->native_offset = frame->native_offset;
4106 if (frame->type == FRAME_TYPE_MANAGED)
4107 method = mono_jit_info_get_method (frame->ji);
4109 if (method) {
4110 sf->method_address = (gsize) frame->ji->code_start;
4112 MonoReflectionMethod *rm = mono_method_get_object_checked (domain, method, NULL, error);
4113 goto_if_nok (error, leave);
4114 MONO_OBJECT_SETREF_INTERNAL (sf, method, rm);
4116 location = mono_debug_lookup_source_location (method, frame->native_offset, domain);
4117 if (location) {
4118 sf->il_offset = location->il_offset;
4120 if (location->source_file) {
4121 MonoString *filename = mono_string_new_checked (domain, location->source_file, error);
4122 goto_if_nok (error, leave);
4123 MONO_OBJECT_SETREF_INTERNAL (sf, filename, filename);
4124 sf->line = location->row;
4125 sf->column = location->column;
4127 mono_debug_free_source_location (location);
4128 } else {
4129 sf->il_offset = -1;
4132 mono_array_setref_internal (thread_frames, i, sf);
4135 mono_gchandle_free_internal (handle);
4138 leave:
4139 g_free (ud.frames);
4140 return is_ok (error);
4144 * mono_threads_request_thread_dump:
4146 * Ask all threads except the current to print their stacktrace to stdout.
4148 void
4149 mono_threads_request_thread_dump (void)
4151 /*The new thread dump code runs out of the finalizer thread. */
4152 thread_dump_requested = TRUE;
4153 mono_gc_finalize_notify ();
4156 struct ref_stack {
4157 gpointer *refs;
4158 gint allocated; /* +1 so that refs [allocated] == NULL */
4159 gint bottom;
4162 typedef struct ref_stack RefStack;
4164 static RefStack *
4165 ref_stack_new (gint initial_size)
4167 RefStack *rs;
4169 initial_size = MAX (initial_size, 16) + 1;
4170 rs = g_new0 (RefStack, 1);
4171 rs->refs = g_new0 (gpointer, initial_size);
4172 rs->allocated = initial_size;
4173 return rs;
4176 static void
4177 ref_stack_destroy (gpointer ptr)
4179 RefStack *rs = (RefStack *)ptr;
4181 if (rs != NULL) {
4182 g_free (rs->refs);
4183 g_free (rs);
4187 static void
4188 ref_stack_push (RefStack *rs, gpointer ptr)
4190 g_assert (rs != NULL);
4192 if (rs->bottom >= rs->allocated) {
4193 rs->refs = (void **)g_realloc (rs->refs, rs->allocated * 2 * sizeof (gpointer) + 1);
4194 rs->allocated <<= 1;
4195 rs->refs [rs->allocated] = NULL;
4197 rs->refs [rs->bottom++] = ptr;
4200 static void
4201 ref_stack_pop (RefStack *rs)
4203 if (rs == NULL || rs->bottom == 0)
4204 return;
4206 rs->bottom--;
4207 rs->refs [rs->bottom] = NULL;
4210 static gboolean
4211 ref_stack_find (RefStack *rs, gpointer ptr)
4213 gpointer *refs;
4215 if (rs == NULL)
4216 return FALSE;
4218 for (refs = rs->refs; refs && *refs; refs++) {
4219 if (*refs == ptr)
4220 return TRUE;
4222 return FALSE;
4226 * mono_thread_push_appdomain_ref:
4228 * Register that the current thread may have references to objects in domain
4229 * @domain on its stack. Each call to this function should be paired with a
4230 * call to pop_appdomain_ref.
4232 void
4233 mono_thread_push_appdomain_ref (MonoDomain *domain)
4235 MonoInternalThread *thread = mono_thread_internal_current ();
4237 if (thread) {
4238 /* printf ("PUSH REF: %" G_GSIZE_FORMAT " -> %s.\n", (gsize)thread->tid, domain->friendly_name); */
4239 SPIN_LOCK (thread->lock_thread_id);
4240 if (thread->appdomain_refs == NULL)
4241 thread->appdomain_refs = ref_stack_new (16);
4242 ref_stack_push ((RefStack *)thread->appdomain_refs, domain);
4243 SPIN_UNLOCK (thread->lock_thread_id);
4247 void
4248 mono_thread_pop_appdomain_ref (void)
4250 MonoInternalThread *thread = mono_thread_internal_current ();
4252 if (thread) {
4253 /* printf ("POP REF: %" G_GSIZE_FORMAT " -> %s.\n", (gsize)thread->tid, ((MonoDomain*)(thread->appdomain_refs->data))->friendly_name); */
4254 SPIN_LOCK (thread->lock_thread_id);
4255 ref_stack_pop ((RefStack *)thread->appdomain_refs);
4256 SPIN_UNLOCK (thread->lock_thread_id);
4260 gboolean
4261 mono_thread_internal_has_appdomain_ref (MonoInternalThread *thread, MonoDomain *domain)
4263 gboolean res;
4264 SPIN_LOCK (thread->lock_thread_id);
4265 res = ref_stack_find ((RefStack *)thread->appdomain_refs, domain);
4266 SPIN_UNLOCK (thread->lock_thread_id);
4267 return res;
4270 gboolean
4271 mono_thread_has_appdomain_ref (MonoThread *thread, MonoDomain *domain)
4273 return mono_thread_internal_has_appdomain_ref (thread->internal_thread, domain);
4276 typedef struct abort_appdomain_data {
4277 struct wait_data wait;
4278 MonoDomain *domain;
4279 } abort_appdomain_data;
4281 static void
4282 collect_appdomain_thread (gpointer key, gpointer value, gpointer user_data)
4284 MonoInternalThread *thread = (MonoInternalThread*)value;
4285 abort_appdomain_data *data = (abort_appdomain_data*)user_data;
4286 MonoDomain *domain = data->domain;
4288 if (mono_thread_internal_has_appdomain_ref (thread, domain)) {
4289 /* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread->tid, domain->friendly_name); */
4291 if(data->wait.num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
4292 data->wait.handles [data->wait.num] = mono_threads_open_thread_handle (thread->handle);
4293 data->wait.threads [data->wait.num] = thread;
4294 data->wait.num++;
4295 } else {
4296 /* Just ignore the rest, we can't do anything with
4297 * them yet
4304 * mono_threads_abort_appdomain_threads:
4306 * Abort threads which has references to the given appdomain.
4308 gboolean
4309 mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
4311 abort_appdomain_data user_data;
4312 gint64 start_time;
4313 int orig_timeout = timeout;
4314 int i;
4316 THREAD_DEBUG (g_message ("%s: starting abort", __func__));
4318 start_time = mono_msec_ticks ();
4319 do {
4320 mono_threads_lock ();
4322 user_data.domain = domain;
4323 user_data.wait.num = 0;
4324 /* This shouldn't take any locks */
4325 mono_g_hash_table_foreach (threads, collect_appdomain_thread, &user_data);
4326 mono_threads_unlock ();
4328 if (user_data.wait.num > 0) {
4329 /* Abort the threads outside the threads lock */
4330 for (i = 0; i < user_data.wait.num; ++i)
4331 mono_thread_internal_abort (user_data.wait.threads [i], TRUE);
4334 * We should wait for the threads either to abort, or to leave the
4335 * domain. We can't do the latter, so we wait with a timeout.
4337 wait_for_tids (&user_data.wait, 100, FALSE);
4340 /* Update remaining time */
4341 timeout -= mono_msec_ticks () - start_time;
4342 start_time = mono_msec_ticks ();
4344 if (orig_timeout != -1 && timeout < 0)
4345 return FALSE;
4347 while (user_data.wait.num > 0);
4349 THREAD_DEBUG (g_message ("%s: abort done", __func__));
4351 return TRUE;
4354 /* This is a JIT icall. This icall is called from a finally block when
4355 * mono_install_handler_block_guard called by another thread has flipped the
4356 * finally block's exvar (see mono_find_exvar_for_offset). In that case, if
4357 * the finally is in an abort protected block, we must defer the abort
4358 * exception until we leave the abort protected block. Otherwise we proceed
4359 * with a synchronous self-abort.
4361 void
4362 ves_icall_thread_finish_async_abort (void)
4364 /* We were called from the handler block and are about to
4365 * leave it. (If we end up postponing the abort because we're
4366 * in an abort protected block, the unwinder won't run and
4367 * won't clear the handler block itself which will confuse the
4368 * unwinder if we're in a try {} catch {} and we throw again.
4369 * ie, this:
4370 * static Constructor () {
4371 * try {
4372 * try {
4373 * } finally {
4374 * icall (); // Thread.Abort landed here,
4375 * // and caused the handler block to be installed
4376 * if (exvar)
4377 * ves_icall_thread_finish_async_abort (); // we're here
4379 * throw E ();
4380 * } catch (E) {
4381 * // unwinder will get confused here and synthesize a self abort
4385 * More interestingly, this doesn't only happen with icalls - a JIT
4386 * trampoline is native code that will cause a handler to be installed.
4387 * So the above situation can happen with any code in a "finally"
4388 * clause.
4390 mono_get_eh_callbacks ()->mono_uninstall_current_handler_block_guard ();
4391 /* Just set the async interruption requested bit. Rely on the icall
4392 * wrapper of this icall to process the thread interruption, respecting
4393 * any abort protection blocks in our call stack.
4395 mono_thread_set_self_interruption_respect_abort_prot ();
4399 * mono_thread_get_undeniable_exception:
4401 * Return an exception which needs to be raised when leaving a catch clause.
4402 * This is used for undeniable exception propagation.
4404 MonoException*
4405 mono_thread_get_undeniable_exception (void)
4407 MonoInternalThread *thread = mono_thread_internal_current ();
4409 if (!(thread && thread->abort_exc && !is_running_protected_wrapper ()))
4410 return NULL;
4412 // We don't want to have our exception effect calls made by
4413 // the catching block
4415 if (!mono_get_eh_callbacks ()->mono_above_abort_threshold ())
4416 return NULL;
4419 * FIXME: Clear the abort exception and return an AppDomainUnloaded
4420 * exception if the thread no longer references a dying appdomain.
4422 thread->abort_exc->trace_ips = NULL;
4423 thread->abort_exc->stack_trace = NULL;
4424 return thread->abort_exc;
4427 #if MONO_SMALL_CONFIG
4428 #define NUM_STATIC_DATA_IDX 4
4429 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
4430 64, 256, 1024, 4096
4432 #else
4433 #define NUM_STATIC_DATA_IDX 8
4434 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
4435 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
4437 #endif
4439 static MonoBitSet *thread_reference_bitmaps [NUM_STATIC_DATA_IDX];
4440 static MonoBitSet *context_reference_bitmaps [NUM_STATIC_DATA_IDX];
4442 static void
4443 mark_slots (void *addr, MonoBitSet **bitmaps, MonoGCMarkFunc mark_func, void *gc_data)
4445 gpointer *static_data = (gpointer *)addr;
4447 for (int i = 0; i < NUM_STATIC_DATA_IDX; ++i) {
4448 void **ptr = (void **)static_data [i];
4450 if (!ptr)
4451 continue;
4453 MONO_BITSET_FOREACH (bitmaps [i], idx, {
4454 void **p = ptr + idx;
4456 if (*p)
4457 mark_func ((MonoObject**)p, gc_data);
4462 static void
4463 mark_tls_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
4465 mark_slots (addr, thread_reference_bitmaps, mark_func, gc_data);
4468 static void
4469 mark_ctx_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
4471 mark_slots (addr, context_reference_bitmaps, mark_func, gc_data);
4475 * mono_alloc_static_data
4477 * Allocate memory blocks for storing threads or context static data
4479 static void
4480 mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, void *alloc_key, gboolean threadlocal)
4482 guint idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4483 int i;
4485 gpointer* static_data = *static_data_ptr;
4486 if (!static_data) {
4487 static MonoGCDescriptor tls_desc = MONO_GC_DESCRIPTOR_NULL;
4488 static MonoGCDescriptor ctx_desc = MONO_GC_DESCRIPTOR_NULL;
4490 if (mono_gc_user_markers_supported ()) {
4491 if (tls_desc == MONO_GC_DESCRIPTOR_NULL)
4492 tls_desc = mono_gc_make_root_descr_user (mark_tls_slots);
4494 if (ctx_desc == MONO_GC_DESCRIPTOR_NULL)
4495 ctx_desc = mono_gc_make_root_descr_user (mark_ctx_slots);
4498 static_data = (void **)mono_gc_alloc_fixed (static_data_size [0], threadlocal ? tls_desc : ctx_desc,
4499 threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC,
4500 alloc_key,
4501 threadlocal ? "ThreadStatic Fields" : "ContextStatic Fields");
4502 *static_data_ptr = static_data;
4503 static_data [0] = static_data;
4506 for (i = 1; i <= idx; ++i) {
4507 if (static_data [i])
4508 continue;
4510 if (mono_gc_user_markers_supported ())
4511 static_data [i] = g_malloc0 (static_data_size [i]);
4512 else
4513 static_data [i] = mono_gc_alloc_fixed (static_data_size [i], MONO_GC_DESCRIPTOR_NULL,
4514 threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC,
4515 alloc_key,
4516 threadlocal ? "ThreadStatic Fields" : "ContextStatic Fields");
4520 static void
4521 mono_free_static_data (gpointer* static_data)
4523 int i;
4524 for (i = 1; i < NUM_STATIC_DATA_IDX; ++i) {
4525 gpointer p = static_data [i];
4526 if (!p)
4527 continue;
4529 * At this point, the static data pointer array is still registered with the
4530 * GC, so must ensure that mark_tls_slots() will not encounter any invalid
4531 * data. Freeing the individual arrays without first nulling their slots
4532 * would make it possible for mark_tls/ctx_slots() to encounter a pointer to
4533 * such an already freed array. See bug #13813.
4535 static_data [i] = NULL;
4536 mono_memory_write_barrier ();
4537 if (mono_gc_user_markers_supported ())
4538 g_free (p);
4539 else
4540 mono_gc_free_fixed (p);
4542 mono_gc_free_fixed (static_data);
4546 * mono_init_static_data_info
4548 * Initializes static data counters
4550 static void mono_init_static_data_info (StaticDataInfo *static_data)
4552 static_data->idx = 0;
4553 static_data->offset = 0;
4554 static_data->freelist = NULL;
4558 * mono_alloc_static_data_slot
4560 * Generates an offset for static data. static_data contains the counters
4561 * used to generate it.
4563 static guint32
4564 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align)
4566 if (!static_data->idx && !static_data->offset) {
4568 * we use the first chunk of the first allocation also as
4569 * an array for the rest of the data
4571 static_data->offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX;
4573 static_data->offset += align - 1;
4574 static_data->offset &= ~(align - 1);
4575 if (static_data->offset + size >= static_data_size [static_data->idx]) {
4576 static_data->idx ++;
4577 g_assert (size <= static_data_size [static_data->idx]);
4578 g_assert (static_data->idx < NUM_STATIC_DATA_IDX);
4579 static_data->offset = 0;
4581 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (static_data->idx, static_data->offset, 0);
4582 static_data->offset += size;
4583 return offset;
4587 * LOCKING: requires that threads_mutex is held
4589 static void
4590 context_adjust_static_data (MonoAppContextHandle ctx_handle)
4592 MonoAppContext *ctx = MONO_HANDLE_RAW (ctx_handle);
4593 if (context_static_info.offset || context_static_info.idx > 0) {
4594 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (context_static_info.idx, context_static_info.offset, 0);
4595 mono_alloc_static_data (&ctx->static_data, offset, ctx, FALSE);
4596 ctx->data->static_data = ctx->static_data;
4601 * LOCKING: requires that threads_mutex is held
4603 static void
4604 alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
4606 MonoInternalThread *thread = (MonoInternalThread *)value;
4607 guint32 offset = GPOINTER_TO_UINT (user);
4609 mono_alloc_static_data (&(thread->static_data), offset, (void *) MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid), TRUE);
4613 * LOCKING: requires that threads_mutex is held
4615 static void
4616 alloc_context_static_data_helper (gpointer key, gpointer value, gpointer user)
4618 MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target_internal (GPOINTER_TO_INT (key));
4620 if (!ctx)
4621 return;
4623 guint32 offset = GPOINTER_TO_UINT (user);
4624 mono_alloc_static_data (&ctx->static_data, offset, ctx, FALSE);
4625 ctx->data->static_data = ctx->static_data;
4628 static StaticDataFreeList*
4629 search_slot_in_freelist (StaticDataInfo *static_data, guint32 size, guint32 align)
4631 StaticDataFreeList* prev = NULL;
4632 StaticDataFreeList* tmp = static_data->freelist;
4633 while (tmp) {
4634 if (tmp->size == size) {
4635 if (prev)
4636 prev->next = tmp->next;
4637 else
4638 static_data->freelist = tmp->next;
4639 return tmp;
4641 prev = tmp;
4642 tmp = tmp->next;
4644 return NULL;
4647 #if SIZEOF_VOID_P == 4
4648 #define ONE_P 1
4649 #else
4650 #define ONE_P 1ll
4651 #endif
4653 static void
4654 update_reference_bitmap (MonoBitSet **sets, guint32 offset, uintptr_t *bitmap, int numbits)
4656 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4657 if (!sets [idx])
4658 sets [idx] = mono_bitset_new (static_data_size [idx] / sizeof (uintptr_t), 0);
4659 MonoBitSet *rb = sets [idx];
4660 offset = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
4661 offset /= sizeof (uintptr_t);
4662 /* offset is now the bitmap offset */
4663 for (int i = 0; i < numbits; ++i) {
4664 if (bitmap [i / sizeof (uintptr_t)] & (ONE_P << (i & (sizeof (uintptr_t) * 8 -1))))
4665 mono_bitset_set_fast (rb, offset + i);
4669 static void
4670 clear_reference_bitmap (MonoBitSet **sets, guint32 offset, guint32 size)
4672 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4673 MonoBitSet *rb = sets [idx];
4674 offset = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
4675 offset /= sizeof (uintptr_t);
4676 /* offset is now the bitmap offset */
4677 for (int i = 0; i < size / sizeof (uintptr_t); i++)
4678 mono_bitset_clear_fast (rb, offset + i);
4681 guint32
4682 mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align, uintptr_t *bitmap, int numbits)
4684 g_assert (static_type == SPECIAL_STATIC_THREAD || static_type == SPECIAL_STATIC_CONTEXT);
4686 StaticDataInfo *info;
4687 MonoBitSet **sets;
4689 if (static_type == SPECIAL_STATIC_THREAD) {
4690 info = &thread_static_info;
4691 sets = thread_reference_bitmaps;
4692 } else {
4693 info = &context_static_info;
4694 sets = context_reference_bitmaps;
4697 mono_threads_lock ();
4699 StaticDataFreeList *item = search_slot_in_freelist (info, size, align);
4700 guint32 offset;
4702 if (item) {
4703 offset = item->offset;
4704 g_free (item);
4705 } else {
4706 offset = mono_alloc_static_data_slot (info, size, align);
4709 update_reference_bitmap (sets, offset, bitmap, numbits);
4711 if (static_type == SPECIAL_STATIC_THREAD) {
4712 /* This can be called during startup */
4713 if (threads != NULL)
4714 mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
4715 } else {
4716 if (contexts != NULL)
4717 g_hash_table_foreach (contexts, alloc_context_static_data_helper, GUINT_TO_POINTER (offset));
4719 ACCESS_SPECIAL_STATIC_OFFSET (offset, type) = SPECIAL_STATIC_OFFSET_TYPE_CONTEXT;
4722 mono_threads_unlock ();
4724 return offset;
4727 gpointer
4728 mono_get_special_static_data_for_thread (MonoInternalThread *thread, guint32 offset)
4730 guint32 static_type = ACCESS_SPECIAL_STATIC_OFFSET (offset, type);
4732 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4733 return get_thread_static_data (thread, offset);
4734 } else {
4735 return get_context_static_data (thread->current_appcontext, offset);
4739 gpointer
4740 mono_get_special_static_data (guint32 offset)
4742 return mono_get_special_static_data_for_thread (mono_thread_internal_current (), offset);
4745 typedef struct {
4746 guint32 offset;
4747 guint32 size;
4748 } OffsetSize;
4751 * LOCKING: requires that threads_mutex is held
4753 static void
4754 free_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
4756 MonoInternalThread *thread = (MonoInternalThread *)value;
4757 OffsetSize *data = (OffsetSize *)user;
4758 int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index);
4759 int off = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, offset);
4760 char *ptr;
4762 if (!thread->static_data || !thread->static_data [idx])
4763 return;
4764 ptr = ((char*) thread->static_data [idx]) + off;
4765 mono_gc_bzero_atomic (ptr, data->size);
4769 * LOCKING: requires that threads_mutex is held
4771 static void
4772 free_context_static_data_helper (gpointer key, gpointer value, gpointer user)
4774 MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target_internal (GPOINTER_TO_INT (key));
4776 if (!ctx)
4777 return;
4779 OffsetSize *data = (OffsetSize *)user;
4780 int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index);
4781 int off = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, offset);
4782 char *ptr;
4784 if (!ctx->static_data || !ctx->static_data [idx])
4785 return;
4787 ptr = ((char*) ctx->static_data [idx]) + off;
4788 mono_gc_bzero_atomic (ptr, data->size);
4791 static void
4792 do_free_special_slot (guint32 offset, guint32 size)
4794 guint32 static_type = ACCESS_SPECIAL_STATIC_OFFSET (offset, type);
4795 MonoBitSet **sets;
4796 StaticDataInfo *info;
4798 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4799 info = &thread_static_info;
4800 sets = thread_reference_bitmaps;
4801 } else {
4802 info = &context_static_info;
4803 sets = context_reference_bitmaps;
4806 guint32 data_offset = offset;
4807 ACCESS_SPECIAL_STATIC_OFFSET (data_offset, type) = 0;
4808 OffsetSize data = { data_offset, size };
4810 clear_reference_bitmap (sets, data.offset, data.size);
4812 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4813 if (threads != NULL)
4814 mono_g_hash_table_foreach (threads, free_thread_static_data_helper, &data);
4815 } else {
4816 if (contexts != NULL)
4817 g_hash_table_foreach (contexts, free_context_static_data_helper, &data);
4820 if (!mono_runtime_is_shutting_down ()) {
4821 StaticDataFreeList *item = g_new0 (StaticDataFreeList, 1);
4823 item->offset = offset;
4824 item->size = size;
4826 item->next = info->freelist;
4827 info->freelist = item;
4831 static void
4832 do_free_special (gpointer key, gpointer value, gpointer data)
4834 MonoClassField *field = (MonoClassField *)key;
4835 guint32 offset = GPOINTER_TO_UINT (value);
4836 gint32 align;
4837 guint32 size;
4838 size = mono_type_size (field->type, &align);
4839 do_free_special_slot (offset, size);
4842 void
4843 mono_alloc_special_static_data_free (GHashTable *special_static_fields)
4845 mono_threads_lock ();
4847 g_hash_table_foreach (special_static_fields, do_free_special, NULL);
4849 mono_threads_unlock ();
4852 #ifdef HOST_WIN32
4853 static void
4854 flush_thread_interrupt_queue (void)
4856 /* Consume pending APC calls for current thread.*/
4857 /* Since this function get's called from interrupt handler it must use a direct */
4858 /* Win32 API call and can't go through mono_coop_win32_wait_for_single_object_ex */
4859 /* or it will detect a pending interrupt and not entering the wait call needed */
4860 /* to consume pending APC's.*/
4861 MONO_ENTER_GC_SAFE;
4862 WaitForSingleObjectEx (GetCurrentThread (), 0, TRUE);
4863 MONO_EXIT_GC_SAFE;
4865 #else
4866 static void
4867 flush_thread_interrupt_queue (void)
4870 #endif
4873 * mono_thread_execute_interruption
4875 * Performs the operation that the requested thread state requires (abort,
4876 * suspend or stop)
4878 static gboolean
4879 mono_thread_execute_interruption (MonoExceptionHandle *pexc)
4881 gboolean fexc = FALSE;
4883 // Optimize away frame if caller supplied one.
4884 if (!pexc) {
4885 HANDLE_FUNCTION_ENTER ();
4886 MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, NULL);
4887 fexc = mono_thread_execute_interruption (&exc);
4888 HANDLE_FUNCTION_RETURN_VAL (fexc);
4891 MONO_REQ_GC_UNSAFE_MODE;
4893 MonoInternalThreadHandle thread = mono_thread_internal_current_handle ();
4894 MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, NULL);
4896 lock_thread_handle (thread);
4897 gboolean unlock = TRUE;
4899 /* MonoThread::interruption_requested can only be changed with atomics */
4900 if (!mono_thread_clear_interruption_requested_handle (thread))
4901 goto exit;
4903 MonoThreadObjectHandle sys_thread;
4904 sys_thread = mono_thread_current_handle ();
4906 flush_thread_interrupt_queue ();
4908 /* Clear the interrupted flag of the thread so it can wait again */
4909 mono_thread_info_clear_self_interrupt ();
4911 /* If there's a pending exception and an AbortRequested, the pending exception takes precedence */
4912 MONO_HANDLE_GET (exc, sys_thread, pending_exception);
4913 if (!MONO_HANDLE_IS_NULL (exc)) {
4914 // sys_thread->pending_exception = NULL;
4915 MONO_HANDLE_SETRAW (sys_thread, pending_exception, NULL);
4916 fexc = TRUE;
4917 goto exit;
4918 } else if (MONO_HANDLE_GETVAL (thread, state) & ThreadState_AbortRequested) {
4919 // Does the thread already have an abort exception?
4920 // If not, create a new one and set it on demand.
4921 // exc = thread->abort_exc;
4922 MONO_HANDLE_GET (exc, thread, abort_exc);
4923 if (MONO_HANDLE_IS_NULL (exc)) {
4924 ERROR_DECL (error);
4925 exc = mono_exception_new_thread_abort (error);
4926 mono_error_assert_ok (error); // FIXME
4927 // thread->abort_exc = exc;
4928 MONO_HANDLE_SET (thread, abort_exc, exc);
4930 fexc = TRUE;
4931 } else if (MONO_HANDLE_GETVAL (thread, state) & ThreadState_SuspendRequested) {
4932 /* calls UNLOCK_THREAD (thread) */
4933 self_suspend_internal ();
4934 unlock = FALSE;
4935 } else if (MONO_HANDLE_GETVAL (thread, thread_interrupt_requested)) {
4936 // thread->thread_interrupt_requested = FALSE
4937 MONO_HANDLE_SETVAL (thread, thread_interrupt_requested, MonoBoolean, FALSE);
4938 unlock_thread_handle (thread);
4939 unlock = FALSE;
4940 ERROR_DECL (error);
4941 exc = mono_exception_new_thread_interrupted (error);
4942 mono_error_assert_ok (error); // FIXME
4943 fexc = TRUE;
4945 exit:
4946 if (unlock)
4947 unlock_thread_handle (thread);
4949 if (fexc)
4950 MONO_HANDLE_ASSIGN (*pexc, exc);
4952 return fexc;
4955 static void
4956 mono_thread_execute_interruption_void (void)
4958 (void)mono_thread_execute_interruption (NULL);
4961 static MonoException*
4962 mono_thread_execute_interruption_ptr (void)
4964 HANDLE_FUNCTION_ENTER ();
4965 MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, NULL);
4966 MonoException * const exc_raw = mono_thread_execute_interruption (&exc) ? MONO_HANDLE_RAW (exc) : NULL;
4967 HANDLE_FUNCTION_RETURN_VAL (exc_raw);
4971 * mono_thread_request_interruption_internal
4973 * A signal handler can call this method to request the interruption of a
4974 * thread. The result of the interruption will depend on the current state of
4975 * the thread. If the result is an exception that needs to be thrown, it is
4976 * provided as return value.
4978 static gboolean
4979 mono_thread_request_interruption_internal (gboolean running_managed, MonoExceptionHandle *pexc)
4981 MonoInternalThread *thread = mono_thread_internal_current ();
4983 /* The thread may already be stopping */
4984 if (thread == NULL)
4985 return FALSE;
4987 if (!mono_thread_set_interruption_requested (thread))
4988 return FALSE;
4990 if (!running_managed || is_running_protected_wrapper ()) {
4991 /* Can't stop while in unmanaged code. Increase the global interruption
4992 request count. When exiting the unmanaged method the count will be
4993 checked and the thread will be interrupted. */
4995 /* this will awake the thread if it is in WaitForSingleObject
4996 or similar */
4997 #ifdef HOST_WIN32
4998 mono_win32_interrupt_wait (thread->thread_info, thread->native_handle, (DWORD)thread->tid);
4999 #else
5000 mono_thread_info_self_interrupt ();
5001 #endif
5002 return FALSE;
5004 return mono_thread_execute_interruption (pexc);
5007 static void
5008 mono_thread_request_interruption_native (void)
5010 (void)mono_thread_request_interruption_internal (FALSE, NULL);
5013 static gboolean
5014 mono_thread_request_interruption_managed (MonoExceptionHandle *exc)
5016 return mono_thread_request_interruption_internal (TRUE, exc);
5019 /*This function should be called by a thread after it has exited all of
5020 * its handle blocks at interruption time.*/
5021 void
5022 mono_thread_resume_interruption (gboolean exec)
5024 MonoInternalThread *thread = mono_thread_internal_current ();
5025 gboolean still_aborting;
5027 /* The thread may already be stopping */
5028 if (thread == NULL)
5029 return;
5031 LOCK_THREAD (thread);
5032 still_aborting = (thread->state & (ThreadState_AbortRequested)) != 0;
5033 UNLOCK_THREAD (thread);
5035 /*This can happen if the protected block called Thread::ResetAbort*/
5036 if (!still_aborting)
5037 return;
5039 if (!mono_thread_set_interruption_requested (thread))
5040 return;
5042 mono_thread_info_self_interrupt ();
5044 if (exec) // Ignore the exception here, it will be raised later.
5045 mono_thread_execute_interruption_void ();
5048 gboolean
5049 mono_thread_interruption_requested (void)
5051 if (thread_interruption_requested) {
5052 MonoInternalThread *thread = mono_thread_internal_current ();
5053 /* The thread may already be stopping */
5054 if (thread != NULL)
5055 return mono_thread_get_interruption_requested (thread);
5057 return FALSE;
5060 static MonoException*
5061 mono_thread_interruption_checkpoint_request (gboolean bypass_abort_protection)
5063 MonoInternalThread *thread = mono_thread_internal_current ();
5065 /* The thread may already be stopping */
5066 if (!thread)
5067 return NULL;
5068 if (!mono_thread_get_interruption_requested (thread))
5069 return NULL;
5070 if (!bypass_abort_protection && !mono_thread_current ()->pending_exception && is_running_protected_wrapper ())
5071 return NULL;
5073 return mono_thread_execute_interruption_ptr ();
5077 * Performs the interruption of the current thread, if one has been requested,
5078 * and the thread is not running a protected wrapper.
5079 * Return the exception which needs to be thrown, if any.
5081 MonoException*
5082 mono_thread_interruption_checkpoint (void)
5084 return mono_thread_interruption_checkpoint_request (FALSE);
5087 gboolean
5088 mono_thread_interruption_checkpoint_bool (void)
5090 return mono_thread_interruption_checkpoint () != NULL;
5093 void
5094 mono_thread_interruption_checkpoint_void (void)
5096 mono_thread_interruption_checkpoint ();
5100 * Performs the interruption of the current thread, if one has been requested.
5101 * Return the exception which needs to be thrown, if any.
5103 MonoException*
5104 mono_thread_force_interruption_checkpoint_noraise (void)
5106 return mono_thread_interruption_checkpoint_request (TRUE);
5110 * mono_set_pending_exception:
5112 * Set the pending exception of the current thread to EXC.
5113 * The exception will be thrown when execution returns to managed code.
5115 void
5116 mono_set_pending_exception (MonoException *exc)
5118 MonoThread *thread = mono_thread_current ();
5120 /* The thread may already be stopping */
5121 if (thread == NULL)
5122 return;
5124 MONO_OBJECT_SETREF_INTERNAL (thread, pending_exception, exc);
5126 mono_thread_request_interruption_native ();
5130 * mono_runtime_set_pending_exception:
5132 * Set the pending exception of the current thread to \p exc.
5133 * The exception will be thrown when execution returns to managed code.
5134 * Can optionally \p overwrite any existing pending exceptions (it's not supported
5135 * to overwrite any pending exceptions if the runtime is processing a thread abort request,
5136 * in which case the behavior will be undefined).
5137 * Return whether the pending exception was set or not.
5138 * It will not be set if:
5139 * * The thread or runtime is stopping or shutting down
5140 * * There already is a pending exception (and \p overwrite is false)
5142 mono_bool
5143 mono_runtime_set_pending_exception (MonoException *exc, mono_bool overwrite)
5145 MonoThread *thread = mono_thread_current ();
5147 /* The thread may already be stopping */
5148 if (thread == NULL)
5149 return FALSE;
5151 /* Don't overwrite any existing pending exceptions unless asked to */
5152 if (!overwrite && thread->pending_exception)
5153 return FALSE;
5155 MONO_OBJECT_SETREF_INTERNAL (thread, pending_exception, exc);
5157 mono_thread_request_interruption_native ();
5159 return TRUE;
5164 * mono_set_pending_exception_handle:
5166 * Set the pending exception of the current thread to EXC.
5167 * The exception will be thrown when execution returns to managed code.
5169 MONO_COLD void
5170 mono_set_pending_exception_handle (MonoExceptionHandle exc)
5172 MonoThread *thread = mono_thread_current ();
5174 /* The thread may already be stopping */
5175 if (thread == NULL)
5176 return;
5178 MONO_OBJECT_SETREF_INTERNAL (thread, pending_exception, MONO_HANDLE_RAW (exc));
5180 mono_thread_request_interruption_native ();
5184 * mono_thread_interruption_request_flag:
5186 * Returns the address of a flag that will be non-zero if an interruption has
5187 * been requested for a thread. The thread to interrupt may not be the current
5188 * thread, so an additional call to mono_thread_interruption_requested() or
5189 * mono_thread_interruption_checkpoint() is allways needed if the flag is not
5190 * zero.
5192 gint32*
5193 mono_thread_interruption_request_flag (void)
5195 return &thread_interruption_requested;
5198 void
5199 mono_thread_init_apartment_state (void)
5201 #ifdef HOST_WIN32
5202 MonoInternalThread* thread = mono_thread_internal_current ();
5204 /* Positive return value indicates success, either
5205 * S_OK if this is first CoInitialize call, or
5206 * S_FALSE if CoInitialize already called, but with same
5207 * threading model. A negative value indicates failure,
5208 * probably due to trying to change the threading model.
5210 if (CoInitializeEx(NULL, (thread->apartment_state == ThreadApartmentState_STA)
5211 ? COINIT_APARTMENTTHREADED
5212 : COINIT_MULTITHREADED) < 0) {
5213 thread->apartment_state = ThreadApartmentState_Unknown;
5215 #endif
5218 void
5219 mono_thread_cleanup_apartment_state (void)
5221 #ifdef HOST_WIN32
5222 MonoInternalThread* thread = mono_thread_internal_current ();
5224 if (thread && thread->apartment_state != ThreadApartmentState_Unknown) {
5225 CoUninitialize ();
5227 #endif
5230 static void
5231 mono_thread_notify_change_state (MonoThreadState old_state, MonoThreadState new_state)
5233 MonoThreadState diff = old_state ^ new_state;
5234 if (diff & ThreadState_Background) {
5235 /* If the thread changes the background mode, the main thread has to
5236 * be notified, since it has to rebuild the list of threads to
5237 * wait for.
5239 MONO_ENTER_GC_SAFE;
5240 mono_os_event_set (&background_change_event);
5241 MONO_EXIT_GC_SAFE;
5245 void
5246 mono_thread_clear_and_set_state (MonoInternalThread *thread, MonoThreadState clear, MonoThreadState set)
5248 LOCK_THREAD (thread);
5250 MonoThreadState const old_state = (MonoThreadState)thread->state;
5251 MonoThreadState const new_state = (old_state & ~clear) | set;
5252 thread->state = new_state;
5254 UNLOCK_THREAD (thread);
5256 mono_thread_notify_change_state (old_state, new_state);
5259 void
5260 mono_thread_set_state (MonoInternalThread *thread, MonoThreadState state)
5262 mono_thread_clear_and_set_state (thread, (MonoThreadState)0, state);
5266 * mono_thread_test_and_set_state:
5267 * Test if current state of \p thread include \p test. If it does not, OR \p set into the state.
5268 * \returns TRUE if \p set was OR'd in.
5270 gboolean
5271 mono_thread_test_and_set_state (MonoInternalThread *thread, MonoThreadState test, MonoThreadState set)
5273 LOCK_THREAD (thread);
5275 MonoThreadState const old_state = (MonoThreadState)thread->state;
5277 if ((old_state & test) != 0) {
5278 UNLOCK_THREAD (thread);
5279 return FALSE;
5282 MonoThreadState const new_state = old_state | set;
5283 thread->state = new_state;
5285 UNLOCK_THREAD (thread);
5287 mono_thread_notify_change_state (old_state, new_state);
5289 return TRUE;
5292 void
5293 mono_thread_clr_state (MonoInternalThread *thread, MonoThreadState state)
5295 mono_thread_clear_and_set_state (thread, state, (MonoThreadState)0);
5298 gboolean
5299 mono_thread_test_state (MonoInternalThread *thread, MonoThreadState test)
5301 LOCK_THREAD (thread);
5303 gboolean const ret = ((thread->state & test) != 0);
5305 UNLOCK_THREAD (thread);
5307 return ret;
5310 static void
5311 self_interrupt_thread (void *_unused)
5313 MonoException *exc;
5314 MonoThreadInfo *info;
5315 MonoContext ctx;
5317 exc = mono_thread_execute_interruption_ptr ();
5318 if (!exc) {
5319 if (mono_threads_are_safepoints_enabled ()) {
5320 /* We can return from an async call in coop, as
5321 * it's simply called when exiting the safepoint */
5322 /* If we're using hybrid suspend, we only self
5323 * interrupt if we were running, hence using
5324 * safepoints */
5325 return;
5328 g_error ("%s: we can't resume from an async call", __func__);
5331 info = mono_thread_info_current ();
5333 /* FIXME using thread_saved_state [ASYNC_SUSPEND_STATE_INDEX] can race with another suspend coming in. */
5334 ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
5336 mono_raise_exception_with_context (exc, &ctx);
5339 static gboolean
5340 mono_jit_info_match (MonoJitInfo *ji, gpointer ip)
5342 if (!ji)
5343 return FALSE;
5344 return ji->code_start <= ip && (char*)ip < (char*)ji->code_start + ji->code_size;
5347 static gboolean
5348 last_managed (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
5350 MonoJitInfo **dest = (MonoJitInfo **)data;
5351 *dest = frame->ji;
5352 return TRUE;
5355 static MonoJitInfo*
5356 mono_thread_info_get_last_managed (MonoThreadInfo *info)
5358 MonoJitInfo *ji = NULL;
5359 if (!info)
5360 return NULL;
5363 * The suspended thread might be holding runtime locks. Make sure we don't try taking
5364 * any runtime locks while unwinding.
5366 mono_thread_info_set_is_async_context (TRUE);
5367 mono_get_eh_callbacks ()->mono_walk_stack_with_state (last_managed, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, &ji);
5368 mono_thread_info_set_is_async_context (FALSE);
5369 return ji;
5372 typedef struct {
5373 MonoInternalThread *thread;
5374 gboolean install_async_abort;
5375 MonoThreadInfoInterruptToken *interrupt_token;
5376 } AbortThreadData;
5378 static SuspendThreadResult
5379 async_abort_critical (MonoThreadInfo *info, gpointer ud)
5381 AbortThreadData *data = (AbortThreadData *)ud;
5382 MonoInternalThread *thread = data->thread;
5383 MonoJitInfo *ji = NULL;
5384 gboolean protected_wrapper;
5385 gboolean running_managed;
5387 if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (mono_thread_info_get_suspend_state (info)))
5388 return MonoResumeThread;
5390 /*someone is already interrupting it*/
5391 if (!mono_thread_set_interruption_requested (thread))
5392 return MonoResumeThread;
5394 ji = mono_thread_info_get_last_managed (info);
5395 protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
5396 running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
5398 if (!protected_wrapper && running_managed) {
5399 /*We are in managed code*/
5400 /*Set the thread to call */
5401 if (data->install_async_abort)
5402 mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
5403 return MonoResumeThread;
5404 } else {
5406 * This will cause waits to be broken.
5407 * It will also prevent the thread from entering a wait, so if the thread returns
5408 * from the wait before it receives the abort signal, it will just spin in the wait
5409 * functions in the io-layer until the signal handler calls QueueUserAPC which will
5410 * make it return.
5412 data->interrupt_token = mono_thread_info_prepare_interrupt (info);
5414 return MonoResumeThread;
5418 static void
5419 async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort)
5421 AbortThreadData data;
5423 g_assert (thread != mono_thread_internal_current ());
5425 data.thread = thread;
5426 data.install_async_abort = install_async_abort;
5427 data.interrupt_token = NULL;
5429 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), TRUE, async_abort_critical, &data);
5430 if (data.interrupt_token)
5431 mono_thread_info_finish_interrupt (data.interrupt_token);
5432 /*FIXME we need to wait for interruption to complete -- figure out how much into interruption we should wait for here*/
5435 static void
5436 self_abort_internal (MonoError *error)
5438 HANDLE_FUNCTION_ENTER ();
5440 error_init (error);
5442 /* FIXME this is insanely broken, it doesn't cause interruption to happen synchronously
5443 * since passing FALSE to mono_thread_request_interruption makes sure it returns NULL */
5446 Self aborts ignore the protected block logic and raise the TAE regardless. This is verified by one of the tests in mono/tests/abort-cctor.cs.
5448 MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, NULL);
5449 if (mono_thread_request_interruption_managed (&exc))
5450 mono_error_set_exception_handle (error, exc);
5451 else
5452 mono_thread_info_self_interrupt ();
5454 HANDLE_FUNCTION_RETURN ();
5457 typedef struct {
5458 MonoInternalThread *thread;
5459 gboolean interrupt;
5460 MonoThreadInfoInterruptToken *interrupt_token;
5461 } SuspendThreadData;
5463 static SuspendThreadResult
5464 async_suspend_critical (MonoThreadInfo *info, gpointer ud)
5466 SuspendThreadData *data = (SuspendThreadData *)ud;
5467 MonoInternalThread *thread = data->thread;
5468 MonoJitInfo *ji = NULL;
5469 gboolean protected_wrapper;
5470 gboolean running_managed;
5472 ji = mono_thread_info_get_last_managed (info);
5473 protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
5474 running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
5476 if (running_managed && !protected_wrapper) {
5477 if (mono_threads_are_safepoints_enabled ()) {
5478 mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
5479 return MonoResumeThread;
5480 } else {
5481 thread->state &= ~ThreadState_SuspendRequested;
5482 thread->state |= ThreadState_Suspended;
5483 return KeepSuspended;
5485 } else {
5486 mono_thread_set_interruption_requested (thread);
5487 if (data->interrupt)
5488 data->interrupt_token = mono_thread_info_prepare_interrupt ((MonoThreadInfo *)thread->thread_info);
5490 return MonoResumeThread;
5494 /* LOCKING: called with @thread longlived->synch_cs held, and releases it */
5495 static void
5496 async_suspend_internal (MonoInternalThread *thread, gboolean interrupt)
5498 SuspendThreadData data;
5500 g_assert (thread != mono_thread_internal_current ());
5502 // g_async_safe_printf ("ASYNC SUSPEND thread %p\n", thread_get_tid (thread));
5504 thread->self_suspended = FALSE;
5506 data.thread = thread;
5507 data.interrupt = interrupt;
5508 data.interrupt_token = NULL;
5510 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), interrupt, async_suspend_critical, &data);
5511 if (data.interrupt_token)
5512 mono_thread_info_finish_interrupt (data.interrupt_token);
5514 UNLOCK_THREAD (thread);
5517 /* LOCKING: called with @thread longlived->synch_cs held, and releases it */
5518 static void
5519 self_suspend_internal (void)
5521 MonoInternalThread *thread;
5522 MonoOSEvent *event;
5523 MonoOSEventWaitRet res;
5525 thread = mono_thread_internal_current ();
5527 // g_async_safe_printf ("SELF SUSPEND thread %p\n", thread_get_tid (thread));
5529 thread->self_suspended = TRUE;
5531 thread->state &= ~ThreadState_SuspendRequested;
5532 thread->state |= ThreadState_Suspended;
5534 UNLOCK_THREAD (thread);
5536 event = thread->suspended;
5538 MONO_ENTER_GC_SAFE;
5539 res = mono_os_event_wait_one (event, MONO_INFINITE_WAIT, TRUE);
5540 g_assert (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0 || res == MONO_OS_EVENT_WAIT_RET_ALERTED);
5541 MONO_EXIT_GC_SAFE;
5544 static void
5545 suspend_for_shutdown_async_call (gpointer unused)
5547 for (;;)
5548 mono_thread_info_yield ();
5551 static SuspendThreadResult
5552 suspend_for_shutdown_critical (MonoThreadInfo *info, gpointer unused)
5554 mono_thread_info_setup_async_call (info, suspend_for_shutdown_async_call, NULL);
5555 return MonoResumeThread;
5558 void
5559 mono_thread_internal_suspend_for_shutdown (MonoInternalThread *thread)
5561 g_assert (thread != mono_thread_internal_current ());
5563 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, suspend_for_shutdown_critical, NULL);
5567 * mono_thread_is_foreign:
5568 * \param thread the thread to query
5570 * This function allows one to determine if a thread was created by the mono runtime and has
5571 * a well defined lifecycle or it's a foreign one, created by the native environment.
5573 * \returns TRUE if \p thread was not created by the runtime.
5575 mono_bool
5576 mono_thread_is_foreign (MonoThread *thread)
5578 mono_bool result;
5579 MONO_ENTER_GC_UNSAFE;
5580 MonoThreadInfo *info = (MonoThreadInfo *)thread->internal_thread->thread_info;
5581 result = (info->runtime_thread == FALSE);
5582 MONO_EXIT_GC_UNSAFE;
5583 return result;
5586 #ifndef HOST_WIN32
5587 static void
5588 threads_native_thread_join_lock (gpointer tid, gpointer value)
5590 pthread_t thread = (pthread_t)tid;
5591 if (thread != pthread_self ()) {
5592 MONO_ENTER_GC_SAFE;
5593 /* This shouldn't block */
5594 mono_threads_join_lock ();
5595 mono_native_thread_join (thread);
5596 mono_threads_join_unlock ();
5597 MONO_EXIT_GC_SAFE;
5600 static void
5601 threads_native_thread_join_nolock (gpointer tid, gpointer value)
5603 pthread_t thread = (pthread_t)tid;
5604 MONO_ENTER_GC_SAFE;
5605 mono_native_thread_join (thread);
5606 MONO_EXIT_GC_SAFE;
5609 static void
5610 threads_add_joinable_thread_nolock (gpointer tid)
5612 g_hash_table_insert (joinable_threads, tid, tid);
5614 #else
5615 static void
5616 threads_native_thread_join_lock (gpointer tid, gpointer value)
5618 MonoNativeThreadId thread_id = (MonoNativeThreadId)(guint64)tid;
5619 HANDLE thread_handle = (HANDLE)value;
5620 if (thread_id != GetCurrentThreadId () && thread_handle != NULL && thread_handle != INVALID_HANDLE_VALUE) {
5621 MONO_ENTER_GC_SAFE;
5622 /* This shouldn't block */
5623 mono_threads_join_lock ();
5624 mono_native_thread_join_handle (thread_handle, TRUE);
5625 mono_threads_join_unlock ();
5626 MONO_EXIT_GC_SAFE;
5630 static void
5631 threads_native_thread_join_nolock (gpointer tid, gpointer value)
5633 HANDLE thread_handle = (HANDLE)value;
5634 MONO_ENTER_GC_SAFE;
5635 mono_native_thread_join_handle (thread_handle, TRUE);
5636 MONO_EXIT_GC_SAFE;
5639 static void
5640 threads_add_joinable_thread_nolock (gpointer tid)
5642 g_hash_table_insert (joinable_threads, tid, (gpointer)OpenThread (SYNCHRONIZE, TRUE, (MonoNativeThreadId)(guint64)tid));
5644 #endif
5646 static void
5647 threads_add_pending_joinable_thread (gpointer tid)
5649 joinable_threads_lock ();
5651 if (!pending_joinable_threads)
5652 pending_joinable_threads = g_hash_table_new (NULL, NULL);
5654 gpointer orig_key;
5655 gpointer value;
5657 if (!g_hash_table_lookup_extended (pending_joinable_threads, tid, &orig_key, &value)) {
5658 g_hash_table_insert (pending_joinable_threads, tid, tid);
5659 UnlockedIncrement (&pending_joinable_thread_count);
5662 joinable_threads_unlock ();
5665 static void
5666 threads_add_pending_joinable_runtime_thread (MonoThreadInfo *mono_thread_info)
5668 g_assert (mono_thread_info);
5670 if (mono_thread_info->runtime_thread) {
5671 threads_add_pending_joinable_thread ((gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (mono_thread_info_get_tid (mono_thread_info))));
5675 static void
5676 threads_remove_pending_joinable_thread_nolock (gpointer tid)
5678 gpointer orig_key;
5679 gpointer value;
5681 if (pending_joinable_threads && g_hash_table_lookup_extended (pending_joinable_threads, tid, &orig_key, &value)) {
5682 g_hash_table_remove (pending_joinable_threads, tid);
5683 if (UnlockedDecrement (&pending_joinable_thread_count) == 0)
5684 mono_coop_cond_broadcast (&zero_pending_joinable_thread_event);
5688 static gboolean
5689 threads_wait_pending_joinable_threads (uint32_t timeout)
5691 if (UnlockedRead (&pending_joinable_thread_count) > 0) {
5692 joinable_threads_lock ();
5693 if (timeout == MONO_INFINITE_WAIT) {
5694 while (UnlockedRead (&pending_joinable_thread_count) > 0)
5695 mono_coop_cond_wait (&zero_pending_joinable_thread_event, &joinable_threads_mutex);
5696 } else {
5697 gint64 start = mono_msec_ticks ();
5698 gint64 elapsed = 0;
5699 while (UnlockedRead (&pending_joinable_thread_count) > 0 && elapsed < timeout) {
5700 mono_coop_cond_timedwait (&zero_pending_joinable_thread_event, &joinable_threads_mutex, timeout - (uint32_t)elapsed);
5701 elapsed = mono_msec_ticks () - start;
5704 joinable_threads_unlock ();
5707 return UnlockedRead (&pending_joinable_thread_count) == 0;
5710 static void
5711 threads_add_unique_joinable_thread_nolock (gpointer tid)
5713 if (!joinable_threads)
5714 joinable_threads = g_hash_table_new (NULL, NULL);
5716 gpointer orig_key;
5717 gpointer value;
5719 if (!g_hash_table_lookup_extended (joinable_threads, tid, &orig_key, &value)) {
5720 threads_add_joinable_thread_nolock (tid);
5721 UnlockedIncrement (&joinable_thread_count);
5725 void
5726 mono_threads_add_joinable_runtime_thread (MonoThreadInfo *thread_info)
5728 g_assert (thread_info);
5729 MonoThreadInfo *mono_thread_info = thread_info;
5731 if (mono_thread_info->runtime_thread) {
5732 gpointer tid = (gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (mono_thread_info_get_tid (mono_thread_info)));
5734 joinable_threads_lock ();
5736 // Add to joinable thread list, if not already included.
5737 threads_add_unique_joinable_thread_nolock (tid);
5739 // Remove thread from pending joinable list, if present.
5740 threads_remove_pending_joinable_thread_nolock (tid);
5742 joinable_threads_unlock ();
5744 mono_gc_finalize_notify ();
5748 static void
5749 threads_add_pending_native_thread_join_call_nolock (gpointer tid)
5751 if (!pending_native_thread_join_calls)
5752 pending_native_thread_join_calls = g_hash_table_new (NULL, NULL);
5754 gpointer orig_key;
5755 gpointer value;
5757 if (!g_hash_table_lookup_extended (pending_native_thread_join_calls, tid, &orig_key, &value))
5758 g_hash_table_insert (pending_native_thread_join_calls, tid, tid);
5761 static void
5762 threads_remove_pending_native_thread_join_call_nolock (gpointer tid)
5764 if (pending_native_thread_join_calls)
5765 g_hash_table_remove (pending_native_thread_join_calls, tid);
5767 mono_coop_cond_broadcast (&pending_native_thread_join_calls_event);
5770 static void
5771 threads_wait_pending_native_thread_join_call_nolock (gpointer tid)
5773 gpointer orig_key;
5774 gpointer value;
5776 while (g_hash_table_lookup_extended (pending_native_thread_join_calls, tid, &orig_key, &value)) {
5777 mono_coop_cond_wait (&pending_native_thread_join_calls_event, &joinable_threads_mutex);
5782 * mono_add_joinable_thread:
5784 * Add TID to the list of joinable threads.
5785 * LOCKING: Acquires the threads lock.
5787 void
5788 mono_threads_add_joinable_thread (gpointer tid)
5791 * We cannot detach from threads because it causes problems like
5792 * 2fd16f60/r114307. So we collect them and join them when
5793 * we have time (in the finalizer thread).
5795 joinable_threads_lock ();
5796 threads_add_unique_joinable_thread_nolock (tid);
5797 joinable_threads_unlock ();
5799 mono_gc_finalize_notify ();
5803 * mono_threads_join_threads:
5805 * Join all joinable threads. This is called from the finalizer thread.
5806 * LOCKING: Acquires the threads lock.
5808 void
5809 mono_threads_join_threads (void)
5811 GHashTableIter iter;
5812 gpointer key = NULL;
5813 gpointer value = NULL;
5814 gboolean found = FALSE;
5816 /* Fastpath */
5817 if (!UnlockedRead (&joinable_thread_count))
5818 return;
5820 while (TRUE) {
5821 joinable_threads_lock ();
5822 if (found) {
5823 // Previous native thread join call completed.
5824 threads_remove_pending_native_thread_join_call_nolock (key);
5826 found = FALSE;
5827 if (g_hash_table_size (joinable_threads)) {
5828 g_hash_table_iter_init (&iter, joinable_threads);
5829 g_hash_table_iter_next (&iter, &key, (void**)&value);
5830 g_hash_table_remove (joinable_threads, key);
5831 UnlockedDecrement (&joinable_thread_count);
5832 found = TRUE;
5834 // Add to table of tid's with pending native thread join call.
5835 threads_add_pending_native_thread_join_call_nolock (key);
5837 joinable_threads_unlock ();
5838 if (found)
5839 threads_native_thread_join_lock (key, value);
5840 else
5841 break;
5846 * mono_thread_join:
5848 * Wait for thread TID to exit.
5849 * LOCKING: Acquires the threads lock.
5851 void
5852 mono_thread_join (gpointer tid)
5854 gboolean found = FALSE;
5855 gpointer orig_key;
5856 gpointer value;
5858 joinable_threads_lock ();
5859 if (!joinable_threads)
5860 joinable_threads = g_hash_table_new (NULL, NULL);
5862 if (g_hash_table_lookup_extended (joinable_threads, tid, &orig_key, &value)) {
5863 g_hash_table_remove (joinable_threads, tid);
5864 UnlockedDecrement (&joinable_thread_count);
5865 found = TRUE;
5867 // Add to table of tid's with pending native join call.
5868 threads_add_pending_native_thread_join_call_nolock (tid);
5871 if (!found) {
5872 // Wait for any pending native thread join call not yet completed for this tid.
5873 threads_wait_pending_native_thread_join_call_nolock (tid);
5876 joinable_threads_unlock ();
5878 if (!found)
5879 return;
5881 threads_native_thread_join_nolock (tid, value);
5883 joinable_threads_lock ();
5884 // Native thread join call completed for this tid.
5885 threads_remove_pending_native_thread_join_call_nolock (tid);
5886 joinable_threads_unlock ();
5889 void
5890 mono_thread_internal_unhandled_exception (MonoObject* exc)
5892 MonoClass *klass = exc->vtable->klass;
5893 if (is_threadabort_exception (klass)) {
5894 mono_thread_internal_reset_abort (mono_thread_internal_current ());
5895 } else if (!is_appdomainunloaded_exception (klass)
5896 && mono_runtime_unhandled_exception_policy_get () == MONO_UNHANDLED_POLICY_CURRENT) {
5897 mono_unhandled_exception_internal (exc);
5898 if (mono_environment_exitcode_get () == 1) {
5899 mono_environment_exitcode_set (255);
5900 mono_invoke_unhandled_exception_hook (exc);
5901 g_assert_not_reached ();
5906 void
5907 ves_icall_System_Threading_Thread_GetStackTraces (MonoArray **out_threads, MonoArray **out_stack_traces)
5909 ERROR_DECL (error);
5910 mono_threads_get_thread_dump (out_threads, out_stack_traces, error);
5911 mono_error_set_pending_exception (error);
5915 * mono_threads_attach_coop_internal: called by native->managed wrappers
5917 * - @cookie:
5918 * - blocking mode: contains gc unsafe transition cookie
5919 * - non-blocking mode: contains random data
5920 * - @stackdata: semi-opaque struct: stackpointer and function_name
5921 * - @return: the original domain which needs to be restored, or NULL.
5923 MonoDomain*
5924 mono_threads_attach_coop_internal (MonoDomain *domain, gpointer *cookie, MonoStackData *stackdata)
5926 MonoDomain *orig;
5927 MonoThreadInfo *info;
5928 gboolean external = FALSE;
5930 orig = mono_domain_get ();
5932 if (!domain) {
5933 /* Happens when called from AOTed code which is only used in the root domain. */
5934 domain = mono_get_root_domain ();
5935 g_assert (domain);
5938 /* On coop, when we detached, we moved the thread from RUNNING->BLOCKING.
5939 * If we try to reattach we do a BLOCKING->RUNNING transition. If the thread
5940 * is fresh, mono_thread_attach() will do a STARTING->RUNNING transition so
5941 * we're only responsible for making the cookie. */
5942 if (mono_threads_is_blocking_transition_enabled ())
5943 external = !(info = mono_thread_info_current_unchecked ()) || !mono_thread_info_is_live (info);
5945 if (!mono_thread_internal_current ()) {
5946 mono_thread_attach (domain);
5948 // #678164
5949 mono_thread_set_state (mono_thread_internal_current (), ThreadState_Background);
5952 if (mono_threads_is_blocking_transition_enabled ()) {
5953 if (external) {
5954 /* mono_thread_attach put the thread in RUNNING mode from STARTING, but we need to
5955 * return the right cookie. */
5956 *cookie = mono_threads_enter_gc_unsafe_region_cookie ();
5957 } else {
5958 /* thread state (BLOCKING|RUNNING) -> RUNNING */
5959 *cookie = mono_threads_enter_gc_unsafe_region_unbalanced_internal (stackdata);
5963 if (orig != domain)
5964 mono_domain_set_fast (domain, TRUE);
5966 return orig;
5970 * mono_threads_attach_coop: called by native->managed wrappers
5972 * - @dummy:
5973 * - blocking mode: contains gc unsafe transition cookie
5974 * - non-blocking mode: contains random data
5975 * - a pointer to stack, used for some checks
5976 * - @return: the original domain which needs to be restored, or NULL.
5978 gpointer
5979 mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
5981 MONO_STACKDATA (stackdata);
5982 stackdata.stackpointer = dummy;
5983 return mono_threads_attach_coop_internal (domain, dummy, &stackdata);
5987 * mono_threads_detach_coop_internal: called by native->managed wrappers
5989 * - @orig: the original domain which needs to be restored, or NULL.
5990 * - @stackdata: semi-opaque struct: stackpointer and function_name
5991 * - @cookie:
5992 * - blocking mode: contains gc unsafe transition cookie
5993 * - non-blocking mode: contains random data
5995 void
5996 mono_threads_detach_coop_internal (MonoDomain *orig, gpointer cookie, MonoStackData *stackdata)
5998 MonoDomain *domain = mono_domain_get ();
5999 g_assert (domain);
6001 if (orig != domain) {
6002 if (!orig)
6003 mono_domain_unset ();
6004 else
6005 mono_domain_set_fast (orig, TRUE);
6008 if (mono_threads_is_blocking_transition_enabled ()) {
6009 /* it won't do anything if cookie is NULL
6010 * thread state RUNNING -> (RUNNING|BLOCKING) */
6011 mono_threads_exit_gc_unsafe_region_unbalanced_internal (cookie, stackdata);
6016 * mono_threads_detach_coop: called by native->managed wrappers
6018 * - @orig: the original domain which needs to be restored, or NULL.
6019 * - @dummy:
6020 * - blocking mode: contains gc unsafe transition cookie
6021 * - non-blocking mode: contains random data
6022 * - a pointer to stack, used for some checks
6024 void
6025 mono_threads_detach_coop (gpointer orig, gpointer *dummy)
6027 MONO_STACKDATA (stackdata);
6028 stackdata.stackpointer = dummy;
6029 mono_threads_detach_coop_internal ((MonoDomain*)orig, *dummy, &stackdata);
6032 #if 0
6033 /* Returns TRUE if the current thread is ready to be interrupted. */
6034 gboolean
6035 mono_threads_is_ready_to_be_interrupted (void)
6037 MonoInternalThread *thread;
6039 thread = mono_thread_internal_current ();
6040 LOCK_THREAD (thread);
6041 if (thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
6042 UNLOCK_THREAD (thread);
6043 return FALSE;
6046 if (mono_thread_get_abort_prot_block_count (thread) || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ()) {
6047 UNLOCK_THREAD (thread);
6048 return FALSE;
6051 UNLOCK_THREAD (thread);
6052 return TRUE;
6054 #endif
6056 void
6057 mono_thread_internal_describe (MonoInternalThread *internal, GString *text)
6059 g_string_append_printf (text, ", thread handle : %p", internal->handle);
6061 if (internal->thread_info) {
6062 g_string_append (text, ", state : ");
6063 mono_thread_info_describe_interrupt_token (internal->thread_info, text);
6066 if (internal->owned_mutexes) {
6067 int i;
6069 g_string_append (text, ", owns : [");
6070 for (i = 0; i < internal->owned_mutexes->len; i++)
6071 g_string_append_printf (text, i == 0 ? "%p" : ", %p", g_ptr_array_index (internal->owned_mutexes, i));
6072 g_string_append (text, "]");
6076 gboolean
6077 mono_thread_internal_is_current (MonoInternalThread *internal)
6079 g_assert (internal);
6080 return mono_native_thread_id_equals (mono_native_thread_id_get (), MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid));
6083 void
6084 mono_set_thread_dump_dir (gchar* dir) {
6085 thread_dump_dir = dir;
6088 #ifdef DISABLE_CRASH_REPORTING
6089 gboolean
6090 mono_threads_summarize (MonoContext *ctx, gchar **out, MonoStackHash *hashes, gboolean silent, gboolean signal_handler_controller, gchar *mem, size_t provided_size)
6092 return FALSE;
6095 gboolean
6096 mono_threads_summarize_one (MonoThreadSummary *out, MonoContext *ctx)
6098 return FALSE;
6101 #else
6103 static gboolean
6104 mono_threads_summarize_native_self (MonoThreadSummary *out, MonoContext *ctx)
6106 if (!mono_get_eh_callbacks ()->mono_summarize_managed_stack)
6107 return FALSE;
6109 memset (out, 0, sizeof (MonoThreadSummary));
6110 out->ctx = ctx;
6112 MonoNativeThreadId current = mono_native_thread_id_get();
6113 out->native_thread_id = (intptr_t) current;
6115 mono_get_eh_callbacks ()->mono_summarize_unmanaged_stack (out);
6117 mono_native_thread_get_name (current, out->name, MONO_MAX_SUMMARY_NAME_LEN);
6119 return TRUE;
6122 // Not safe to call from signal handler
6123 gboolean
6124 mono_threads_summarize_one (MonoThreadSummary *out, MonoContext *ctx)
6126 gboolean success = mono_threads_summarize_native_self (out, ctx);
6128 // Finish this on the same thread
6130 if (success && mono_get_eh_callbacks ()->mono_summarize_managed_stack)
6131 mono_get_eh_callbacks ()->mono_summarize_managed_stack (out);
6133 return success;
6136 #define TIMEOUT_CRASH_REPORTER_FATAL 30
6137 #define MAX_NUM_THREADS 128
6138 typedef struct {
6139 gint32 has_owner; // state of this memory
6141 MonoSemType update; // notify of addition of threads
6143 int nthreads;
6144 MonoNativeThreadId thread_array [MAX_NUM_THREADS]; // ids of threads we're dumping
6146 int nthreads_attached; // Number of threads self-registered
6147 MonoThreadSummary *all_threads [MAX_NUM_THREADS];
6149 gboolean silent; // print to stdout
6150 } SummarizerGlobalState;
6152 #if defined(HAVE_KILL) && !defined(HOST_ANDROID) && defined(HAVE_WAITPID) && ((!defined(HOST_DARWIN) && defined(SYS_fork)) || HAVE_FORK)
6153 #define HAVE_MONO_SUMMARIZER_SUPERVISOR 1
6154 #endif
6156 typedef struct {
6157 MonoSemType supervisor;
6158 pid_t pid;
6159 pid_t supervisor_pid;
6160 } SummarizerSupervisorState;
6162 #ifndef HAVE_MONO_SUMMARIZER_SUPERVISOR
6163 static void
6164 summarizer_supervisor_wait (SummarizerSupervisorState *state)
6166 return;
6169 static pid_t
6170 summarizer_supervisor_start (SummarizerSupervisorState *state)
6172 // nonzero, so caller doesn't think it's the supervisor
6173 return (pid_t) 1;
6176 static void
6177 summarizer_supervisor_end (SummarizerSupervisorState *state)
6179 return;
6182 #else
6183 static void
6184 summarizer_supervisor_wait (SummarizerSupervisorState *state)
6186 sleep (TIMEOUT_CRASH_REPORTER_FATAL);
6188 // If we haven't been SIGKILL'ed yet, we signal our parent
6189 // and then exit
6190 #ifdef HAVE_KILL
6191 g_async_safe_printf("Crash Reporter has timed out, sending SIGSEGV\n");
6192 kill (state->pid, SIGSEGV);
6193 #else
6194 g_error ("kill () is not supported by this platform");
6195 #endif
6197 exit (1);
6200 static pid_t
6201 summarizer_supervisor_start (SummarizerSupervisorState *state)
6203 memset (state, 0, sizeof (*state));
6204 pid_t pid;
6206 state->pid = getpid();
6209 * glibc fork acquires some locks, so if the crash happened inside malloc/free,
6210 * it will deadlock. Call the syscall directly instead.
6212 #if defined(HOST_ANDROID)
6213 /* SYS_fork is defined to be __NR_fork which is not defined in some ndk versions */
6214 // We disable this when we set HAVE_MONO_SUMMARIZER_SUPERVISOR above
6215 g_assert_not_reached ();
6216 #elif !defined(HOST_DARWIN) && defined(SYS_fork)
6217 pid = (pid_t) syscall (SYS_fork);
6218 #elif HAVE_FORK
6219 pid = (pid_t) fork ();
6220 #else
6221 g_assert_not_reached ();
6222 #endif
6224 if (pid != 0)
6225 state->supervisor_pid = pid;
6227 return pid;
6230 static void
6231 summarizer_supervisor_end (SummarizerSupervisorState *state)
6233 #ifdef HAVE_KILL
6234 kill (state->supervisor_pid, SIGKILL);
6235 #endif
6237 #if defined (HAVE_WAITPID)
6238 // Accessed on same thread that sets it.
6239 int status;
6240 waitpid (state->supervisor_pid, &status, 0);
6241 #endif
6243 #endif
6245 static void
6246 collect_thread_id (gpointer key, gpointer value, gpointer user)
6248 CollectThreadIdsUserData *ud = (CollectThreadIdsUserData *)user;
6249 MonoInternalThread *thread = (MonoInternalThread *)value;
6251 if (ud->nthreads < ud->max_threads)
6252 ud->threads [ud->nthreads ++] = thread_get_tid (thread);
6255 static int
6256 collect_thread_ids (MonoNativeThreadId *thread_ids, int max_threads)
6258 CollectThreadIdsUserData ud;
6260 mono_memory_barrier ();
6261 if (!threads)
6262 return 0;
6264 memset (&ud, 0, sizeof (ud));
6265 /* This array contains refs, but its on the stack, so its ok */
6266 ud.threads = thread_ids;
6267 ud.max_threads = max_threads;
6269 mono_threads_lock ();
6270 mono_g_hash_table_foreach (threads, collect_thread_id, &ud);
6271 mono_threads_unlock ();
6273 return ud.nthreads;
6276 static gboolean
6277 summarizer_state_init (SummarizerGlobalState *state, MonoNativeThreadId current, int *my_index)
6279 gint32 started_state = mono_atomic_cas_i32 (&state->has_owner, 1 /* set */, 0 /* compare */);
6280 gboolean not_started = started_state == 0;
6281 if (not_started) {
6282 state->nthreads = collect_thread_ids (state->thread_array, MAX_NUM_THREADS);
6283 mono_os_sem_init (&state->update, 0);
6286 for (int i = 0; i < state->nthreads; i++) {
6287 if (state->thread_array [i] == current) {
6288 *my_index = i;
6289 break;
6293 return not_started;
6296 static void
6297 summarizer_signal_other_threads (SummarizerGlobalState *state, MonoNativeThreadId current, int current_idx)
6299 sigset_t sigset, old_sigset;
6300 sigemptyset(&sigset);
6301 sigaddset(&sigset, SIGTERM);
6303 for (int i=0; i < state->nthreads; i++) {
6304 sigprocmask (SIG_UNBLOCK, &sigset, &old_sigset);
6306 if (i == current_idx)
6307 continue;
6309 #ifdef HAVE_PTHREAD_KILL
6310 pthread_kill (state->thread_array [i], SIGTERM);
6312 if (!state->silent)
6313 g_async_safe_printf("Pkilling 0x%zx from 0x%zx\n", MONO_NATIVE_THREAD_ID_TO_UINT (state->thread_array [i]), MONO_NATIVE_THREAD_ID_TO_UINT (current));
6314 #else
6315 g_error ("pthread_kill () is not supported by this platform");
6316 #endif
6320 // Returns true when there are shared global references to "this_thread"
6321 static gboolean
6322 summarizer_post_dump (SummarizerGlobalState *state, MonoThreadSummary *this_thread, int current_idx)
6324 mono_memory_barrier ();
6326 gpointer old = mono_atomic_cas_ptr ((volatile gpointer *)&state->all_threads [current_idx], this_thread, NULL);
6328 if (old == GINT_TO_POINTER (-1)) {
6329 g_async_safe_printf ("Trying to register response after dumping period ended");
6330 return FALSE;
6331 } else if (old != NULL) {
6332 g_async_safe_printf ("Thread dump raced for thread slot.");
6333 return FALSE;
6336 // We added our pointer
6337 gint32 count = mono_atomic_inc_i32 ((volatile gint32 *) &state->nthreads_attached);
6338 if (count == state->nthreads)
6339 mono_os_sem_post (&state->update);
6341 return TRUE;
6344 // A lockless spinwait with a timeout
6345 // Used in environments where locks are unsafe
6347 // If set_pos is true, we wait until the expected number of threads have
6348 // responded and then count that the expected number are set. If it is not true,
6349 // then we wait for them to be unset.
6350 static void
6351 summary_timedwait (SummarizerGlobalState *state, int timeout_seconds)
6353 const gint64 milliseconds_in_second = 1000;
6354 gint64 timeout_total = milliseconds_in_second * timeout_seconds;
6356 gint64 end = mono_msec_ticks () + timeout_total;
6358 while (TRUE) {
6359 if (mono_atomic_load_i32 ((volatile gint32 *) &state->nthreads_attached) == state->nthreads)
6360 break;
6362 gint64 now = mono_msec_ticks ();
6363 gint64 remaining = end - now;
6364 if (remaining <= 0)
6365 break;
6367 mono_os_sem_timedwait (&state->update, remaining, MONO_SEM_FLAGS_NONE);
6370 return;
6373 static MonoThreadSummary *
6374 summarizer_try_read_thread (SummarizerGlobalState *state, int index)
6376 gpointer old_value = NULL;
6377 gpointer new_value = GINT_TO_POINTER(-1);
6379 do {
6380 old_value = state->all_threads [index];
6381 } while (mono_atomic_cas_ptr ((volatile gpointer *) &state->all_threads [index], new_value, old_value) != old_value);
6383 MonoThreadSummary *thread = (MonoThreadSummary *) old_value;
6384 return thread;
6387 static void
6388 summarizer_state_term (SummarizerGlobalState *state, gchar **out, gchar *mem, size_t provided_size, MonoThreadSummary *controlling)
6390 // See the array writes
6391 mono_memory_barrier ();
6393 MonoThreadSummary *threads [MAX_NUM_THREADS];
6394 memset (threads, 0, sizeof(threads));
6396 mono_summarize_timeline_phase_log (MonoSummaryManagedStacks);
6397 for (int i=0; i < state->nthreads; i++) {
6398 threads [i] = summarizer_try_read_thread (state, i);
6399 if (!threads [i])
6400 continue;
6402 // We are doing this dump on the controlling thread because this isn't
6403 // an async context sometimes. There's still some reliance on malloc here, but it's
6404 // much more stable to do it all from the controlling thread.
6406 // This is non-null, checked in mono_threads_summarize
6407 // with early exit there
6408 mono_get_eh_callbacks ()->mono_summarize_managed_stack (threads [i]);
6411 MonoStateWriter writer;
6412 memset (&writer, 0, sizeof (writer));
6414 mono_summarize_timeline_phase_log (MonoSummaryStateWriter);
6415 mono_summarize_native_state_begin (&writer, mem, provided_size);
6416 for (int i=0; i < state->nthreads; i++) {
6417 MonoThreadSummary *thread = threads [i];
6418 if (!thread)
6419 continue;
6421 mono_summarize_native_state_add_thread (&writer, thread, thread->ctx, thread == controlling);
6422 // Set non-shared state to notify the waiting thread to clean up
6423 // without having to keep our shared state alive
6424 mono_atomic_store_i32 (&thread->done, 0x1);
6425 mono_os_sem_post (&thread->done_wait);
6427 *out = mono_summarize_native_state_end (&writer);
6428 mono_summarize_timeline_phase_log (MonoSummaryStateWriterDone);
6430 mono_os_sem_destroy (&state->update);
6432 memset (state, 0, sizeof (*state));
6433 mono_atomic_store_i32 ((volatile gint32 *)&state->has_owner, 0);
6436 static void
6437 summarizer_state_wait (MonoThreadSummary *thread)
6439 gint64 milliseconds_in_second = 1000;
6441 // cond_wait can spuriously wake up, so we need to check
6442 // done
6443 while (!mono_atomic_load_i32 (&thread->done))
6444 mono_os_sem_timedwait (&thread->done_wait, milliseconds_in_second, MONO_SEM_FLAGS_NONE);
6447 static gboolean
6448 mono_threads_summarize_execute_internal (MonoContext *ctx, gchar **out, MonoStackHash *hashes, gboolean silent, gchar *working_mem, size_t provided_size, gboolean this_thread_controls)
6450 static SummarizerGlobalState state;
6452 int current_idx;
6453 MonoNativeThreadId current = mono_native_thread_id_get ();
6454 gboolean thread_given_control = summarizer_state_init (&state, current, &current_idx);
6456 g_assert (this_thread_controls == thread_given_control);
6458 if (state.nthreads == 0) {
6459 if (!silent)
6460 g_async_safe_printf("No threads attached to runtime.\n");
6461 memset (&state, 0, sizeof (state));
6462 return FALSE;
6465 if (this_thread_controls) {
6466 g_assert (working_mem);
6468 mono_summarize_timeline_phase_log (MonoSummarySuspendHandshake);
6469 state.silent = silent;
6470 summarizer_signal_other_threads (&state, current, current_idx);
6471 mono_summarize_timeline_phase_log (MonoSummaryUnmanagedStacks);
6474 MonoStateMem mem;
6475 gboolean success = mono_state_alloc_mem (&mem, (long) current, sizeof (MonoThreadSummary));
6476 if (!success)
6477 return FALSE;
6479 MonoThreadSummary *this_thread = (MonoThreadSummary *) mem.mem;
6481 if (mono_threads_summarize_native_self (this_thread, ctx)) {
6482 // Init the synchronization between the controlling thread and the
6483 // providing thread
6484 mono_os_sem_init (&this_thread->done_wait, 0);
6486 // Store a reference to our stack memory into global state
6487 gboolean success = summarizer_post_dump (&state, this_thread, current_idx);
6488 if (!success && !state.silent)
6489 g_async_safe_printf("Thread 0x%zx reported itself.\n", MONO_NATIVE_THREAD_ID_TO_UINT (current));
6490 } else if (!state.silent) {
6491 g_async_safe_printf("Thread 0x%zx couldn't report itself.\n", MONO_NATIVE_THREAD_ID_TO_UINT (current));
6494 // From summarizer, wait and dump.
6495 if (this_thread_controls) {
6496 if (!state.silent)
6497 g_async_safe_printf("Entering thread summarizer pause from 0x%zx\n", MONO_NATIVE_THREAD_ID_TO_UINT (current));
6499 // Wait up to 2 seconds for all of the other threads to catch up
6500 summary_timedwait (&state, 2);
6502 if (!state.silent)
6503 g_async_safe_printf("Finished thread summarizer pause from 0x%zx.\n", MONO_NATIVE_THREAD_ID_TO_UINT (current));
6505 // Dump and cleanup all the stack memory
6506 summarizer_state_term (&state, out, working_mem, provided_size, this_thread);
6507 } else {
6508 // Wait here, keeping our stack memory alive
6509 // for the dumper
6510 summarizer_state_wait (this_thread);
6513 // FIXME: How many threads should be counted?
6514 if (hashes)
6515 *hashes = this_thread->hashes;
6517 mono_state_free_mem (&mem);
6519 return TRUE;
6522 gboolean
6523 mono_threads_summarize_execute (MonoContext *ctx, gchar **out, MonoStackHash *hashes, gboolean silent, gchar *working_mem, size_t provided_size)
6525 return mono_threads_summarize_execute_internal (ctx, out, hashes, silent, working_mem, provided_size, FALSE);
6528 gboolean
6529 mono_threads_summarize (MonoContext *ctx, gchar **out, MonoStackHash *hashes, gboolean silent, gboolean signal_handler_controller, gchar *mem, size_t provided_size)
6531 if (!mono_get_eh_callbacks ()->mono_summarize_managed_stack)
6532 return FALSE;
6534 // The staggered values are due to the need to use inc_i64 for the first value
6535 static gint64 next_pending_request_id = 0;
6536 static gint64 request_available_to_run = 1;
6537 gint64 this_request_id = mono_atomic_inc_i64 ((volatile gint64 *) &next_pending_request_id);
6539 // This is a global queue of summary requests.
6540 // It's not safe to signal a thread while they're in the
6541 // middle of a dump. Dladdr is not reentrant. It's the one lock
6542 // we rely on being able to take.
6544 // We don't use it in almost any other place in managed code, so
6545 // our problem is in the stack dumping code racing with the signalling code.
6547 // A dump is wait-free to the degree that it's not going to loop indefinitely.
6548 // If we're running from a crash handler block, we're not in any position to
6549 // wait for an in-flight dump to finish. If we crashed while dumping, we cannot dump.
6550 // We should simply return so we can die cleanly.
6552 // signal_handler_controller should be set only from a handler that expects itself to be the only
6553 // entry point, where the runtime already being dumping means we should just give up
6555 gboolean success = FALSE;
6557 while (TRUE) {
6558 gint64 next_request_id = mono_atomic_load_i64 ((volatile gint64 *) &request_available_to_run);
6560 if (next_request_id == this_request_id) {
6561 gboolean already_async = mono_thread_info_is_async_context ();
6562 if (!already_async)
6563 mono_thread_info_set_is_async_context (TRUE);
6565 SummarizerSupervisorState synch;
6566 if (summarizer_supervisor_start (&synch)) {
6567 g_assert (mem);
6568 success = mono_threads_summarize_execute_internal (ctx, out, hashes, silent, mem, provided_size, TRUE);
6569 summarizer_supervisor_end (&synch);
6570 } else {
6571 summarizer_supervisor_wait (&synch);
6574 if (!already_async)
6575 mono_thread_info_set_is_async_context (FALSE);
6577 // Only the thread that gets the ticket can unblock future dumpers.
6578 mono_atomic_inc_i64 ((volatile gint64 *) &request_available_to_run);
6579 break;
6580 } else if (signal_handler_controller) {
6581 // We're done. We can't do anything.
6582 g_async_safe_printf ("Attempted to dump for critical failure when already in dump. Error reporting crashed?");
6583 mono_summarize_double_fault_log ();
6584 break;
6585 } else {
6586 if (!silent)
6587 g_async_safe_printf ("Waiting for in-flight dump to complete.");
6588 sleep (2);
6592 return success;
6595 #endif
6597 #ifdef ENABLE_NETCORE
6598 void
6599 ves_icall_System_Threading_Thread_StartInternal (MonoThreadObjectHandle thread_handle, MonoError *error)
6601 MonoThread *internal = MONO_HANDLE_RAW (thread_handle);
6602 gboolean res;
6604 THREAD_DEBUG (g_message("%s: Trying to start a new thread: this (%p)", __func__, internal));
6606 LOCK_THREAD (internal);
6608 if ((internal->state & ThreadState_Unstarted) == 0) {
6609 UNLOCK_THREAD (internal);
6610 mono_error_set_exception_thread_state (error, "Thread has already been started.");
6611 return;
6614 if ((internal->state & ThreadState_Aborted) != 0) {
6615 UNLOCK_THREAD (internal);
6616 return;
6619 res = create_thread (internal, internal, NULL, NULL, NULL, MONO_THREAD_CREATE_FLAGS_NONE, error);
6620 if (!res) {
6621 UNLOCK_THREAD (internal);
6622 return;
6625 internal->state &= ~ThreadState_Unstarted;
6627 THREAD_DEBUG (g_message ("%s: Started thread ID %" G_GSIZE_FORMAT " (handle %p)", __func__, (gsize)internal->tid, internal->handle));
6629 UNLOCK_THREAD (internal);
6632 void
6633 ves_icall_System_Threading_Thread_InitInternal (MonoThreadObjectHandle thread_handle, MonoError *error)
6635 MonoThread *internal = MONO_HANDLE_RAW (thread_handle);
6637 // Need to initialize thread objects created from managed code
6638 init_internal_thread_object (internal);
6639 internal->state = ThreadState_Unstarted;
6640 MONO_OBJECT_SETREF_INTERNAL (internal, internal_thread, internal);
6643 guint64
6644 ves_icall_System_Threading_Thread_GetCurrentOSThreadId (MonoError *error)
6646 return mono_native_thread_os_id_get ();
6649 #endif