HANDLE_FUNCTION_RETURN_VAL must not use handles in current frame (#15873)
[mono-project.git] / mono / metadata / threads.c
blob2c03359300f5f1f4276c47a975793a4c23c2f3a1
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);
1461 UNLOCK_THREAD (internal);
1463 return_val_if_nok (error, NULL);
1464 return internal;
1467 MonoInternalThreadHandle
1468 mono_thread_create_internal_handle (MonoDomain *domain, gpointer func, gpointer arg, MonoThreadCreateFlags flags, MonoError *error)
1470 // FIXME invert
1471 return MONO_HANDLE_NEW (MonoInternalThread, mono_thread_create_internal (domain, func, arg, flags, error));
1475 * mono_thread_create:
1477 void
1478 mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
1480 MONO_ENTER_GC_UNSAFE;
1481 ERROR_DECL (error);
1482 if (!mono_thread_create_checked (domain, func, arg, error))
1483 mono_error_cleanup (error);
1484 MONO_EXIT_GC_UNSAFE;
1487 gboolean
1488 mono_thread_create_checked (MonoDomain *domain, gpointer func, gpointer arg, MonoError *error)
1490 return (NULL != mono_thread_create_internal (domain, func, arg, MONO_THREAD_CREATE_FLAGS_NONE, error));
1494 * mono_thread_attach:
1496 MonoThread *
1497 mono_thread_attach (MonoDomain *domain)
1499 MonoInternalThread *internal;
1500 MonoThread *thread;
1501 MonoThreadInfo *info;
1502 MonoNativeThreadId tid;
1504 if (mono_thread_internal_current_is_attached ()) {
1505 if (domain != mono_domain_get ())
1506 mono_domain_set_fast (domain, TRUE);
1507 /* Already attached */
1508 return mono_thread_current ();
1511 info = mono_thread_info_attach ();
1512 g_assert (info);
1514 tid=mono_native_thread_id_get ();
1516 if (mono_runtime_get_no_exec ())
1517 return NULL;
1519 internal = create_internal_thread_object ();
1521 thread = create_thread_object (domain, internal);
1523 if (!mono_thread_attach_internal (thread, FALSE, TRUE)) {
1524 /* Mono is shutting down, so just wait for the end */
1525 for (;;)
1526 mono_thread_info_sleep (10000, NULL);
1529 THREAD_DEBUG (g_message ("%s: Attached thread ID %" G_GSIZE_FORMAT " (handle %p)", __func__, tid, internal->handle));
1531 if (mono_thread_attach_cb)
1532 mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), info->stack_end);
1534 fire_attach_profiler_events (tid);
1536 return thread;
1540 * mono_thread_detach:
1542 void
1543 mono_thread_detach (MonoThread *thread)
1545 if (thread)
1546 mono_thread_detach_internal (thread->internal_thread);
1550 * mono_thread_detach_if_exiting:
1552 * Detach the current thread from the runtime if it is exiting, i.e. it is running pthread dtors.
1553 * This should be used at the end of embedding code which calls into managed code, and which
1554 * can be called from pthread dtors, like <code>dealloc:</code> implementations in Objective-C.
1556 mono_bool
1557 mono_thread_detach_if_exiting (void)
1559 if (mono_thread_info_is_exiting ()) {
1560 MonoInternalThread *thread;
1562 thread = mono_thread_internal_current ();
1563 if (thread) {
1564 // Switch to GC Unsafe thread state before detaching;
1565 // don't expect to undo this switch, hence unbalanced.
1566 gpointer dummy;
1567 (void) mono_threads_enter_gc_unsafe_region_unbalanced (&dummy);
1569 mono_thread_detach_internal (thread);
1570 mono_thread_info_detach ();
1571 return TRUE;
1574 return FALSE;
1577 gboolean
1578 mono_thread_internal_current_is_attached (void)
1580 MonoInternalThread *internal;
1582 internal = GET_CURRENT_OBJECT ();
1583 if (!internal)
1584 return FALSE;
1586 return TRUE;
1590 * mono_thread_exit:
1592 void
1593 mono_thread_exit (void)
1595 MonoInternalThread *thread = mono_thread_internal_current ();
1597 THREAD_DEBUG (g_message ("%s: mono_thread_exit for %p (%" G_GSIZE_FORMAT ")", __func__, thread, (gsize)thread->tid));
1599 mono_thread_detach_internal (thread);
1601 /* we could add a callback here for embedders to use. */
1602 if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread))
1603 exit (mono_environment_exitcode_get ());
1605 mono_thread_info_exit (0);
1608 static void
1609 mono_thread_construct_internal (MonoThreadObjectHandle this_obj_handle)
1611 MonoInternalThread * const internal = create_internal_thread_object ();
1613 internal->state = ThreadState_Unstarted;
1615 int const thread_gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, this_obj_handle), TRUE);
1617 MonoThreadObject *this_obj = MONO_HANDLE_RAW (this_obj_handle);
1619 mono_atomic_cas_ptr ((volatile gpointer *)&this_obj->internal_thread, internal, NULL);
1621 mono_gchandle_free_internal (thread_gchandle);
1624 #ifndef ENABLE_NETCORE
1625 void
1626 ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThreadObjectHandle this_obj_handle, MonoError *error)
1628 mono_thread_construct_internal (this_obj_handle);
1630 #endif
1632 MonoThreadObjectHandle
1633 ves_icall_System_Threading_Thread_GetCurrentThread (MonoError *error)
1635 return MONO_HANDLE_NEW (MonoThreadObject, mono_thread_current ());
1638 static MonoInternalThread*
1639 thread_handle_to_internal_ptr (MonoThreadObjectHandle thread_handle)
1641 return MONO_HANDLE_GETVAL(thread_handle, internal_thread); // InternalThreads are always pinned.
1644 static void
1645 mono_error_set_exception_thread_state (MonoError *error, const char *exception_message)
1647 mono_error_set_generic_error (error, "System.Threading", "ThreadStateException", "%s", exception_message);
1650 static void
1651 mono_error_set_exception_thread_not_started_or_dead (MonoError *error)
1653 mono_error_set_exception_thread_state (error, "Thread has not been started, or is dead.");
1656 #ifndef ENABLE_NETCORE
1657 MonoBoolean
1658 ves_icall_System_Threading_Thread_Thread_internal (MonoThreadObjectHandle thread_handle, MonoObjectHandle start_handle, MonoError *error)
1660 MonoInternalThread *internal;
1661 gboolean res;
1662 MonoThread *this_obj = MONO_HANDLE_RAW (thread_handle);
1663 MonoObject *start = MONO_HANDLE_RAW (start_handle);
1665 THREAD_DEBUG (g_message("%s: Trying to start a new thread: this (%p) start (%p)", __func__, this_obj, start));
1667 internal = thread_handle_to_internal_ptr (thread_handle);
1669 if (!internal) {
1670 mono_thread_construct_internal (thread_handle);
1671 internal = thread_handle_to_internal_ptr (thread_handle);
1672 g_assert (internal);
1675 LOCK_THREAD (internal);
1677 if ((internal->state & ThreadState_Unstarted) == 0) {
1678 UNLOCK_THREAD (internal);
1679 mono_error_set_exception_thread_state (error, "Thread has already been started.");
1680 return FALSE;
1683 if ((internal->state & ThreadState_Aborted) != 0) {
1684 UNLOCK_THREAD (internal);
1685 return TRUE;
1688 res = create_thread (this_obj, internal, start, NULL, NULL, MONO_THREAD_CREATE_FLAGS_NONE, error);
1689 if (!res) {
1690 UNLOCK_THREAD (internal);
1691 return FALSE;
1694 internal->state &= ~ThreadState_Unstarted;
1696 THREAD_DEBUG (g_message ("%s: Started thread ID %" G_GSIZE_FORMAT " (handle %p)", __func__, tid, thread));
1698 UNLOCK_THREAD (internal);
1699 return TRUE;
1701 #endif
1704 * This is called from the finalizer of the internal thread object.
1706 void
1707 ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThreadHandle this_obj_handle, MonoError *error)
1709 MonoInternalThread *this_obj = mono_internal_thread_handle_ptr (this_obj_handle);
1710 THREAD_DEBUG (g_message ("%s: Closing thread %p, handle %p", __func__, this_obj, this_obj->handle));
1713 * Since threads keep a reference to their thread object while running, by
1714 * the time this function is called, the thread has already exited/detached,
1715 * i.e. mono_thread_detach_internal () has ran. The exception is during
1716 * shutdown, when mono_thread_detach_internal () can be called after this.
1718 if (this_obj->handle) {
1719 mono_threads_close_thread_handle (this_obj->handle);
1720 this_obj->handle = NULL;
1723 mono_threads_close_native_thread_handle (MONO_GPOINTER_TO_NATIVE_THREAD_HANDLE (this_obj->native_handle));
1724 this_obj->native_handle = NULL;
1726 /* Possibly free synch_cs, if the thread already detached also. */
1727 dec_longlived_thread_data (this_obj->longlived);
1730 if (this_obj->name) {
1731 void *name = this_obj->name;
1732 this_obj->name = NULL;
1733 g_free (name);
1737 void
1738 ves_icall_System_Threading_Thread_Sleep_internal (gint32 ms, MonoError *error)
1740 THREAD_DEBUG (g_message ("%s: Sleeping for %d ms", __func__, ms));
1742 if (mono_thread_current_check_pending_interrupt ())
1743 return;
1745 MonoInternalThread * const thread = mono_thread_internal_current ();
1747 HANDLE_LOOP_PREPARE;
1749 while (TRUE) {
1750 gboolean alerted = FALSE;
1752 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1754 (void)mono_thread_info_sleep (ms, &alerted);
1756 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1758 if (!alerted)
1759 return;
1761 SETUP_ICALL_FRAME;
1763 MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, NULL);
1765 const gboolean interrupt = mono_thread_execute_interruption (&exc);
1767 if (interrupt)
1768 mono_set_pending_exception_handle (exc);
1770 CLEAR_ICALL_FRAME;
1772 if (interrupt)
1773 return;
1774 if (ms == MONO_INFINITE_WAIT) // FIXME: !MONO_INFINITE_WAIT
1775 continue;
1776 return;
1780 void
1781 ves_icall_System_Threading_Thread_SpinWait_nop (MonoError *error)
1785 #ifndef ENABLE_NETCORE
1786 gint32
1787 ves_icall_System_Threading_Thread_GetDomainID (MonoError *error)
1789 return mono_domain_get()->domain_id;
1791 #endif
1794 * mono_thread_get_name:
1796 * Return the name of the thread. NAME_LEN is set to the length of the name.
1797 * Return NULL if the thread has no name. The returned memory is owned by the
1798 * caller.
1800 gunichar2*
1801 mono_thread_get_name (MonoInternalThread *this_obj, guint32 *name_len)
1803 gunichar2 *res;
1805 LOCK_THREAD (this_obj);
1807 if (!this_obj->name) {
1808 *name_len = 0;
1809 res = NULL;
1810 } else {
1811 *name_len = this_obj->name_len;
1812 res = g_new (gunichar2, this_obj->name_len);
1813 memcpy (res, this_obj->name, sizeof (gunichar2) * this_obj->name_len);
1816 UNLOCK_THREAD (this_obj);
1818 return res;
1822 * mono_thread_get_name_utf8:
1823 * \returns the name of the thread in UTF-8.
1824 * Return NULL if the thread has no name.
1825 * The returned memory is owned by the caller (g_free it).
1827 char *
1828 mono_thread_get_name_utf8 (MonoThread *thread)
1830 if (thread == NULL)
1831 return NULL;
1833 MonoInternalThread *internal = thread->internal_thread;
1834 if (internal == NULL)
1835 return NULL;
1837 LOCK_THREAD (internal);
1839 char *tname = g_utf16_to_utf8 (internal->name, internal->name_len, NULL, NULL, NULL);
1841 UNLOCK_THREAD (internal);
1843 return tname;
1847 * mono_thread_get_managed_id:
1848 * \returns the \c Thread.ManagedThreadId value of \p thread.
1849 * Returns \c -1 if \p thread is NULL.
1851 int32_t
1852 mono_thread_get_managed_id (MonoThread *thread)
1854 if (thread == NULL)
1855 return -1;
1857 MonoInternalThread *internal = thread->internal_thread;
1858 if (internal == NULL)
1859 return -1;
1861 int32_t id = internal->managed_id;
1863 return id;
1866 #ifndef ENABLE_NETCORE
1867 MonoStringHandle
1868 ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThreadHandle thread_handle, MonoError *error)
1870 // InternalThreads are always pinned, so shallowly coop-handleize.
1871 MonoInternalThread * const this_obj = mono_internal_thread_handle_ptr (thread_handle);
1873 MonoStringHandle str = MONO_HANDLE_NEW (MonoString, NULL);
1875 LOCK_THREAD (this_obj);
1877 if (this_obj->name)
1878 MONO_HANDLE_ASSIGN (str, mono_string_new_utf16_handle (mono_domain_get (), this_obj->name, this_obj->name_len, error));
1880 UNLOCK_THREAD (this_obj);
1882 return str;
1884 #endif
1886 void
1887 mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, gboolean reset, MonoError *error)
1889 MonoNativeThreadId tid = 0;
1891 LOCK_THREAD (this_obj);
1893 error_init (error);
1895 if (reset) {
1896 this_obj->flags &= ~MONO_THREAD_FLAG_NAME_SET;
1897 } else if (this_obj->flags & MONO_THREAD_FLAG_NAME_SET) {
1898 UNLOCK_THREAD (this_obj);
1900 mono_error_set_invalid_operation (error, "%s", "Thread.Name can only be set once.");
1901 return;
1903 if (this_obj->name) {
1904 g_free (this_obj->name);
1905 this_obj->name_len = 0;
1907 if (name) {
1908 this_obj->name = g_memdup (mono_string_chars_internal (name), mono_string_length_internal (name) * sizeof (gunichar2));
1909 this_obj->name_len = mono_string_length_internal (name);
1911 if (permanent)
1912 this_obj->flags |= MONO_THREAD_FLAG_NAME_SET;
1914 else
1915 this_obj->name = NULL;
1917 if (!(this_obj->state & ThreadState_Stopped))
1918 tid = thread_get_tid (this_obj);
1920 UNLOCK_THREAD (this_obj);
1922 if (this_obj->name && tid) {
1923 char *tname = mono_string_to_utf8_checked_internal (name, error);
1924 return_if_nok (error);
1925 MONO_PROFILER_RAISE (thread_name, ((uintptr_t)tid, tname));
1926 mono_native_thread_set_name (tid, tname);
1927 mono_free (tname);
1931 void
1932 ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj, MonoString *name)
1934 ERROR_DECL (error);
1935 mono_thread_set_name_internal (this_obj, name, TRUE, FALSE, error);
1936 mono_error_set_pending_exception (error);
1939 #ifndef ENABLE_NETCORE
1941 * ves_icall_System_Threading_Thread_GetPriority_internal:
1942 * @param this_obj: The MonoInternalThread on which to operate.
1944 * Gets the priority of the given thread.
1945 * @return: The priority of the given thread.
1948 ves_icall_System_Threading_Thread_GetPriority (MonoThreadObjectHandle this_obj, MonoError *error)
1950 gint32 priority;
1952 MonoInternalThread *internal = thread_handle_to_internal_ptr (this_obj);
1954 LOCK_THREAD (internal);
1955 priority = internal->priority;
1956 UNLOCK_THREAD (internal);
1958 return priority;
1960 #endif
1963 * ves_icall_System_Threading_Thread_SetPriority_internal:
1964 * @param this_obj: The MonoInternalThread on which to operate.
1965 * @param priority: The priority to set.
1967 * Sets the priority of the given thread.
1969 void
1970 ves_icall_System_Threading_Thread_SetPriority (MonoThreadObjectHandle this_obj, int priority, MonoError *error)
1972 MonoInternalThread *internal = thread_handle_to_internal_ptr (this_obj);
1974 LOCK_THREAD (internal);
1975 internal->priority = priority;
1976 if (internal->thread_info != NULL)
1977 mono_thread_internal_set_priority (internal, (MonoThreadPriority)priority);
1978 UNLOCK_THREAD (internal);
1981 /* If the array is already in the requested domain, we just return it,
1982 otherwise we return a copy in that domain. */
1983 static MonoArrayHandle
1984 byte_array_to_domain (MonoArrayHandle arr, MonoDomain *domain, MonoError *error)
1986 HANDLE_FUNCTION_ENTER ()
1988 if (MONO_HANDLE_IS_NULL (arr))
1989 return MONO_HANDLE_NEW (MonoArray, NULL);
1991 if (MONO_HANDLE_DOMAIN (arr) == domain)
1992 return arr;
1994 size_t const size = mono_array_handle_length (arr);
1996 // Capture arrays into common representation for repetitious code.
1997 // These two variables could also be an array of size 2 and
1998 // repitition implemented with a loop.
1999 struct {
2000 MonoArrayHandle handle;
2001 gpointer p;
2002 guint gchandle;
2004 source = { arr },
2005 dest = { mono_array_new_handle (domain, mono_defaults.byte_class, size, error) };
2006 goto_if_nok (error, exit);
2008 // Pin both arrays.
2009 source.p = mono_array_handle_pin_with_size (source.handle, size, 0, &source.gchandle);
2010 dest.p = mono_array_handle_pin_with_size (dest.handle, size, 0, &dest.gchandle);
2012 memmove (dest.p, source.p, size);
2013 exit:
2014 // Unpin both arrays.
2015 mono_gchandle_free_internal (source.gchandle);
2016 mono_gchandle_free_internal (dest.gchandle);
2018 HANDLE_FUNCTION_RETURN_REF (MonoArray, dest.handle)
2021 #ifndef ENABLE_NETCORE
2022 MonoArrayHandle
2023 ves_icall_System_Threading_Thread_ByteArrayToRootDomain (MonoArrayHandle arr, MonoError *error)
2025 return byte_array_to_domain (arr, mono_get_root_domain (), error);
2028 MonoArrayHandle
2029 ves_icall_System_Threading_Thread_ByteArrayToCurrentDomain (MonoArrayHandle arr, MonoError *error)
2031 return byte_array_to_domain (arr, mono_domain_get (), error);
2033 #endif
2036 * mono_thread_current:
2038 MonoThread *
2039 mono_thread_current (void)
2041 #ifdef ENABLE_NETCORE
2042 return mono_thread_internal_current ();
2043 #else
2044 MonoDomain *domain = mono_domain_get ();
2045 MonoInternalThread *internal = mono_thread_internal_current ();
2046 MonoThread **current_thread_ptr;
2048 g_assert (internal);
2049 current_thread_ptr = get_current_thread_ptr_for_domain (domain, internal);
2051 if (!*current_thread_ptr) {
2052 g_assert (domain != mono_get_root_domain ());
2053 *current_thread_ptr = create_thread_object (domain, internal);
2055 return *current_thread_ptr;
2056 #endif
2059 static MonoThreadObjectHandle
2060 mono_thread_current_handle (void)
2062 return MONO_HANDLE_NEW (MonoThreadObject, mono_thread_current ());
2065 /* Return the thread object belonging to INTERNAL in the current domain */
2066 static MonoThread *
2067 mono_thread_current_for_thread (MonoInternalThread *internal)
2069 #ifdef ENABLE_NETCORE
2070 return mono_thread_internal_current ();
2071 #else
2072 MonoDomain *domain = mono_domain_get ();
2073 MonoThread **current_thread_ptr;
2075 g_assert (internal);
2076 current_thread_ptr = get_current_thread_ptr_for_domain (domain, internal);
2078 if (!*current_thread_ptr) {
2079 g_assert (domain != mono_get_root_domain ());
2080 *current_thread_ptr = create_thread_object (domain, internal);
2082 return *current_thread_ptr;
2083 #endif
2086 MonoInternalThread*
2087 mono_thread_internal_current (void)
2089 MonoInternalThread *res = GET_CURRENT_OBJECT ();
2090 THREAD_DEBUG (g_message ("%s: returning %p", __func__, res));
2091 return res;
2094 MonoInternalThreadHandle
2095 mono_thread_internal_current_handle (void)
2097 return MONO_HANDLE_NEW (MonoInternalThread, mono_thread_internal_current ());
2100 static MonoThreadInfoWaitRet
2101 mono_join_uninterrupted (MonoThreadHandle* thread_to_join, gint32 ms, MonoError *error)
2103 MonoThreadInfoWaitRet ret;
2104 gint32 wait = ms;
2106 const gint64 start = (ms == -1) ? 0 : mono_msec_ticks ();
2107 while (TRUE) {
2108 MONO_ENTER_GC_SAFE;
2109 ret = mono_thread_info_wait_one_handle (thread_to_join, wait, TRUE);
2110 MONO_EXIT_GC_SAFE;
2112 if (ret != MONO_THREAD_INFO_WAIT_RET_ALERTED)
2113 return ret;
2115 MonoException *exc = mono_thread_execute_interruption_ptr ();
2116 if (exc) {
2117 mono_error_set_exception_instance (error, exc);
2118 return ret;
2121 if (ms == -1)
2122 continue;
2124 /* Re-calculate ms according to the time passed */
2125 const gint32 diff_ms = (gint32)(mono_msec_ticks () - start);
2126 if (diff_ms >= ms) {
2127 ret = MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
2128 return ret;
2130 wait = ms - diff_ms;
2133 return ret;
2136 MonoBoolean
2137 ves_icall_System_Threading_Thread_Join_internal (MonoThreadObjectHandle thread_handle, int ms, MonoError *error)
2139 if (mono_thread_current_check_pending_interrupt ())
2140 return FALSE;
2142 // Internal threads are pinned so shallow coop/handle.
2143 MonoInternalThread * const thread = thread_handle_to_internal_ptr (thread_handle);
2144 MonoThreadHandle *handle = thread->handle;
2145 MonoInternalThread *cur_thread = mono_thread_internal_current ();
2146 gboolean ret = FALSE;
2148 LOCK_THREAD (thread);
2150 if ((thread->state & ThreadState_Unstarted) != 0) {
2151 UNLOCK_THREAD (thread);
2153 mono_error_set_exception_thread_state (error, "Thread has not been started.");
2154 return FALSE;
2157 UNLOCK_THREAD (thread);
2159 if (ms == -1)
2160 ms = MONO_INFINITE_WAIT;
2161 THREAD_DEBUG (g_message ("%s: joining thread handle %p, %d ms", __func__, handle, ms));
2163 mono_thread_set_state (cur_thread, ThreadState_WaitSleepJoin);
2165 ret = mono_join_uninterrupted (handle, ms, error);
2167 mono_thread_clr_state (cur_thread, ThreadState_WaitSleepJoin);
2169 if (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0) {
2170 THREAD_DEBUG (g_message ("%s: join successful", __func__));
2172 mono_error_assert_ok (error);
2174 /* Wait for the thread to really exit */
2175 MonoNativeThreadId tid = thread_get_tid (thread);
2176 mono_thread_join ((gpointer)(gsize)tid);
2178 return TRUE;
2181 THREAD_DEBUG (g_message ("%s: join failed", __func__));
2183 return FALSE;
2186 #define MANAGED_WAIT_FAILED 0x7fffffff
2188 static gint32
2189 map_native_wait_result_to_managed (MonoW32HandleWaitRet val, gsize numobjects)
2191 if (val >= MONO_W32HANDLE_WAIT_RET_SUCCESS_0 && val < MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + numobjects) {
2192 return WAIT_OBJECT_0 + (val - MONO_W32HANDLE_WAIT_RET_SUCCESS_0);
2193 } else if (val >= MONO_W32HANDLE_WAIT_RET_ABANDONED_0 && val < MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + numobjects) {
2194 return WAIT_ABANDONED_0 + (val - MONO_W32HANDLE_WAIT_RET_ABANDONED_0);
2195 } else if (val == MONO_W32HANDLE_WAIT_RET_ALERTED) {
2196 return WAIT_IO_COMPLETION;
2197 } else if (val == MONO_W32HANDLE_WAIT_RET_TIMEOUT) {
2198 return WAIT_TIMEOUT;
2199 } else if (val == MONO_W32HANDLE_WAIT_RET_TOO_MANY_POSTS) {
2200 return WAIT_TOO_MANY_POSTS;
2201 } else if (val == MONO_W32HANDLE_WAIT_RET_NOT_OWNED_BY_CALLER) {
2202 return WAIT_NOT_OWNED_BY_CALLER;
2203 } else if (val == MONO_W32HANDLE_WAIT_RET_FAILED) {
2204 /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
2205 return MANAGED_WAIT_FAILED;
2206 } else {
2207 g_error ("%s: unknown val value %d", __func__, val);
2211 gint32
2212 ves_icall_System_Threading_WaitHandle_Wait_internal (gpointer *handles, gint32 numhandles, MonoBoolean waitall, gint32 timeout, MonoError *error)
2214 /* Do this WaitSleepJoin check before creating objects */
2215 if (mono_thread_current_check_pending_interrupt ())
2216 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
2218 MonoInternalThread * const thread = mono_thread_internal_current ();
2220 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
2222 gint64 start = 0;
2224 if (timeout == -1)
2225 timeout = MONO_INFINITE_WAIT;
2226 if (timeout != MONO_INFINITE_WAIT)
2227 start = mono_msec_ticks ();
2229 guint32 timeoutLeft = timeout;
2231 MonoW32HandleWaitRet ret;
2233 HANDLE_LOOP_PREPARE;
2235 for (;;) {
2237 /* mono_w32handle_wait_multiple optimizes the case for numhandles == 1 */
2238 ret = mono_w32handle_wait_multiple (handles, numhandles, waitall, timeoutLeft, TRUE, error);
2240 if (ret != MONO_W32HANDLE_WAIT_RET_ALERTED)
2241 break;
2243 SETUP_ICALL_FRAME;
2245 MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, NULL);
2247 const gboolean interrupt = mono_thread_execute_interruption (&exc);
2249 if (interrupt)
2250 mono_error_set_exception_handle (error, exc);
2252 CLEAR_ICALL_FRAME;
2254 if (interrupt)
2255 break;
2257 if (timeout != MONO_INFINITE_WAIT) {
2258 gint64 const elapsed = mono_msec_ticks () - start;
2259 if (elapsed >= timeout) {
2260 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
2261 break;
2264 timeoutLeft = timeout - elapsed;
2268 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
2270 return map_native_wait_result_to_managed (ret, numhandles);
2273 #if HAVE_API_SUPPORT_WIN32_SIGNAL_OBJECT_AND_WAIT
2274 gint32
2275 ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (gpointer toSignal, gpointer toWait, gint32 ms, MonoError *error)
2277 MonoW32HandleWaitRet ret;
2278 MonoInternalThread *thread = mono_thread_internal_current ();
2280 if (ms == -1)
2281 ms = MONO_INFINITE_WAIT;
2283 if (mono_thread_current_check_pending_interrupt ())
2284 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
2286 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
2288 ret = mono_w32handle_signal_and_wait (toSignal, toWait, ms, TRUE);
2290 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
2292 return map_native_wait_result_to_managed (ret, 1);
2295 #endif
2297 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
2299 return mono_atomic_inc_i32 (location);
2302 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
2304 #if SIZEOF_VOID_P == 4
2305 if (G_UNLIKELY ((size_t)location & 0x7)) {
2306 gint64 ret;
2307 mono_interlocked_lock ();
2308 (*location)++;
2309 ret = *location;
2310 mono_interlocked_unlock ();
2311 return ret;
2313 #endif
2314 return mono_atomic_inc_i64 (location);
2317 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
2319 return mono_atomic_dec_i32(location);
2322 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
2324 #if SIZEOF_VOID_P == 4
2325 if (G_UNLIKELY ((size_t)location & 0x7)) {
2326 gint64 ret;
2327 mono_interlocked_lock ();
2328 (*location)--;
2329 ret = *location;
2330 mono_interlocked_unlock ();
2331 return ret;
2333 #endif
2334 return mono_atomic_dec_i64 (location);
2337 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location, gint32 value)
2339 return mono_atomic_xchg_i32(location, value);
2342 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location, MonoObject *value)
2344 MonoObject *res;
2345 res = (MonoObject *) mono_atomic_xchg_ptr((gpointer *) location, value);
2346 mono_gc_wbarrier_generic_nostore_internal (location);
2347 return res;
2350 gpointer ves_icall_System_Threading_Interlocked_Exchange_IntPtr (gpointer *location, gpointer value)
2352 return mono_atomic_xchg_ptr(location, value);
2355 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location, gfloat value)
2357 IntFloatUnion val, ret;
2359 val.fval = value;
2360 ret.ival = mono_atomic_xchg_i32((gint32 *) location, val.ival);
2362 return ret.fval;
2365 gint64
2366 ves_icall_System_Threading_Interlocked_Exchange_Long (gint64 *location, gint64 value)
2368 #if SIZEOF_VOID_P == 4
2369 if (G_UNLIKELY ((size_t)location & 0x7)) {
2370 gint64 ret;
2371 mono_interlocked_lock ();
2372 ret = *location;
2373 *location = value;
2374 mono_interlocked_unlock ();
2375 return ret;
2377 #endif
2378 return mono_atomic_xchg_i64 (location, value);
2381 gdouble
2382 ves_icall_System_Threading_Interlocked_Exchange_Double (gdouble *location, gdouble value)
2384 LongDoubleUnion val, ret;
2386 val.fval = value;
2387 ret.ival = (gint64)mono_atomic_xchg_i64((gint64 *) location, val.ival);
2389 return ret.fval;
2392 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location, gint32 value, gint32 comparand)
2394 return mono_atomic_cas_i32(location, value, comparand);
2397 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int_Success(gint32 *location, gint32 value, gint32 comparand, MonoBoolean *success)
2399 gint32 r = mono_atomic_cas_i32(location, value, comparand);
2400 *success = r == comparand;
2401 return r;
2404 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location, MonoObject *value, MonoObject *comparand)
2406 MonoObject *res;
2407 res = (MonoObject *) mono_atomic_cas_ptr((gpointer *) location, value, comparand);
2408 mono_gc_wbarrier_generic_nostore_internal (location);
2409 return res;
2412 gpointer ves_icall_System_Threading_Interlocked_CompareExchange_IntPtr(gpointer *location, gpointer value, gpointer comparand)
2414 return mono_atomic_cas_ptr(location, value, comparand);
2417 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location, gfloat value, gfloat comparand)
2419 IntFloatUnion val, ret, cmp;
2421 val.fval = value;
2422 cmp.fval = comparand;
2423 ret.ival = mono_atomic_cas_i32((gint32 *) location, val.ival, cmp.ival);
2425 return ret.fval;
2428 gdouble
2429 ves_icall_System_Threading_Interlocked_CompareExchange_Double (gdouble *location, gdouble value, gdouble comparand)
2431 #if SIZEOF_VOID_P == 8
2432 LongDoubleUnion val, comp, ret;
2434 val.fval = value;
2435 comp.fval = comparand;
2436 ret.ival = (gint64)mono_atomic_cas_ptr((gpointer *) location, (gpointer)val.ival, (gpointer)comp.ival);
2438 return ret.fval;
2439 #else
2440 gdouble old;
2442 mono_interlocked_lock ();
2443 old = *location;
2444 if (old == comparand)
2445 *location = value;
2446 mono_interlocked_unlock ();
2448 return old;
2449 #endif
2452 gint64
2453 ves_icall_System_Threading_Interlocked_CompareExchange_Long (gint64 *location, gint64 value, gint64 comparand)
2455 #if SIZEOF_VOID_P == 4
2456 if (G_UNLIKELY ((size_t)location & 0x7)) {
2457 gint64 old;
2458 mono_interlocked_lock ();
2459 old = *location;
2460 if (old == comparand)
2461 *location = value;
2462 mono_interlocked_unlock ();
2463 return old;
2465 #endif
2466 return mono_atomic_cas_i64 (location, value, comparand);
2469 MonoObject*
2470 ves_icall_System_Threading_Interlocked_CompareExchange_T (MonoObject **location, MonoObject *value, MonoObject *comparand)
2472 MonoObject *res;
2473 res = (MonoObject *)mono_atomic_cas_ptr ((volatile gpointer *)location, value, comparand);
2474 mono_gc_wbarrier_generic_nostore_internal (location);
2475 return res;
2478 MonoObject*
2479 ves_icall_System_Threading_Interlocked_Exchange_T (MonoObject **location, MonoObject *value)
2481 MonoObject *res;
2482 MONO_CHECK_NULL (location, NULL);
2483 res = (MonoObject *)mono_atomic_xchg_ptr ((volatile gpointer *)location, value);
2484 mono_gc_wbarrier_generic_nostore_internal (location);
2485 return res;
2488 gint32
2489 ves_icall_System_Threading_Interlocked_Add_Int (gint32 *location, gint32 value)
2491 return mono_atomic_add_i32 (location, value);
2494 gint64
2495 ves_icall_System_Threading_Interlocked_Add_Long (gint64 *location, gint64 value)
2497 #if SIZEOF_VOID_P == 4
2498 if (G_UNLIKELY ((size_t)location & 0x7)) {
2499 gint64 ret;
2500 mono_interlocked_lock ();
2501 *location += value;
2502 ret = *location;
2503 mono_interlocked_unlock ();
2504 return ret;
2506 #endif
2507 return mono_atomic_add_i64 (location, value);
2510 gint64
2511 ves_icall_System_Threading_Interlocked_Read_Long (gint64 *location)
2513 #if SIZEOF_VOID_P == 4
2514 if (G_UNLIKELY ((size_t)location & 0x7)) {
2515 gint64 ret;
2516 mono_interlocked_lock ();
2517 ret = *location;
2518 mono_interlocked_unlock ();
2519 return ret;
2521 #endif
2522 return mono_atomic_load_i64 (location);
2525 void
2526 ves_icall_System_Threading_Interlocked_MemoryBarrierProcessWide (void)
2528 mono_memory_barrier_process_wide ();
2531 void
2532 ves_icall_System_Threading_Thread_MemoryBarrier (void)
2534 mono_memory_barrier ();
2537 void
2538 ves_icall_System_Threading_Thread_ClrState (MonoInternalThreadHandle this_obj, guint32 state, MonoError *error)
2540 // InternalThreads are always pinned, so shallowly coop-handleize.
2541 mono_thread_clr_state (mono_internal_thread_handle_ptr (this_obj), (MonoThreadState)state);
2544 void
2545 ves_icall_System_Threading_Thread_SetState (MonoInternalThreadHandle thread_handle, guint32 state, MonoError *error)
2547 // InternalThreads are always pinned, so shallowly coop-handleize.
2548 mono_thread_set_state (mono_internal_thread_handle_ptr (thread_handle), (MonoThreadState)state);
2551 guint32
2552 ves_icall_System_Threading_Thread_GetState (MonoInternalThreadHandle thread_handle, MonoError *error)
2554 // InternalThreads are always pinned, so shallowly coop-handleize.
2555 MonoInternalThread *this_obj = mono_internal_thread_handle_ptr (thread_handle);
2557 guint32 state;
2559 LOCK_THREAD (this_obj);
2561 state = this_obj->state;
2563 UNLOCK_THREAD (this_obj);
2565 return state;
2568 void
2569 ves_icall_System_Threading_Thread_Interrupt_internal (MonoThreadObjectHandle thread_handle, MonoError *error)
2571 // Internal threads are pinned so shallow coop/handle.
2572 MonoInternalThread * const thread = thread_handle_to_internal_ptr (thread_handle);
2573 MonoInternalThread * const current = mono_thread_internal_current ();
2575 LOCK_THREAD (thread);
2577 thread->thread_interrupt_requested = TRUE;
2578 gboolean const throw_ = current != thread && (thread->state & ThreadState_WaitSleepJoin);
2580 UNLOCK_THREAD (thread);
2582 if (throw_)
2583 async_abort_internal (thread, FALSE);
2587 * mono_thread_current_check_pending_interrupt:
2588 * Checks if there's a interruption request and set the pending exception if so.
2589 * \returns true if a pending exception was set
2591 gboolean
2592 mono_thread_current_check_pending_interrupt (void)
2594 MonoInternalThread *thread = mono_thread_internal_current ();
2595 gboolean throw_ = FALSE;
2597 LOCK_THREAD (thread);
2599 if (thread->thread_interrupt_requested) {
2600 throw_ = TRUE;
2601 thread->thread_interrupt_requested = FALSE;
2604 UNLOCK_THREAD (thread);
2606 if (throw_) {
2607 ERROR_DECL (error);
2608 mono_error_set_thread_interrupted (error);
2609 mono_error_set_pending_exception (error);
2611 return throw_;
2614 static gboolean
2615 request_thread_abort (MonoInternalThread *thread, MonoObjectHandle *state, gboolean appdomain_unload)
2616 // state is a pointer to a handle in order to be optional,
2617 // and be passed unspecified from functions not using handles.
2618 // When raw pointers is gone, it need not be a pointer,
2619 // though this would still work efficiently.
2621 LOCK_THREAD (thread);
2623 /* With self abort we always throw a new exception */
2624 if (thread == mono_thread_internal_current ())
2625 thread->abort_exc = NULL;
2627 if (thread->state & (ThreadState_AbortRequested | ThreadState_Stopped))
2629 UNLOCK_THREAD (thread);
2630 return FALSE;
2633 if ((thread->state & ThreadState_Unstarted) != 0) {
2634 thread->state |= ThreadState_Aborted;
2635 UNLOCK_THREAD (thread);
2636 return FALSE;
2639 thread->state |= ThreadState_AbortRequested;
2640 if (appdomain_unload)
2641 thread->flags |= MONO_THREAD_FLAG_APPDOMAIN_ABORT;
2642 else
2643 thread->flags &= ~MONO_THREAD_FLAG_APPDOMAIN_ABORT;
2645 mono_gchandle_free_internal (thread->abort_state_handle);
2646 thread->abort_state_handle = 0;
2649 if (state && !MONO_HANDLE_IS_NULL (*state)) {
2650 thread->abort_state_handle = mono_gchandle_from_handle (*state, FALSE);
2651 g_assert (thread->abort_state_handle);
2654 thread->abort_exc = NULL;
2656 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));
2658 /* During shutdown, we can't wait for other threads */
2659 if (!shutting_down)
2660 /* Make sure the thread is awake */
2661 mono_thread_resume (thread);
2663 UNLOCK_THREAD (thread);
2664 return TRUE;
2667 #ifndef ENABLE_NETCORE
2668 void
2669 ves_icall_System_Threading_Thread_Abort (MonoInternalThreadHandle thread_handle, MonoObjectHandle state, MonoError *error)
2671 // InternalThreads are always pinned, so shallowly coop-handleize.
2672 MonoInternalThread * const thread = mono_internal_thread_handle_ptr (thread_handle);
2673 gboolean is_self = thread == mono_thread_internal_current ();
2675 /* For self aborts we always process the abort */
2676 if (!request_thread_abort (thread, &state, FALSE) && !is_self)
2677 return;
2679 if (is_self) {
2680 self_abort_internal (error);
2681 } else {
2682 async_abort_internal (thread, TRUE);
2685 #endif
2688 * mono_thread_internal_abort:
2689 * Request thread \p thread to be aborted.
2690 * \p thread MUST NOT be the current thread.
2692 void
2693 mono_thread_internal_abort (MonoInternalThread *thread, gboolean appdomain_unload)
2695 g_assert (thread != mono_thread_internal_current ());
2697 if (!request_thread_abort (thread, NULL, appdomain_unload))
2698 return;
2699 async_abort_internal (thread, TRUE);
2702 #ifndef ENABLE_NETCORE
2703 void
2704 ves_icall_System_Threading_Thread_ResetAbort (MonoThreadObjectHandle this_obj, MonoError *error)
2706 MonoInternalThread *thread = mono_thread_internal_current ();
2707 gboolean was_aborting, is_domain_abort;
2709 LOCK_THREAD (thread);
2710 was_aborting = thread->state & ThreadState_AbortRequested;
2711 is_domain_abort = thread->flags & MONO_THREAD_FLAG_APPDOMAIN_ABORT;
2713 if (was_aborting && !is_domain_abort)
2714 thread->state &= ~ThreadState_AbortRequested;
2715 UNLOCK_THREAD (thread);
2717 if (!was_aborting) {
2718 mono_error_set_exception_thread_state (error, "Unable to reset abort because no abort was requested");
2719 return;
2720 } else if (is_domain_abort) {
2721 /* Silently ignore abort resets in unloading appdomains */
2722 return;
2725 mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
2726 thread->abort_exc = NULL;
2727 mono_gchandle_free_internal (thread->abort_state_handle);
2728 /* This is actually not necessary - the handle
2729 only counts if the exception is set */
2730 thread->abort_state_handle = 0;
2732 #endif
2734 void
2735 mono_thread_internal_reset_abort (MonoInternalThread *thread)
2737 LOCK_THREAD (thread);
2739 thread->state &= ~ThreadState_AbortRequested;
2741 if (thread->abort_exc) {
2742 mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
2743 thread->abort_exc = NULL;
2744 mono_gchandle_free_internal (thread->abort_state_handle);
2745 /* This is actually not necessary - the handle
2746 only counts if the exception is set */
2747 thread->abort_state_handle = 0;
2750 UNLOCK_THREAD (thread);
2753 #ifndef ENABLE_NETCORE
2754 MonoObjectHandle
2755 ves_icall_System_Threading_Thread_GetAbortExceptionState (MonoThreadObjectHandle this_obj, MonoError *error)
2757 MonoInternalThread *thread = thread_handle_to_internal_ptr (this_obj);
2759 if (!thread->abort_state_handle)
2760 return NULL_HANDLE; // No state. No error.
2762 // Convert gc handle to coop handle.
2763 MonoObjectHandle state = mono_gchandle_get_target_handle (thread->abort_state_handle);
2764 g_assert (MONO_HANDLE_BOOL (state));
2766 MonoDomain *domain = mono_domain_get ();
2767 if (MONO_HANDLE_DOMAIN (state) == domain)
2768 return state; // No need to cross domain, return state directly.
2770 // Attempt move state cross-domain.
2771 MonoObjectHandle deserialized = mono_object_xdomain_representation (state, domain, error);
2773 // If deserialized is null, there must be an error, and vice versa.
2774 g_assert (is_ok (error) == MONO_HANDLE_BOOL (deserialized));
2776 if (MONO_HANDLE_BOOL (deserialized))
2777 return deserialized; // Cross-domain serialization succeeded. Return it.
2779 // Wrap error in InvalidOperationException.
2780 ERROR_DECL (error_creating_exception);
2781 MonoExceptionHandle invalid_op_exc = mono_exception_new_invalid_operation (
2782 "Thread.ExceptionState cannot access an ExceptionState from a different AppDomain", error_creating_exception);
2783 mono_error_assert_ok (error_creating_exception);
2784 g_assert (!is_ok (error) && 1);
2785 MONO_HANDLE_SET (invalid_op_exc, inner_ex, mono_error_convert_to_exception_handle (error));
2786 error_init_reuse (error);
2787 mono_error_set_exception_handle (error, invalid_op_exc);
2788 g_assert (!is_ok (error) && 2);
2790 // There is state, but we failed to return it.
2791 return NULL_HANDLE;
2793 #endif
2795 static gboolean
2796 mono_thread_suspend (MonoInternalThread *thread)
2798 LOCK_THREAD (thread);
2800 if (thread->state & (ThreadState_Unstarted | ThreadState_Aborted | ThreadState_Stopped))
2802 UNLOCK_THREAD (thread);
2803 return FALSE;
2806 if (thread->state & (ThreadState_Suspended | ThreadState_SuspendRequested | ThreadState_AbortRequested))
2808 UNLOCK_THREAD (thread);
2809 return TRUE;
2812 thread->state |= ThreadState_SuspendRequested;
2813 MONO_ENTER_GC_SAFE;
2814 mono_os_event_reset (thread->suspended);
2815 MONO_EXIT_GC_SAFE;
2817 if (thread == mono_thread_internal_current ()) {
2818 /* calls UNLOCK_THREAD (thread) */
2819 self_suspend_internal ();
2820 } else {
2821 /* calls UNLOCK_THREAD (thread) */
2822 async_suspend_internal (thread, FALSE);
2825 return TRUE;
2828 #ifndef ENABLE_NETCORE
2829 void
2830 ves_icall_System_Threading_Thread_Suspend (MonoThreadObjectHandle this_obj, MonoError *error)
2832 if (!mono_thread_suspend (thread_handle_to_internal_ptr (this_obj)))
2833 mono_error_set_exception_thread_not_started_or_dead (error);
2836 #endif
2838 /* LOCKING: LOCK_THREAD(thread) must be held */
2839 static gboolean
2840 mono_thread_resume (MonoInternalThread *thread)
2842 if ((thread->state & ThreadState_SuspendRequested) != 0) {
2843 // g_async_safe_printf ("RESUME (1) thread %p\n", thread_get_tid (thread));
2844 thread->state &= ~ThreadState_SuspendRequested;
2845 MONO_ENTER_GC_SAFE;
2846 mono_os_event_set (thread->suspended);
2847 MONO_EXIT_GC_SAFE;
2848 return TRUE;
2851 if ((thread->state & ThreadState_Suspended) == 0 ||
2852 (thread->state & ThreadState_Unstarted) != 0 ||
2853 (thread->state & ThreadState_Aborted) != 0 ||
2854 (thread->state & ThreadState_Stopped) != 0)
2856 // g_async_safe_printf ("RESUME (2) thread %p\n", thread_get_tid (thread));
2857 return FALSE;
2860 // g_async_safe_printf ("RESUME (3) thread %p\n", thread_get_tid (thread));
2862 MONO_ENTER_GC_SAFE;
2863 mono_os_event_set (thread->suspended);
2864 MONO_EXIT_GC_SAFE;
2866 if (!thread->self_suspended) {
2867 UNLOCK_THREAD (thread);
2869 /* Awake the thread */
2870 if (!mono_thread_info_resume (thread_get_tid (thread)))
2871 return FALSE;
2873 LOCK_THREAD (thread);
2876 thread->state &= ~ThreadState_Suspended;
2878 return TRUE;
2881 #ifndef ENABLE_NETCORE
2882 void
2883 ves_icall_System_Threading_Thread_Resume (MonoThreadObjectHandle thread_handle, MonoError *error)
2885 // Internal threads are pinned so shallow coop/handle.
2886 MonoInternalThread * const internal_thread = thread_handle_to_internal_ptr (thread_handle);
2887 gboolean exception = FALSE;
2889 if (!internal_thread) {
2890 exception = TRUE;
2891 } else {
2892 LOCK_THREAD (internal_thread);
2893 if (!mono_thread_resume (internal_thread))
2894 exception = TRUE;
2895 UNLOCK_THREAD (internal_thread);
2898 if (exception)
2899 mono_error_set_exception_thread_not_started_or_dead (error);
2901 #endif
2903 gboolean
2904 mono_threads_is_critical_method (MonoMethod *method)
2906 switch (method->wrapper_type) {
2907 case MONO_WRAPPER_RUNTIME_INVOKE:
2908 case MONO_WRAPPER_XDOMAIN_INVOKE:
2909 case MONO_WRAPPER_XDOMAIN_DISPATCH:
2910 return TRUE;
2912 return FALSE;
2915 static gboolean
2916 find_wrapper (MonoMethod *m, gint no, gint ilo, gboolean managed, gpointer data)
2918 if (managed)
2919 return TRUE;
2921 if (mono_threads_is_critical_method (m)) {
2922 *((gboolean*)data) = TRUE;
2923 return TRUE;
2925 return FALSE;
2928 static gboolean
2929 is_running_protected_wrapper (void)
2931 gboolean found = FALSE;
2932 mono_stack_walk (find_wrapper, &found);
2933 return found;
2937 * mono_thread_stop:
2939 void
2940 mono_thread_stop (MonoThread *thread)
2942 MonoInternalThread *internal = thread->internal_thread;
2944 if (!request_thread_abort (internal, NULL, FALSE))
2945 return;
2947 if (internal == mono_thread_internal_current ()) {
2948 ERROR_DECL (error);
2949 self_abort_internal (error);
2951 This function is part of the embeding API and has no way to return the exception
2952 to be thrown. So what we do is keep the old behavior and raise the exception.
2954 mono_error_raise_exception_deprecated (error); /* OK to throw, see note */
2955 } else {
2956 async_abort_internal (internal, TRUE);
2960 gint8
2961 ves_icall_System_Threading_Thread_VolatileRead1 (void *ptr)
2963 gint8 tmp = *(volatile gint8 *)ptr;
2964 mono_memory_barrier ();
2965 return tmp;
2968 gint16
2969 ves_icall_System_Threading_Thread_VolatileRead2 (void *ptr)
2971 gint16 tmp = *(volatile gint16 *)ptr;
2972 mono_memory_barrier ();
2973 return tmp;
2976 gint32
2977 ves_icall_System_Threading_Thread_VolatileRead4 (void *ptr)
2979 gint32 tmp = *(volatile gint32 *)ptr;
2980 mono_memory_barrier ();
2981 return tmp;
2984 gint64
2985 ves_icall_System_Threading_Thread_VolatileRead8 (void *ptr)
2987 gint64 tmp = *(volatile gint64 *)ptr;
2988 mono_memory_barrier ();
2989 return tmp;
2992 void *
2993 ves_icall_System_Threading_Thread_VolatileReadIntPtr (void *ptr)
2995 volatile void *tmp = *(volatile void **)ptr;
2996 mono_memory_barrier ();
2997 return (void *) tmp;
3000 void *
3001 ves_icall_System_Threading_Thread_VolatileReadObject (void *ptr)
3003 volatile MonoObject *tmp = *(volatile MonoObject **)ptr;
3004 mono_memory_barrier ();
3005 return (MonoObject *) tmp;
3008 double
3009 ves_icall_System_Threading_Thread_VolatileReadDouble (void *ptr)
3011 double tmp = *(volatile double *)ptr;
3012 mono_memory_barrier ();
3013 return tmp;
3016 float
3017 ves_icall_System_Threading_Thread_VolatileReadFloat (void *ptr)
3019 float tmp = *(volatile float *)ptr;
3020 mono_memory_barrier ();
3021 return tmp;
3024 gint8
3025 ves_icall_System_Threading_Volatile_Read1 (void *ptr)
3027 return mono_atomic_load_i8 ((volatile gint8 *)ptr);
3030 gint16
3031 ves_icall_System_Threading_Volatile_Read2 (void *ptr)
3033 return mono_atomic_load_i16 ((volatile gint16 *)ptr);
3036 gint32
3037 ves_icall_System_Threading_Volatile_Read4 (void *ptr)
3039 return mono_atomic_load_i32 ((volatile gint32 *)ptr);
3042 gint64
3043 ves_icall_System_Threading_Volatile_Read8 (void *ptr)
3045 #if SIZEOF_VOID_P == 4
3046 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
3047 gint64 val;
3048 mono_interlocked_lock ();
3049 val = *(gint64*)ptr;
3050 mono_interlocked_unlock ();
3051 return val;
3053 #endif
3054 return mono_atomic_load_i64 ((volatile gint64 *)ptr);
3057 guint64
3058 ves_icall_System_Threading_Volatile_ReadU8 (void *ptr)
3060 #if SIZEOF_VOID_P == 4
3061 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
3062 guint64 val;
3063 mono_interlocked_lock ();
3064 val = *(guint64*)ptr;
3065 mono_interlocked_unlock ();
3066 return val;
3068 #endif
3069 return (guint64)mono_atomic_load_i64 ((volatile gint64 *)ptr);
3072 void *
3073 ves_icall_System_Threading_Volatile_ReadIntPtr (void *ptr)
3075 return mono_atomic_load_ptr ((volatile gpointer *)ptr);
3078 double
3079 ves_icall_System_Threading_Volatile_ReadDouble (void *ptr)
3081 LongDoubleUnion u;
3083 #if SIZEOF_VOID_P == 4
3084 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
3085 double val;
3086 mono_interlocked_lock ();
3087 val = *(double*)ptr;
3088 mono_interlocked_unlock ();
3089 return val;
3091 #endif
3093 u.ival = mono_atomic_load_i64 ((volatile gint64 *)ptr);
3095 return u.fval;
3098 float
3099 ves_icall_System_Threading_Volatile_ReadFloat (void *ptr)
3101 IntFloatUnion u;
3103 u.ival = mono_atomic_load_i32 ((volatile gint32 *)ptr);
3105 return u.fval;
3108 MonoObject*
3109 ves_icall_System_Threading_Volatile_Read_T (void *ptr)
3111 return (MonoObject *)mono_atomic_load_ptr ((volatile gpointer *)ptr);
3114 void
3115 ves_icall_System_Threading_Thread_VolatileWrite1 (void *ptr, gint8 value)
3117 mono_memory_barrier ();
3118 *(volatile gint8 *)ptr = value;
3121 void
3122 ves_icall_System_Threading_Thread_VolatileWrite2 (void *ptr, gint16 value)
3124 mono_memory_barrier ();
3125 *(volatile gint16 *)ptr = value;
3128 void
3129 ves_icall_System_Threading_Thread_VolatileWrite4 (void *ptr, gint32 value)
3131 mono_memory_barrier ();
3132 *(volatile gint32 *)ptr = value;
3135 void
3136 ves_icall_System_Threading_Thread_VolatileWrite8 (void *ptr, gint64 value)
3138 mono_memory_barrier ();
3139 *(volatile gint64 *)ptr = value;
3142 void
3143 ves_icall_System_Threading_Thread_VolatileWriteIntPtr (void *ptr, void *value)
3145 mono_memory_barrier ();
3146 *(volatile void **)ptr = value;
3149 void
3150 ves_icall_System_Threading_Thread_VolatileWriteObject (void *ptr, MonoObject *value)
3152 mono_memory_barrier ();
3153 mono_gc_wbarrier_generic_store_internal (ptr, value);
3156 void
3157 ves_icall_System_Threading_Thread_VolatileWriteDouble (void *ptr, double value)
3159 mono_memory_barrier ();
3160 *(volatile double *)ptr = value;
3163 void
3164 ves_icall_System_Threading_Thread_VolatileWriteFloat (void *ptr, float value)
3166 mono_memory_barrier ();
3167 *(volatile float *)ptr = value;
3170 void
3171 ves_icall_System_Threading_Volatile_Write1 (void *ptr, gint8 value)
3173 mono_atomic_store_i8 ((volatile gint8 *)ptr, value);
3176 void
3177 ves_icall_System_Threading_Volatile_Write2 (void *ptr, gint16 value)
3179 mono_atomic_store_i16 ((volatile gint16 *)ptr, value);
3182 void
3183 ves_icall_System_Threading_Volatile_Write4 (void *ptr, gint32 value)
3185 mono_atomic_store_i32 ((volatile gint32 *)ptr, value);
3188 void
3189 ves_icall_System_Threading_Volatile_Write8 (void *ptr, gint64 value)
3191 #if SIZEOF_VOID_P == 4
3192 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
3193 mono_interlocked_lock ();
3194 *(gint64*)ptr = value;
3195 mono_interlocked_unlock ();
3196 return;
3198 #endif
3200 mono_atomic_store_i64 ((volatile gint64 *)ptr, value);
3203 void
3204 ves_icall_System_Threading_Volatile_WriteU8 (void *ptr, guint64 value)
3206 #if SIZEOF_VOID_P == 4
3207 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
3208 mono_interlocked_lock ();
3209 *(guint64*)ptr = value;
3210 mono_interlocked_unlock ();
3211 return;
3213 #endif
3215 mono_atomic_store_i64 ((volatile gint64 *)ptr, (gint64)value);
3218 void
3219 ves_icall_System_Threading_Volatile_WriteIntPtr (void *ptr, void *value)
3221 mono_atomic_store_ptr ((volatile gpointer *)ptr, value);
3224 void
3225 ves_icall_System_Threading_Volatile_WriteDouble (void *ptr, double value)
3227 LongDoubleUnion u;
3229 #if SIZEOF_VOID_P == 4
3230 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
3231 mono_interlocked_lock ();
3232 *(double*)ptr = value;
3233 mono_interlocked_unlock ();
3234 return;
3236 #endif
3238 u.fval = value;
3240 mono_atomic_store_i64 ((volatile gint64 *)ptr, u.ival);
3243 void
3244 ves_icall_System_Threading_Volatile_WriteFloat (void *ptr, float value)
3246 IntFloatUnion u;
3248 u.fval = value;
3250 mono_atomic_store_i32 ((volatile gint32 *)ptr, u.ival);
3253 void
3254 ves_icall_System_Threading_Volatile_Write_T (void *ptr, MonoObject *value)
3256 mono_gc_wbarrier_generic_store_atomic_internal (ptr, value);
3259 static void
3260 free_context (void *user_data)
3262 ContextStaticData *data = (ContextStaticData*)user_data;
3264 mono_threads_lock ();
3267 * There is no guarantee that, by the point this reference queue callback
3268 * has been invoked, the GC handle associated with the object will fail to
3269 * resolve as one might expect. So if we don't free and remove the GC
3270 * handle here, free_context_static_data_helper () could end up resolving
3271 * a GC handle to an actually-dead context which would contain a pointer
3272 * to an already-freed static data segment, resulting in a crash when
3273 * accessing it.
3275 g_hash_table_remove (contexts, GUINT_TO_POINTER (data->gc_handle));
3277 mono_threads_unlock ();
3279 mono_gchandle_free_internal (data->gc_handle);
3280 mono_free_static_data (data->static_data);
3281 g_free (data);
3284 void
3285 mono_threads_register_app_context (MonoAppContextHandle ctx, MonoError *error)
3287 error_init (error);
3288 mono_threads_lock ();
3290 //g_print ("Registering context %d in domain %d\n", ctx->context_id, ctx->domain_id);
3292 if (!contexts)
3293 contexts = g_hash_table_new (NULL, NULL);
3295 if (!context_queue)
3296 context_queue = mono_gc_reference_queue_new_internal (free_context);
3298 gpointer gch = GUINT_TO_POINTER (mono_gchandle_new_weakref_from_handle (MONO_HANDLE_CAST (MonoObject, ctx)));
3299 g_hash_table_insert (contexts, gch, gch);
3302 * We use this intermediate structure to contain a duplicate pointer to
3303 * the static data because we can't rely on being able to resolve the GC
3304 * handle in the reference queue callback.
3306 ContextStaticData *data = g_new0 (ContextStaticData, 1);
3307 data->gc_handle = GPOINTER_TO_UINT (gch);
3308 MONO_HANDLE_SETVAL (ctx, data, ContextStaticData*, data);
3310 context_adjust_static_data (ctx);
3311 mono_gc_reference_queue_add_handle (context_queue, ctx, data);
3313 mono_threads_unlock ();
3315 MONO_PROFILER_RAISE (context_loaded, (MONO_HANDLE_RAW (ctx)));
3318 void
3319 ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppContextHandle ctx, MonoError *error)
3321 mono_threads_register_app_context (ctx, error);
3324 void
3325 mono_threads_release_app_context (MonoAppContext* ctx, MonoError *error)
3328 * NOTE: Since finalizers are unreliable for the purposes of ensuring
3329 * cleanup in exceptional circumstances, we don't actually do any
3330 * cleanup work here. We instead do this via a reference queue.
3333 //g_print ("Releasing context %d in domain %d\n", ctx->context_id, ctx->domain_id);
3335 MONO_PROFILER_RAISE (context_unloaded, (ctx));
3338 void
3339 ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContextHandle ctx, MonoError *error)
3341 mono_threads_release_app_context (MONO_HANDLE_RAW (ctx), error); /* FIXME use handles in mono_threads_release_app_context */
3344 void mono_thread_init (MonoThreadStartCB start_cb,
3345 MonoThreadAttachCB attach_cb)
3347 mono_coop_mutex_init_recursive (&threads_mutex);
3349 #if SIZEOF_VOID_P == 4
3350 mono_os_mutex_init (&interlocked_mutex);
3351 #endif
3352 mono_coop_mutex_init_recursive(&joinable_threads_mutex);
3354 mono_os_event_init (&background_change_event, FALSE);
3356 mono_coop_cond_init (&pending_native_thread_join_calls_event);
3357 mono_coop_cond_init (&zero_pending_joinable_thread_event);
3359 mono_init_static_data_info (&thread_static_info);
3360 mono_init_static_data_info (&context_static_info);
3362 mono_thread_start_cb = start_cb;
3363 mono_thread_attach_cb = attach_cb;
3367 static gpointer
3368 thread_attach (MonoThreadInfo *info)
3370 return mono_gc_thread_attach (info);
3373 static void
3374 thread_detach (MonoThreadInfo *info)
3376 MonoInternalThread *internal;
3377 guint32 gchandle;
3379 /* If a delegate is passed to native code and invoked on a thread we dont
3380 * know about, marshal will register it with mono_threads_attach_coop, but
3381 * we have no way of knowing when that thread goes away. SGen has a TSD
3382 * so we assume that if the domain is still registered, we can detach
3383 * the thread */
3385 g_assert (info);
3386 g_assert (mono_thread_info_is_current (info));
3388 if (!mono_thread_info_try_get_internal_thread_gchandle (info, &gchandle))
3389 return;
3391 internal = (MonoInternalThread*) mono_gchandle_get_target_internal (gchandle);
3392 g_assert (internal);
3394 mono_thread_detach_internal (internal);
3397 static void
3398 thread_detach_with_lock (MonoThreadInfo *info)
3400 mono_gc_thread_detach_with_lock (info);
3403 static gboolean
3404 thread_in_critical_region (MonoThreadInfo *info)
3406 return mono_gc_thread_in_critical_region (info);
3409 static gboolean
3410 ip_in_critical_region (MonoDomain *domain, gpointer ip)
3412 MonoJitInfo *ji;
3413 MonoMethod *method;
3416 * We pass false for 'try_aot' so this becomes async safe.
3417 * It won't find aot methods whose jit info is not yet loaded,
3418 * so we preload their jit info in the JIT.
3420 ji = mono_jit_info_table_find_internal (domain, ip, FALSE, FALSE);
3421 if (!ji)
3422 return FALSE;
3424 method = mono_jit_info_get_method (ji);
3425 g_assert (method);
3427 return mono_gc_is_critical_method (method);
3430 static void
3431 thread_flags_changing (MonoThreadInfoFlags old, MonoThreadInfoFlags new_)
3433 mono_gc_skip_thread_changing (!!(new_ & MONO_THREAD_INFO_FLAGS_NO_GC));
3436 static void
3437 thread_flags_changed (MonoThreadInfoFlags old, MonoThreadInfoFlags new_)
3439 mono_gc_skip_thread_changed (!!(new_ & MONO_THREAD_INFO_FLAGS_NO_GC));
3442 void
3443 mono_thread_callbacks_init (void)
3445 MonoThreadInfoCallbacks cb;
3447 memset (&cb, 0, sizeof(cb));
3448 cb.thread_attach = thread_attach;
3449 cb.thread_detach = thread_detach;
3450 cb.thread_detach_with_lock = thread_detach_with_lock;
3451 cb.ip_in_critical_region = ip_in_critical_region;
3452 cb.thread_in_critical_region = thread_in_critical_region;
3453 cb.thread_flags_changing = thread_flags_changing;
3454 cb.thread_flags_changed = thread_flags_changed;
3455 mono_thread_info_callbacks_init (&cb);
3459 * mono_thread_cleanup:
3461 void
3462 mono_thread_cleanup (void)
3464 /* Wait for pending threads to park on joinable threads list */
3465 /* NOTE, waiting on this should be extremely rare and will only happen */
3466 /* under certain specific conditions. */
3467 gboolean wait_result = threads_wait_pending_joinable_threads (2000);
3468 if (!wait_result)
3469 g_warning ("Waiting on threads to park on joinable thread list timed out.");
3471 mono_threads_join_threads ();
3473 #if !defined(HOST_WIN32)
3474 /* The main thread must abandon any held mutexes (particularly
3475 * important for named mutexes as they are shared across
3476 * processes, see bug 74680.) This will happen when the
3477 * thread exits, but if it's not running in a subthread it
3478 * won't exit in time.
3480 if (!mono_runtime_get_no_exec ())
3481 mono_w32mutex_abandon (mono_thread_internal_current ());
3482 #endif
3484 #if 0
3485 /* This stuff needs more testing, it seems one of these
3486 * critical sections can be locked when mono_thread_cleanup is
3487 * called.
3489 mono_coop_mutex_destroy (&threads_mutex);
3490 mono_os_mutex_destroy (&interlocked_mutex);
3491 mono_os_mutex_destroy (&delayed_free_table_mutex);
3492 mono_os_mutex_destroy (&small_id_mutex);
3493 mono_coop_cond_destroy (&zero_pending_joinable_thread_event);
3494 mono_coop_cond_destroy (&pending_native_thread_join_calls_event);
3495 mono_os_event_destroy (&background_change_event);
3496 #endif
3499 void
3500 mono_threads_install_cleanup (MonoThreadCleanupFunc func)
3502 mono_thread_cleanup_fn = func;
3506 * mono_thread_set_manage_callback:
3508 void
3509 mono_thread_set_manage_callback (MonoThread *thread, MonoThreadManageCallback func)
3511 thread->internal_thread->manage_callback = func;
3514 G_GNUC_UNUSED
3515 static void print_tids (gpointer key, gpointer value, gpointer user)
3517 /* GPOINTER_TO_UINT breaks horribly if sizeof(void *) >
3518 * sizeof(uint) and a cast to uint would overflow
3520 /* Older versions of glib don't have G_GSIZE_FORMAT, so just
3521 * print this as a pointer.
3523 g_message ("Waiting for: %p", key);
3526 struct wait_data
3528 MonoThreadHandle *handles[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
3529 MonoInternalThread *threads[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
3530 guint32 num;
3533 static void
3534 wait_for_tids (struct wait_data *wait, guint32 timeout, gboolean check_state_change)
3536 guint32 i;
3537 MonoThreadInfoWaitRet ret;
3539 THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num));
3541 /* Add the thread state change event, so it wakes
3542 * up if a thread changes to background mode. */
3544 MONO_ENTER_GC_SAFE;
3545 if (check_state_change)
3546 ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, &background_change_event, FALSE, timeout, TRUE);
3547 else
3548 ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, NULL, TRUE, timeout, TRUE);
3549 MONO_EXIT_GC_SAFE;
3551 if (ret == MONO_THREAD_INFO_WAIT_RET_FAILED) {
3552 /* See the comment in build_wait_tids() */
3553 THREAD_DEBUG (g_message ("%s: Wait failed", __func__));
3554 return;
3557 for( i = 0; i < wait->num; i++)
3558 mono_threads_close_thread_handle (wait->handles [i]);
3560 if (ret >= MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 && ret < (MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + wait->num)) {
3561 MonoInternalThread *internal;
3563 internal = wait->threads [ret - MONO_THREAD_INFO_WAIT_RET_SUCCESS_0];
3565 mono_threads_lock ();
3566 if (mono_g_hash_table_lookup (threads, (gpointer) internal->tid) == internal)
3567 g_error ("%s: failed to call mono_thread_detach_internal on thread %p, InternalThread: %p", __func__, internal->tid, internal);
3568 mono_threads_unlock ();
3572 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
3574 struct wait_data *wait=(struct wait_data *)user;
3576 if(wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS - 1) {
3577 MonoInternalThread *thread=(MonoInternalThread *)value;
3579 /* Ignore background threads, we abort them later */
3580 /* Do not lock here since it is not needed and the caller holds threads_lock */
3581 if (thread->state & ThreadState_Background) {
3582 THREAD_DEBUG (g_message ("%s: ignoring background thread %" G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3583 return; /* just leave, ignore */
3586 if (mono_gc_is_finalizer_internal_thread (thread)) {
3587 THREAD_DEBUG (g_message ("%s: ignoring finalizer thread %" G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3588 return;
3591 if (thread == mono_thread_internal_current ()) {
3592 THREAD_DEBUG (g_message ("%s: ignoring current thread %" G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3593 return;
3596 if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread)) {
3597 THREAD_DEBUG (g_message ("%s: ignoring main thread %" G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3598 return;
3601 if (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE) {
3602 THREAD_DEBUG (g_message ("%s: ignoring thread %" G_GSIZE_FORMAT "with DONT_MANAGE flag set.", __func__, (gsize)thread->tid));
3603 return;
3606 THREAD_DEBUG (g_message ("%s: Invoking mono_thread_manage callback on thread %p", __func__, thread));
3607 if ((thread->manage_callback == NULL) || (thread->manage_callback (thread->root_domain_thread) == TRUE)) {
3608 wait->handles[wait->num]=mono_threads_open_thread_handle (thread->handle);
3609 wait->threads[wait->num]=thread;
3610 wait->num++;
3612 THREAD_DEBUG (g_message ("%s: adding thread %" G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3613 } else {
3614 THREAD_DEBUG (g_message ("%s: ignoring (because of callback) thread %" G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3618 } else {
3619 /* Just ignore the rest, we can't do anything with
3620 * them yet
3625 static void
3626 abort_threads (gpointer key, gpointer value, gpointer user)
3628 struct wait_data *wait=(struct wait_data *)user;
3629 MonoNativeThreadId self = mono_native_thread_id_get ();
3630 MonoInternalThread *thread = (MonoInternalThread *)value;
3632 if (wait->num >= MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS)
3633 return;
3635 if (mono_native_thread_id_equals (thread_get_tid (thread), self))
3636 return;
3637 if (mono_gc_is_finalizer_internal_thread (thread))
3638 return;
3640 if ((thread->flags & MONO_THREAD_FLAG_DONT_MANAGE))
3641 return;
3643 wait->handles[wait->num] = mono_threads_open_thread_handle (thread->handle);
3644 wait->threads[wait->num] = thread;
3645 wait->num++;
3647 THREAD_DEBUG (g_print ("%s: Aborting id: %" G_GSIZE_FORMAT "\n", __func__, (gsize)thread->tid));
3648 mono_thread_internal_abort (thread, FALSE);
3651 /**
3652 * mono_threads_set_shutting_down:
3654 * Is called by a thread that wants to shut down Mono. If the runtime is already
3655 * shutting down, the calling thread is suspended/stopped, and this function never
3656 * returns.
3658 void
3659 mono_threads_set_shutting_down (void)
3661 MonoInternalThread *current_thread = mono_thread_internal_current ();
3663 mono_threads_lock ();
3665 if (shutting_down) {
3666 mono_threads_unlock ();
3668 /* Make sure we're properly suspended/stopped */
3670 LOCK_THREAD (current_thread);
3672 if (current_thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
3673 UNLOCK_THREAD (current_thread);
3674 mono_thread_execute_interruption_void ();
3675 } else {
3676 UNLOCK_THREAD (current_thread);
3679 /*since we're killing the thread, detach it.*/
3680 mono_thread_detach_internal (current_thread);
3682 /* Wake up other threads potentially waiting for us */
3683 mono_thread_info_exit (0);
3684 } else {
3685 shutting_down = TRUE;
3687 /* Not really a background state change, but this will
3688 * interrupt the main thread if it is waiting for all
3689 * the other threads.
3691 MONO_ENTER_GC_SAFE;
3692 mono_os_event_set (&background_change_event);
3693 MONO_EXIT_GC_SAFE;
3695 mono_threads_unlock ();
3700 * mono_thread_manage:
3702 void
3703 mono_thread_manage (void)
3705 struct wait_data wait_data;
3706 struct wait_data *wait = &wait_data;
3708 memset (wait, 0, sizeof (struct wait_data));
3709 /* join each thread that's still running */
3710 THREAD_DEBUG (g_message ("%s: Joining each running thread...", __func__));
3712 mono_threads_lock ();
3713 if(threads==NULL) {
3714 THREAD_DEBUG (g_message("%s: No threads", __func__));
3715 mono_threads_unlock ();
3716 return;
3719 mono_threads_unlock ();
3721 do {
3722 mono_threads_lock ();
3723 if (shutting_down) {
3724 /* somebody else is shutting down */
3725 mono_threads_unlock ();
3726 break;
3728 THREAD_DEBUG (g_message ("%s: There are %d threads to join", __func__, mono_g_hash_table_size (threads));
3729 mono_g_hash_table_foreach (threads, print_tids, NULL));
3731 MONO_ENTER_GC_SAFE;
3732 mono_os_event_reset (&background_change_event);
3733 MONO_EXIT_GC_SAFE;
3734 wait->num=0;
3735 /* We must zero all InternalThread pointers to avoid making the GC unhappy. */
3736 memset (wait->threads, 0, sizeof (wait->threads));
3737 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
3738 mono_threads_unlock ();
3739 if (wait->num > 0)
3740 /* Something to wait for */
3741 wait_for_tids (wait, MONO_INFINITE_WAIT, TRUE);
3742 THREAD_DEBUG (g_message ("%s: I have %d threads after waiting.", __func__, wait->num));
3743 } while(wait->num>0);
3745 /* Mono is shutting down, so just wait for the end */
3746 if (!mono_runtime_try_shutdown ()) {
3747 /*FIXME mono_thread_suspend probably should call mono_thread_execute_interruption when self interrupting. */
3748 mono_thread_suspend (mono_thread_internal_current ());
3749 mono_thread_execute_interruption_void ();
3752 #ifndef ENABLE_NETCORE
3754 * Under netcore, we don't abort any threads, just exit.
3755 * This is not a problem since we don't do runtime cleanup either.
3758 * Remove everything but the finalizer thread and self.
3759 * Also abort all the background threads
3760 * */
3761 do {
3762 mono_threads_lock ();
3764 wait->num = 0;
3765 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
3766 memset (wait->threads, 0, sizeof (wait->threads));
3767 mono_g_hash_table_foreach (threads, abort_threads, wait);
3769 mono_threads_unlock ();
3771 THREAD_DEBUG (g_message ("%s: wait->num is now %d", __func__, wait->num));
3772 if (wait->num > 0) {
3773 /* Something to wait for */
3774 wait_for_tids (wait, MONO_INFINITE_WAIT, FALSE);
3776 } while (wait->num > 0);
3777 #endif
3780 * give the subthreads a chance to really quit (this is mainly needed
3781 * to get correct user and system times from getrusage/wait/time(1)).
3782 * This could be removed if we avoid pthread_detach() and use pthread_join().
3784 mono_thread_info_yield ();
3787 static void
3788 collect_threads_for_suspend (gpointer key, gpointer value, gpointer user_data)
3790 MonoInternalThread *thread = (MonoInternalThread*)value;
3791 struct wait_data *wait = (struct wait_data*)user_data;
3794 * We try to exclude threads early, to avoid running into the MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
3795 * limitation.
3796 * This needs no locking.
3798 if ((thread->state & ThreadState_Suspended) != 0 ||
3799 (thread->state & ThreadState_Stopped) != 0)
3800 return;
3802 if (wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
3803 wait->handles [wait->num] = mono_threads_open_thread_handle (thread->handle);
3804 wait->threads [wait->num] = thread;
3805 wait->num++;
3810 * mono_thread_suspend_all_other_threads:
3812 * Suspend all managed threads except the finalizer thread and this thread. It is
3813 * not possible to resume them later.
3815 void mono_thread_suspend_all_other_threads (void)
3817 struct wait_data wait_data;
3818 struct wait_data *wait = &wait_data;
3819 int i;
3820 MonoNativeThreadId self = mono_native_thread_id_get ();
3821 guint32 eventidx = 0;
3822 gboolean starting, finished;
3824 memset (wait, 0, sizeof (struct wait_data));
3826 * The other threads could be in an arbitrary state at this point, i.e.
3827 * they could be starting up, shutting down etc. This means that there could be
3828 * threads which are not even in the threads hash table yet.
3832 * First we set a barrier which will be checked by all threads before they
3833 * are added to the threads hash table, and they will exit if the flag is set.
3834 * This ensures that no threads could be added to the hash later.
3835 * We will use shutting_down as the barrier for now.
3837 g_assert (shutting_down);
3840 * We make multiple calls to WaitForMultipleObjects since:
3841 * - we can only wait for MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS threads
3842 * - some threads could exit without becoming suspended
3844 finished = FALSE;
3845 while (!finished) {
3847 * Make a copy of the hashtable since we can't do anything with
3848 * threads while threads_mutex is held.
3850 wait->num = 0;
3851 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
3852 memset (wait->threads, 0, sizeof (wait->threads));
3853 mono_threads_lock ();
3854 mono_g_hash_table_foreach (threads, collect_threads_for_suspend, wait);
3855 mono_threads_unlock ();
3857 eventidx = 0;
3858 /* Get the suspended events that we'll be waiting for */
3859 for (i = 0; i < wait->num; ++i) {
3860 MonoInternalThread *thread = wait->threads [i];
3862 if (mono_native_thread_id_equals (thread_get_tid (thread), self)
3863 || mono_gc_is_finalizer_internal_thread (thread)
3864 || (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)
3866 mono_threads_close_thread_handle (wait->handles [i]);
3867 wait->threads [i] = NULL;
3868 continue;
3871 LOCK_THREAD (thread);
3873 if (thread->state & (ThreadState_Suspended | ThreadState_Stopped)) {
3874 UNLOCK_THREAD (thread);
3875 mono_threads_close_thread_handle (wait->handles [i]);
3876 wait->threads [i] = NULL;
3877 continue;
3880 ++eventidx;
3882 /* Convert abort requests into suspend requests */
3883 if ((thread->state & ThreadState_AbortRequested) != 0)
3884 thread->state &= ~ThreadState_AbortRequested;
3886 thread->state |= ThreadState_SuspendRequested;
3887 MONO_ENTER_GC_SAFE;
3888 mono_os_event_reset (thread->suspended);
3889 MONO_EXIT_GC_SAFE;
3891 /* Signal the thread to suspend + calls UNLOCK_THREAD (thread) */
3892 async_suspend_internal (thread, TRUE);
3894 mono_threads_close_thread_handle (wait->handles [i]);
3895 wait->threads [i] = NULL;
3897 if (eventidx <= 0) {
3899 * If there are threads which are starting up, we wait until they
3900 * are suspended when they try to register in the threads hash.
3901 * This is guaranteed to finish, since the threads which can create new
3902 * threads get suspended after a while.
3903 * FIXME: The finalizer thread can still create new threads.
3905 mono_threads_lock ();
3906 if (threads_starting_up)
3907 starting = mono_g_hash_table_size (threads_starting_up) > 0;
3908 else
3909 starting = FALSE;
3910 mono_threads_unlock ();
3911 if (starting)
3912 mono_thread_info_sleep (100, NULL);
3913 else
3914 finished = TRUE;
3919 typedef struct {
3920 MonoInternalThread *thread;
3921 MonoStackFrameInfo *frames;
3922 int nframes, max_frames;
3923 int nthreads, max_threads;
3924 MonoInternalThread **threads;
3925 } ThreadDumpUserData;
3927 static gboolean thread_dump_requested;
3929 /* This needs to be async safe */
3930 static gboolean
3931 collect_frame (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
3933 ThreadDumpUserData *ud = (ThreadDumpUserData *)data;
3935 if (ud->nframes < ud->max_frames) {
3936 memcpy (&ud->frames [ud->nframes], frame, sizeof (MonoStackFrameInfo));
3937 ud->nframes ++;
3940 return FALSE;
3943 /* This needs to be async safe */
3944 static SuspendThreadResult
3945 get_thread_dump (MonoThreadInfo *info, gpointer ud)
3947 ThreadDumpUserData *user_data = (ThreadDumpUserData *)ud;
3948 MonoInternalThread *thread = user_data->thread;
3950 #if 0
3951 /* This no longer works with remote unwinding */
3952 g_string_append_printf (text, " tid=0x%p this=0x%p ", (gpointer)(gsize)thread->tid, thread);
3953 mono_thread_internal_describe (thread, text);
3954 g_string_append (text, "\n");
3955 #endif
3957 if (thread == mono_thread_internal_current ())
3958 mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (collect_frame, NULL, MONO_UNWIND_SIGNAL_SAFE, ud);
3959 else
3960 mono_get_eh_callbacks ()->mono_walk_stack_with_state (collect_frame, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, ud);
3962 return MonoResumeThread;
3965 typedef struct {
3966 int nthreads, max_threads;
3968 guint32 *threads;
3969 } CollectThreadsUserData;
3971 typedef struct {
3972 int nthreads, max_threads;
3973 MonoNativeThreadId *threads;
3974 } CollectThreadIdsUserData;
3976 static void
3977 collect_thread (gpointer key, gpointer value, gpointer user)
3979 CollectThreadsUserData *ud = (CollectThreadsUserData *)user;
3980 MonoInternalThread *thread = (MonoInternalThread *)value;
3982 if (ud->nthreads < ud->max_threads)
3983 ud->threads [ud->nthreads ++] = mono_gchandle_new_internal (&thread->obj, TRUE);
3986 static void
3987 collect_thread_id (gpointer key, gpointer value, gpointer user)
3989 CollectThreadIdsUserData *ud = (CollectThreadIdsUserData *)user;
3990 MonoInternalThread *thread = (MonoInternalThread *)value;
3992 if (ud->nthreads < ud->max_threads)
3993 ud->threads [ud->nthreads ++] = thread_get_tid (thread);
3997 * Collect running threads into the THREADS array.
3998 * THREADS should be an array allocated on the stack.
4000 static int
4001 collect_threads (guint32 *thread_handles, int max_threads)
4003 CollectThreadsUserData ud;
4005 mono_memory_barrier ();
4006 if (!threads)
4007 return 0;
4009 memset (&ud, 0, sizeof (ud));
4010 /* This array contains refs, but its on the stack, so its ok */
4011 ud.threads = thread_handles;
4012 ud.max_threads = max_threads;
4014 mono_threads_lock ();
4015 mono_g_hash_table_foreach (threads, collect_thread, &ud);
4016 mono_threads_unlock ();
4018 return ud.nthreads;
4021 static int
4022 collect_thread_ids (MonoNativeThreadId *thread_ids, int max_threads)
4024 CollectThreadIdsUserData ud;
4026 mono_memory_barrier ();
4027 if (!threads)
4028 return 0;
4030 memset (&ud, 0, sizeof (ud));
4031 /* This array contains refs, but its on the stack, so its ok */
4032 ud.threads = thread_ids;
4033 ud.max_threads = max_threads;
4035 mono_threads_lock ();
4036 mono_g_hash_table_foreach (threads, collect_thread_id, &ud);
4037 mono_threads_unlock ();
4039 return ud.nthreads;
4042 static void
4043 dump_thread (MonoInternalThread *thread, ThreadDumpUserData *ud, FILE* output_file)
4045 GString* text = g_string_new (0);
4046 char *name;
4047 GError *gerror = NULL;
4048 int i;
4050 ud->thread = thread;
4051 ud->nframes = 0;
4053 /* Collect frames for the thread */
4054 if (thread == mono_thread_internal_current ()) {
4055 get_thread_dump (mono_thread_info_current (), ud);
4056 } else {
4057 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, get_thread_dump, ud);
4061 * Do all the non async-safe work outside of get_thread_dump.
4063 if (thread->name) {
4064 name = g_utf16_to_utf8 (thread->name, thread->name_len, NULL, NULL, &gerror);
4065 g_assert (!gerror);
4066 g_string_append_printf (text, "\n\"%s\"", name);
4067 g_free (name);
4069 else if (thread->threadpool_thread) {
4070 g_string_append (text, "\n\"<threadpool thread>\"");
4071 } else {
4072 g_string_append (text, "\n\"<unnamed thread>\"");
4075 for (i = 0; i < ud->nframes; ++i) {
4076 MonoStackFrameInfo *frame = &ud->frames [i];
4077 MonoMethod *method = NULL;
4079 if (frame->type == FRAME_TYPE_MANAGED)
4080 method = mono_jit_info_get_method (frame->ji);
4082 if (method) {
4083 gchar *location = mono_debug_print_stack_frame (method, frame->native_offset, frame->domain);
4084 g_string_append_printf (text, " %s\n", location);
4085 g_free (location);
4086 } else {
4087 g_string_append_printf (text, " at <unknown> <0x%05x>\n", frame->native_offset);
4091 g_fprintf (output_file, "%s", text->str);
4093 #if PLATFORM_WIN32 && TARGET_WIN32 && _DEBUG
4094 OutputDebugStringA(text->str);
4095 #endif
4097 g_string_free (text, TRUE);
4098 fflush (output_file);
4101 static void
4102 mono_get_time_of_day (struct timeval *tv) {
4103 #ifdef WIN32
4104 struct _timeb time;
4105 _ftime(&time);
4106 tv->tv_sec = time.time;
4107 tv->tv_usec = time.millitm * 1000;
4108 #else
4109 if (gettimeofday (tv, NULL) == -1) {
4110 g_error ("gettimeofday() failed; errno is %d (%s)", errno, strerror (errno));
4112 #endif
4115 static void
4116 mono_local_time (const struct timeval *tv, struct tm *tm) {
4117 #ifdef HAVE_LOCALTIME_R
4118 localtime_r(&tv->tv_sec, tm);
4119 #else
4120 time_t const tv_sec = tv->tv_sec; // Copy due to Win32/Posix contradiction.
4121 *tm = *localtime (&tv_sec);
4122 #endif
4125 void
4126 mono_threads_perform_thread_dump (void)
4128 FILE* output_file = NULL;
4129 ThreadDumpUserData ud;
4130 guint32 thread_array [128];
4131 int tindex, nthreads;
4133 if (!thread_dump_requested)
4134 return;
4136 if (thread_dump_dir != NULL) {
4137 GString* path = g_string_new (0);
4138 char time_str[80];
4139 struct timeval tv;
4140 long ms;
4141 struct tm tod;
4142 mono_get_time_of_day (&tv);
4143 mono_local_time(&tv, &tod);
4144 strftime(time_str, sizeof(time_str), MONO_STRFTIME_F "_" MONO_STRFTIME_T, &tod);
4145 ms = tv.tv_usec / 1000;
4146 g_string_append_printf (path, "%s/%s.%03ld.tdump", thread_dump_dir, time_str, ms);
4147 output_file = fopen (path->str, "w");
4148 g_string_free (path, TRUE);
4150 if (output_file == NULL) {
4151 g_print ("Full thread dump:\n");
4154 /* Make a copy of the threads hash to avoid doing work inside threads_lock () */
4155 nthreads = collect_threads (thread_array, 128);
4157 memset (&ud, 0, sizeof (ud));
4158 ud.frames = g_new0 (MonoStackFrameInfo, 256);
4159 ud.max_frames = 256;
4161 for (tindex = 0; tindex < nthreads; ++tindex) {
4162 guint32 handle = thread_array [tindex];
4163 MonoInternalThread *thread = (MonoInternalThread *) mono_gchandle_get_target_internal (handle);
4164 dump_thread (thread, &ud, output_file != NULL ? output_file : stdout);
4165 mono_gchandle_free_internal (handle);
4168 if (output_file != NULL) {
4169 fclose (output_file);
4171 g_free (ud.frames);
4173 thread_dump_requested = FALSE;
4176 /* Obtain the thread dump of all threads */
4177 static gboolean
4178 mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_frames, MonoError *error)
4181 ThreadDumpUserData ud;
4182 guint32 thread_array [128];
4183 MonoDomain *domain = mono_domain_get ();
4184 MonoDebugSourceLocation *location;
4185 int tindex, nthreads;
4187 error_init (error);
4189 *out_threads = NULL;
4190 *out_stack_frames = NULL;
4192 /* Make a copy of the threads hash to avoid doing work inside threads_lock () */
4193 nthreads = collect_threads (thread_array, 128);
4195 memset (&ud, 0, sizeof (ud));
4196 ud.frames = g_new0 (MonoStackFrameInfo, 256);
4197 ud.max_frames = 256;
4199 *out_threads = mono_array_new_checked (domain, mono_defaults.thread_class, nthreads, error);
4200 goto_if_nok (error, leave);
4201 *out_stack_frames = mono_array_new_checked (domain, mono_defaults.array_class, nthreads, error);
4202 goto_if_nok (error, leave);
4204 for (tindex = 0; tindex < nthreads; ++tindex) {
4205 guint32 handle = thread_array [tindex];
4206 MonoInternalThread *thread = (MonoInternalThread *) mono_gchandle_get_target_internal (handle);
4208 MonoArray *thread_frames;
4209 int i;
4211 ud.thread = thread;
4212 ud.nframes = 0;
4214 /* Collect frames for the thread */
4215 if (thread == mono_thread_internal_current ()) {
4216 get_thread_dump (mono_thread_info_current (), &ud);
4217 } else {
4218 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, get_thread_dump, &ud);
4221 mono_array_setref_fast (*out_threads, tindex, mono_thread_current_for_thread (thread));
4223 thread_frames = mono_array_new_checked (domain, mono_defaults.stack_frame_class, ud.nframes, error);
4224 goto_if_nok (error, leave);
4225 mono_array_setref_fast (*out_stack_frames, tindex, thread_frames);
4227 for (i = 0; i < ud.nframes; ++i) {
4228 MonoStackFrameInfo *frame = &ud.frames [i];
4229 MonoMethod *method = NULL;
4230 MonoStackFrame *sf = (MonoStackFrame *)mono_object_new_checked (domain, mono_defaults.stack_frame_class, error);
4231 goto_if_nok (error, leave);
4233 sf->native_offset = frame->native_offset;
4235 if (frame->type == FRAME_TYPE_MANAGED)
4236 method = mono_jit_info_get_method (frame->ji);
4238 if (method) {
4239 sf->method_address = (gsize) frame->ji->code_start;
4241 MonoReflectionMethod *rm = mono_method_get_object_checked (domain, method, NULL, error);
4242 goto_if_nok (error, leave);
4243 MONO_OBJECT_SETREF_INTERNAL (sf, method, rm);
4245 location = mono_debug_lookup_source_location (method, frame->native_offset, domain);
4246 if (location) {
4247 sf->il_offset = location->il_offset;
4249 if (location->source_file) {
4250 MonoString *filename = mono_string_new_checked (domain, location->source_file, error);
4251 goto_if_nok (error, leave);
4252 MONO_OBJECT_SETREF_INTERNAL (sf, filename, filename);
4253 sf->line = location->row;
4254 sf->column = location->column;
4256 mono_debug_free_source_location (location);
4257 } else {
4258 sf->il_offset = -1;
4261 mono_array_setref_internal (thread_frames, i, sf);
4264 mono_gchandle_free_internal (handle);
4267 leave:
4268 g_free (ud.frames);
4269 return is_ok (error);
4273 * mono_threads_request_thread_dump:
4275 * Ask all threads except the current to print their stacktrace to stdout.
4277 void
4278 mono_threads_request_thread_dump (void)
4280 /*The new thread dump code runs out of the finalizer thread. */
4281 thread_dump_requested = TRUE;
4282 mono_gc_finalize_notify ();
4285 struct ref_stack {
4286 gpointer *refs;
4287 gint allocated; /* +1 so that refs [allocated] == NULL */
4288 gint bottom;
4291 typedef struct ref_stack RefStack;
4293 static RefStack *
4294 ref_stack_new (gint initial_size)
4296 RefStack *rs;
4298 initial_size = MAX (initial_size, 16) + 1;
4299 rs = g_new0 (RefStack, 1);
4300 rs->refs = g_new0 (gpointer, initial_size);
4301 rs->allocated = initial_size;
4302 return rs;
4305 static void
4306 ref_stack_destroy (gpointer ptr)
4308 RefStack *rs = (RefStack *)ptr;
4310 if (rs != NULL) {
4311 g_free (rs->refs);
4312 g_free (rs);
4316 static void
4317 ref_stack_push (RefStack *rs, gpointer ptr)
4319 g_assert (rs != NULL);
4321 if (rs->bottom >= rs->allocated) {
4322 rs->refs = (void **)g_realloc (rs->refs, rs->allocated * 2 * sizeof (gpointer) + 1);
4323 rs->allocated <<= 1;
4324 rs->refs [rs->allocated] = NULL;
4326 rs->refs [rs->bottom++] = ptr;
4329 static void
4330 ref_stack_pop (RefStack *rs)
4332 if (rs == NULL || rs->bottom == 0)
4333 return;
4335 rs->bottom--;
4336 rs->refs [rs->bottom] = NULL;
4339 static gboolean
4340 ref_stack_find (RefStack *rs, gpointer ptr)
4342 gpointer *refs;
4344 if (rs == NULL)
4345 return FALSE;
4347 for (refs = rs->refs; refs && *refs; refs++) {
4348 if (*refs == ptr)
4349 return TRUE;
4351 return FALSE;
4355 * mono_thread_push_appdomain_ref:
4357 * Register that the current thread may have references to objects in domain
4358 * @domain on its stack. Each call to this function should be paired with a
4359 * call to pop_appdomain_ref.
4361 void
4362 mono_thread_push_appdomain_ref (MonoDomain *domain)
4364 MonoInternalThread *thread = mono_thread_internal_current ();
4366 if (thread) {
4367 /* printf ("PUSH REF: %" G_GSIZE_FORMAT " -> %s.\n", (gsize)thread->tid, domain->friendly_name); */
4368 SPIN_LOCK (thread->lock_thread_id);
4369 if (thread->appdomain_refs == NULL)
4370 thread->appdomain_refs = ref_stack_new (16);
4371 ref_stack_push ((RefStack *)thread->appdomain_refs, domain);
4372 SPIN_UNLOCK (thread->lock_thread_id);
4376 void
4377 mono_thread_pop_appdomain_ref (void)
4379 MonoInternalThread *thread = mono_thread_internal_current ();
4381 if (thread) {
4382 /* printf ("POP REF: %" G_GSIZE_FORMAT " -> %s.\n", (gsize)thread->tid, ((MonoDomain*)(thread->appdomain_refs->data))->friendly_name); */
4383 SPIN_LOCK (thread->lock_thread_id);
4384 ref_stack_pop ((RefStack *)thread->appdomain_refs);
4385 SPIN_UNLOCK (thread->lock_thread_id);
4389 gboolean
4390 mono_thread_internal_has_appdomain_ref (MonoInternalThread *thread, MonoDomain *domain)
4392 gboolean res;
4393 SPIN_LOCK (thread->lock_thread_id);
4394 res = ref_stack_find ((RefStack *)thread->appdomain_refs, domain);
4395 SPIN_UNLOCK (thread->lock_thread_id);
4396 return res;
4399 gboolean
4400 mono_thread_has_appdomain_ref (MonoThread *thread, MonoDomain *domain)
4402 return mono_thread_internal_has_appdomain_ref (thread->internal_thread, domain);
4405 typedef struct abort_appdomain_data {
4406 struct wait_data wait;
4407 MonoDomain *domain;
4408 } abort_appdomain_data;
4410 static void
4411 collect_appdomain_thread (gpointer key, gpointer value, gpointer user_data)
4413 MonoInternalThread *thread = (MonoInternalThread*)value;
4414 abort_appdomain_data *data = (abort_appdomain_data*)user_data;
4415 MonoDomain *domain = data->domain;
4417 if (mono_thread_internal_has_appdomain_ref (thread, domain)) {
4418 /* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread->tid, domain->friendly_name); */
4420 if(data->wait.num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
4421 data->wait.handles [data->wait.num] = mono_threads_open_thread_handle (thread->handle);
4422 data->wait.threads [data->wait.num] = thread;
4423 data->wait.num++;
4424 } else {
4425 /* Just ignore the rest, we can't do anything with
4426 * them yet
4433 * mono_threads_abort_appdomain_threads:
4435 * Abort threads which has references to the given appdomain.
4437 gboolean
4438 mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
4440 abort_appdomain_data user_data;
4441 gint64 start_time;
4442 int orig_timeout = timeout;
4443 int i;
4445 THREAD_DEBUG (g_message ("%s: starting abort", __func__));
4447 start_time = mono_msec_ticks ();
4448 do {
4449 mono_threads_lock ();
4451 user_data.domain = domain;
4452 user_data.wait.num = 0;
4453 /* This shouldn't take any locks */
4454 mono_g_hash_table_foreach (threads, collect_appdomain_thread, &user_data);
4455 mono_threads_unlock ();
4457 if (user_data.wait.num > 0) {
4458 /* Abort the threads outside the threads lock */
4459 for (i = 0; i < user_data.wait.num; ++i)
4460 mono_thread_internal_abort (user_data.wait.threads [i], TRUE);
4463 * We should wait for the threads either to abort, or to leave the
4464 * domain. We can't do the latter, so we wait with a timeout.
4466 wait_for_tids (&user_data.wait, 100, FALSE);
4469 /* Update remaining time */
4470 timeout -= mono_msec_ticks () - start_time;
4471 start_time = mono_msec_ticks ();
4473 if (orig_timeout != -1 && timeout < 0)
4474 return FALSE;
4476 while (user_data.wait.num > 0);
4478 THREAD_DEBUG (g_message ("%s: abort done", __func__));
4480 return TRUE;
4483 /* This is a JIT icall. This icall is called from a finally block when
4484 * mono_install_handler_block_guard called by another thread has flipped the
4485 * finally block's exvar (see mono_find_exvar_for_offset). In that case, if
4486 * the finally is in an abort protected block, we must defer the abort
4487 * exception until we leave the abort protected block. Otherwise we proceed
4488 * with a synchronous self-abort.
4490 void
4491 ves_icall_thread_finish_async_abort (void)
4493 /* We were called from the handler block and are about to
4494 * leave it. (If we end up postponing the abort because we're
4495 * in an abort protected block, the unwinder won't run and
4496 * won't clear the handler block itself which will confuse the
4497 * unwinder if we're in a try {} catch {} and we throw again.
4498 * ie, this:
4499 * static Constructor () {
4500 * try {
4501 * try {
4502 * } finally {
4503 * icall (); // Thread.Abort landed here,
4504 * // and caused the handler block to be installed
4505 * if (exvar)
4506 * ves_icall_thread_finish_async_abort (); // we're here
4508 * throw E ();
4509 * } catch (E) {
4510 * // unwinder will get confused here and synthesize a self abort
4514 * More interestingly, this doesn't only happen with icalls - a JIT
4515 * trampoline is native code that will cause a handler to be installed.
4516 * So the above situation can happen with any code in a "finally"
4517 * clause.
4519 mono_get_eh_callbacks ()->mono_uninstall_current_handler_block_guard ();
4520 /* Just set the async interruption requested bit. Rely on the icall
4521 * wrapper of this icall to process the thread interruption, respecting
4522 * any abort protection blocks in our call stack.
4524 mono_thread_set_self_interruption_respect_abort_prot ();
4528 * mono_thread_get_undeniable_exception:
4530 * Return an exception which needs to be raised when leaving a catch clause.
4531 * This is used for undeniable exception propagation.
4533 MonoException*
4534 mono_thread_get_undeniable_exception (void)
4536 MonoInternalThread *thread = mono_thread_internal_current ();
4538 if (!(thread && thread->abort_exc && !is_running_protected_wrapper ()))
4539 return NULL;
4541 // We don't want to have our exception effect calls made by
4542 // the catching block
4544 if (!mono_get_eh_callbacks ()->mono_above_abort_threshold ())
4545 return NULL;
4548 * FIXME: Clear the abort exception and return an AppDomainUnloaded
4549 * exception if the thread no longer references a dying appdomain.
4551 thread->abort_exc->trace_ips = NULL;
4552 thread->abort_exc->stack_trace = NULL;
4553 return thread->abort_exc;
4556 #if MONO_SMALL_CONFIG
4557 #define NUM_STATIC_DATA_IDX 4
4558 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
4559 64, 256, 1024, 4096
4561 #else
4562 #define NUM_STATIC_DATA_IDX 8
4563 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
4564 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
4566 #endif
4568 static MonoBitSet *thread_reference_bitmaps [NUM_STATIC_DATA_IDX];
4569 static MonoBitSet *context_reference_bitmaps [NUM_STATIC_DATA_IDX];
4571 static void
4572 mark_slots (void *addr, MonoBitSet **bitmaps, MonoGCMarkFunc mark_func, void *gc_data)
4574 gpointer *static_data = (gpointer *)addr;
4576 for (int i = 0; i < NUM_STATIC_DATA_IDX; ++i) {
4577 void **ptr = (void **)static_data [i];
4579 if (!ptr)
4580 continue;
4582 MONO_BITSET_FOREACH (bitmaps [i], idx, {
4583 void **p = ptr + idx;
4585 if (*p)
4586 mark_func ((MonoObject**)p, gc_data);
4591 static void
4592 mark_tls_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
4594 mark_slots (addr, thread_reference_bitmaps, mark_func, gc_data);
4597 static void
4598 mark_ctx_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
4600 mark_slots (addr, context_reference_bitmaps, mark_func, gc_data);
4604 * mono_alloc_static_data
4606 * Allocate memory blocks for storing threads or context static data
4608 static void
4609 mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, void *alloc_key, gboolean threadlocal)
4611 guint idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4612 int i;
4614 gpointer* static_data = *static_data_ptr;
4615 if (!static_data) {
4616 static MonoGCDescriptor tls_desc = MONO_GC_DESCRIPTOR_NULL;
4617 static MonoGCDescriptor ctx_desc = MONO_GC_DESCRIPTOR_NULL;
4619 if (mono_gc_user_markers_supported ()) {
4620 if (tls_desc == MONO_GC_DESCRIPTOR_NULL)
4621 tls_desc = mono_gc_make_root_descr_user (mark_tls_slots);
4623 if (ctx_desc == MONO_GC_DESCRIPTOR_NULL)
4624 ctx_desc = mono_gc_make_root_descr_user (mark_ctx_slots);
4627 static_data = (void **)mono_gc_alloc_fixed (static_data_size [0], threadlocal ? tls_desc : ctx_desc,
4628 threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC,
4629 alloc_key,
4630 threadlocal ? "ThreadStatic Fields" : "ContextStatic Fields");
4631 *static_data_ptr = static_data;
4632 static_data [0] = static_data;
4635 for (i = 1; i <= idx; ++i) {
4636 if (static_data [i])
4637 continue;
4639 if (mono_gc_user_markers_supported ())
4640 static_data [i] = g_malloc0 (static_data_size [i]);
4641 else
4642 static_data [i] = mono_gc_alloc_fixed (static_data_size [i], MONO_GC_DESCRIPTOR_NULL,
4643 threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC,
4644 alloc_key,
4645 threadlocal ? "ThreadStatic Fields" : "ContextStatic Fields");
4649 static void
4650 mono_free_static_data (gpointer* static_data)
4652 int i;
4653 for (i = 1; i < NUM_STATIC_DATA_IDX; ++i) {
4654 gpointer p = static_data [i];
4655 if (!p)
4656 continue;
4658 * At this point, the static data pointer array is still registered with the
4659 * GC, so must ensure that mark_tls_slots() will not encounter any invalid
4660 * data. Freeing the individual arrays without first nulling their slots
4661 * would make it possible for mark_tls/ctx_slots() to encounter a pointer to
4662 * such an already freed array. See bug #13813.
4664 static_data [i] = NULL;
4665 mono_memory_write_barrier ();
4666 if (mono_gc_user_markers_supported ())
4667 g_free (p);
4668 else
4669 mono_gc_free_fixed (p);
4671 mono_gc_free_fixed (static_data);
4675 * mono_init_static_data_info
4677 * Initializes static data counters
4679 static void mono_init_static_data_info (StaticDataInfo *static_data)
4681 static_data->idx = 0;
4682 static_data->offset = 0;
4683 static_data->freelist = NULL;
4687 * mono_alloc_static_data_slot
4689 * Generates an offset for static data. static_data contains the counters
4690 * used to generate it.
4692 static guint32
4693 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align)
4695 if (!static_data->idx && !static_data->offset) {
4697 * we use the first chunk of the first allocation also as
4698 * an array for the rest of the data
4700 static_data->offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX;
4702 static_data->offset += align - 1;
4703 static_data->offset &= ~(align - 1);
4704 if (static_data->offset + size >= static_data_size [static_data->idx]) {
4705 static_data->idx ++;
4706 g_assert (size <= static_data_size [static_data->idx]);
4707 g_assert (static_data->idx < NUM_STATIC_DATA_IDX);
4708 static_data->offset = 0;
4710 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (static_data->idx, static_data->offset, 0);
4711 static_data->offset += size;
4712 return offset;
4716 * LOCKING: requires that threads_mutex is held
4718 static void
4719 context_adjust_static_data (MonoAppContextHandle ctx_handle)
4721 MonoAppContext *ctx = MONO_HANDLE_RAW (ctx_handle);
4722 if (context_static_info.offset || context_static_info.idx > 0) {
4723 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (context_static_info.idx, context_static_info.offset, 0);
4724 mono_alloc_static_data (&ctx->static_data, offset, ctx, FALSE);
4725 ctx->data->static_data = ctx->static_data;
4730 * LOCKING: requires that threads_mutex is held
4732 static void
4733 alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
4735 MonoInternalThread *thread = (MonoInternalThread *)value;
4736 guint32 offset = GPOINTER_TO_UINT (user);
4738 mono_alloc_static_data (&(thread->static_data), offset, (void *) MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid), TRUE);
4742 * LOCKING: requires that threads_mutex is held
4744 static void
4745 alloc_context_static_data_helper (gpointer key, gpointer value, gpointer user)
4747 MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target_internal (GPOINTER_TO_INT (key));
4749 if (!ctx)
4750 return;
4752 guint32 offset = GPOINTER_TO_UINT (user);
4753 mono_alloc_static_data (&ctx->static_data, offset, ctx, FALSE);
4754 ctx->data->static_data = ctx->static_data;
4757 static StaticDataFreeList*
4758 search_slot_in_freelist (StaticDataInfo *static_data, guint32 size, guint32 align)
4760 StaticDataFreeList* prev = NULL;
4761 StaticDataFreeList* tmp = static_data->freelist;
4762 while (tmp) {
4763 if (tmp->size == size) {
4764 if (prev)
4765 prev->next = tmp->next;
4766 else
4767 static_data->freelist = tmp->next;
4768 return tmp;
4770 prev = tmp;
4771 tmp = tmp->next;
4773 return NULL;
4776 #if SIZEOF_VOID_P == 4
4777 #define ONE_P 1
4778 #else
4779 #define ONE_P 1ll
4780 #endif
4782 static void
4783 update_reference_bitmap (MonoBitSet **sets, guint32 offset, uintptr_t *bitmap, int numbits)
4785 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4786 if (!sets [idx])
4787 sets [idx] = mono_bitset_new (static_data_size [idx] / sizeof (uintptr_t), 0);
4788 MonoBitSet *rb = sets [idx];
4789 offset = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
4790 offset /= sizeof (uintptr_t);
4791 /* offset is now the bitmap offset */
4792 for (int i = 0; i < numbits; ++i) {
4793 if (bitmap [i / sizeof (uintptr_t)] & (ONE_P << (i & (sizeof (uintptr_t) * 8 -1))))
4794 mono_bitset_set_fast (rb, offset + i);
4798 static void
4799 clear_reference_bitmap (MonoBitSet **sets, guint32 offset, guint32 size)
4801 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4802 MonoBitSet *rb = sets [idx];
4803 offset = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
4804 offset /= sizeof (uintptr_t);
4805 /* offset is now the bitmap offset */
4806 for (int i = 0; i < size / sizeof (uintptr_t); i++)
4807 mono_bitset_clear_fast (rb, offset + i);
4810 guint32
4811 mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align, uintptr_t *bitmap, int numbits)
4813 g_assert (static_type == SPECIAL_STATIC_THREAD || static_type == SPECIAL_STATIC_CONTEXT);
4815 StaticDataInfo *info;
4816 MonoBitSet **sets;
4818 if (static_type == SPECIAL_STATIC_THREAD) {
4819 info = &thread_static_info;
4820 sets = thread_reference_bitmaps;
4821 } else {
4822 info = &context_static_info;
4823 sets = context_reference_bitmaps;
4826 mono_threads_lock ();
4828 StaticDataFreeList *item = search_slot_in_freelist (info, size, align);
4829 guint32 offset;
4831 if (item) {
4832 offset = item->offset;
4833 g_free (item);
4834 } else {
4835 offset = mono_alloc_static_data_slot (info, size, align);
4838 update_reference_bitmap (sets, offset, bitmap, numbits);
4840 if (static_type == SPECIAL_STATIC_THREAD) {
4841 /* This can be called during startup */
4842 if (threads != NULL)
4843 mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
4844 } else {
4845 if (contexts != NULL)
4846 g_hash_table_foreach (contexts, alloc_context_static_data_helper, GUINT_TO_POINTER (offset));
4848 ACCESS_SPECIAL_STATIC_OFFSET (offset, type) = SPECIAL_STATIC_OFFSET_TYPE_CONTEXT;
4851 mono_threads_unlock ();
4853 return offset;
4856 gpointer
4857 mono_get_special_static_data_for_thread (MonoInternalThread *thread, guint32 offset)
4859 guint32 static_type = ACCESS_SPECIAL_STATIC_OFFSET (offset, type);
4861 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4862 return get_thread_static_data (thread, offset);
4863 } else {
4864 return get_context_static_data (thread->current_appcontext, offset);
4868 gpointer
4869 mono_get_special_static_data (guint32 offset)
4871 return mono_get_special_static_data_for_thread (mono_thread_internal_current (), offset);
4874 typedef struct {
4875 guint32 offset;
4876 guint32 size;
4877 } OffsetSize;
4880 * LOCKING: requires that threads_mutex is held
4882 static void
4883 free_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
4885 MonoInternalThread *thread = (MonoInternalThread *)value;
4886 OffsetSize *data = (OffsetSize *)user;
4887 int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index);
4888 int off = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, offset);
4889 char *ptr;
4891 if (!thread->static_data || !thread->static_data [idx])
4892 return;
4893 ptr = ((char*) thread->static_data [idx]) + off;
4894 mono_gc_bzero_atomic (ptr, data->size);
4898 * LOCKING: requires that threads_mutex is held
4900 static void
4901 free_context_static_data_helper (gpointer key, gpointer value, gpointer user)
4903 MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target_internal (GPOINTER_TO_INT (key));
4905 if (!ctx)
4906 return;
4908 OffsetSize *data = (OffsetSize *)user;
4909 int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index);
4910 int off = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, offset);
4911 char *ptr;
4913 if (!ctx->static_data || !ctx->static_data [idx])
4914 return;
4916 ptr = ((char*) ctx->static_data [idx]) + off;
4917 mono_gc_bzero_atomic (ptr, data->size);
4920 static void
4921 do_free_special_slot (guint32 offset, guint32 size)
4923 guint32 static_type = ACCESS_SPECIAL_STATIC_OFFSET (offset, type);
4924 MonoBitSet **sets;
4925 StaticDataInfo *info;
4927 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4928 info = &thread_static_info;
4929 sets = thread_reference_bitmaps;
4930 } else {
4931 info = &context_static_info;
4932 sets = context_reference_bitmaps;
4935 guint32 data_offset = offset;
4936 ACCESS_SPECIAL_STATIC_OFFSET (data_offset, type) = 0;
4937 OffsetSize data = { data_offset, size };
4939 clear_reference_bitmap (sets, data.offset, data.size);
4941 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4942 if (threads != NULL)
4943 mono_g_hash_table_foreach (threads, free_thread_static_data_helper, &data);
4944 } else {
4945 if (contexts != NULL)
4946 g_hash_table_foreach (contexts, free_context_static_data_helper, &data);
4949 if (!mono_runtime_is_shutting_down ()) {
4950 StaticDataFreeList *item = g_new0 (StaticDataFreeList, 1);
4952 item->offset = offset;
4953 item->size = size;
4955 item->next = info->freelist;
4956 info->freelist = item;
4960 static void
4961 do_free_special (gpointer key, gpointer value, gpointer data)
4963 MonoClassField *field = (MonoClassField *)key;
4964 guint32 offset = GPOINTER_TO_UINT (value);
4965 gint32 align;
4966 guint32 size;
4967 size = mono_type_size (field->type, &align);
4968 do_free_special_slot (offset, size);
4971 void
4972 mono_alloc_special_static_data_free (GHashTable *special_static_fields)
4974 mono_threads_lock ();
4976 g_hash_table_foreach (special_static_fields, do_free_special, NULL);
4978 mono_threads_unlock ();
4981 #ifdef HOST_WIN32
4982 static void
4983 flush_thread_interrupt_queue (void)
4985 /* Consume pending APC calls for current thread.*/
4986 /* Since this function get's called from interrupt handler it must use a direct */
4987 /* Win32 API call and can't go through mono_coop_win32_wait_for_single_object_ex */
4988 /* or it will detect a pending interrupt and not entering the wait call needed */
4989 /* to consume pending APC's.*/
4990 MONO_ENTER_GC_SAFE;
4991 WaitForSingleObjectEx (GetCurrentThread (), 0, TRUE);
4992 MONO_EXIT_GC_SAFE;
4994 #else
4995 static void
4996 flush_thread_interrupt_queue (void)
4999 #endif
5002 * mono_thread_execute_interruption
5004 * Performs the operation that the requested thread state requires (abort,
5005 * suspend or stop)
5007 static gboolean
5008 mono_thread_execute_interruption (MonoExceptionHandle *pexc)
5010 gboolean fexc = FALSE;
5012 // Optimize away frame if caller supplied one.
5013 if (!pexc) {
5014 HANDLE_FUNCTION_ENTER ();
5015 MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, NULL);
5016 fexc = mono_thread_execute_interruption (&exc);
5017 HANDLE_FUNCTION_RETURN_VAL (fexc);
5020 MONO_REQ_GC_UNSAFE_MODE;
5022 MonoInternalThreadHandle thread = mono_thread_internal_current_handle ();
5023 MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, NULL);
5025 lock_thread_handle (thread);
5026 gboolean unlock = TRUE;
5028 /* MonoThread::interruption_requested can only be changed with atomics */
5029 if (!mono_thread_clear_interruption_requested_handle (thread))
5030 goto exit;
5032 MonoThreadObjectHandle sys_thread;
5033 sys_thread = mono_thread_current_handle ();
5035 flush_thread_interrupt_queue ();
5037 /* Clear the interrupted flag of the thread so it can wait again */
5038 mono_thread_info_clear_self_interrupt ();
5040 /* If there's a pending exception and an AbortRequested, the pending exception takes precedence */
5041 MONO_HANDLE_GET (exc, sys_thread, pending_exception);
5042 if (!MONO_HANDLE_IS_NULL (exc)) {
5043 // sys_thread->pending_exception = NULL;
5044 MONO_HANDLE_SETRAW (sys_thread, pending_exception, NULL);
5045 fexc = TRUE;
5046 goto exit;
5047 } else if (MONO_HANDLE_GETVAL (thread, state) & ThreadState_AbortRequested) {
5048 // Does the thread already have an abort exception?
5049 // If not, create a new one and set it on demand.
5050 // exc = thread->abort_exc;
5051 MONO_HANDLE_GET (exc, thread, abort_exc);
5052 if (MONO_HANDLE_IS_NULL (exc)) {
5053 ERROR_DECL (error);
5054 exc = mono_exception_new_thread_abort (error);
5055 mono_error_assert_ok (error); // FIXME
5056 // thread->abort_exc = exc;
5057 MONO_HANDLE_SET (thread, abort_exc, exc);
5059 fexc = TRUE;
5060 } else if (MONO_HANDLE_GETVAL (thread, state) & ThreadState_SuspendRequested) {
5061 /* calls UNLOCK_THREAD (thread) */
5062 self_suspend_internal ();
5063 unlock = FALSE;
5064 } else if (MONO_HANDLE_GETVAL (thread, thread_interrupt_requested)) {
5065 // thread->thread_interrupt_requested = FALSE
5066 MONO_HANDLE_SETVAL (thread, thread_interrupt_requested, MonoBoolean, FALSE);
5067 unlock_thread_handle (thread);
5068 unlock = FALSE;
5069 ERROR_DECL (error);
5070 exc = mono_exception_new_thread_interrupted (error);
5071 mono_error_assert_ok (error); // FIXME
5072 fexc = TRUE;
5074 exit:
5075 if (unlock)
5076 unlock_thread_handle (thread);
5078 if (fexc)
5079 MONO_HANDLE_ASSIGN (*pexc, exc);
5081 return fexc;
5084 static void
5085 mono_thread_execute_interruption_void (void)
5087 (void)mono_thread_execute_interruption (NULL);
5090 static MonoException*
5091 mono_thread_execute_interruption_ptr (void)
5093 HANDLE_FUNCTION_ENTER ();
5094 MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, NULL);
5095 MonoException * const exc_raw = mono_thread_execute_interruption (&exc) ? MONO_HANDLE_RAW (exc) : NULL;
5096 HANDLE_FUNCTION_RETURN_VAL (exc_raw);
5100 * mono_thread_request_interruption_internal
5102 * A signal handler can call this method to request the interruption of a
5103 * thread. The result of the interruption will depend on the current state of
5104 * the thread. If the result is an exception that needs to be thrown, it is
5105 * provided as return value.
5107 static gboolean
5108 mono_thread_request_interruption_internal (gboolean running_managed, MonoExceptionHandle *pexc)
5110 MonoInternalThread *thread = mono_thread_internal_current ();
5112 /* The thread may already be stopping */
5113 if (thread == NULL)
5114 return FALSE;
5116 if (!mono_thread_set_interruption_requested (thread))
5117 return FALSE;
5119 if (!running_managed || is_running_protected_wrapper ()) {
5120 /* Can't stop while in unmanaged code. Increase the global interruption
5121 request count. When exiting the unmanaged method the count will be
5122 checked and the thread will be interrupted. */
5124 /* this will awake the thread if it is in WaitForSingleObject
5125 or similar */
5126 #ifdef HOST_WIN32
5127 mono_win32_interrupt_wait (thread->thread_info, thread->native_handle, (DWORD)thread->tid);
5128 #else
5129 mono_thread_info_self_interrupt ();
5130 #endif
5131 return FALSE;
5133 return mono_thread_execute_interruption (pexc);
5136 static void
5137 mono_thread_request_interruption_native (void)
5139 (void)mono_thread_request_interruption_internal (FALSE, NULL);
5142 static gboolean
5143 mono_thread_request_interruption_managed (MonoExceptionHandle *exc)
5145 return mono_thread_request_interruption_internal (TRUE, exc);
5148 /*This function should be called by a thread after it has exited all of
5149 * its handle blocks at interruption time.*/
5150 void
5151 mono_thread_resume_interruption (gboolean exec)
5153 MonoInternalThread *thread = mono_thread_internal_current ();
5154 gboolean still_aborting;
5156 /* The thread may already be stopping */
5157 if (thread == NULL)
5158 return;
5160 LOCK_THREAD (thread);
5161 still_aborting = (thread->state & (ThreadState_AbortRequested)) != 0;
5162 UNLOCK_THREAD (thread);
5164 /*This can happen if the protected block called Thread::ResetAbort*/
5165 if (!still_aborting)
5166 return;
5168 if (!mono_thread_set_interruption_requested (thread))
5169 return;
5171 mono_thread_info_self_interrupt ();
5173 if (exec) // Ignore the exception here, it will be raised later.
5174 mono_thread_execute_interruption_void ();
5177 gboolean
5178 mono_thread_interruption_requested (void)
5180 if (thread_interruption_requested) {
5181 MonoInternalThread *thread = mono_thread_internal_current ();
5182 /* The thread may already be stopping */
5183 if (thread != NULL)
5184 return mono_thread_get_interruption_requested (thread);
5186 return FALSE;
5189 static MonoException*
5190 mono_thread_interruption_checkpoint_request (gboolean bypass_abort_protection)
5192 MonoInternalThread *thread = mono_thread_internal_current ();
5194 /* The thread may already be stopping */
5195 if (!thread)
5196 return NULL;
5197 if (!mono_thread_get_interruption_requested (thread))
5198 return NULL;
5199 if (!bypass_abort_protection && !mono_thread_current ()->pending_exception && is_running_protected_wrapper ())
5200 return NULL;
5202 return mono_thread_execute_interruption_ptr ();
5206 * Performs the interruption of the current thread, if one has been requested,
5207 * and the thread is not running a protected wrapper.
5208 * Return the exception which needs to be thrown, if any.
5210 MonoException*
5211 mono_thread_interruption_checkpoint (void)
5213 return mono_thread_interruption_checkpoint_request (FALSE);
5216 gboolean
5217 mono_thread_interruption_checkpoint_bool (void)
5219 return mono_thread_interruption_checkpoint () != NULL;
5222 void
5223 mono_thread_interruption_checkpoint_void (void)
5225 mono_thread_interruption_checkpoint ();
5229 * Performs the interruption of the current thread, if one has been requested.
5230 * Return the exception which needs to be thrown, if any.
5232 MonoException*
5233 mono_thread_force_interruption_checkpoint_noraise (void)
5235 return mono_thread_interruption_checkpoint_request (TRUE);
5239 * mono_set_pending_exception:
5241 * Set the pending exception of the current thread to EXC.
5242 * The exception will be thrown when execution returns to managed code.
5244 void
5245 mono_set_pending_exception (MonoException *exc)
5247 MonoThread *thread = mono_thread_current ();
5249 /* The thread may already be stopping */
5250 if (thread == NULL)
5251 return;
5253 MONO_OBJECT_SETREF_INTERNAL (thread, pending_exception, exc);
5255 mono_thread_request_interruption_native ();
5259 * mono_runtime_set_pending_exception:
5261 * Set the pending exception of the current thread to \p exc.
5262 * The exception will be thrown when execution returns to managed code.
5263 * Can optionally \p overwrite any existing pending exceptions (it's not supported
5264 * to overwrite any pending exceptions if the runtime is processing a thread abort request,
5265 * in which case the behavior will be undefined).
5266 * Return whether the pending exception was set or not.
5267 * It will not be set if:
5268 * * The thread or runtime is stopping or shutting down
5269 * * There already is a pending exception (and \p overwrite is false)
5271 mono_bool
5272 mono_runtime_set_pending_exception (MonoException *exc, mono_bool overwrite)
5274 MonoThread *thread = mono_thread_current ();
5276 /* The thread may already be stopping */
5277 if (thread == NULL)
5278 return FALSE;
5280 /* Don't overwrite any existing pending exceptions unless asked to */
5281 if (!overwrite && thread->pending_exception)
5282 return FALSE;
5284 MONO_OBJECT_SETREF_INTERNAL (thread, pending_exception, exc);
5286 mono_thread_request_interruption_native ();
5288 return TRUE;
5293 * mono_set_pending_exception_handle:
5295 * Set the pending exception of the current thread to EXC.
5296 * The exception will be thrown when execution returns to managed code.
5298 MONO_COLD void
5299 mono_set_pending_exception_handle (MonoExceptionHandle exc)
5301 MonoThread *thread = mono_thread_current ();
5303 /* The thread may already be stopping */
5304 if (thread == NULL)
5305 return;
5307 MONO_OBJECT_SETREF_INTERNAL (thread, pending_exception, MONO_HANDLE_RAW (exc));
5309 mono_thread_request_interruption_native ();
5313 * mono_thread_interruption_request_flag:
5315 * Returns the address of a flag that will be non-zero if an interruption has
5316 * been requested for a thread. The thread to interrupt may not be the current
5317 * thread, so an additional call to mono_thread_interruption_requested() or
5318 * mono_thread_interruption_checkpoint() is allways needed if the flag is not
5319 * zero.
5321 gint32*
5322 mono_thread_interruption_request_flag (void)
5324 return &thread_interruption_requested;
5327 void
5328 mono_thread_init_apartment_state (void)
5330 #ifdef HOST_WIN32
5331 MonoInternalThread* thread = mono_thread_internal_current ();
5333 /* Positive return value indicates success, either
5334 * S_OK if this is first CoInitialize call, or
5335 * S_FALSE if CoInitialize already called, but with same
5336 * threading model. A negative value indicates failure,
5337 * probably due to trying to change the threading model.
5339 if (CoInitializeEx(NULL, (thread->apartment_state == ThreadApartmentState_STA)
5340 ? COINIT_APARTMENTTHREADED
5341 : COINIT_MULTITHREADED) < 0) {
5342 thread->apartment_state = ThreadApartmentState_Unknown;
5344 #endif
5347 void
5348 mono_thread_cleanup_apartment_state (void)
5350 #ifdef HOST_WIN32
5351 MonoInternalThread* thread = mono_thread_internal_current ();
5353 if (thread && thread->apartment_state != ThreadApartmentState_Unknown) {
5354 CoUninitialize ();
5356 #endif
5359 static void
5360 mono_thread_notify_change_state (MonoThreadState old_state, MonoThreadState new_state)
5362 MonoThreadState diff = old_state ^ new_state;
5363 if (diff & ThreadState_Background) {
5364 /* If the thread changes the background mode, the main thread has to
5365 * be notified, since it has to rebuild the list of threads to
5366 * wait for.
5368 MONO_ENTER_GC_SAFE;
5369 mono_os_event_set (&background_change_event);
5370 MONO_EXIT_GC_SAFE;
5374 void
5375 mono_thread_clear_and_set_state (MonoInternalThread *thread, MonoThreadState clear, MonoThreadState set)
5377 LOCK_THREAD (thread);
5379 MonoThreadState const old_state = (MonoThreadState)thread->state;
5380 MonoThreadState const new_state = (old_state & ~clear) | set;
5381 thread->state = new_state;
5383 UNLOCK_THREAD (thread);
5385 mono_thread_notify_change_state (old_state, new_state);
5388 void
5389 mono_thread_set_state (MonoInternalThread *thread, MonoThreadState state)
5391 mono_thread_clear_and_set_state (thread, (MonoThreadState)0, state);
5395 * mono_thread_test_and_set_state:
5396 * Test if current state of \p thread include \p test. If it does not, OR \p set into the state.
5397 * \returns TRUE if \p set was OR'd in.
5399 gboolean
5400 mono_thread_test_and_set_state (MonoInternalThread *thread, MonoThreadState test, MonoThreadState set)
5402 LOCK_THREAD (thread);
5404 MonoThreadState const old_state = (MonoThreadState)thread->state;
5406 if ((old_state & test) != 0) {
5407 UNLOCK_THREAD (thread);
5408 return FALSE;
5411 MonoThreadState const new_state = old_state | set;
5412 thread->state = new_state;
5414 UNLOCK_THREAD (thread);
5416 mono_thread_notify_change_state (old_state, new_state);
5418 return TRUE;
5421 void
5422 mono_thread_clr_state (MonoInternalThread *thread, MonoThreadState state)
5424 mono_thread_clear_and_set_state (thread, state, (MonoThreadState)0);
5427 gboolean
5428 mono_thread_test_state (MonoInternalThread *thread, MonoThreadState test)
5430 LOCK_THREAD (thread);
5432 gboolean const ret = ((thread->state & test) != 0);
5434 UNLOCK_THREAD (thread);
5436 return ret;
5439 static void
5440 self_interrupt_thread (void *_unused)
5442 MonoException *exc;
5443 MonoThreadInfo *info;
5444 MonoContext ctx;
5446 exc = mono_thread_execute_interruption_ptr ();
5447 if (!exc) {
5448 if (mono_threads_are_safepoints_enabled ()) {
5449 /* We can return from an async call in coop, as
5450 * it's simply called when exiting the safepoint */
5451 /* If we're using hybrid suspend, we only self
5452 * interrupt if we were running, hence using
5453 * safepoints */
5454 return;
5457 g_error ("%s: we can't resume from an async call", __func__);
5460 info = mono_thread_info_current ();
5462 /* FIXME using thread_saved_state [ASYNC_SUSPEND_STATE_INDEX] can race with another suspend coming in. */
5463 ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
5465 mono_raise_exception_with_context (exc, &ctx);
5468 static gboolean
5469 mono_jit_info_match (MonoJitInfo *ji, gpointer ip)
5471 if (!ji)
5472 return FALSE;
5473 return ji->code_start <= ip && (char*)ip < (char*)ji->code_start + ji->code_size;
5476 static gboolean
5477 last_managed (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
5479 MonoJitInfo **dest = (MonoJitInfo **)data;
5480 *dest = frame->ji;
5481 return TRUE;
5484 static MonoJitInfo*
5485 mono_thread_info_get_last_managed (MonoThreadInfo *info)
5487 MonoJitInfo *ji = NULL;
5488 if (!info)
5489 return NULL;
5492 * The suspended thread might be holding runtime locks. Make sure we don't try taking
5493 * any runtime locks while unwinding.
5495 mono_thread_info_set_is_async_context (TRUE);
5496 mono_get_eh_callbacks ()->mono_walk_stack_with_state (last_managed, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, &ji);
5497 mono_thread_info_set_is_async_context (FALSE);
5498 return ji;
5501 typedef struct {
5502 MonoInternalThread *thread;
5503 gboolean install_async_abort;
5504 MonoThreadInfoInterruptToken *interrupt_token;
5505 } AbortThreadData;
5507 static SuspendThreadResult
5508 async_abort_critical (MonoThreadInfo *info, gpointer ud)
5510 AbortThreadData *data = (AbortThreadData *)ud;
5511 MonoInternalThread *thread = data->thread;
5512 MonoJitInfo *ji = NULL;
5513 gboolean protected_wrapper;
5514 gboolean running_managed;
5516 if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (mono_thread_info_get_suspend_state (info)))
5517 return MonoResumeThread;
5519 /*someone is already interrupting it*/
5520 if (!mono_thread_set_interruption_requested (thread))
5521 return MonoResumeThread;
5523 ji = mono_thread_info_get_last_managed (info);
5524 protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
5525 running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
5527 if (!protected_wrapper && running_managed) {
5528 /*We are in managed code*/
5529 /*Set the thread to call */
5530 if (data->install_async_abort)
5531 mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
5532 return MonoResumeThread;
5533 } else {
5535 * This will cause waits to be broken.
5536 * It will also prevent the thread from entering a wait, so if the thread returns
5537 * from the wait before it receives the abort signal, it will just spin in the wait
5538 * functions in the io-layer until the signal handler calls QueueUserAPC which will
5539 * make it return.
5541 data->interrupt_token = mono_thread_info_prepare_interrupt (info);
5543 return MonoResumeThread;
5547 static void
5548 async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort)
5550 AbortThreadData data;
5552 g_assert (thread != mono_thread_internal_current ());
5554 data.thread = thread;
5555 data.install_async_abort = install_async_abort;
5556 data.interrupt_token = NULL;
5558 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), TRUE, async_abort_critical, &data);
5559 if (data.interrupt_token)
5560 mono_thread_info_finish_interrupt (data.interrupt_token);
5561 /*FIXME we need to wait for interruption to complete -- figure out how much into interruption we should wait for here*/
5564 static void
5565 self_abort_internal (MonoError *error)
5567 HANDLE_FUNCTION_ENTER ();
5569 error_init (error);
5571 /* FIXME this is insanely broken, it doesn't cause interruption to happen synchronously
5572 * since passing FALSE to mono_thread_request_interruption makes sure it returns NULL */
5575 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.
5577 MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, NULL);
5578 if (mono_thread_request_interruption_managed (&exc))
5579 mono_error_set_exception_handle (error, exc);
5580 else
5581 mono_thread_info_self_interrupt ();
5583 HANDLE_FUNCTION_RETURN ();
5586 typedef struct {
5587 MonoInternalThread *thread;
5588 gboolean interrupt;
5589 MonoThreadInfoInterruptToken *interrupt_token;
5590 } SuspendThreadData;
5592 static SuspendThreadResult
5593 async_suspend_critical (MonoThreadInfo *info, gpointer ud)
5595 SuspendThreadData *data = (SuspendThreadData *)ud;
5596 MonoInternalThread *thread = data->thread;
5597 MonoJitInfo *ji = NULL;
5598 gboolean protected_wrapper;
5599 gboolean running_managed;
5601 ji = mono_thread_info_get_last_managed (info);
5602 protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
5603 running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
5605 if (running_managed && !protected_wrapper) {
5606 if (mono_threads_are_safepoints_enabled ()) {
5607 mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
5608 return MonoResumeThread;
5609 } else {
5610 thread->state &= ~ThreadState_SuspendRequested;
5611 thread->state |= ThreadState_Suspended;
5612 return KeepSuspended;
5614 } else {
5615 mono_thread_set_interruption_requested (thread);
5616 if (data->interrupt)
5617 data->interrupt_token = mono_thread_info_prepare_interrupt ((MonoThreadInfo *)thread->thread_info);
5619 return MonoResumeThread;
5623 /* LOCKING: called with @thread longlived->synch_cs held, and releases it */
5624 static void
5625 async_suspend_internal (MonoInternalThread *thread, gboolean interrupt)
5627 SuspendThreadData data;
5629 g_assert (thread != mono_thread_internal_current ());
5631 // g_async_safe_printf ("ASYNC SUSPEND thread %p\n", thread_get_tid (thread));
5633 thread->self_suspended = FALSE;
5635 data.thread = thread;
5636 data.interrupt = interrupt;
5637 data.interrupt_token = NULL;
5639 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), interrupt, async_suspend_critical, &data);
5640 if (data.interrupt_token)
5641 mono_thread_info_finish_interrupt (data.interrupt_token);
5643 UNLOCK_THREAD (thread);
5646 /* LOCKING: called with @thread longlived->synch_cs held, and releases it */
5647 static void
5648 self_suspend_internal (void)
5650 MonoInternalThread *thread;
5651 MonoOSEvent *event;
5652 MonoOSEventWaitRet res;
5654 thread = mono_thread_internal_current ();
5656 // g_async_safe_printf ("SELF SUSPEND thread %p\n", thread_get_tid (thread));
5658 thread->self_suspended = TRUE;
5660 thread->state &= ~ThreadState_SuspendRequested;
5661 thread->state |= ThreadState_Suspended;
5663 UNLOCK_THREAD (thread);
5665 event = thread->suspended;
5667 MONO_ENTER_GC_SAFE;
5668 res = mono_os_event_wait_one (event, MONO_INFINITE_WAIT, TRUE);
5669 g_assert (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0 || res == MONO_OS_EVENT_WAIT_RET_ALERTED);
5670 MONO_EXIT_GC_SAFE;
5673 static void
5674 suspend_for_shutdown_async_call (gpointer unused)
5676 for (;;)
5677 mono_thread_info_yield ();
5680 static SuspendThreadResult
5681 suspend_for_shutdown_critical (MonoThreadInfo *info, gpointer unused)
5683 mono_thread_info_setup_async_call (info, suspend_for_shutdown_async_call, NULL);
5684 return MonoResumeThread;
5687 void
5688 mono_thread_internal_suspend_for_shutdown (MonoInternalThread *thread)
5690 g_assert (thread != mono_thread_internal_current ());
5692 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, suspend_for_shutdown_critical, NULL);
5696 * mono_thread_is_foreign:
5697 * \param thread the thread to query
5699 * This function allows one to determine if a thread was created by the mono runtime and has
5700 * a well defined lifecycle or it's a foreign one, created by the native environment.
5702 * \returns TRUE if \p thread was not created by the runtime.
5704 mono_bool
5705 mono_thread_is_foreign (MonoThread *thread)
5707 mono_bool result;
5708 MONO_ENTER_GC_UNSAFE;
5709 MonoThreadInfo *info = (MonoThreadInfo *)thread->internal_thread->thread_info;
5710 result = (info->runtime_thread == FALSE);
5711 MONO_EXIT_GC_UNSAFE;
5712 return result;
5715 #ifndef HOST_WIN32
5716 static void
5717 threads_native_thread_join_lock (gpointer tid, gpointer value)
5719 pthread_t thread = (pthread_t)tid;
5720 if (thread != pthread_self ()) {
5721 MONO_ENTER_GC_SAFE;
5722 /* This shouldn't block */
5723 mono_threads_join_lock ();
5724 mono_native_thread_join (thread);
5725 mono_threads_join_unlock ();
5726 MONO_EXIT_GC_SAFE;
5729 static void
5730 threads_native_thread_join_nolock (gpointer tid, gpointer value)
5732 pthread_t thread = (pthread_t)tid;
5733 MONO_ENTER_GC_SAFE;
5734 mono_native_thread_join (thread);
5735 MONO_EXIT_GC_SAFE;
5738 static void
5739 threads_add_joinable_thread_nolock (gpointer tid)
5741 g_hash_table_insert (joinable_threads, tid, tid);
5743 #else
5744 static void
5745 threads_native_thread_join_lock (gpointer tid, gpointer value)
5747 MonoNativeThreadId thread_id = (MonoNativeThreadId)(guint64)tid;
5748 HANDLE thread_handle = (HANDLE)value;
5749 if (thread_id != GetCurrentThreadId () && thread_handle != NULL && thread_handle != INVALID_HANDLE_VALUE) {
5750 MONO_ENTER_GC_SAFE;
5751 /* This shouldn't block */
5752 mono_threads_join_lock ();
5753 mono_native_thread_join_handle (thread_handle, TRUE);
5754 mono_threads_join_unlock ();
5755 MONO_EXIT_GC_SAFE;
5759 static void
5760 threads_native_thread_join_nolock (gpointer tid, gpointer value)
5762 HANDLE thread_handle = (HANDLE)value;
5763 MONO_ENTER_GC_SAFE;
5764 mono_native_thread_join_handle (thread_handle, TRUE);
5765 MONO_EXIT_GC_SAFE;
5768 static void
5769 threads_add_joinable_thread_nolock (gpointer tid)
5771 g_hash_table_insert (joinable_threads, tid, (gpointer)OpenThread (SYNCHRONIZE, TRUE, (MonoNativeThreadId)(guint64)tid));
5773 #endif
5775 static void
5776 threads_add_pending_joinable_thread (gpointer tid)
5778 joinable_threads_lock ();
5780 if (!pending_joinable_threads)
5781 pending_joinable_threads = g_hash_table_new (NULL, NULL);
5783 gpointer orig_key;
5784 gpointer value;
5786 if (!g_hash_table_lookup_extended (pending_joinable_threads, tid, &orig_key, &value)) {
5787 g_hash_table_insert (pending_joinable_threads, tid, tid);
5788 UnlockedIncrement (&pending_joinable_thread_count);
5791 joinable_threads_unlock ();
5794 static void
5795 threads_add_pending_joinable_runtime_thread (MonoThreadInfo *mono_thread_info)
5797 g_assert (mono_thread_info);
5799 if (mono_thread_info->runtime_thread) {
5800 threads_add_pending_joinable_thread ((gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (mono_thread_info_get_tid (mono_thread_info))));
5804 static void
5805 threads_remove_pending_joinable_thread_nolock (gpointer tid)
5807 gpointer orig_key;
5808 gpointer value;
5810 if (pending_joinable_threads && g_hash_table_lookup_extended (pending_joinable_threads, tid, &orig_key, &value)) {
5811 g_hash_table_remove (pending_joinable_threads, tid);
5812 if (UnlockedDecrement (&pending_joinable_thread_count) == 0)
5813 mono_coop_cond_broadcast (&zero_pending_joinable_thread_event);
5817 static gboolean
5818 threads_wait_pending_joinable_threads (uint32_t timeout)
5820 if (UnlockedRead (&pending_joinable_thread_count) > 0) {
5821 joinable_threads_lock ();
5822 if (timeout == MONO_INFINITE_WAIT) {
5823 while (UnlockedRead (&pending_joinable_thread_count) > 0)
5824 mono_coop_cond_wait (&zero_pending_joinable_thread_event, &joinable_threads_mutex);
5825 } else {
5826 gint64 start = mono_msec_ticks ();
5827 gint64 elapsed = 0;
5828 while (UnlockedRead (&pending_joinable_thread_count) > 0 && elapsed < timeout) {
5829 mono_coop_cond_timedwait (&zero_pending_joinable_thread_event, &joinable_threads_mutex, timeout - (uint32_t)elapsed);
5830 elapsed = mono_msec_ticks () - start;
5833 joinable_threads_unlock ();
5836 return UnlockedRead (&pending_joinable_thread_count) == 0;
5839 static void
5840 threads_add_unique_joinable_thread_nolock (gpointer tid)
5842 if (!joinable_threads)
5843 joinable_threads = g_hash_table_new (NULL, NULL);
5845 gpointer orig_key;
5846 gpointer value;
5848 if (!g_hash_table_lookup_extended (joinable_threads, tid, &orig_key, &value)) {
5849 threads_add_joinable_thread_nolock (tid);
5850 UnlockedIncrement (&joinable_thread_count);
5854 void
5855 mono_threads_add_joinable_runtime_thread (MonoThreadInfo *thread_info)
5857 g_assert (thread_info);
5858 MonoThreadInfo *mono_thread_info = thread_info;
5860 if (mono_thread_info->runtime_thread) {
5861 gpointer tid = (gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (mono_thread_info_get_tid (mono_thread_info)));
5863 joinable_threads_lock ();
5865 // Add to joinable thread list, if not already included.
5866 threads_add_unique_joinable_thread_nolock (tid);
5868 // Remove thread from pending joinable list, if present.
5869 threads_remove_pending_joinable_thread_nolock (tid);
5871 joinable_threads_unlock ();
5873 mono_gc_finalize_notify ();
5877 static void
5878 threads_add_pending_native_thread_join_call_nolock (gpointer tid)
5880 if (!pending_native_thread_join_calls)
5881 pending_native_thread_join_calls = g_hash_table_new (NULL, NULL);
5883 gpointer orig_key;
5884 gpointer value;
5886 if (!g_hash_table_lookup_extended (pending_native_thread_join_calls, tid, &orig_key, &value))
5887 g_hash_table_insert (pending_native_thread_join_calls, tid, tid);
5890 static void
5891 threads_remove_pending_native_thread_join_call_nolock (gpointer tid)
5893 if (pending_native_thread_join_calls)
5894 g_hash_table_remove (pending_native_thread_join_calls, tid);
5896 mono_coop_cond_broadcast (&pending_native_thread_join_calls_event);
5899 static void
5900 threads_wait_pending_native_thread_join_call_nolock (gpointer tid)
5902 gpointer orig_key;
5903 gpointer value;
5905 while (g_hash_table_lookup_extended (pending_native_thread_join_calls, tid, &orig_key, &value)) {
5906 mono_coop_cond_wait (&pending_native_thread_join_calls_event, &joinable_threads_mutex);
5911 * mono_add_joinable_thread:
5913 * Add TID to the list of joinable threads.
5914 * LOCKING: Acquires the threads lock.
5916 void
5917 mono_threads_add_joinable_thread (gpointer tid)
5920 * We cannot detach from threads because it causes problems like
5921 * 2fd16f60/r114307. So we collect them and join them when
5922 * we have time (in the finalizer thread).
5924 joinable_threads_lock ();
5925 threads_add_unique_joinable_thread_nolock (tid);
5926 joinable_threads_unlock ();
5928 mono_gc_finalize_notify ();
5932 * mono_threads_join_threads:
5934 * Join all joinable threads. This is called from the finalizer thread.
5935 * LOCKING: Acquires the threads lock.
5937 void
5938 mono_threads_join_threads (void)
5940 GHashTableIter iter;
5941 gpointer key = NULL;
5942 gpointer value = NULL;
5943 gboolean found = FALSE;
5945 /* Fastpath */
5946 if (!UnlockedRead (&joinable_thread_count))
5947 return;
5949 while (TRUE) {
5950 joinable_threads_lock ();
5951 if (found) {
5952 // Previous native thread join call completed.
5953 threads_remove_pending_native_thread_join_call_nolock (key);
5955 found = FALSE;
5956 if (g_hash_table_size (joinable_threads)) {
5957 g_hash_table_iter_init (&iter, joinable_threads);
5958 g_hash_table_iter_next (&iter, &key, (void**)&value);
5959 g_hash_table_remove (joinable_threads, key);
5960 UnlockedDecrement (&joinable_thread_count);
5961 found = TRUE;
5963 // Add to table of tid's with pending native thread join call.
5964 threads_add_pending_native_thread_join_call_nolock (key);
5966 joinable_threads_unlock ();
5967 if (found)
5968 threads_native_thread_join_lock (key, value);
5969 else
5970 break;
5975 * mono_thread_join:
5977 * Wait for thread TID to exit.
5978 * LOCKING: Acquires the threads lock.
5980 void
5981 mono_thread_join (gpointer tid)
5983 gboolean found = FALSE;
5984 gpointer orig_key;
5985 gpointer value;
5987 joinable_threads_lock ();
5988 if (!joinable_threads)
5989 joinable_threads = g_hash_table_new (NULL, NULL);
5991 if (g_hash_table_lookup_extended (joinable_threads, tid, &orig_key, &value)) {
5992 g_hash_table_remove (joinable_threads, tid);
5993 UnlockedDecrement (&joinable_thread_count);
5994 found = TRUE;
5996 // Add to table of tid's with pending native join call.
5997 threads_add_pending_native_thread_join_call_nolock (tid);
6000 if (!found) {
6001 // Wait for any pending native thread join call not yet completed for this tid.
6002 threads_wait_pending_native_thread_join_call_nolock (tid);
6005 joinable_threads_unlock ();
6007 if (!found)
6008 return;
6010 threads_native_thread_join_nolock (tid, value);
6012 joinable_threads_lock ();
6013 // Native thread join call completed for this tid.
6014 threads_remove_pending_native_thread_join_call_nolock (tid);
6015 joinable_threads_unlock ();
6018 void
6019 mono_thread_internal_unhandled_exception (MonoObject* exc)
6021 MonoClass *klass = exc->vtable->klass;
6022 if (is_threadabort_exception (klass)) {
6023 mono_thread_internal_reset_abort (mono_thread_internal_current ());
6024 } else if (!is_appdomainunloaded_exception (klass)
6025 && mono_runtime_unhandled_exception_policy_get () == MONO_UNHANDLED_POLICY_CURRENT) {
6026 mono_unhandled_exception_internal (exc);
6027 if (mono_environment_exitcode_get () == 1) {
6028 mono_environment_exitcode_set (255);
6029 mono_invoke_unhandled_exception_hook (exc);
6030 g_assert_not_reached ();
6035 void
6036 ves_icall_System_Threading_Thread_GetStackTraces (MonoArray **out_threads, MonoArray **out_stack_traces)
6038 ERROR_DECL (error);
6039 mono_threads_get_thread_dump (out_threads, out_stack_traces, error);
6040 mono_error_set_pending_exception (error);
6044 * mono_threads_attach_coop_internal: called by native->managed wrappers
6046 * - @cookie:
6047 * - blocking mode: contains gc unsafe transition cookie
6048 * - non-blocking mode: contains random data
6049 * - @stackdata: semi-opaque struct: stackpointer and function_name
6050 * - @return: the original domain which needs to be restored, or NULL.
6052 MonoDomain*
6053 mono_threads_attach_coop_internal (MonoDomain *domain, gpointer *cookie, MonoStackData *stackdata)
6055 MonoDomain *orig;
6056 MonoThreadInfo *info;
6057 gboolean external = FALSE;
6059 orig = mono_domain_get ();
6061 if (!domain) {
6062 /* Happens when called from AOTed code which is only used in the root domain. */
6063 domain = mono_get_root_domain ();
6064 g_assert (domain);
6067 /* On coop, when we detached, we moved the thread from RUNNING->BLOCKING.
6068 * If we try to reattach we do a BLOCKING->RUNNING transition. If the thread
6069 * is fresh, mono_thread_attach() will do a STARTING->RUNNING transition so
6070 * we're only responsible for making the cookie. */
6071 if (mono_threads_is_blocking_transition_enabled ())
6072 external = !(info = mono_thread_info_current_unchecked ()) || !mono_thread_info_is_live (info);
6074 if (!mono_thread_internal_current ()) {
6075 mono_thread_attach (domain);
6077 // #678164
6078 mono_thread_set_state (mono_thread_internal_current (), ThreadState_Background);
6081 if (mono_threads_is_blocking_transition_enabled ()) {
6082 if (external) {
6083 /* mono_thread_attach put the thread in RUNNING mode from STARTING, but we need to
6084 * return the right cookie. */
6085 *cookie = mono_threads_enter_gc_unsafe_region_cookie ();
6086 } else {
6087 /* thread state (BLOCKING|RUNNING) -> RUNNING */
6088 *cookie = mono_threads_enter_gc_unsafe_region_unbalanced_internal (stackdata);
6092 if (orig != domain)
6093 mono_domain_set_fast (domain, TRUE);
6095 return orig;
6099 * mono_threads_attach_coop: called by native->managed wrappers
6101 * - @dummy:
6102 * - blocking mode: contains gc unsafe transition cookie
6103 * - non-blocking mode: contains random data
6104 * - a pointer to stack, used for some checks
6105 * - @return: the original domain which needs to be restored, or NULL.
6107 gpointer
6108 mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
6110 MONO_STACKDATA (stackdata);
6111 stackdata.stackpointer = dummy;
6112 return mono_threads_attach_coop_internal (domain, dummy, &stackdata);
6116 * mono_threads_detach_coop_internal: called by native->managed wrappers
6118 * - @orig: the original domain which needs to be restored, or NULL.
6119 * - @stackdata: semi-opaque struct: stackpointer and function_name
6120 * - @cookie:
6121 * - blocking mode: contains gc unsafe transition cookie
6122 * - non-blocking mode: contains random data
6124 void
6125 mono_threads_detach_coop_internal (MonoDomain *orig, gpointer cookie, MonoStackData *stackdata)
6127 MonoDomain *domain = mono_domain_get ();
6128 g_assert (domain);
6130 if (orig != domain) {
6131 if (!orig)
6132 mono_domain_unset ();
6133 else
6134 mono_domain_set_fast (orig, TRUE);
6137 if (mono_threads_is_blocking_transition_enabled ()) {
6138 /* it won't do anything if cookie is NULL
6139 * thread state RUNNING -> (RUNNING|BLOCKING) */
6140 mono_threads_exit_gc_unsafe_region_unbalanced_internal (cookie, stackdata);
6145 * mono_threads_detach_coop: called by native->managed wrappers
6147 * - @orig: the original domain which needs to be restored, or NULL.
6148 * - @dummy:
6149 * - blocking mode: contains gc unsafe transition cookie
6150 * - non-blocking mode: contains random data
6151 * - a pointer to stack, used for some checks
6153 void
6154 mono_threads_detach_coop (gpointer orig, gpointer *dummy)
6156 MONO_STACKDATA (stackdata);
6157 stackdata.stackpointer = dummy;
6158 mono_threads_detach_coop_internal ((MonoDomain*)orig, *dummy, &stackdata);
6161 #if 0
6162 /* Returns TRUE if the current thread is ready to be interrupted. */
6163 gboolean
6164 mono_threads_is_ready_to_be_interrupted (void)
6166 MonoInternalThread *thread;
6168 thread = mono_thread_internal_current ();
6169 LOCK_THREAD (thread);
6170 if (thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
6171 UNLOCK_THREAD (thread);
6172 return FALSE;
6175 if (mono_thread_get_abort_prot_block_count (thread) || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ()) {
6176 UNLOCK_THREAD (thread);
6177 return FALSE;
6180 UNLOCK_THREAD (thread);
6181 return TRUE;
6183 #endif
6185 void
6186 mono_thread_internal_describe (MonoInternalThread *internal, GString *text)
6188 g_string_append_printf (text, ", thread handle : %p", internal->handle);
6190 if (internal->thread_info) {
6191 g_string_append (text, ", state : ");
6192 mono_thread_info_describe_interrupt_token (internal->thread_info, text);
6195 if (internal->owned_mutexes) {
6196 int i;
6198 g_string_append (text, ", owns : [");
6199 for (i = 0; i < internal->owned_mutexes->len; i++)
6200 g_string_append_printf (text, i == 0 ? "%p" : ", %p", g_ptr_array_index (internal->owned_mutexes, i));
6201 g_string_append (text, "]");
6205 gboolean
6206 mono_thread_internal_is_current (MonoInternalThread *internal)
6208 g_assert (internal);
6209 return mono_native_thread_id_equals (mono_native_thread_id_get (), MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid));
6212 void
6213 mono_set_thread_dump_dir (gchar* dir) {
6214 thread_dump_dir = dir;
6217 #ifdef DISABLE_CRASH_REPORTING
6218 gboolean
6219 mono_threads_summarize (MonoContext *ctx, gchar **out, MonoStackHash *hashes, gboolean silent, gboolean signal_handler_controller, gchar *mem, size_t provided_size)
6221 return FALSE;
6224 gboolean
6225 mono_threads_summarize_one (MonoThreadSummary *out, MonoContext *ctx)
6227 return FALSE;
6230 #else
6232 static gboolean
6233 mono_threads_summarize_native_self (MonoThreadSummary *out, MonoContext *ctx)
6235 if (!mono_get_eh_callbacks ()->mono_summarize_managed_stack)
6236 return FALSE;
6238 memset (out, 0, sizeof (MonoThreadSummary));
6239 out->ctx = ctx;
6241 MonoNativeThreadId current = mono_native_thread_id_get();
6242 out->native_thread_id = (intptr_t) current;
6244 mono_get_eh_callbacks ()->mono_summarize_unmanaged_stack (out);
6246 mono_native_thread_get_name (current, out->name, MONO_MAX_SUMMARY_NAME_LEN);
6248 return TRUE;
6251 // Not safe to call from signal handler
6252 gboolean
6253 mono_threads_summarize_one (MonoThreadSummary *out, MonoContext *ctx)
6255 gboolean success = mono_threads_summarize_native_self (out, ctx);
6257 // Finish this on the same thread
6259 if (success && mono_get_eh_callbacks ()->mono_summarize_managed_stack)
6260 mono_get_eh_callbacks ()->mono_summarize_managed_stack (out);
6262 return success;
6265 #define TIMEOUT_CRASH_REPORTER_FATAL 30
6266 #define MAX_NUM_THREADS 128
6267 typedef struct {
6268 gint32 has_owner; // state of this memory
6270 MonoSemType update; // notify of addition of threads
6272 int nthreads;
6273 MonoNativeThreadId thread_array [MAX_NUM_THREADS]; // ids of threads we're dumping
6275 int nthreads_attached; // Number of threads self-registered
6276 MonoThreadSummary *all_threads [MAX_NUM_THREADS];
6278 gboolean silent; // print to stdout
6279 } SummarizerGlobalState;
6281 #if defined(HAVE_KILL) && !defined(HOST_ANDROID) && defined(HAVE_WAITPID) && ((!defined(HOST_DARWIN) && defined(SYS_fork)) || HAVE_FORK)
6282 #define HAVE_MONO_SUMMARIZER_SUPERVISOR 1
6283 #endif
6285 typedef struct {
6286 MonoSemType supervisor;
6287 pid_t pid;
6288 pid_t supervisor_pid;
6289 } SummarizerSupervisorState;
6291 #ifndef HAVE_MONO_SUMMARIZER_SUPERVISOR
6292 static void
6293 summarizer_supervisor_wait (SummarizerSupervisorState *state)
6295 return;
6298 static pid_t
6299 summarizer_supervisor_start (SummarizerSupervisorState *state)
6301 // nonzero, so caller doesn't think it's the supervisor
6302 return (pid_t) 1;
6305 static void
6306 summarizer_supervisor_end (SummarizerSupervisorState *state)
6308 return;
6311 #else
6312 static void
6313 summarizer_supervisor_wait (SummarizerSupervisorState *state)
6315 sleep (TIMEOUT_CRASH_REPORTER_FATAL);
6317 // If we haven't been SIGKILL'ed yet, we signal our parent
6318 // and then exit
6319 #ifdef HAVE_KILL
6320 g_async_safe_printf("Crash Reporter has timed out, sending SIGSEGV\n");
6321 kill (state->pid, SIGSEGV);
6322 #else
6323 g_error ("kill () is not supported by this platform");
6324 #endif
6326 exit (1);
6329 static pid_t
6330 summarizer_supervisor_start (SummarizerSupervisorState *state)
6332 memset (state, 0, sizeof (*state));
6333 pid_t pid;
6335 state->pid = getpid();
6338 * glibc fork acquires some locks, so if the crash happened inside malloc/free,
6339 * it will deadlock. Call the syscall directly instead.
6341 #if defined(HOST_ANDROID)
6342 /* SYS_fork is defined to be __NR_fork which is not defined in some ndk versions */
6343 // We disable this when we set HAVE_MONO_SUMMARIZER_SUPERVISOR above
6344 g_assert_not_reached ();
6345 #elif !defined(HOST_DARWIN) && defined(SYS_fork)
6346 pid = (pid_t) syscall (SYS_fork);
6347 #elif HAVE_FORK
6348 pid = (pid_t) fork ();
6349 #else
6350 g_assert_not_reached ();
6351 #endif
6353 if (pid != 0)
6354 state->supervisor_pid = pid;
6356 return pid;
6359 static void
6360 summarizer_supervisor_end (SummarizerSupervisorState *state)
6362 #ifdef HAVE_KILL
6363 kill (state->supervisor_pid, SIGKILL);
6364 #endif
6366 #if defined (HAVE_WAITPID)
6367 // Accessed on same thread that sets it.
6368 int status;
6369 waitpid (state->supervisor_pid, &status, 0);
6370 #endif
6372 #endif
6374 static gboolean
6375 summarizer_state_init (SummarizerGlobalState *state, MonoNativeThreadId current, int *my_index)
6377 gint32 started_state = mono_atomic_cas_i32 (&state->has_owner, 1 /* set */, 0 /* compare */);
6378 gboolean not_started = started_state == 0;
6379 if (not_started) {
6380 state->nthreads = collect_thread_ids (state->thread_array, MAX_NUM_THREADS);
6381 mono_os_sem_init (&state->update, 0);
6384 for (int i = 0; i < state->nthreads; i++) {
6385 if (state->thread_array [i] == current) {
6386 *my_index = i;
6387 break;
6391 return not_started;
6394 static void
6395 summarizer_signal_other_threads (SummarizerGlobalState *state, MonoNativeThreadId current, int current_idx)
6397 sigset_t sigset, old_sigset;
6398 sigemptyset(&sigset);
6399 sigaddset(&sigset, SIGTERM);
6401 for (int i=0; i < state->nthreads; i++) {
6402 sigprocmask (SIG_UNBLOCK, &sigset, &old_sigset);
6404 if (i == current_idx)
6405 continue;
6407 #ifdef HAVE_PTHREAD_KILL
6408 pthread_kill (state->thread_array [i], SIGTERM);
6410 if (!state->silent)
6411 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));
6412 #else
6413 g_error ("pthread_kill () is not supported by this platform");
6414 #endif
6418 // Returns true when there are shared global references to "this_thread"
6419 static gboolean
6420 summarizer_post_dump (SummarizerGlobalState *state, MonoThreadSummary *this_thread, int current_idx)
6422 mono_memory_barrier ();
6424 gpointer old = mono_atomic_cas_ptr ((volatile gpointer *)&state->all_threads [current_idx], this_thread, NULL);
6426 if (old == GINT_TO_POINTER (-1)) {
6427 g_async_safe_printf ("Trying to register response after dumping period ended");
6428 return FALSE;
6429 } else if (old != NULL) {
6430 g_async_safe_printf ("Thread dump raced for thread slot.");
6431 return FALSE;
6434 // We added our pointer
6435 gint32 count = mono_atomic_inc_i32 ((volatile gint32 *) &state->nthreads_attached);
6436 if (count == state->nthreads)
6437 mono_os_sem_post (&state->update);
6439 return TRUE;
6442 // A lockless spinwait with a timeout
6443 // Used in environments where locks are unsafe
6445 // If set_pos is true, we wait until the expected number of threads have
6446 // responded and then count that the expected number are set. If it is not true,
6447 // then we wait for them to be unset.
6448 static void
6449 summary_timedwait (SummarizerGlobalState *state, int timeout_seconds)
6451 const gint64 milliseconds_in_second = 1000;
6452 gint64 timeout_total = milliseconds_in_second * timeout_seconds;
6454 gint64 end = mono_msec_ticks () + timeout_total;
6456 while (TRUE) {
6457 if (mono_atomic_load_i32 ((volatile gint32 *) &state->nthreads_attached) == state->nthreads)
6458 break;
6460 gint64 now = mono_msec_ticks ();
6461 gint64 remaining = end - now;
6462 if (remaining <= 0)
6463 break;
6465 mono_os_sem_timedwait (&state->update, remaining, MONO_SEM_FLAGS_NONE);
6468 return;
6471 static MonoThreadSummary *
6472 summarizer_try_read_thread (SummarizerGlobalState *state, int index)
6474 gpointer old_value = NULL;
6475 gpointer new_value = GINT_TO_POINTER(-1);
6477 do {
6478 old_value = state->all_threads [index];
6479 } while (mono_atomic_cas_ptr ((volatile gpointer *) &state->all_threads [index], new_value, old_value) != old_value);
6481 MonoThreadSummary *thread = (MonoThreadSummary *) old_value;
6482 return thread;
6485 static void
6486 summarizer_state_term (SummarizerGlobalState *state, gchar **out, gchar *mem, size_t provided_size, MonoThreadSummary *controlling)
6488 // See the array writes
6489 mono_memory_barrier ();
6491 MonoThreadSummary *threads [MAX_NUM_THREADS];
6492 memset (threads, 0, sizeof(threads));
6494 mono_summarize_timeline_phase_log (MonoSummaryManagedStacks);
6495 for (int i=0; i < state->nthreads; i++) {
6496 threads [i] = summarizer_try_read_thread (state, i);
6497 if (!threads [i])
6498 continue;
6500 // We are doing this dump on the controlling thread because this isn't
6501 // an async context sometimes. There's still some reliance on malloc here, but it's
6502 // much more stable to do it all from the controlling thread.
6504 // This is non-null, checked in mono_threads_summarize
6505 // with early exit there
6506 mono_get_eh_callbacks ()->mono_summarize_managed_stack (threads [i]);
6509 MonoStateWriter writer;
6510 memset (&writer, 0, sizeof (writer));
6512 mono_summarize_timeline_phase_log (MonoSummaryStateWriter);
6513 mono_summarize_native_state_begin (&writer, mem, provided_size);
6514 for (int i=0; i < state->nthreads; i++) {
6515 MonoThreadSummary *thread = threads [i];
6516 if (!thread)
6517 continue;
6519 mono_summarize_native_state_add_thread (&writer, thread, thread->ctx, thread == controlling);
6520 // Set non-shared state to notify the waiting thread to clean up
6521 // without having to keep our shared state alive
6522 mono_atomic_store_i32 (&thread->done, 0x1);
6523 mono_os_sem_post (&thread->done_wait);
6525 *out = mono_summarize_native_state_end (&writer);
6526 mono_summarize_timeline_phase_log (MonoSummaryStateWriterDone);
6528 mono_os_sem_destroy (&state->update);
6530 memset (state, 0, sizeof (*state));
6531 mono_atomic_store_i32 ((volatile gint32 *)&state->has_owner, 0);
6534 static void
6535 summarizer_state_wait (MonoThreadSummary *thread)
6537 gint64 milliseconds_in_second = 1000;
6539 // cond_wait can spuriously wake up, so we need to check
6540 // done
6541 while (!mono_atomic_load_i32 (&thread->done))
6542 mono_os_sem_timedwait (&thread->done_wait, milliseconds_in_second, MONO_SEM_FLAGS_NONE);
6545 static gboolean
6546 mono_threads_summarize_execute_internal (MonoContext *ctx, gchar **out, MonoStackHash *hashes, gboolean silent, gchar *working_mem, size_t provided_size, gboolean this_thread_controls)
6548 static SummarizerGlobalState state;
6550 int current_idx;
6551 MonoNativeThreadId current = mono_native_thread_id_get ();
6552 gboolean thread_given_control = summarizer_state_init (&state, current, &current_idx);
6554 g_assert (this_thread_controls == thread_given_control);
6556 if (state.nthreads == 0) {
6557 if (!silent)
6558 g_async_safe_printf("No threads attached to runtime.\n");
6559 memset (&state, 0, sizeof (state));
6560 return FALSE;
6563 if (this_thread_controls) {
6564 g_assert (working_mem);
6566 mono_summarize_timeline_phase_log (MonoSummarySuspendHandshake);
6567 state.silent = silent;
6568 summarizer_signal_other_threads (&state, current, current_idx);
6569 mono_summarize_timeline_phase_log (MonoSummaryUnmanagedStacks);
6572 MonoStateMem mem;
6573 gboolean success = mono_state_alloc_mem (&mem, (long) current, sizeof (MonoThreadSummary));
6574 if (!success)
6575 return FALSE;
6577 MonoThreadSummary *this_thread = (MonoThreadSummary *) mem.mem;
6579 if (mono_threads_summarize_native_self (this_thread, ctx)) {
6580 // Init the synchronization between the controlling thread and the
6581 // providing thread
6582 mono_os_sem_init (&this_thread->done_wait, 0);
6584 // Store a reference to our stack memory into global state
6585 gboolean success = summarizer_post_dump (&state, this_thread, current_idx);
6586 if (!success && !state.silent)
6587 g_async_safe_printf("Thread 0x%zx reported itself.\n", MONO_NATIVE_THREAD_ID_TO_UINT (current));
6588 } else if (!state.silent) {
6589 g_async_safe_printf("Thread 0x%zx couldn't report itself.\n", MONO_NATIVE_THREAD_ID_TO_UINT (current));
6592 // From summarizer, wait and dump.
6593 if (this_thread_controls) {
6594 if (!state.silent)
6595 g_async_safe_printf("Entering thread summarizer pause from 0x%zx\n", MONO_NATIVE_THREAD_ID_TO_UINT (current));
6597 // Wait up to 2 seconds for all of the other threads to catch up
6598 summary_timedwait (&state, 2);
6600 if (!state.silent)
6601 g_async_safe_printf("Finished thread summarizer pause from 0x%zx.\n", MONO_NATIVE_THREAD_ID_TO_UINT (current));
6603 // Dump and cleanup all the stack memory
6604 summarizer_state_term (&state, out, working_mem, provided_size, this_thread);
6605 } else {
6606 // Wait here, keeping our stack memory alive
6607 // for the dumper
6608 summarizer_state_wait (this_thread);
6611 // FIXME: How many threads should be counted?
6612 if (hashes)
6613 *hashes = this_thread->hashes;
6615 mono_state_free_mem (&mem);
6617 return TRUE;
6620 gboolean
6621 mono_threads_summarize_execute (MonoContext *ctx, gchar **out, MonoStackHash *hashes, gboolean silent, gchar *working_mem, size_t provided_size)
6623 return mono_threads_summarize_execute_internal (ctx, out, hashes, silent, working_mem, provided_size, FALSE);
6626 gboolean
6627 mono_threads_summarize (MonoContext *ctx, gchar **out, MonoStackHash *hashes, gboolean silent, gboolean signal_handler_controller, gchar *mem, size_t provided_size)
6629 if (!mono_get_eh_callbacks ()->mono_summarize_managed_stack)
6630 return FALSE;
6632 // The staggered values are due to the need to use inc_i64 for the first value
6633 static gint64 next_pending_request_id = 0;
6634 static gint64 request_available_to_run = 1;
6635 gint64 this_request_id = mono_atomic_inc_i64 ((volatile gint64 *) &next_pending_request_id);
6637 // This is a global queue of summary requests.
6638 // It's not safe to signal a thread while they're in the
6639 // middle of a dump. Dladdr is not reentrant. It's the one lock
6640 // we rely on being able to take.
6642 // We don't use it in almost any other place in managed code, so
6643 // our problem is in the stack dumping code racing with the signalling code.
6645 // A dump is wait-free to the degree that it's not going to loop indefinitely.
6646 // If we're running from a crash handler block, we're not in any position to
6647 // wait for an in-flight dump to finish. If we crashed while dumping, we cannot dump.
6648 // We should simply return so we can die cleanly.
6650 // signal_handler_controller should be set only from a handler that expects itself to be the only
6651 // entry point, where the runtime already being dumping means we should just give up
6653 gboolean success = FALSE;
6655 while (TRUE) {
6656 gint64 next_request_id = mono_atomic_load_i64 ((volatile gint64 *) &request_available_to_run);
6658 if (next_request_id == this_request_id) {
6659 gboolean already_async = mono_thread_info_is_async_context ();
6660 if (!already_async)
6661 mono_thread_info_set_is_async_context (TRUE);
6663 SummarizerSupervisorState synch;
6664 if (summarizer_supervisor_start (&synch)) {
6665 g_assert (mem);
6666 success = mono_threads_summarize_execute_internal (ctx, out, hashes, silent, mem, provided_size, TRUE);
6667 summarizer_supervisor_end (&synch);
6668 } else {
6669 summarizer_supervisor_wait (&synch);
6672 if (!already_async)
6673 mono_thread_info_set_is_async_context (FALSE);
6675 // Only the thread that gets the ticket can unblock future dumpers.
6676 mono_atomic_inc_i64 ((volatile gint64 *) &request_available_to_run);
6677 break;
6678 } else if (signal_handler_controller) {
6679 // We're done. We can't do anything.
6680 g_async_safe_printf ("Attempted to dump for critical failure when already in dump. Error reporting crashed?");
6681 mono_summarize_double_fault_log ();
6682 break;
6683 } else {
6684 if (!silent)
6685 g_async_safe_printf ("Waiting for in-flight dump to complete.");
6686 sleep (2);
6690 return success;
6693 #endif
6695 #ifdef ENABLE_NETCORE
6696 void
6697 ves_icall_System_Threading_Thread_StartInternal (MonoThreadObjectHandle thread_handle, MonoError *error)
6699 MonoThread *internal = MONO_HANDLE_RAW (thread_handle);
6700 gboolean res;
6702 THREAD_DEBUG (g_message("%s: Trying to start a new thread: this (%p)", __func__, internal));
6704 LOCK_THREAD (internal);
6706 if ((internal->state & ThreadState_Unstarted) == 0) {
6707 UNLOCK_THREAD (internal);
6708 mono_error_set_exception_thread_state (error, "Thread has already been started.");
6709 return;
6712 if ((internal->state & ThreadState_Aborted) != 0) {
6713 UNLOCK_THREAD (internal);
6714 return;
6717 res = create_thread (internal, internal, NULL, NULL, NULL, MONO_THREAD_CREATE_FLAGS_NONE, error);
6718 if (!res) {
6719 UNLOCK_THREAD (internal);
6720 return;
6723 internal->state &= ~ThreadState_Unstarted;
6725 THREAD_DEBUG (g_message ("%s: Started thread ID %" G_GSIZE_FORMAT " (handle %p)", __func__, (gsize)internal->tid, internal->handle));
6727 UNLOCK_THREAD (internal);
6730 void
6731 ves_icall_System_Threading_Thread_InitInternal (MonoThreadObjectHandle thread_handle, MonoError *error)
6733 MonoThread *internal = MONO_HANDLE_RAW (thread_handle);
6735 // Need to initialize thread objects created from managed code
6736 init_internal_thread_object (internal);
6737 internal->state = ThreadState_Unstarted;
6738 MONO_OBJECT_SETREF_INTERNAL (internal, internal_thread, internal);
6741 guint64
6742 ves_icall_System_Threading_Thread_GetCurrentOSThreadId (MonoError *error)
6744 return mono_native_thread_os_id_get ();
6747 #endif