Whittle away at warnings: (#18874)
[mono-project.git] / mono / metadata / threads.c
blob62d4a18ae9e5c993a2a3da36baf82c53ac1c8b78
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>
61 #include <mono/metadata/mono-config.h>
62 #include <mono/utils/mono-tls-inline.h>
63 #include <mono/utils/lifo-semaphore.h>
65 #ifdef HAVE_SYS_WAIT_H
66 #include <sys/wait.h>
67 #endif
69 #include <signal.h>
71 #if _MSC_VER
72 #pragma warning(disable:4312) // FIXME pointer cast to different size
73 #endif
75 #if defined(HOST_WIN32)
76 #include <objbase.h>
77 #include <sys/timeb.h>
78 extern gboolean
79 mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle);
80 #endif
82 #if defined(HOST_FUCHSIA)
83 #include <zircon/syscalls.h>
84 #endif
86 #if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
87 #define USE_TKILL_ON_ANDROID 1
88 #endif
90 #ifdef HOST_ANDROID
91 #include <errno.h>
93 #ifdef USE_TKILL_ON_ANDROID
94 extern int tkill (pid_t tid, int signal);
95 #endif
96 #endif
98 #include "icall-decl.h"
100 /*#define THREAD_DEBUG(a) do { a; } while (0)*/
101 #define THREAD_DEBUG(a)
102 /*#define THREAD_WAIT_DEBUG(a) do { a; } while (0)*/
103 #define THREAD_WAIT_DEBUG(a)
104 /*#define LIBGC_DEBUG(a) do { a; } while (0)*/
105 #define LIBGC_DEBUG(a)
107 #define SPIN_TRYLOCK(i) (mono_atomic_cas_i32 (&(i), 1, 0) == 0)
108 #define SPIN_LOCK(i) do { \
109 if (SPIN_TRYLOCK (i)) \
110 break; \
111 } while (1)
113 #define SPIN_UNLOCK(i) i = 0
115 #define LOCK_THREAD(thread) lock_thread((thread))
116 #define UNLOCK_THREAD(thread) unlock_thread((thread))
118 typedef union {
119 gint32 ival;
120 gfloat fval;
121 } IntFloatUnion;
123 typedef union {
124 gint64 ival;
125 gdouble fval;
126 } LongDoubleUnion;
128 typedef struct _StaticDataFreeList StaticDataFreeList;
129 struct _StaticDataFreeList {
130 StaticDataFreeList *next;
131 guint32 offset;
132 guint32 size;
135 typedef struct {
136 int idx;
137 int offset;
138 StaticDataFreeList *freelist;
139 } StaticDataInfo;
141 /* Controls access to the 'threads' hash table */
142 static void mono_threads_lock (void);
143 static void mono_threads_unlock (void);
144 static MonoCoopMutex threads_mutex;
146 /* Controls access to the 'joinable_threads' hash table */
147 #define joinable_threads_lock() mono_coop_mutex_lock (&joinable_threads_mutex)
148 #define joinable_threads_unlock() mono_coop_mutex_unlock (&joinable_threads_mutex)
149 static MonoCoopMutex joinable_threads_mutex;
151 /* Holds current status of static data heap */
152 static StaticDataInfo thread_static_info;
153 static StaticDataInfo context_static_info;
155 /* The hash of existing threads (key is thread ID, value is
156 * MonoInternalThread*) that need joining before exit
158 static MonoGHashTable *threads=NULL;
160 /* List of app context GC handles.
161 * Added to from mono_threads_register_app_context ().
163 static GHashTable *contexts = NULL;
165 /* Cleanup queue for contexts. */
166 static MonoReferenceQueue *context_queue;
169 * Threads which are starting up and they are not in the 'threads' hash yet.
170 * When mono_thread_attach_internal is called for a thread, it will be removed from this hash table.
171 * Protected by mono_threads_lock ().
173 static MonoGHashTable *threads_starting_up = NULL;
175 /* Contains tids */
176 /* Protected by the threads lock */
177 static GHashTable *joinable_threads;
178 static gint32 joinable_thread_count;
180 /* mono_threads_join_threads will take threads from joinable_threads list and wait for them. */
181 /* When this happens, the tid is not on the list anymore so mono_thread_join assumes the thread has complete */
182 /* and will return back to the caller. This could cause a race since caller of join assumes thread has completed */
183 /* and on some OS it could cause errors. Keeping the tid's currently pending a native thread join call */
184 /* in a separate table (only affecting callers interested in this internal join detail) and look at that table in mono_thread_join */
185 /* will close this race. */
186 static GHashTable *pending_native_thread_join_calls;
187 static MonoCoopCond pending_native_thread_join_calls_event;
189 static GHashTable *pending_joinable_threads;
190 static gint32 pending_joinable_thread_count;
192 static MonoCoopCond zero_pending_joinable_thread_event;
194 static void threads_add_pending_joinable_runtime_thread (MonoThreadInfo *mono_thread_info);
195 static gboolean threads_wait_pending_joinable_threads (uint32_t timeout);
196 static gchar* thread_dump_dir = NULL;
198 #define SET_CURRENT_OBJECT mono_tls_set_thread
199 #define GET_CURRENT_OBJECT mono_tls_get_thread
201 /* function called at thread start */
202 static MonoThreadStartCB mono_thread_start_cb = NULL;
204 /* function called at thread attach */
205 static MonoThreadAttachCB mono_thread_attach_cb = NULL;
207 /* function called at thread cleanup */
208 static MonoThreadCleanupFunc mono_thread_cleanup_fn = NULL;
210 /* The default stack size for each thread */
211 static guint32 default_stacksize = 0;
212 #define default_stacksize_for_thread(thread) ((thread)->stack_size? (thread)->stack_size: default_stacksize)
214 static void context_adjust_static_data (MonoAppContextHandle ctx);
215 static void mono_free_static_data (gpointer* static_data);
216 static void mono_init_static_data_info (StaticDataInfo *static_data);
217 static guint32 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align);
218 static gboolean mono_thread_resume (MonoInternalThread* thread);
219 static void async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort);
220 static void self_abort_internal (MonoError *error);
221 static void async_suspend_internal (MonoInternalThread *thread, gboolean interrupt);
222 static void self_suspend_internal (void);
224 static gboolean
225 mono_thread_set_interruption_requested_flags (MonoInternalThread *thread, gboolean sync);
227 MONO_COLD void
228 mono_set_pending_exception_handle (MonoExceptionHandle exc);
230 static MonoException*
231 mono_thread_execute_interruption_ptr (void);
233 static void
234 mono_thread_execute_interruption_void (void);
236 static gboolean
237 mono_thread_execute_interruption (MonoExceptionHandle *pexc);
239 static void ref_stack_destroy (gpointer rs);
241 #if SIZEOF_VOID_P == 4
242 /* Spin lock for unaligned InterlockedXXX 64 bit functions on 32bit platforms. */
243 #define mono_interlocked_lock() mono_os_mutex_lock (&interlocked_mutex)
244 #define mono_interlocked_unlock() mono_os_mutex_unlock (&interlocked_mutex)
245 static mono_mutex_t interlocked_mutex;
246 #endif
248 /* global count of thread interruptions requested */
249 gint32 mono_thread_interruption_request_flag;
251 /* Event signaled when a thread changes its background mode */
252 static MonoOSEvent background_change_event;
254 static gboolean shutting_down = FALSE;
256 static gint32 managed_thread_id_counter = 0;
258 static void
259 mono_threads_lock (void)
261 mono_locks_coop_acquire (&threads_mutex, ThreadsLock);
264 static void
265 mono_threads_unlock (void)
267 mono_locks_coop_release (&threads_mutex, ThreadsLock);
271 static guint32
272 get_next_managed_thread_id (void)
274 return mono_atomic_inc_i32 (&managed_thread_id_counter);
278 * We separate interruptions/exceptions into either sync (they can be processed anytime,
279 * normally as soon as they are set, and are set by the same thread) and async (they can't
280 * be processed inside abort protected blocks and are normally set by other threads). We
281 * can have both a pending sync and async interruption. In this case, the sync exception is
282 * processed first. Since we clean sync flag first, mono_thread_execute_interruption must
283 * also handle all sync type exceptions before the async type exceptions.
285 enum {
286 INTERRUPT_SYNC_REQUESTED_BIT = 0x1,
287 INTERRUPT_ASYNC_REQUESTED_BIT = 0x2,
288 INTERRUPT_REQUESTED_MASK = 0x3,
289 ABORT_PROT_BLOCK_SHIFT = 2,
290 ABORT_PROT_BLOCK_BITS = 8,
291 ABORT_PROT_BLOCK_MASK = (((1 << ABORT_PROT_BLOCK_BITS) - 1) << ABORT_PROT_BLOCK_SHIFT)
294 static int
295 mono_thread_get_abort_prot_block_count (MonoInternalThread *thread)
297 gsize state = thread->thread_state;
298 return (state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT;
301 gboolean
302 mono_threads_is_current_thread_in_protected_block (void)
304 MonoInternalThread *thread = mono_thread_internal_current ();
306 return mono_thread_get_abort_prot_block_count (thread) > 0;
309 void
310 mono_threads_begin_abort_protected_block (void)
312 MonoInternalThread *thread = mono_thread_internal_current ();
313 gsize old_state, new_state;
314 int new_val;
315 do {
316 old_state = thread->thread_state;
318 new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) + 1;
319 //bounds check abort_prot_count
320 g_assert (new_val > 0);
321 g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
323 new_state = old_state + (1 << ABORT_PROT_BLOCK_SHIFT);
324 } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
326 /* Defer async request since we won't be able to process until exiting the block */
327 if (new_val == 1 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT)) {
328 mono_atomic_dec_i32 (&mono_thread_interruption_request_flag);
329 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, mono_thread_interruption_request_flag);
330 if (mono_thread_interruption_request_flag < 0)
331 g_warning ("bad mono_thread_interruption_request_flag state");
332 } else {
333 THREADS_INTERRUPT_DEBUG ("[%d] begin abort protected block old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, mono_thread_interruption_request_flag);
337 static gboolean
338 mono_thread_state_has_interruption (gsize state)
340 /* pending exception, self abort */
341 if (state & INTERRUPT_SYNC_REQUESTED_BIT)
342 return TRUE;
344 /* abort, interruption, suspend */
345 if ((state & INTERRUPT_ASYNC_REQUESTED_BIT) && !(state & ABORT_PROT_BLOCK_MASK))
346 return TRUE;
348 return FALSE;
351 gboolean
352 mono_threads_end_abort_protected_block (void)
354 MonoInternalThread *thread = mono_thread_internal_current ();
355 gsize old_state, new_state;
356 int new_val;
357 do {
358 old_state = thread->thread_state;
360 //bounds check abort_prot_count
361 new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) - 1;
362 g_assert (new_val >= 0);
363 g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
365 new_state = old_state - (1 << ABORT_PROT_BLOCK_SHIFT);
366 } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
368 if (new_val == 0 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT)) {
369 mono_atomic_inc_i32 (&mono_thread_interruption_request_flag);
370 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, mono_thread_interruption_request_flag);
371 } else {
372 THREADS_INTERRUPT_DEBUG ("[%d] end abort protected block old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, mono_thread_interruption_request_flag);
375 return mono_thread_state_has_interruption (new_state);
378 static gboolean
379 mono_thread_get_interruption_requested (MonoInternalThread *thread)
381 gsize state = thread->thread_state;
383 return mono_thread_state_has_interruption (state);
387 * Returns TRUE is there was a state change
388 * We clear a single interruption request, sync has priority.
390 static gboolean
391 mono_thread_clear_interruption_requested (MonoInternalThread *thread)
393 gsize old_state, new_state;
394 do {
395 old_state = thread->thread_state;
397 // no interruption to process
398 if (!(old_state & INTERRUPT_SYNC_REQUESTED_BIT) &&
399 (!(old_state & INTERRUPT_ASYNC_REQUESTED_BIT) || (old_state & ABORT_PROT_BLOCK_MASK)))
400 return FALSE;
402 if (old_state & INTERRUPT_SYNC_REQUESTED_BIT)
403 new_state = old_state & ~INTERRUPT_SYNC_REQUESTED_BIT;
404 else
405 new_state = old_state & ~INTERRUPT_ASYNC_REQUESTED_BIT;
406 } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
408 mono_atomic_dec_i32 (&mono_thread_interruption_request_flag);
409 THREADS_INTERRUPT_DEBUG ("[%d] clear interruption old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, mono_thread_interruption_request_flag);
410 if (mono_thread_interruption_request_flag < 0)
411 g_warning ("bad mono_thread_interruption_request_flag state");
412 return TRUE;
415 static gboolean
416 mono_thread_clear_interruption_requested_handle (MonoInternalThreadHandle thread)
418 // Internal threads are pinned so shallow coop/handle.
419 return mono_thread_clear_interruption_requested (mono_internal_thread_handle_ptr (thread));
422 /* Returns TRUE is there was a state change and the interruption can be processed */
423 static gboolean
424 mono_thread_set_interruption_requested (MonoInternalThread *thread)
426 //always force when the current thread is doing it to itself.
427 gboolean sync = thread == mono_thread_internal_current ();
428 /* Normally synchronous interruptions can bypass abort protection. */
429 return mono_thread_set_interruption_requested_flags (thread, sync);
432 /* Returns TRUE if there was a state change and the interruption can be
433 * processed. This variant defers a self abort when inside an abort protected
434 * block. Normally this should only be done when a thread has received an
435 * outside indication that it should abort. (For example when the JIT sets a
436 * flag in an finally block.)
439 static gboolean
440 mono_thread_set_self_interruption_respect_abort_prot (void)
442 MonoInternalThread *thread = mono_thread_internal_current ();
443 /* N.B. Sets the ASYNC_REQUESTED_BIT for current this thread,
444 * which is unusual. */
445 return mono_thread_set_interruption_requested_flags (thread, FALSE);
448 /* Returns TRUE if there was a state change and the interruption can be processed. */
449 static gboolean
450 mono_thread_set_interruption_requested_flags (MonoInternalThread *thread, gboolean sync)
452 gsize old_state, new_state;
453 do {
454 old_state = thread->thread_state;
456 //Already set
457 if ((sync && (old_state & INTERRUPT_SYNC_REQUESTED_BIT)) ||
458 (!sync && (old_state & INTERRUPT_ASYNC_REQUESTED_BIT)))
459 return FALSE;
461 if (sync)
462 new_state = old_state | INTERRUPT_SYNC_REQUESTED_BIT;
463 else
464 new_state = old_state | INTERRUPT_ASYNC_REQUESTED_BIT;
465 } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
467 if (sync || !(new_state & ABORT_PROT_BLOCK_MASK)) {
468 mono_atomic_inc_i32 (&mono_thread_interruption_request_flag);
469 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, mono_thread_interruption_request_flag);
470 } else {
471 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, mono_thread_interruption_request_flag);
474 return sync || !(new_state & ABORT_PROT_BLOCK_MASK);
477 static MonoNativeThreadId
478 thread_get_tid (MonoInternalThread *thread)
480 /* We store the tid as a guint64 to keep the object layout constant between platforms */
481 return MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid);
484 static void
485 free_synch_cs (MonoCoopMutex *synch_cs)
487 g_assert (synch_cs);
488 mono_coop_mutex_destroy (synch_cs);
489 g_free (synch_cs);
492 static void
493 free_longlived_thread_data (void *user_data)
495 MonoLongLivedThreadData *lltd = (MonoLongLivedThreadData*)user_data;
496 free_synch_cs (lltd->synch_cs);
498 g_free (lltd);
501 static void
502 init_longlived_thread_data (MonoLongLivedThreadData *lltd)
504 mono_refcount_init (lltd, free_longlived_thread_data);
505 mono_refcount_inc (lltd);
506 /* Initial refcount is 2: decremented once by
507 * mono_thread_detach_internal and once by the MonoInternalThread
508 * finalizer - whichever one happens later will deallocate. */
510 lltd->synch_cs = g_new0 (MonoCoopMutex, 1);
511 mono_coop_mutex_init_recursive (lltd->synch_cs);
513 mono_memory_barrier ();
516 static void
517 dec_longlived_thread_data (MonoLongLivedThreadData *lltd)
519 mono_refcount_dec (lltd);
522 static void
523 lock_thread (MonoInternalThread *thread)
525 g_assert (thread->longlived);
526 g_assert (thread->longlived->synch_cs);
528 mono_coop_mutex_lock (thread->longlived->synch_cs);
531 static void
532 unlock_thread (MonoInternalThread *thread)
534 mono_coop_mutex_unlock (thread->longlived->synch_cs);
537 static void
538 lock_thread_handle (MonoInternalThreadHandle thread)
540 lock_thread (mono_internal_thread_handle_ptr (thread));
543 static void
544 unlock_thread_handle (MonoInternalThreadHandle thread)
546 unlock_thread (mono_internal_thread_handle_ptr (thread));
549 static gboolean
550 is_appdomainunloaded_exception (MonoClass *klass)
552 #ifdef ENABLE_NETCORE
553 return FALSE;
554 #else
555 return klass == mono_class_get_appdomain_unloaded_exception_class ();
556 #endif
559 static gboolean
560 is_threadabort_exception (MonoClass *klass)
562 return klass == mono_defaults.threadabortexception_class;
566 * A special static data offset (guint32) consists of 3 parts:
568 * [0] 6-bit index into the array of chunks.
569 * [6] 25-bit offset into the array.
570 * [31] Bit indicating thread or context static.
573 typedef union {
574 struct {
575 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
576 guint32 type : 1;
577 guint32 offset : 25;
578 guint32 index : 6;
579 #else
580 guint32 index : 6;
581 guint32 offset : 25;
582 guint32 type : 1;
583 #endif
584 } fields;
585 guint32 raw;
586 } SpecialStaticOffset;
588 #define SPECIAL_STATIC_OFFSET_TYPE_THREAD 0
589 #define SPECIAL_STATIC_OFFSET_TYPE_CONTEXT 1
591 static guint32
592 MAKE_SPECIAL_STATIC_OFFSET (guint32 index, guint32 offset, guint32 type)
594 SpecialStaticOffset special_static_offset;
595 memset (&special_static_offset, 0, sizeof (special_static_offset));
596 special_static_offset.fields.index = index;
597 special_static_offset.fields.offset = offset;
598 special_static_offset.fields.type = type;
599 return special_static_offset.raw;
602 #define ACCESS_SPECIAL_STATIC_OFFSET(x,f) \
603 (((SpecialStaticOffset *) &(x))->fields.f)
605 static gpointer
606 get_thread_static_data (MonoInternalThread *thread, guint32 offset)
608 g_assert (ACCESS_SPECIAL_STATIC_OFFSET (offset, type) == SPECIAL_STATIC_OFFSET_TYPE_THREAD);
610 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
611 int off = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
613 return ((char *) thread->static_data [idx]) + off;
616 static gpointer
617 get_context_static_data (MonoAppContext *ctx, guint32 offset)
619 g_assert (ACCESS_SPECIAL_STATIC_OFFSET (offset, type) == SPECIAL_STATIC_OFFSET_TYPE_CONTEXT);
621 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
622 int off = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
624 return ((char *) ctx->static_data [idx]) + off;
627 static MonoThread**
628 get_current_thread_ptr_for_domain (MonoDomain *domain, MonoInternalThread *thread)
630 guint32 offset;
632 MONO_STATIC_POINTER_INIT (MonoClassField, current_thread_field)
634 current_thread_field = mono_class_get_field_from_name_full (mono_defaults.thread_class, "current_thread", NULL);
635 g_assert (current_thread_field);
637 MONO_STATIC_POINTER_INIT_END (MonoClassField, current_thread_field)
639 ERROR_DECL (thread_vt_error);
640 mono_class_vtable_checked (domain, mono_defaults.thread_class, thread_vt_error);
641 mono_error_assert_ok (thread_vt_error);
642 mono_domain_lock (domain);
643 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, current_thread_field));
644 mono_domain_unlock (domain);
645 g_assert (offset);
647 return (MonoThread **)get_thread_static_data (thread, offset);
650 static void
651 set_current_thread_for_domain (MonoDomain *domain, MonoInternalThread *thread, MonoThread *current)
653 #ifndef ENABLE_NETCORE
654 MonoThread **current_thread_ptr = get_current_thread_ptr_for_domain (domain, thread);
656 g_assert (current->obj.vtable->domain == domain);
658 g_assert (!*current_thread_ptr);
659 *current_thread_ptr = current;
660 #endif
663 static MonoThread*
664 create_thread_object (MonoDomain *domain, MonoInternalThread *internal)
666 #ifdef ENABLE_NETCORE
667 MONO_OBJECT_SETREF_INTERNAL (internal, internal_thread, internal);
668 return internal;
669 #else
670 MonoThread *thread;
671 MonoVTable *vtable;
672 ERROR_DECL (error);
674 vtable = mono_class_vtable_checked (domain, mono_defaults.thread_class, error);
675 mono_error_assert_ok (error);
677 thread = (MonoThread*)mono_object_new_mature (vtable, error);
678 /* only possible failure mode is OOM, from which we don't expect to recover. */
679 mono_error_assert_ok (error);
681 MONO_OBJECT_SETREF_INTERNAL (thread, internal_thread, internal);
683 return thread;
684 #endif
687 static void
688 init_internal_thread_object (MonoInternalThread *thread)
690 thread->longlived = g_new0 (MonoLongLivedThreadData, 1);
691 init_longlived_thread_data (thread->longlived);
693 thread->apartment_state = ThreadApartmentState_Unknown;
694 thread->managed_id = get_next_managed_thread_id ();
695 if (mono_gc_is_moving ()) {
696 thread->thread_pinning_ref = thread;
697 MONO_GC_REGISTER_ROOT_PINNING (thread->thread_pinning_ref, MONO_ROOT_SOURCE_THREADING, NULL, "Thread Pinning Reference");
700 thread->priority = MONO_THREAD_PRIORITY_NORMAL;
702 thread->suspended = g_new0 (MonoOSEvent, 1);
703 mono_os_event_init (thread->suspended, TRUE);
706 static MonoInternalThread*
707 create_internal_thread_object (void)
709 ERROR_DECL (error);
710 MonoInternalThread *thread;
711 MonoVTable *vt;
713 vt = mono_class_vtable_checked (mono_get_root_domain (), mono_defaults.internal_thread_class, error);
714 mono_error_assert_ok (error);
715 thread = (MonoInternalThread*) mono_object_new_mature (vt, error);
716 /* only possible failure mode is OOM, from which we don't exect to recover */
717 mono_error_assert_ok (error);
719 init_internal_thread_object (thread);
721 return thread;
724 static void
725 mono_thread_internal_set_priority (MonoInternalThread *internal, MonoThreadPriority priority)
727 g_assert (internal);
729 g_assert (priority >= MONO_THREAD_PRIORITY_LOWEST);
730 g_assert (priority <= MONO_THREAD_PRIORITY_HIGHEST);
731 g_assert (MONO_THREAD_PRIORITY_LOWEST < MONO_THREAD_PRIORITY_HIGHEST);
733 #ifdef HOST_WIN32
734 BOOL res;
735 DWORD last_error;
737 g_assert (internal->native_handle);
739 MONO_ENTER_GC_SAFE;
740 res = SetThreadPriority (internal->native_handle, (int)priority - 2);
741 last_error = GetLastError ();
742 MONO_EXIT_GC_SAFE;
743 if (!res)
744 g_error ("%s: SetThreadPriority failed, error %d", __func__, last_error);
745 #elif defined(HOST_FUCHSIA)
746 int z_priority;
748 if (priority == MONO_THREAD_PRIORITY_LOWEST)
749 z_priority = ZX_PRIORITY_LOWEST;
750 else if (priority == MONO_THREAD_PRIORITY_BELOW_NORMAL)
751 z_priority = ZX_PRIORITY_LOW;
752 else if (priority == MONO_THREAD_PRIORITY_NORMAL)
753 z_priority = ZX_PRIORITY_DEFAULT;
754 else if (priority == MONO_THREAD_PRIORITY_ABOVE_NORMAL)
755 z_priority = ZX_PRIORITY_HIGH;
756 else if (priority == MONO_THREAD_PRIORITY_HIGHEST)
757 z_priority = ZX_PRIORITY_HIGHEST;
758 else
759 return;
762 // When this API becomes available on an arbitrary thread, we can use it,
763 // not available on current Zircon
765 #else /* !HOST_WIN32 and not HOST_FUCHSIA */
766 pthread_t tid;
767 int policy;
768 struct sched_param param;
769 gint res;
771 tid = thread_get_tid (internal);
773 MONO_ENTER_GC_SAFE;
774 res = pthread_getschedparam (tid, &policy, &param);
775 MONO_EXIT_GC_SAFE;
776 if (res != 0)
777 g_error ("%s: pthread_getschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
779 #ifdef _POSIX_PRIORITY_SCHEDULING
780 int max, min;
782 /* Necessary to get valid priority range */
784 MONO_ENTER_GC_SAFE;
785 #if defined(__PASE__)
786 /* only priorities allowed by IBM i */
787 min = PRIORITY_MIN;
788 max = PRIORITY_MAX;
789 #else
790 min = sched_get_priority_min (policy);
791 max = sched_get_priority_max (policy);
792 #endif
793 MONO_EXIT_GC_SAFE;
795 /* Not tunable. Bail out */
796 if ((min == -1) || (max == -1))
797 return;
799 if (max > 0 && min >= 0 && max > min) {
800 double srange, drange, sposition, dposition;
801 srange = MONO_THREAD_PRIORITY_HIGHEST - MONO_THREAD_PRIORITY_LOWEST;
802 drange = max - min;
803 sposition = priority - MONO_THREAD_PRIORITY_LOWEST;
804 dposition = (sposition / srange) * drange;
805 param.sched_priority = (int)(dposition + min);
806 } else
807 #endif
809 switch (policy) {
810 case SCHED_FIFO:
811 case SCHED_RR:
812 param.sched_priority = 50;
813 break;
814 #ifdef SCHED_BATCH
815 case SCHED_BATCH:
816 #endif
817 case SCHED_OTHER:
818 param.sched_priority = 0;
819 break;
820 default:
821 g_warning ("%s: unknown policy %d", __func__, policy);
822 return;
826 MONO_ENTER_GC_SAFE;
827 #if defined(__PASE__)
828 /* only scheduling param allowed by IBM i */
829 res = pthread_setschedparam (tid, SCHED_OTHER, &param);
830 #else
831 res = pthread_setschedparam (tid, policy, &param);
832 #endif
833 MONO_EXIT_GC_SAFE;
834 if (res != 0) {
835 if (res == EPERM) {
836 #if !defined(_AIX)
837 /* AIX doesn't like doing this and will spam this every time;
838 * weirdly, i doesn't complain
840 g_warning ("%s: pthread_setschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
841 #endif
842 return;
844 g_error ("%s: pthread_setschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
846 #endif /* HOST_WIN32 */
849 static void
850 mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, void *alloc_key, gboolean threadlocal);
852 static gboolean
853 mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean force_domain)
855 MonoThreadInfo *info;
856 MonoInternalThread *internal;
857 MonoDomain *domain, *root_domain;
858 guint32 gchandle;
860 g_assert (thread);
862 info = mono_thread_info_current ();
863 g_assert (info);
865 internal = thread->internal_thread;
866 g_assert (internal);
868 /* It is needed to store the MonoInternalThread on the MonoThreadInfo, because of the following case:
869 * - the MonoInternalThread TLS key is destroyed: set it to NULL
870 * - the MonoThreadInfo TLS key is destroyed: calls mono_thread_info_detach
871 * - it calls MonoThreadInfoCallbacks.thread_detach
872 * - mono_thread_internal_current returns NULL -> fails to detach the MonoInternalThread. */
873 mono_thread_info_set_internal_thread_gchandle (info, mono_gchandle_new_internal ((MonoObject*) internal, FALSE));
875 internal->handle = mono_threads_open_thread_handle (info->handle);
876 internal->native_handle = MONO_NATIVE_THREAD_HANDLE_TO_GPOINTER (mono_threads_open_native_thread_handle (info->native_handle));
877 internal->tid = MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ());
878 internal->thread_info = info;
879 internal->small_id = info->small_id;
881 THREAD_DEBUG (g_message ("%s: (%" G_GSIZE_FORMAT ") Setting current_object_key to %p", __func__, mono_native_thread_id_get (), internal));
883 SET_CURRENT_OBJECT (internal);
885 domain = mono_object_domain (thread);
887 mono_thread_push_appdomain_ref (domain);
888 if (!mono_domain_set_fast (domain, force_domain)) {
889 mono_thread_pop_appdomain_ref ();
890 goto fail;
893 mono_threads_lock ();
895 if (shutting_down && !force_attach) {
896 mono_threads_unlock ();
897 mono_thread_pop_appdomain_ref ();
898 goto fail;
901 if (threads_starting_up)
902 mono_g_hash_table_remove (threads_starting_up, thread);
904 if (!threads) {
905 threads = mono_g_hash_table_new_type_internal (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_THREADING, NULL, "Thread Table");
908 /* We don't need to duplicate thread->handle, because it is
909 * only closed when the thread object is finalized by the GC. */
910 mono_g_hash_table_insert_internal (threads, (gpointer)(gsize)(internal->tid), internal);
912 /* We have to do this here because mono_thread_start_cb
913 * requires that root_domain_thread is set up. */
914 if (thread_static_info.offset || thread_static_info.idx > 0) {
915 /* get the current allocated size */
916 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (thread_static_info.idx, thread_static_info.offset, 0);
917 mono_alloc_static_data (&internal->static_data, offset, (void*)(gsize)MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid), TRUE);
920 mono_threads_unlock ();
922 root_domain = mono_get_root_domain ();
924 g_assert (!internal->root_domain_thread);
925 if (domain != root_domain)
926 MONO_OBJECT_SETREF_INTERNAL (internal, root_domain_thread, create_thread_object (root_domain, internal));
927 else
928 MONO_OBJECT_SETREF_INTERNAL (internal, root_domain_thread, thread);
930 if (domain != root_domain)
931 set_current_thread_for_domain (root_domain, internal, internal->root_domain_thread);
933 set_current_thread_for_domain (domain, internal, thread);
935 THREAD_DEBUG (g_message ("%s: Attached thread ID %" G_GSIZE_FORMAT " (handle %p)", __func__, internal->tid, internal->handle));
937 return TRUE;
939 fail:
940 mono_threads_lock ();
941 if (threads_starting_up)
942 mono_g_hash_table_remove (threads_starting_up, thread);
943 mono_threads_unlock ();
945 if (!mono_thread_info_try_get_internal_thread_gchandle (info, &gchandle))
946 g_error ("%s: failed to get gchandle, info %p", __func__, info);
948 mono_gchandle_free_internal (gchandle);
950 mono_thread_info_unset_internal_thread_gchandle (info);
952 SET_CURRENT_OBJECT(NULL);
954 return FALSE;
957 static void
958 mono_thread_detach_internal (MonoInternalThread *thread)
960 MonoThreadInfo *info;
961 MonoInternalThread *value;
962 gboolean removed;
963 guint32 gchandle;
965 g_assert (mono_thread_internal_is_current (thread));
967 g_assert (thread != NULL);
968 SET_CURRENT_OBJECT (thread);
970 info = thread->thread_info;
971 g_assert (info);
973 THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%" G_GSIZE_FORMAT ")", __func__, thread, (gsize)thread->tid));
975 MONO_PROFILER_RAISE (thread_stopping, (thread->tid));
978 * Prevent race condition between thread shutdown and runtime shutdown.
979 * Including all runtime threads in the pending joinable count will make
980 * sure shutdown will wait for it to get onto the joinable thread list before
981 * critical resources have been cleanup (like GC memory). Threads getting onto
982 * the joinable thread list should just about to exit and not blocking a potential
983 * join call. Owner of threads attached to the runtime but not identified as runtime
984 * threads needs to make sure thread detach calls won't race with runtime shutdown.
986 threads_add_pending_joinable_runtime_thread (info);
988 #ifndef HOST_WIN32
989 mono_w32mutex_abandon (thread);
990 #endif
992 mono_gchandle_free_internal (thread->abort_state_handle);
993 thread->abort_state_handle = 0;
995 thread->abort_exc = NULL;
996 thread->current_appcontext = NULL;
998 LOCK_THREAD (thread);
1000 thread->state |= ThreadState_Stopped;
1001 thread->state &= ~ThreadState_Background;
1003 UNLOCK_THREAD (thread);
1006 An interruption request has leaked to cleanup. Adjust the global counter.
1008 This can happen is the abort source thread finds the abortee (this) thread
1009 in unmanaged code. If this thread never trips back to managed code or check
1010 the local flag it will be left set and positively unbalance the global counter.
1012 Leaving the counter unbalanced will cause a performance degradation since all threads
1013 will now keep checking their local flags all the time.
1015 mono_thread_clear_interruption_requested (thread);
1017 mono_threads_lock ();
1019 g_assert (threads);
1021 if (!mono_g_hash_table_lookup_extended (threads, (gpointer)thread->tid, NULL, (gpointer*) &value)) {
1022 g_error ("%s: thread %p (tid: %p) should not have been removed yet from threads", __func__, thread, thread->tid);
1023 } else if (thread != value) {
1024 /* We have to check whether the thread object for the tid is still the same in the table because the
1025 * thread might have been destroyed and the tid reused in the meantime, in which case the tid would be in
1026 * the table, but with another thread object. */
1027 g_error ("%s: thread %p (tid: %p) do not match with value %p (tid: %p)", __func__, thread, thread->tid, value, value->tid);
1030 removed = mono_g_hash_table_remove (threads, (gpointer)thread->tid);
1031 g_assert (removed);
1033 mono_threads_unlock ();
1035 /* Don't close the handle here, wait for the object finalizer
1036 * to do it. Otherwise, the following race condition applies:
1038 * 1) Thread exits (and mono_thread_detach_internal() closes the handle)
1040 * 2) Some other handle is reassigned the same slot
1042 * 3) Another thread tries to join the first thread, and
1043 * blocks waiting for the reassigned handle to be signalled
1044 * (which might never happen). This is possible, because the
1045 * thread calling Join() still has a reference to the first
1046 * thread's object.
1049 mono_release_type_locks (thread);
1051 MONO_PROFILER_RAISE (thread_stopped, (thread->tid));
1052 MONO_PROFILER_RAISE (gc_root_unregister, ((const mono_byte*)(info->stack_start_limit)));
1053 MONO_PROFILER_RAISE (gc_root_unregister, ((const mono_byte*)(info->handle_stack)));
1056 * This will signal async signal handlers that the thread has exited.
1057 * The profiler callback needs this to be set, so it cannot be done earlier.
1059 mono_domain_unset ();
1060 mono_memory_barrier ();
1062 mono_thread_pop_appdomain_ref ();
1064 mono_free_static_data (thread->static_data);
1065 thread->static_data = NULL;
1066 ref_stack_destroy (thread->appdomain_refs);
1067 thread->appdomain_refs = NULL;
1069 g_assert (thread->suspended);
1070 mono_os_event_destroy (thread->suspended);
1071 g_free (thread->suspended);
1072 thread->suspended = NULL;
1074 if (mono_thread_cleanup_fn)
1075 mono_thread_cleanup_fn (thread_get_tid (thread));
1077 mono_memory_barrier ();
1079 if (mono_gc_is_moving ()) {
1080 MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref);
1081 thread->thread_pinning_ref = NULL;
1084 /* There is no more any guarantee that `thread` is alive */
1085 mono_memory_barrier ();
1087 SET_CURRENT_OBJECT (NULL);
1088 mono_domain_unset ();
1090 if (!mono_thread_info_try_get_internal_thread_gchandle (info, &gchandle))
1091 g_error ("%s: failed to get gchandle, info = %p", __func__, info);
1093 mono_gchandle_free_internal (gchandle);
1095 mono_thread_info_unset_internal_thread_gchandle (info);
1097 /* Possibly free synch_cs, if the finalizer for InternalThread already
1098 * ran also. */
1099 dec_longlived_thread_data (thread->longlived);
1101 MONO_PROFILER_RAISE (thread_exited, (thread->tid));
1103 /* Don't need to close the handle to this thread, even though we took a
1104 * reference in mono_thread_attach (), because the GC will do it
1105 * when the Thread object is finalised.
1109 typedef struct {
1110 gint32 ref;
1111 MonoThread *thread;
1112 MonoObject *start_delegate;
1113 MonoObject *start_delegate_arg;
1114 MonoThreadStart start_func;
1115 gpointer start_func_arg;
1116 gboolean force_attach;
1117 gboolean failed;
1118 MonoCoopSem registered;
1119 } StartInfo;
1121 static void
1122 fire_attach_profiler_events (MonoNativeThreadId tid)
1124 MONO_PROFILER_RAISE (thread_started, ((uintptr_t) tid));
1126 MonoThreadInfo *info = mono_thread_info_current ();
1128 MONO_PROFILER_RAISE (gc_root_register, (
1129 (const mono_byte*)(info->stack_start_limit),
1130 (char *) info->stack_end - (char *) info->stack_start_limit,
1131 MONO_ROOT_SOURCE_STACK,
1132 (void *) tid,
1133 "Thread Stack"));
1135 // The handle stack is a pseudo-root similar to the finalizer queues.
1136 MONO_PROFILER_RAISE (gc_root_register, (
1137 (const mono_byte*)info->handle_stack,
1139 MONO_ROOT_SOURCE_HANDLE,
1140 (void *) tid,
1141 "Handle Stack"));
1144 static guint32 WINAPI
1145 start_wrapper_internal (StartInfo *start_info, gsize *stack_ptr)
1147 ERROR_DECL (error);
1148 MonoThreadStart start_func;
1149 void *start_func_arg;
1150 gsize tid;
1152 * We don't create a local to hold start_info->thread, so hopefully it won't get pinned during a
1153 * GC stack walk.
1155 MonoThread *thread;
1156 MonoInternalThread *internal;
1157 MonoObject *start_delegate;
1158 MonoObject *start_delegate_arg;
1160 thread = start_info->thread;
1161 internal = thread->internal_thread;
1163 THREAD_DEBUG (g_message ("%s: (%" G_GSIZE_FORMAT ") Start wrapper", __func__, mono_native_thread_id_get ()));
1165 if (!mono_thread_attach_internal (thread, start_info->force_attach, FALSE)) {
1166 start_info->failed = TRUE;
1168 mono_coop_sem_post (&start_info->registered);
1170 if (mono_atomic_dec_i32 (&start_info->ref) == 0) {
1171 mono_coop_sem_destroy (&start_info->registered);
1172 g_free (start_info);
1175 return 0;
1178 mono_thread_internal_set_priority (internal, (MonoThreadPriority)internal->priority);
1180 tid = internal->tid;
1182 start_delegate = start_info->start_delegate;
1183 start_delegate_arg = start_info->start_delegate_arg;
1184 start_func = start_info->start_func;
1185 start_func_arg = start_info->start_func_arg;
1187 /* This MUST be called before any managed code can be
1188 * executed, as it calls the callback function that (for the
1189 * jit) sets the lmf marker.
1192 if (mono_thread_start_cb)
1193 mono_thread_start_cb (tid, stack_ptr, (gpointer)start_func);
1195 /* On 2.0 profile (and higher), set explicitly since state might have been
1196 Unknown */
1197 if (internal->apartment_state == ThreadApartmentState_Unknown)
1198 internal->apartment_state = ThreadApartmentState_MTA;
1200 mono_thread_init_apartment_state ();
1202 /* Let the thread that called Start() know we're ready */
1203 mono_coop_sem_post (&start_info->registered);
1205 if (mono_atomic_dec_i32 (&start_info->ref) == 0) {
1206 mono_coop_sem_destroy (&start_info->registered);
1207 g_free (start_info);
1210 /* start_info is not valid anymore */
1211 start_info = NULL;
1214 * Call this after calling start_notify, since the profiler callback might want
1215 * to lock the thread, and the lock is held by thread_start () which waits for
1216 * start_notify.
1218 fire_attach_profiler_events ((MonoNativeThreadId) tid);
1220 /* if the name was set before starting, we didn't invoke the profiler callback */
1221 // This is a little racy, ok.
1223 if (internal->name.chars) {
1225 LOCK_THREAD (internal);
1227 if (internal->name.chars) {
1228 MONO_PROFILER_RAISE (thread_name, (internal->tid, internal->name.chars));
1229 mono_native_thread_set_name (MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid), internal->name.chars);
1232 UNLOCK_THREAD (internal);
1235 /* start_func is set only for unmanaged start functions */
1236 if (start_func) {
1237 start_func (start_func_arg);
1238 } else {
1239 #ifdef ENABLE_NETCORE
1240 /* Call a callback in the RuntimeThread class */
1241 g_assert (start_delegate == NULL);
1243 MONO_STATIC_POINTER_INIT (MonoMethod, cb)
1245 cb = mono_class_get_method_from_name_checked (internal->obj.vtable->klass, "StartCallback", 0, 0, error);
1246 g_assert (cb);
1247 mono_error_assert_ok (error);
1249 MONO_STATIC_POINTER_INIT_END (MonoMethod, cb)
1251 mono_runtime_invoke_checked (cb, internal, NULL, error);
1252 #else
1253 void *args [1];
1255 g_assert (start_delegate != NULL);
1257 /* we may want to handle the exception here. See comment below on unhandled exceptions */
1258 args [0] = (gpointer) start_delegate_arg;
1259 mono_runtime_delegate_invoke_checked (start_delegate, args, error);
1260 #endif
1262 if (!is_ok (error)) {
1263 MonoException *ex = mono_error_convert_to_exception (error);
1265 g_assert (ex != NULL);
1266 MonoClass *klass = mono_object_class (ex);
1267 if ((mono_runtime_unhandled_exception_policy_get () != MONO_UNHANDLED_POLICY_LEGACY) &&
1268 !is_threadabort_exception (klass)) {
1269 mono_unhandled_exception_internal (&ex->object);
1270 mono_invoke_unhandled_exception_hook (&ex->object);
1271 g_assert_not_reached ();
1273 } else {
1274 mono_error_cleanup (error);
1278 /* If the thread calls ExitThread at all, this remaining code
1279 * will not be executed, but the main thread will eventually
1280 * call mono_thread_detach_internal() on this thread's behalf.
1283 THREAD_DEBUG (g_message ("%s: (%" G_GSIZE_FORMAT ") Start wrapper terminating", __func__, mono_native_thread_id_get ()));
1285 /* Do any cleanup needed for apartment state. This
1286 * cannot be done in mono_thread_detach_internal since
1287 * mono_thread_detach_internal could be called for a
1288 * thread other than the current thread.
1289 * mono_thread_cleanup_apartment_state cleans up apartment
1290 * for the current thead */
1291 mono_thread_cleanup_apartment_state ();
1293 mono_thread_detach_internal (internal);
1295 return 0;
1298 static mono_thread_start_return_t WINAPI
1299 start_wrapper (gpointer data)
1301 StartInfo *start_info;
1302 MonoThreadInfo *info;
1303 gsize res;
1305 start_info = (StartInfo*) data;
1306 g_assert (start_info);
1308 info = mono_thread_info_attach ();
1309 info->runtime_thread = TRUE;
1311 /* Run the actual main function of the thread */
1312 res = start_wrapper_internal (start_info, (gsize*)info->stack_end);
1314 mono_thread_info_exit (res);
1316 g_assert_not_reached ();
1320 * create_thread:
1322 * Common thread creation code.
1323 * LOCKING: Acquires the threads lock.
1325 static gboolean
1326 create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *start_delegate, MonoThreadStart start_func, gpointer start_func_arg,
1327 MonoThreadCreateFlags flags, MonoError *error)
1329 StartInfo *start_info = NULL;
1330 MonoNativeThreadId tid;
1331 gboolean ret;
1332 gsize stack_set_size;
1334 if (start_delegate)
1335 g_assert (!start_func && !start_func_arg);
1336 if (start_func)
1337 g_assert (!start_delegate);
1339 if (flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL) {
1340 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER));
1341 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE));
1343 if (flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER) {
1344 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL));
1345 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE));
1349 * Join joinable threads to prevent running out of threads since the finalizer
1350 * thread might be blocked/backlogged.
1352 mono_threads_join_threads ();
1354 error_init (error);
1356 mono_threads_lock ();
1357 if (shutting_down && !(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE)) {
1358 mono_threads_unlock ();
1359 mono_error_set_execution_engine (error, "Couldn't create thread. Runtime is shutting down.");
1360 return FALSE;
1362 if (threads_starting_up == NULL) {
1363 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");
1365 mono_g_hash_table_insert_internal (threads_starting_up, thread, thread);
1366 mono_threads_unlock ();
1368 #ifndef ENABLE_NETCORE
1369 internal->threadpool_thread = flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL;
1370 if (internal->threadpool_thread)
1371 mono_thread_set_state (internal, ThreadState_Background);
1372 #endif
1374 internal->debugger_thread = flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER;
1376 start_info = g_new0 (StartInfo, 1);
1377 start_info->ref = 2;
1378 start_info->thread = thread;
1379 start_info->start_delegate = start_delegate;
1380 start_info->start_delegate_arg = thread->start_obj;
1381 start_info->start_func = start_func;
1382 start_info->start_func_arg = start_func_arg;
1383 start_info->force_attach = flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE;
1384 start_info->failed = FALSE;
1385 mono_coop_sem_init (&start_info->registered, 0);
1387 if (flags != MONO_THREAD_CREATE_FLAGS_SMALL_STACK)
1388 stack_set_size = default_stacksize_for_thread (internal);
1389 else
1390 stack_set_size = 0;
1392 if (!mono_thread_platform_create_thread (start_wrapper, start_info, &stack_set_size, &tid)) {
1393 /* The thread couldn't be created, so set an exception */
1394 mono_threads_lock ();
1395 mono_g_hash_table_remove (threads_starting_up, thread);
1396 mono_threads_unlock ();
1397 mono_error_set_execution_engine (error, "Couldn't create thread. Error 0x%x", mono_w32error_get_last());
1398 /* ref is not going to be decremented in start_wrapper_internal */
1399 mono_atomic_dec_i32 (&start_info->ref);
1400 ret = FALSE;
1401 goto done;
1404 internal->stack_size = (int) stack_set_size;
1406 THREAD_DEBUG (g_message ("%s: (%" G_GSIZE_FORMAT ") Launching thread %p (%" G_GSIZE_FORMAT ")", __func__, mono_native_thread_id_get (), internal, (gsize)internal->tid));
1409 * Wait for the thread to set up its TLS data etc, so
1410 * theres no potential race condition if someone tries
1411 * to look up the data believing the thread has
1412 * started
1415 mono_coop_sem_wait (&start_info->registered, MONO_SEM_FLAGS_NONE);
1417 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));
1419 ret = !start_info->failed;
1421 done:
1422 if (mono_atomic_dec_i32 (&start_info->ref) == 0) {
1423 mono_coop_sem_destroy (&start_info->registered);
1424 g_free (start_info);
1427 return ret;
1431 * mono_thread_new_init:
1433 void
1434 mono_thread_new_init (intptr_t tid, gpointer stack_start, gpointer func)
1436 if (mono_thread_start_cb) {
1437 mono_thread_start_cb (tid, stack_start, func);
1442 * mono_threads_set_default_stacksize:
1444 void
1445 mono_threads_set_default_stacksize (guint32 stacksize)
1447 default_stacksize = stacksize;
1451 * mono_threads_get_default_stacksize:
1453 guint32
1454 mono_threads_get_default_stacksize (void)
1456 return default_stacksize;
1460 * mono_thread_create_internal:
1462 * ARG should not be a GC reference.
1464 MonoInternalThread*
1465 mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, MonoThreadCreateFlags flags, MonoError *error)
1467 MonoThread *thread;
1468 MonoInternalThread *internal;
1469 gboolean res;
1471 error_init (error);
1473 internal = create_internal_thread_object ();
1475 thread = create_thread_object (domain, internal);
1477 LOCK_THREAD (internal);
1479 res = create_thread (thread, internal, NULL, (MonoThreadStart) func, arg, flags, error);
1480 (void)res;
1482 UNLOCK_THREAD (internal);
1484 return_val_if_nok (error, NULL);
1485 return internal;
1488 MonoInternalThreadHandle
1489 mono_thread_create_internal_handle (MonoDomain *domain, gpointer func, gpointer arg, MonoThreadCreateFlags flags, MonoError *error)
1491 // FIXME invert
1492 return MONO_HANDLE_NEW (MonoInternalThread, mono_thread_create_internal (domain, func, arg, flags, error));
1496 * mono_thread_create:
1498 void
1499 mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
1501 MONO_ENTER_GC_UNSAFE;
1502 ERROR_DECL (error);
1503 if (!mono_thread_create_checked (domain, func, arg, error))
1504 mono_error_cleanup (error);
1505 MONO_EXIT_GC_UNSAFE;
1508 gboolean
1509 mono_thread_create_checked (MonoDomain *domain, gpointer func, gpointer arg, MonoError *error)
1511 return (NULL != mono_thread_create_internal (domain, func, arg, MONO_THREAD_CREATE_FLAGS_NONE, error));
1515 * mono_thread_attach:
1517 MonoThread *
1518 mono_thread_attach (MonoDomain *domain)
1520 MonoInternalThread *internal;
1521 MonoThread *thread;
1522 MonoThreadInfo *info;
1523 MonoNativeThreadId tid;
1525 if (mono_thread_internal_current_is_attached ()) {
1526 if (domain != mono_domain_get ())
1527 mono_domain_set_fast (domain, TRUE);
1528 /* Already attached */
1529 return mono_thread_current ();
1532 info = mono_thread_info_attach ();
1533 g_assert (info);
1535 tid=mono_native_thread_id_get ();
1537 if (mono_runtime_get_no_exec ())
1538 return NULL;
1540 internal = create_internal_thread_object ();
1542 thread = create_thread_object (domain, internal);
1544 if (!mono_thread_attach_internal (thread, FALSE, TRUE)) {
1545 /* Mono is shutting down, so just wait for the end */
1546 for (;;)
1547 mono_thread_info_sleep (10000, NULL);
1550 THREAD_DEBUG (g_message ("%s: Attached thread ID %" G_GSIZE_FORMAT " (handle %p)", __func__, tid, internal->handle));
1552 if (mono_thread_attach_cb)
1553 mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), info->stack_end);
1555 fire_attach_profiler_events (tid);
1557 return thread;
1561 * mono_threads_attach_tools_thread:
1563 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
1565 * A tools thread is a very special kind of thread that needs access to core
1566 * runtime facilities but should not be counted as a regular thread for high
1567 * order facilities such as executing managed code or accessing the managed
1568 * heap.
1570 * This is intended only for low-level utilities than need to be able to use
1571 * some low-level runtime facilities when doing things like resolving
1572 * backtraces in their background processing thread.
1574 * Note in particular that the thread is not fully attached - it does not have
1575 * a "current domain" because it cannot run managed code or interact with
1576 * managed objects. However it can act on some metadata, and use our low-level
1577 * locks and the coop thread state machine (ie GC Safe and GC Unsafe
1578 * transitions make sense).
1580 void
1581 mono_threads_attach_tools_thread (void)
1583 MonoThreadInfo *info = mono_thread_info_attach ();
1584 g_assert (info);
1585 mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_GC | MONO_THREAD_INFO_FLAGS_NO_SAMPLE);
1589 * mono_thread_detach:
1591 void
1592 mono_thread_detach (MonoThread *thread)
1594 if (thread)
1595 mono_thread_detach_internal (thread->internal_thread);
1601 * mono_thread_detach_if_exiting:
1603 * Detach the current thread from the runtime if it is exiting, i.e. it is running pthread dtors.
1604 * This should be used at the end of embedding code which calls into managed code, and which
1605 * can be called from pthread dtors, like <code>dealloc:</code> implementations in Objective-C.
1607 mono_bool
1608 mono_thread_detach_if_exiting (void)
1610 if (mono_thread_info_is_exiting ()) {
1611 MonoInternalThread *thread;
1613 thread = mono_thread_internal_current ();
1614 if (thread) {
1615 // Switch to GC Unsafe thread state before detaching;
1616 // don't expect to undo this switch, hence unbalanced.
1617 gpointer dummy;
1618 (void) mono_threads_enter_gc_unsafe_region_unbalanced (&dummy);
1620 mono_thread_detach_internal (thread);
1621 mono_thread_info_detach ();
1622 return TRUE;
1625 return FALSE;
1628 gboolean
1629 mono_thread_internal_current_is_attached (void)
1631 MonoInternalThread *internal;
1633 internal = GET_CURRENT_OBJECT ();
1634 if (!internal)
1635 return FALSE;
1637 return TRUE;
1641 * mono_thread_exit:
1643 void
1644 mono_thread_exit (void)
1646 MonoInternalThread *thread = mono_thread_internal_current ();
1648 THREAD_DEBUG (g_message ("%s: mono_thread_exit for %p (%" G_GSIZE_FORMAT ")", __func__, thread, (gsize)thread->tid));
1650 mono_thread_detach_internal (thread);
1652 /* we could add a callback here for embedders to use. */
1653 if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread))
1654 exit (mono_environment_exitcode_get ());
1656 mono_thread_info_exit (0);
1659 static void
1660 mono_thread_construct_internal (MonoThreadObjectHandle this_obj_handle)
1662 MonoInternalThread * const internal = create_internal_thread_object ();
1664 internal->state = ThreadState_Unstarted;
1666 int const thread_gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, this_obj_handle), TRUE);
1668 MonoThreadObject *this_obj = MONO_HANDLE_RAW (this_obj_handle);
1670 mono_atomic_cas_ptr ((volatile gpointer *)&this_obj->internal_thread, internal, NULL);
1672 mono_gchandle_free_internal (thread_gchandle);
1675 #ifndef ENABLE_NETCORE
1676 void
1677 ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThreadObjectHandle this_obj_handle, MonoError *error)
1679 mono_thread_construct_internal (this_obj_handle);
1681 #endif
1683 void
1684 ves_icall_System_Threading_Thread_GetCurrentThread (MonoThread *volatile* thread)
1686 *thread = mono_thread_current ();
1689 static MonoInternalThread*
1690 thread_handle_to_internal_ptr (MonoThreadObjectHandle thread_handle)
1692 return MONO_HANDLE_GETVAL(thread_handle, internal_thread); // InternalThreads are always pinned.
1695 static void
1696 mono_error_set_exception_thread_state (MonoError *error, const char *exception_message)
1698 mono_error_set_generic_error (error, "System.Threading", "ThreadStateException", "%s", exception_message);
1701 static void
1702 mono_error_set_exception_thread_not_started_or_dead (MonoError *error)
1704 mono_error_set_exception_thread_state (error, "Thread has not been started, or is dead.");
1707 #ifndef ENABLE_NETCORE
1708 MonoBoolean
1709 ves_icall_System_Threading_Thread_Thread_internal (MonoThreadObjectHandle thread_handle, MonoObjectHandle start_handle, MonoError *error)
1711 MonoInternalThread *internal;
1712 gboolean res;
1713 MonoThread *this_obj = MONO_HANDLE_RAW (thread_handle);
1714 MonoObject *start = MONO_HANDLE_RAW (start_handle);
1716 THREAD_DEBUG (g_message("%s: Trying to start a new thread: this (%p) start (%p)", __func__, this_obj, start));
1718 internal = thread_handle_to_internal_ptr (thread_handle);
1720 if (!internal) {
1721 mono_thread_construct_internal (thread_handle);
1722 internal = thread_handle_to_internal_ptr (thread_handle);
1723 g_assert (internal);
1726 LOCK_THREAD (internal);
1728 if ((internal->state & ThreadState_Unstarted) == 0) {
1729 UNLOCK_THREAD (internal);
1730 mono_error_set_exception_thread_state (error, "Thread has already been started.");
1731 return FALSE;
1734 if ((internal->state & ThreadState_Aborted) != 0) {
1735 UNLOCK_THREAD (internal);
1736 return TRUE;
1739 res = create_thread (this_obj, internal, start, NULL, NULL, MONO_THREAD_CREATE_FLAGS_NONE, error);
1740 if (!res) {
1741 UNLOCK_THREAD (internal);
1742 return FALSE;
1745 internal->state &= ~ThreadState_Unstarted;
1747 THREAD_DEBUG (g_message ("%s: Started thread ID %" G_GSIZE_FORMAT " (handle %p)", __func__, tid, thread));
1749 UNLOCK_THREAD (internal);
1750 return TRUE;
1752 #endif
1754 static
1755 void
1756 mono_thread_name_cleanup (MonoThreadName* name)
1758 MonoThreadName const old_name = *name;
1759 memset (name, 0, sizeof (*name));
1760 if (old_name.free)
1761 g_free (old_name.chars);
1765 * This is called from the finalizer of the internal thread object.
1767 void
1768 ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThreadHandle this_obj_handle, MonoError *error)
1770 MonoInternalThread *this_obj = mono_internal_thread_handle_ptr (this_obj_handle);
1771 THREAD_DEBUG (g_message ("%s: Closing thread %p, handle %p", __func__, this_obj, this_obj->handle));
1774 * Since threads keep a reference to their thread object while running, by
1775 * the time this function is called, the thread has already exited/detached,
1776 * i.e. mono_thread_detach_internal () has ran. The exception is during
1777 * shutdown, when mono_thread_detach_internal () can be called after this.
1779 if (this_obj->handle) {
1780 mono_threads_close_thread_handle (this_obj->handle);
1781 this_obj->handle = NULL;
1784 mono_threads_close_native_thread_handle (MONO_GPOINTER_TO_NATIVE_THREAD_HANDLE (this_obj->native_handle));
1785 this_obj->native_handle = NULL;
1787 /* Possibly free synch_cs, if the thread already detached also. */
1788 dec_longlived_thread_data (this_obj->longlived);
1790 mono_thread_name_cleanup (&this_obj->name);
1793 static void
1794 mono_sleep_internal (gint32 ms, MonoBoolean allow_interruption, MonoError *error)
1796 THREAD_DEBUG (g_message ("%s: Sleeping for %d ms", __func__, ms));
1798 if (mono_thread_current_check_pending_interrupt ())
1799 return;
1801 MonoInternalThread * const thread = mono_thread_internal_current ();
1803 HANDLE_LOOP_PREPARE;
1805 while (TRUE) {
1806 gboolean alerted = FALSE;
1808 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1810 (void)mono_thread_info_sleep (ms, &alerted);
1812 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1814 if (!alerted)
1815 return;
1817 if (allow_interruption) {
1818 SETUP_ICALL_FRAME;
1820 MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, NULL);
1822 const gboolean interrupt = mono_thread_execute_interruption (&exc);
1824 if (interrupt)
1825 mono_set_pending_exception_handle (exc);
1827 CLEAR_ICALL_FRAME;
1829 if (interrupt)
1830 return;
1833 if (ms == MONO_INFINITE_WAIT) // FIXME: !MONO_INFINITE_WAIT
1834 continue;
1835 return;
1839 #ifdef ENABLE_NETCORE
1840 void
1841 ves_icall_System_Threading_Thread_Sleep_internal (gint32 ms, MonoBoolean allow_interruption, MonoError *error)
1843 mono_sleep_internal (ms, allow_interruption, error);
1845 #else
1846 void
1847 ves_icall_System_Threading_Thread_Sleep_internal (gint32 ms, MonoError *error)
1849 mono_sleep_internal (ms, TRUE, error);
1852 void
1853 ves_icall_System_Threading_Thread_SpinWait_nop (MonoError *error)
1856 #endif
1858 #ifndef ENABLE_NETCORE
1859 gint32
1860 ves_icall_System_Threading_Thread_GetDomainID (MonoError *error)
1862 return mono_domain_get()->domain_id;
1864 #endif
1867 * mono_thread_get_name_utf8:
1868 * \returns the name of the thread in UTF-8.
1869 * Return NULL if the thread has no name.
1870 * The returned memory is owned by the caller (g_free it).
1872 char *
1873 mono_thread_get_name_utf8 (MonoThread *thread)
1875 if (thread == NULL)
1876 return NULL;
1878 MonoInternalThread *internal = thread->internal_thread;
1880 // This is a little racy, ok.
1882 if (internal == NULL || !internal->name.chars)
1883 return NULL;
1885 LOCK_THREAD (internal);
1887 char *tname = (char*)g_memdup (internal->name.chars, internal->name.length + 1);
1889 UNLOCK_THREAD (internal);
1891 return tname;
1895 * mono_thread_get_managed_id:
1896 * \returns the \c Thread.ManagedThreadId value of \p thread.
1897 * Returns \c -1 if \p thread is NULL.
1899 int32_t
1900 mono_thread_get_managed_id (MonoThread *thread)
1902 if (thread == NULL)
1903 return -1;
1905 MonoInternalThread *internal = thread->internal_thread;
1906 if (internal == NULL)
1907 return -1;
1909 int32_t id = internal->managed_id;
1911 return id;
1914 #ifndef ENABLE_NETCORE
1915 MonoStringHandle
1916 ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThreadHandle thread_handle, MonoError *error)
1918 // InternalThreads are always pinned, so shallowly coop-handleize.
1919 MonoInternalThread * const this_obj = mono_internal_thread_handle_ptr (thread_handle);
1921 MonoStringHandle str = NULL_HANDLE_STRING;
1923 // This is a little racy, ok.
1925 if (this_obj->name.chars) {
1926 LOCK_THREAD (this_obj);
1928 if (this_obj->name.chars)
1929 str = mono_string_new_utf8_len (mono_domain_get (), this_obj->name.chars, this_obj->name.length, error);
1931 UNLOCK_THREAD (this_obj);
1934 return str;
1936 #endif
1938 // Unusal function:
1939 // - MonoError is optional -- failure is usually not interesting, except the documented failure mode for managed callers.
1940 // - name16 only used on Windows.
1941 // - name8 is either constant, or g_free'able -- this function always takes ownership and never copies.
1943 void
1944 mono_thread_set_name (MonoInternalThread *this_obj,
1945 const char* name8, size_t name8_length, const gunichar2* name16,
1946 MonoSetThreadNameFlags flags, MonoError *error)
1948 // A special case for the threadpool worker.
1949 // It sets the name repeatedly, in case it has changed, per-spec,
1950 // and if not done carefully, this can be a performance problem.
1952 // This is racy but ok. The name will always be valid (or absent).
1953 // In an unusual race, the name might briefly not revert to what the spec requires.
1955 if ((flags & MonoSetThreadNameFlag_RepeatedlyButOptimized) && name8 == this_obj->name.chars) {
1956 // Length is presumed to match.
1957 return;
1960 MonoNativeThreadId tid = 0;
1962 const gboolean constant = !!(flags & MonoSetThreadNameFlag_Constant);
1964 #if HOST_WIN32 // On Windows, if name8 is supplied, then name16 must be also.
1965 g_assert (!name8 || name16);
1966 #endif
1968 LOCK_THREAD (this_obj);
1970 if (flags & MonoSetThreadNameFlag_Reset) {
1971 this_obj->flags &= ~MONO_THREAD_FLAG_NAME_SET;
1972 } else if (this_obj->flags & MONO_THREAD_FLAG_NAME_SET) {
1973 UNLOCK_THREAD (this_obj);
1975 if (error)
1976 mono_error_set_invalid_operation (error, "%s", "Thread.Name can only be set once.");
1978 if (!constant)
1979 g_free ((char*)name8);
1980 return;
1983 mono_thread_name_cleanup (&this_obj->name);
1985 if (name8) {
1986 this_obj->name.chars = (char*)name8;
1987 this_obj->name.length = name8_length;
1988 this_obj->name.free = !constant;
1989 if (flags & MonoSetThreadNameFlag_Permanent)
1990 this_obj->flags |= MONO_THREAD_FLAG_NAME_SET;
1993 if (!(this_obj->state & ThreadState_Stopped))
1994 tid = thread_get_tid (this_obj);
1996 UNLOCK_THREAD (this_obj);
1998 if (name8 && tid) {
1999 MONO_PROFILER_RAISE (thread_name, ((uintptr_t)tid, name8));
2000 mono_native_thread_set_name (tid, name8);
2003 mono_thread_set_name_windows (this_obj->native_handle, name16);
2005 mono_free (0); // FIXME keep mono-publib.c in use and its functions exported
2008 void
2009 ves_icall_System_Threading_Thread_SetName_icall (MonoInternalThreadHandle thread_handle, const gunichar2* name16, gint32 name16_length, MonoError* error)
2011 long name8_length = 0;
2013 char* name8 = name16 ? g_utf16_to_utf8 (name16, name16_length, NULL, &name8_length, NULL) : NULL;
2015 mono_thread_set_name (mono_internal_thread_handle_ptr (thread_handle),
2016 name8, (gint32)name8_length, name16, MonoSetThreadNameFlag_Permanent, error);
2019 #ifndef ENABLE_NETCORE
2021 * ves_icall_System_Threading_Thread_GetPriority_internal:
2022 * @param this_obj: The MonoInternalThread on which to operate.
2024 * Gets the priority of the given thread.
2025 * @return: The priority of the given thread.
2028 ves_icall_System_Threading_Thread_GetPriority (MonoThreadObjectHandle this_obj, MonoError *error)
2030 gint32 priority;
2032 MonoInternalThread *internal = thread_handle_to_internal_ptr (this_obj);
2034 LOCK_THREAD (internal);
2035 priority = internal->priority;
2036 UNLOCK_THREAD (internal);
2038 return priority;
2040 #endif
2043 * ves_icall_System_Threading_Thread_SetPriority_internal:
2044 * @param this_obj: The MonoInternalThread on which to operate.
2045 * @param priority: The priority to set.
2047 * Sets the priority of the given thread.
2049 void
2050 ves_icall_System_Threading_Thread_SetPriority (MonoThreadObjectHandle this_obj, int priority, MonoError *error)
2052 MonoInternalThread *internal = thread_handle_to_internal_ptr (this_obj);
2054 LOCK_THREAD (internal);
2055 internal->priority = priority;
2056 if (internal->thread_info != NULL)
2057 mono_thread_internal_set_priority (internal, (MonoThreadPriority)priority);
2058 UNLOCK_THREAD (internal);
2061 /* If the array is already in the requested domain, we just return it,
2062 otherwise we return a copy in that domain. */
2063 static MonoArrayHandle
2064 byte_array_to_domain (MonoArrayHandle arr, MonoDomain *domain, MonoError *error)
2066 HANDLE_FUNCTION_ENTER ()
2068 if (MONO_HANDLE_IS_NULL (arr))
2069 return MONO_HANDLE_NEW (MonoArray, NULL);
2071 if (MONO_HANDLE_DOMAIN (arr) == domain)
2072 return arr;
2074 size_t const size = mono_array_handle_length (arr);
2076 // Capture arrays into common representation for repetitious code.
2077 // These two variables could also be an array of size 2 and
2078 // repetition implemented with a loop.
2079 struct {
2080 MonoArrayHandle handle;
2081 gpointer p;
2082 guint gchandle;
2084 source = { arr },
2085 dest = { mono_array_new_handle (domain, mono_defaults.byte_class, size, error) };
2086 goto_if_nok (error, exit);
2088 // Pin both arrays.
2089 source.p = mono_array_handle_pin_with_size (source.handle, size, 0, &source.gchandle);
2090 dest.p = mono_array_handle_pin_with_size (dest.handle, size, 0, &dest.gchandle);
2092 memmove (dest.p, source.p, size);
2093 exit:
2094 // Unpin both arrays.
2095 mono_gchandle_free_internal (source.gchandle);
2096 mono_gchandle_free_internal (dest.gchandle);
2098 HANDLE_FUNCTION_RETURN_REF (MonoArray, dest.handle)
2101 #ifndef ENABLE_NETCORE
2102 MonoArrayHandle
2103 ves_icall_System_Threading_Thread_ByteArrayToRootDomain (MonoArrayHandle arr, MonoError *error)
2105 return byte_array_to_domain (arr, mono_get_root_domain (), error);
2108 MonoArrayHandle
2109 ves_icall_System_Threading_Thread_ByteArrayToCurrentDomain (MonoArrayHandle arr, MonoError *error)
2111 return byte_array_to_domain (arr, mono_domain_get (), error);
2113 #endif
2116 * mono_thread_current:
2118 MonoThread *
2119 mono_thread_current (void)
2121 #ifdef ENABLE_NETCORE
2122 return mono_thread_internal_current ();
2123 #else
2124 MonoDomain *domain = mono_domain_get ();
2125 MonoInternalThread *internal = mono_thread_internal_current ();
2126 MonoThread **current_thread_ptr;
2128 g_assert (internal);
2129 current_thread_ptr = get_current_thread_ptr_for_domain (domain, internal);
2131 if (!*current_thread_ptr) {
2132 g_assert (domain != mono_get_root_domain ());
2133 *current_thread_ptr = create_thread_object (domain, internal);
2135 return *current_thread_ptr;
2136 #endif
2139 static MonoThreadObjectHandle
2140 mono_thread_current_handle (void)
2142 return MONO_HANDLE_NEW (MonoThreadObject, mono_thread_current ());
2145 /* Return the thread object belonging to INTERNAL in the current domain */
2146 static MonoThread *
2147 mono_thread_current_for_thread (MonoInternalThread *internal)
2149 #ifdef ENABLE_NETCORE
2150 return mono_thread_internal_current ();
2151 #else
2152 MonoDomain *domain = mono_domain_get ();
2153 MonoThread **current_thread_ptr;
2155 g_assert (internal);
2156 current_thread_ptr = get_current_thread_ptr_for_domain (domain, internal);
2158 if (!*current_thread_ptr) {
2159 g_assert (domain != mono_get_root_domain ());
2160 *current_thread_ptr = create_thread_object (domain, internal);
2162 return *current_thread_ptr;
2163 #endif
2166 MonoInternalThread*
2167 mono_thread_internal_current (void)
2169 MonoInternalThread *res = GET_CURRENT_OBJECT ();
2170 THREAD_DEBUG (g_message ("%s: returning %p", __func__, res));
2171 return res;
2174 MonoInternalThreadHandle
2175 mono_thread_internal_current_handle (void)
2177 return MONO_HANDLE_NEW (MonoInternalThread, mono_thread_internal_current ());
2180 static MonoThreadInfoWaitRet
2181 mono_join_uninterrupted (MonoThreadHandle* thread_to_join, gint32 ms, MonoError *error)
2183 MonoThreadInfoWaitRet ret;
2184 gint32 wait = ms;
2186 const gint64 start = (ms == -1) ? 0 : mono_msec_ticks ();
2187 while (TRUE) {
2188 MONO_ENTER_GC_SAFE;
2189 ret = mono_thread_info_wait_one_handle (thread_to_join, wait, TRUE);
2190 MONO_EXIT_GC_SAFE;
2192 if (ret != MONO_THREAD_INFO_WAIT_RET_ALERTED)
2193 return ret;
2195 MonoException *exc = mono_thread_execute_interruption_ptr ();
2196 if (exc) {
2197 mono_error_set_exception_instance (error, exc);
2198 return ret;
2201 if (ms == -1)
2202 continue;
2204 /* Re-calculate ms according to the time passed */
2205 const gint32 diff_ms = (gint32)(mono_msec_ticks () - start);
2206 if (diff_ms >= ms) {
2207 ret = MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
2208 return ret;
2210 wait = ms - diff_ms;
2213 return ret;
2216 MonoBoolean
2217 ves_icall_System_Threading_Thread_Join_internal (MonoThreadObjectHandle thread_handle, int ms, MonoError *error)
2219 if (mono_thread_current_check_pending_interrupt ())
2220 return FALSE;
2222 // Internal threads are pinned so shallow coop/handle.
2223 MonoInternalThread * const thread = thread_handle_to_internal_ptr (thread_handle);
2224 MonoThreadHandle *handle = thread->handle;
2225 MonoInternalThread *cur_thread = mono_thread_internal_current ();
2226 gboolean ret = FALSE;
2228 LOCK_THREAD (thread);
2230 if ((thread->state & ThreadState_Unstarted) != 0) {
2231 UNLOCK_THREAD (thread);
2233 mono_error_set_exception_thread_state (error, "Thread has not been started.");
2234 return FALSE;
2237 UNLOCK_THREAD (thread);
2239 if (ms == -1)
2240 ms = MONO_INFINITE_WAIT;
2241 THREAD_DEBUG (g_message ("%s: joining thread handle %p, %d ms", __func__, handle, ms));
2243 mono_thread_set_state (cur_thread, ThreadState_WaitSleepJoin);
2245 ret = mono_join_uninterrupted (handle, ms, error);
2247 mono_thread_clr_state (cur_thread, ThreadState_WaitSleepJoin);
2249 if (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0) {
2250 THREAD_DEBUG (g_message ("%s: join successful", __func__));
2252 mono_error_assert_ok (error);
2254 /* Wait for the thread to really exit */
2255 MonoNativeThreadId tid = thread_get_tid (thread);
2256 mono_thread_join ((gpointer)(gsize)tid);
2258 return TRUE;
2261 THREAD_DEBUG (g_message ("%s: join failed", __func__));
2263 return FALSE;
2266 #define MANAGED_WAIT_FAILED 0x7fffffff
2268 static gint32
2269 map_native_wait_result_to_managed (MonoW32HandleWaitRet val, gsize numobjects)
2271 if (val >= MONO_W32HANDLE_WAIT_RET_SUCCESS_0 && val < MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + numobjects) {
2272 return WAIT_OBJECT_0 + (val - MONO_W32HANDLE_WAIT_RET_SUCCESS_0);
2273 } else if (val >= MONO_W32HANDLE_WAIT_RET_ABANDONED_0 && val < MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + numobjects) {
2274 return WAIT_ABANDONED_0 + (val - MONO_W32HANDLE_WAIT_RET_ABANDONED_0);
2275 } else if (val == MONO_W32HANDLE_WAIT_RET_ALERTED) {
2276 return WAIT_IO_COMPLETION;
2277 } else if (val == MONO_W32HANDLE_WAIT_RET_TIMEOUT) {
2278 return WAIT_TIMEOUT;
2279 } else if (val == MONO_W32HANDLE_WAIT_RET_TOO_MANY_POSTS) {
2280 return WAIT_TOO_MANY_POSTS;
2281 } else if (val == MONO_W32HANDLE_WAIT_RET_NOT_OWNED_BY_CALLER) {
2282 return WAIT_NOT_OWNED_BY_CALLER;
2283 } else if (val == MONO_W32HANDLE_WAIT_RET_FAILED) {
2284 /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
2285 return MANAGED_WAIT_FAILED;
2286 } else {
2287 g_error ("%s: unknown val value %d", __func__, val);
2291 gint32
2292 ves_icall_System_Threading_WaitHandle_Wait_internal (gpointer *handles, gint32 numhandles, MonoBoolean waitall, gint32 timeout, MonoError *error)
2294 /* Do this WaitSleepJoin check before creating objects */
2295 if (mono_thread_current_check_pending_interrupt ())
2296 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
2298 MonoInternalThread * const thread = mono_thread_internal_current ();
2300 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
2302 gint64 start = 0;
2304 if (timeout == -1)
2305 timeout = MONO_INFINITE_WAIT;
2306 if (timeout != MONO_INFINITE_WAIT)
2307 start = mono_msec_ticks ();
2309 guint32 timeoutLeft = timeout;
2311 MonoW32HandleWaitRet ret;
2313 HANDLE_LOOP_PREPARE;
2315 for (;;) {
2317 /* mono_w32handle_wait_multiple optimizes the case for numhandles == 1 */
2318 ret = mono_w32handle_wait_multiple (handles, numhandles, waitall, timeoutLeft, TRUE, error);
2320 if (ret != MONO_W32HANDLE_WAIT_RET_ALERTED)
2321 break;
2323 SETUP_ICALL_FRAME;
2325 MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, NULL);
2327 const gboolean interrupt = mono_thread_execute_interruption (&exc);
2329 if (interrupt)
2330 mono_error_set_exception_handle (error, exc);
2332 CLEAR_ICALL_FRAME;
2334 if (interrupt)
2335 break;
2337 if (timeout != MONO_INFINITE_WAIT) {
2338 gint64 const elapsed = mono_msec_ticks () - start;
2339 if (elapsed >= timeout) {
2340 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
2341 break;
2344 timeoutLeft = timeout - elapsed;
2348 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
2350 return map_native_wait_result_to_managed (ret, numhandles);
2353 #if HAVE_API_SUPPORT_WIN32_SIGNAL_OBJECT_AND_WAIT
2354 gint32
2355 ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (gpointer toSignal, gpointer toWait, gint32 ms, MonoError *error)
2357 MonoW32HandleWaitRet ret;
2358 MonoInternalThread *thread = mono_thread_internal_current ();
2360 if (ms == -1)
2361 ms = MONO_INFINITE_WAIT;
2363 if (mono_thread_current_check_pending_interrupt ())
2364 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
2366 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
2368 ret = mono_w32handle_signal_and_wait (toSignal, toWait, ms, TRUE);
2370 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
2372 return map_native_wait_result_to_managed (ret, 1);
2375 #endif
2377 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
2379 return mono_atomic_inc_i32 (location);
2382 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
2384 #if SIZEOF_VOID_P == 4
2385 if (G_UNLIKELY ((size_t)location & 0x7)) {
2386 gint64 ret;
2387 mono_interlocked_lock ();
2388 (*location)++;
2389 ret = *location;
2390 mono_interlocked_unlock ();
2391 return ret;
2393 #endif
2394 return mono_atomic_inc_i64 (location);
2397 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
2399 return mono_atomic_dec_i32(location);
2402 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
2404 #if SIZEOF_VOID_P == 4
2405 if (G_UNLIKELY ((size_t)location & 0x7)) {
2406 gint64 ret;
2407 mono_interlocked_lock ();
2408 (*location)--;
2409 ret = *location;
2410 mono_interlocked_unlock ();
2411 return ret;
2413 #endif
2414 return mono_atomic_dec_i64 (location);
2417 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location, gint32 value)
2419 return mono_atomic_xchg_i32(location, value);
2422 void
2423 ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject *volatile*location, MonoObject *volatile*value, MonoObject *volatile*res)
2425 // Coop-equivalency here via pointers to pointers.
2426 // value and res are to managed frames, location ought to be (or member or global) but it cannot be guaranteed.
2428 // This also handles generic T case, T constrained to a class.
2430 // This is not entirely convincing due to lack of volatile in the caller, however coop also
2431 // presently breaks identity of location and would therefore never work.
2433 *res = (MonoObject*)mono_atomic_xchg_ptr ((volatile gpointer*)location, *value);
2434 mono_gc_wbarrier_generic_nostore_internal ((gpointer)location); // FIXME volatile
2437 gpointer ves_icall_System_Threading_Interlocked_Exchange_IntPtr (gpointer *location, gpointer value)
2439 return mono_atomic_xchg_ptr(location, value);
2442 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location, gfloat value)
2444 IntFloatUnion val, ret;
2446 val.fval = value;
2447 ret.ival = mono_atomic_xchg_i32((gint32 *) location, val.ival);
2449 return ret.fval;
2452 gint64
2453 ves_icall_System_Threading_Interlocked_Exchange_Long (gint64 *location, gint64 value)
2455 #if SIZEOF_VOID_P == 4
2456 if (G_UNLIKELY ((size_t)location & 0x7)) {
2457 gint64 ret;
2458 mono_interlocked_lock ();
2459 ret = *location;
2460 *location = value;
2461 mono_interlocked_unlock ();
2462 return ret;
2464 #endif
2465 return mono_atomic_xchg_i64 (location, value);
2468 gdouble
2469 ves_icall_System_Threading_Interlocked_Exchange_Double (gdouble *location, gdouble value)
2471 LongDoubleUnion val, ret;
2473 val.fval = value;
2474 ret.ival = (gint64)mono_atomic_xchg_i64((gint64 *) location, val.ival);
2476 return ret.fval;
2479 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location, gint32 value, gint32 comparand)
2481 return mono_atomic_cas_i32(location, value, comparand);
2484 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int_Success(gint32 *location, gint32 value, gint32 comparand, MonoBoolean *success)
2486 gint32 r = mono_atomic_cas_i32(location, value, comparand);
2487 *success = r == comparand;
2488 return r;
2491 void
2492 ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject *volatile*location, MonoObject *volatile*value, MonoObject *volatile*comparand, MonoObject *volatile* res)
2494 // Coop-equivalency here via pointers to pointers.
2495 // value and comparand and res are to managed frames, location ought to be (or member or global) but it cannot be guaranteed.
2497 // This also handles generic T case, T constrained to a class.
2499 // This is not entirely convincing due to lack of volatile in the caller, however coop also
2500 // presently breaks identity of location and would therefore never work.
2502 *res = (MonoObject*)mono_atomic_cas_ptr ((volatile gpointer*)location, *value, *comparand);
2503 mono_gc_wbarrier_generic_nostore_internal ((gpointer)location); // FIXME volatile
2506 gpointer ves_icall_System_Threading_Interlocked_CompareExchange_IntPtr(gpointer *location, gpointer value, gpointer comparand)
2508 return mono_atomic_cas_ptr(location, value, comparand);
2511 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location, gfloat value, gfloat comparand)
2513 IntFloatUnion val, ret, cmp;
2515 val.fval = value;
2516 cmp.fval = comparand;
2517 ret.ival = mono_atomic_cas_i32((gint32 *) location, val.ival, cmp.ival);
2519 return ret.fval;
2522 gdouble
2523 ves_icall_System_Threading_Interlocked_CompareExchange_Double (gdouble *location, gdouble value, gdouble comparand)
2525 #if SIZEOF_VOID_P == 8
2526 LongDoubleUnion val, comp, ret;
2528 val.fval = value;
2529 comp.fval = comparand;
2530 ret.ival = (gint64)mono_atomic_cas_ptr((gpointer *) location, (gpointer)val.ival, (gpointer)comp.ival);
2532 return ret.fval;
2533 #else
2534 gdouble old;
2536 mono_interlocked_lock ();
2537 old = *location;
2538 if (old == comparand)
2539 *location = value;
2540 mono_interlocked_unlock ();
2542 return old;
2543 #endif
2546 gint64
2547 ves_icall_System_Threading_Interlocked_CompareExchange_Long (gint64 *location, gint64 value, gint64 comparand)
2549 #if SIZEOF_VOID_P == 4
2550 if (G_UNLIKELY ((size_t)location & 0x7)) {
2551 gint64 old;
2552 mono_interlocked_lock ();
2553 old = *location;
2554 if (old == comparand)
2555 *location = value;
2556 mono_interlocked_unlock ();
2557 return old;
2559 #endif
2560 return mono_atomic_cas_i64 (location, value, comparand);
2563 gint32
2564 ves_icall_System_Threading_Interlocked_Add_Int (gint32 *location, gint32 value)
2566 return mono_atomic_add_i32 (location, value);
2569 gint64
2570 ves_icall_System_Threading_Interlocked_Add_Long (gint64 *location, gint64 value)
2572 #if SIZEOF_VOID_P == 4
2573 if (G_UNLIKELY ((size_t)location & 0x7)) {
2574 gint64 ret;
2575 mono_interlocked_lock ();
2576 *location += value;
2577 ret = *location;
2578 mono_interlocked_unlock ();
2579 return ret;
2581 #endif
2582 return mono_atomic_add_i64 (location, value);
2585 gint64
2586 ves_icall_System_Threading_Interlocked_Read_Long (gint64 *location)
2588 #if SIZEOF_VOID_P == 4
2589 if (G_UNLIKELY ((size_t)location & 0x7)) {
2590 gint64 ret;
2591 mono_interlocked_lock ();
2592 ret = *location;
2593 mono_interlocked_unlock ();
2594 return ret;
2596 #endif
2597 return mono_atomic_load_i64 (location);
2600 void
2601 ves_icall_System_Threading_Interlocked_MemoryBarrierProcessWide (void)
2603 mono_memory_barrier_process_wide ();
2606 void
2607 ves_icall_System_Threading_Thread_MemoryBarrier (void)
2609 mono_memory_barrier ();
2612 void
2613 ves_icall_System_Threading_Thread_ClrState (MonoInternalThreadHandle this_obj, guint32 state, MonoError *error)
2615 // InternalThreads are always pinned, so shallowly coop-handleize.
2616 mono_thread_clr_state (mono_internal_thread_handle_ptr (this_obj), (MonoThreadState)state);
2619 void
2620 ves_icall_System_Threading_Thread_SetState (MonoInternalThreadHandle thread_handle, guint32 state, MonoError *error)
2622 // InternalThreads are always pinned, so shallowly coop-handleize.
2623 mono_thread_set_state (mono_internal_thread_handle_ptr (thread_handle), (MonoThreadState)state);
2626 guint32
2627 ves_icall_System_Threading_Thread_GetState (MonoInternalThreadHandle thread_handle, MonoError *error)
2629 // InternalThreads are always pinned, so shallowly coop-handleize.
2630 MonoInternalThread *this_obj = mono_internal_thread_handle_ptr (thread_handle);
2632 guint32 state;
2634 LOCK_THREAD (this_obj);
2636 state = this_obj->state;
2638 UNLOCK_THREAD (this_obj);
2640 return state;
2643 void
2644 ves_icall_System_Threading_Thread_Interrupt_internal (MonoThreadObjectHandle thread_handle, MonoError *error)
2646 // Internal threads are pinned so shallow coop/handle.
2647 MonoInternalThread * const thread = thread_handle_to_internal_ptr (thread_handle);
2648 MonoInternalThread * const current = mono_thread_internal_current ();
2650 LOCK_THREAD (thread);
2652 thread->thread_interrupt_requested = TRUE;
2653 gboolean const throw_ = current != thread && (thread->state & ThreadState_WaitSleepJoin);
2655 UNLOCK_THREAD (thread);
2657 if (throw_)
2658 async_abort_internal (thread, FALSE);
2662 * mono_thread_current_check_pending_interrupt:
2663 * Checks if there's a interruption request and set the pending exception if so.
2664 * \returns true if a pending exception was set
2666 gboolean
2667 mono_thread_current_check_pending_interrupt (void)
2669 MonoInternalThread *thread = mono_thread_internal_current ();
2670 gboolean throw_ = FALSE;
2672 LOCK_THREAD (thread);
2674 if (thread->thread_interrupt_requested) {
2675 throw_ = TRUE;
2676 thread->thread_interrupt_requested = FALSE;
2679 UNLOCK_THREAD (thread);
2681 if (throw_) {
2682 ERROR_DECL (error);
2683 mono_error_set_thread_interrupted (error);
2684 mono_error_set_pending_exception (error);
2686 return throw_;
2689 static gboolean
2690 request_thread_abort (MonoInternalThread *thread, MonoObjectHandle *state, gboolean appdomain_unload)
2691 // state is a pointer to a handle in order to be optional,
2692 // and be passed unspecified from functions not using handles.
2693 // When raw pointers is gone, it need not be a pointer,
2694 // though this would still work efficiently.
2696 LOCK_THREAD (thread);
2698 /* With self abort we always throw a new exception */
2699 if (thread == mono_thread_internal_current ())
2700 thread->abort_exc = NULL;
2702 if (thread->state & (ThreadState_AbortRequested | ThreadState_Stopped))
2704 UNLOCK_THREAD (thread);
2705 return FALSE;
2708 if ((thread->state & ThreadState_Unstarted) != 0) {
2709 thread->state |= ThreadState_Aborted;
2710 UNLOCK_THREAD (thread);
2711 return FALSE;
2714 thread->state |= ThreadState_AbortRequested;
2715 if (appdomain_unload)
2716 thread->flags |= MONO_THREAD_FLAG_APPDOMAIN_ABORT;
2717 else
2718 thread->flags &= ~MONO_THREAD_FLAG_APPDOMAIN_ABORT;
2720 mono_gchandle_free_internal (thread->abort_state_handle);
2721 thread->abort_state_handle = 0;
2724 if (state && !MONO_HANDLE_IS_NULL (*state)) {
2725 thread->abort_state_handle = mono_gchandle_from_handle (*state, FALSE);
2726 g_assert (thread->abort_state_handle);
2729 thread->abort_exc = NULL;
2731 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));
2733 /* During shutdown, we can't wait for other threads */
2734 if (!shutting_down)
2735 /* Make sure the thread is awake */
2736 mono_thread_resume (thread);
2738 UNLOCK_THREAD (thread);
2739 return TRUE;
2742 #ifndef ENABLE_NETCORE
2743 void
2744 ves_icall_System_Threading_Thread_Abort (MonoInternalThreadHandle thread_handle, MonoObjectHandle state, MonoError *error)
2746 // InternalThreads are always pinned, so shallowly coop-handleize.
2747 MonoInternalThread * const thread = mono_internal_thread_handle_ptr (thread_handle);
2748 gboolean is_self = thread == mono_thread_internal_current ();
2750 /* For self aborts we always process the abort */
2751 if (!request_thread_abort (thread, &state, FALSE) && !is_self)
2752 return;
2754 if (is_self) {
2755 self_abort_internal (error);
2756 } else {
2757 async_abort_internal (thread, TRUE);
2760 #endif
2763 * mono_thread_internal_abort:
2764 * Request thread \p thread to be aborted.
2765 * \p thread MUST NOT be the current thread.
2767 void
2768 mono_thread_internal_abort (MonoInternalThread *thread, gboolean appdomain_unload)
2770 g_assert (thread != mono_thread_internal_current ());
2772 if (!request_thread_abort (thread, NULL, appdomain_unload))
2773 return;
2774 async_abort_internal (thread, TRUE);
2777 #ifndef ENABLE_NETCORE
2778 void
2779 ves_icall_System_Threading_Thread_ResetAbort (MonoThreadObjectHandle this_obj, MonoError *error)
2781 MonoInternalThread *thread = mono_thread_internal_current ();
2782 gboolean was_aborting, is_domain_abort;
2784 LOCK_THREAD (thread);
2785 was_aborting = thread->state & ThreadState_AbortRequested;
2786 is_domain_abort = thread->flags & MONO_THREAD_FLAG_APPDOMAIN_ABORT;
2788 if (was_aborting && !is_domain_abort)
2789 thread->state &= ~ThreadState_AbortRequested;
2790 UNLOCK_THREAD (thread);
2792 if (!was_aborting) {
2793 mono_error_set_exception_thread_state (error, "Unable to reset abort because no abort was requested");
2794 return;
2795 } else if (is_domain_abort) {
2796 /* Silently ignore abort resets in unloading appdomains */
2797 return;
2800 mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
2801 thread->abort_exc = NULL;
2802 mono_gchandle_free_internal (thread->abort_state_handle);
2803 /* This is actually not necessary - the handle
2804 only counts if the exception is set */
2805 thread->abort_state_handle = 0;
2807 #endif
2809 void
2810 mono_thread_internal_reset_abort (MonoInternalThread *thread)
2812 LOCK_THREAD (thread);
2814 thread->state &= ~ThreadState_AbortRequested;
2816 if (thread->abort_exc) {
2817 mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
2818 thread->abort_exc = NULL;
2819 mono_gchandle_free_internal (thread->abort_state_handle);
2820 /* This is actually not necessary - the handle
2821 only counts if the exception is set */
2822 thread->abort_state_handle = 0;
2825 UNLOCK_THREAD (thread);
2828 #ifndef ENABLE_NETCORE
2829 MonoObjectHandle
2830 ves_icall_System_Threading_Thread_GetAbortExceptionState (MonoThreadObjectHandle this_obj, MonoError *error)
2832 MonoInternalThread *thread = thread_handle_to_internal_ptr (this_obj);
2834 if (!thread->abort_state_handle)
2835 return NULL_HANDLE; // No state. No error.
2837 // Convert gc handle to coop handle.
2838 MonoObjectHandle state = mono_gchandle_get_target_handle (thread->abort_state_handle);
2839 g_assert (MONO_HANDLE_BOOL (state));
2841 MonoDomain *domain = mono_domain_get ();
2842 if (MONO_HANDLE_DOMAIN (state) == domain)
2843 return state; // No need to cross domain, return state directly.
2845 // Attempt move state cross-domain.
2846 MonoObjectHandle deserialized = mono_object_xdomain_representation (state, domain, error);
2848 // If deserialized is null, there must be an error, and vice versa.
2849 g_assert (is_ok (error) == MONO_HANDLE_BOOL (deserialized));
2851 if (MONO_HANDLE_BOOL (deserialized))
2852 return deserialized; // Cross-domain serialization succeeded. Return it.
2854 // Wrap error in InvalidOperationException.
2855 ERROR_DECL (error_creating_exception);
2856 MonoExceptionHandle invalid_op_exc = mono_exception_new_invalid_operation (
2857 "Thread.ExceptionState cannot access an ExceptionState from a different AppDomain", error_creating_exception);
2858 mono_error_assert_ok (error_creating_exception);
2859 g_assert (!is_ok (error) && 1);
2860 MONO_HANDLE_SET (invalid_op_exc, inner_ex, mono_error_convert_to_exception_handle (error));
2861 error_init_reuse (error);
2862 mono_error_set_exception_handle (error, invalid_op_exc);
2863 g_assert (!is_ok (error) && 2);
2865 // There is state, but we failed to return it.
2866 return NULL_HANDLE;
2868 #endif
2870 static gboolean
2871 mono_thread_suspend (MonoInternalThread *thread)
2873 LOCK_THREAD (thread);
2875 if (thread->state & (ThreadState_Unstarted | ThreadState_Aborted | ThreadState_Stopped))
2877 UNLOCK_THREAD (thread);
2878 return FALSE;
2881 if (thread->state & (ThreadState_Suspended | ThreadState_SuspendRequested | ThreadState_AbortRequested))
2883 UNLOCK_THREAD (thread);
2884 return TRUE;
2887 thread->state |= ThreadState_SuspendRequested;
2888 MONO_ENTER_GC_SAFE;
2889 mono_os_event_reset (thread->suspended);
2890 MONO_EXIT_GC_SAFE;
2892 if (thread == mono_thread_internal_current ()) {
2893 /* calls UNLOCK_THREAD (thread) */
2894 self_suspend_internal ();
2895 } else {
2896 /* calls UNLOCK_THREAD (thread) */
2897 async_suspend_internal (thread, FALSE);
2900 return TRUE;
2903 #ifndef ENABLE_NETCORE
2904 void
2905 ves_icall_System_Threading_Thread_Suspend (MonoThreadObjectHandle this_obj, MonoError *error)
2907 if (!mono_thread_suspend (thread_handle_to_internal_ptr (this_obj)))
2908 mono_error_set_exception_thread_not_started_or_dead (error);
2911 #endif
2913 /* LOCKING: LOCK_THREAD(thread) must be held */
2914 static gboolean
2915 mono_thread_resume (MonoInternalThread *thread)
2917 if ((thread->state & ThreadState_SuspendRequested) != 0) {
2918 // g_async_safe_printf ("RESUME (1) thread %p\n", thread_get_tid (thread));
2919 thread->state &= ~ThreadState_SuspendRequested;
2920 MONO_ENTER_GC_SAFE;
2921 mono_os_event_set (thread->suspended);
2922 MONO_EXIT_GC_SAFE;
2923 return TRUE;
2926 if ((thread->state & ThreadState_Suspended) == 0 ||
2927 (thread->state & ThreadState_Unstarted) != 0 ||
2928 (thread->state & ThreadState_Aborted) != 0 ||
2929 (thread->state & ThreadState_Stopped) != 0)
2931 // g_async_safe_printf ("RESUME (2) thread %p\n", thread_get_tid (thread));
2932 return FALSE;
2935 // g_async_safe_printf ("RESUME (3) thread %p\n", thread_get_tid (thread));
2937 MONO_ENTER_GC_SAFE;
2938 mono_os_event_set (thread->suspended);
2939 MONO_EXIT_GC_SAFE;
2941 if (!thread->self_suspended) {
2942 UNLOCK_THREAD (thread);
2944 /* Awake the thread */
2945 if (!mono_thread_info_resume (thread_get_tid (thread)))
2946 return FALSE;
2948 LOCK_THREAD (thread);
2951 thread->state &= ~ThreadState_Suspended;
2953 return TRUE;
2956 #ifndef ENABLE_NETCORE
2957 void
2958 ves_icall_System_Threading_Thread_Resume (MonoThreadObjectHandle thread_handle, MonoError *error)
2960 // Internal threads are pinned so shallow coop/handle.
2961 MonoInternalThread * const internal_thread = thread_handle_to_internal_ptr (thread_handle);
2962 gboolean exception = FALSE;
2964 if (!internal_thread) {
2965 exception = TRUE;
2966 } else {
2967 LOCK_THREAD (internal_thread);
2968 if (!mono_thread_resume (internal_thread))
2969 exception = TRUE;
2970 UNLOCK_THREAD (internal_thread);
2973 if (exception)
2974 mono_error_set_exception_thread_not_started_or_dead (error);
2976 #endif
2978 gboolean
2979 mono_threads_is_critical_method (MonoMethod *method)
2981 switch (method->wrapper_type) {
2982 case MONO_WRAPPER_RUNTIME_INVOKE:
2983 case MONO_WRAPPER_XDOMAIN_INVOKE:
2984 case MONO_WRAPPER_XDOMAIN_DISPATCH:
2985 return TRUE;
2987 return FALSE;
2990 static gboolean
2991 find_wrapper (MonoMethod *m, gint no, gint ilo, gboolean managed, gpointer data)
2993 if (managed)
2994 return TRUE;
2996 if (mono_threads_is_critical_method (m)) {
2997 *((gboolean*)data) = TRUE;
2998 return TRUE;
3000 return FALSE;
3003 static gboolean
3004 is_running_protected_wrapper (void)
3006 gboolean found = FALSE;
3007 mono_stack_walk (find_wrapper, &found);
3008 return found;
3012 * mono_thread_stop:
3014 void
3015 mono_thread_stop (MonoThread *thread)
3017 MonoInternalThread *internal = thread->internal_thread;
3019 if (!request_thread_abort (internal, NULL, FALSE))
3020 return;
3022 if (internal == mono_thread_internal_current ()) {
3023 ERROR_DECL (error);
3024 self_abort_internal (error);
3026 This function is part of the embeding API and has no way to return the exception
3027 to be thrown. So what we do is keep the old behavior and raise the exception.
3029 mono_error_raise_exception_deprecated (error); /* OK to throw, see note */
3030 } else {
3031 async_abort_internal (internal, TRUE);
3035 gint8
3036 ves_icall_System_Threading_Thread_VolatileRead1 (void *ptr)
3038 gint8 tmp = *(volatile gint8 *)ptr;
3039 mono_memory_barrier ();
3040 return tmp;
3043 gint16
3044 ves_icall_System_Threading_Thread_VolatileRead2 (void *ptr)
3046 gint16 tmp = *(volatile gint16 *)ptr;
3047 mono_memory_barrier ();
3048 return tmp;
3051 gint32
3052 ves_icall_System_Threading_Thread_VolatileRead4 (void *ptr)
3054 gint32 tmp = *(volatile gint32 *)ptr;
3055 mono_memory_barrier ();
3056 return tmp;
3059 gint64
3060 ves_icall_System_Threading_Thread_VolatileRead8 (void *ptr)
3062 gint64 tmp = *(volatile gint64 *)ptr;
3063 mono_memory_barrier ();
3064 return tmp;
3067 void *
3068 ves_icall_System_Threading_Thread_VolatileReadIntPtr (void *ptr)
3070 volatile void *tmp = *(volatile void **)ptr;
3071 mono_memory_barrier ();
3072 return (void *) tmp;
3075 void *
3076 ves_icall_System_Threading_Thread_VolatileReadObject (void *ptr)
3078 volatile MonoObject *tmp = *(volatile MonoObject **)ptr;
3079 mono_memory_barrier ();
3080 return (MonoObject *) tmp;
3083 double
3084 ves_icall_System_Threading_Thread_VolatileReadDouble (void *ptr)
3086 double tmp = *(volatile double *)ptr;
3087 mono_memory_barrier ();
3088 return tmp;
3091 float
3092 ves_icall_System_Threading_Thread_VolatileReadFloat (void *ptr)
3094 float tmp = *(volatile float *)ptr;
3095 mono_memory_barrier ();
3096 return tmp;
3099 gint64
3100 ves_icall_System_Threading_Volatile_Read8 (void *ptr)
3102 #if SIZEOF_VOID_P == 4
3103 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
3104 gint64 val;
3105 mono_interlocked_lock ();
3106 val = *(gint64*)ptr;
3107 mono_interlocked_unlock ();
3108 return val;
3110 #endif
3111 return mono_atomic_load_i64 ((volatile gint64 *)ptr);
3114 guint64
3115 ves_icall_System_Threading_Volatile_ReadU8 (void *ptr)
3117 #if SIZEOF_VOID_P == 4
3118 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
3119 guint64 val;
3120 mono_interlocked_lock ();
3121 val = *(guint64*)ptr;
3122 mono_interlocked_unlock ();
3123 return val;
3125 #endif
3126 return (guint64)mono_atomic_load_i64 ((volatile gint64 *)ptr);
3129 double
3130 ves_icall_System_Threading_Volatile_ReadDouble (void *ptr)
3132 LongDoubleUnion u;
3134 #if SIZEOF_VOID_P == 4
3135 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
3136 double val;
3137 mono_interlocked_lock ();
3138 val = *(double*)ptr;
3139 mono_interlocked_unlock ();
3140 return val;
3142 #endif
3144 u.ival = mono_atomic_load_i64 ((volatile gint64 *)ptr);
3146 return u.fval;
3149 void
3150 ves_icall_System_Threading_Thread_VolatileWrite1 (void *ptr, gint8 value)
3152 mono_memory_barrier ();
3153 *(volatile gint8 *)ptr = value;
3156 void
3157 ves_icall_System_Threading_Thread_VolatileWrite2 (void *ptr, gint16 value)
3159 mono_memory_barrier ();
3160 *(volatile gint16 *)ptr = value;
3163 void
3164 ves_icall_System_Threading_Thread_VolatileWrite4 (void *ptr, gint32 value)
3166 mono_memory_barrier ();
3167 *(volatile gint32 *)ptr = value;
3170 void
3171 ves_icall_System_Threading_Thread_VolatileWrite8 (void *ptr, gint64 value)
3173 mono_memory_barrier ();
3174 *(volatile gint64 *)ptr = value;
3177 void
3178 ves_icall_System_Threading_Thread_VolatileWriteIntPtr (void *ptr, void *value)
3180 mono_memory_barrier ();
3181 *(volatile void **)ptr = value;
3184 void
3185 ves_icall_System_Threading_Thread_VolatileWriteObject (void *ptr, MonoObject *value)
3187 mono_memory_barrier ();
3188 mono_gc_wbarrier_generic_store_internal (ptr, value);
3191 void
3192 ves_icall_System_Threading_Thread_VolatileWriteDouble (void *ptr, double value)
3194 mono_memory_barrier ();
3195 *(volatile double *)ptr = value;
3198 void
3199 ves_icall_System_Threading_Thread_VolatileWriteFloat (void *ptr, float value)
3201 mono_memory_barrier ();
3202 *(volatile float *)ptr = value;
3205 void
3206 ves_icall_System_Threading_Volatile_Write8 (void *ptr, gint64 value)
3208 #if SIZEOF_VOID_P == 4
3209 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
3210 mono_interlocked_lock ();
3211 *(gint64*)ptr = value;
3212 mono_interlocked_unlock ();
3213 return;
3215 #endif
3217 mono_atomic_store_i64 ((volatile gint64 *)ptr, value);
3220 void
3221 ves_icall_System_Threading_Volatile_WriteU8 (void *ptr, guint64 value)
3223 #if SIZEOF_VOID_P == 4
3224 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
3225 mono_interlocked_lock ();
3226 *(guint64*)ptr = value;
3227 mono_interlocked_unlock ();
3228 return;
3230 #endif
3232 mono_atomic_store_i64 ((volatile gint64 *)ptr, (gint64)value);
3235 void
3236 ves_icall_System_Threading_Volatile_WriteDouble (void *ptr, double value)
3238 LongDoubleUnion u;
3240 #if SIZEOF_VOID_P == 4
3241 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
3242 mono_interlocked_lock ();
3243 *(double*)ptr = value;
3244 mono_interlocked_unlock ();
3245 return;
3247 #endif
3249 u.fval = value;
3251 mono_atomic_store_i64 ((volatile gint64 *)ptr, u.ival);
3254 static void
3255 free_context (void *user_data)
3257 ContextStaticData *data = (ContextStaticData*)user_data;
3259 mono_threads_lock ();
3262 * There is no guarantee that, by the point this reference queue callback
3263 * has been invoked, the GC handle associated with the object will fail to
3264 * resolve as one might expect. So if we don't free and remove the GC
3265 * handle here, free_context_static_data_helper () could end up resolving
3266 * a GC handle to an actually-dead context which would contain a pointer
3267 * to an already-freed static data segment, resulting in a crash when
3268 * accessing it.
3270 g_hash_table_remove (contexts, GUINT_TO_POINTER (data->gc_handle));
3272 mono_threads_unlock ();
3274 mono_gchandle_free_internal (data->gc_handle);
3275 mono_free_static_data (data->static_data);
3276 g_free (data);
3279 void
3280 mono_threads_register_app_context (MonoAppContextHandle ctx, MonoError *error)
3282 error_init (error);
3283 mono_threads_lock ();
3285 //g_print ("Registering context %d in domain %d\n", ctx->context_id, ctx->domain_id);
3287 if (!contexts)
3288 contexts = g_hash_table_new (NULL, NULL);
3290 if (!context_queue)
3291 context_queue = mono_gc_reference_queue_new_internal (free_context);
3293 gpointer gch = GUINT_TO_POINTER (mono_gchandle_new_weakref_from_handle (MONO_HANDLE_CAST (MonoObject, ctx)));
3294 g_hash_table_insert (contexts, gch, gch);
3297 * We use this intermediate structure to contain a duplicate pointer to
3298 * the static data because we can't rely on being able to resolve the GC
3299 * handle in the reference queue callback.
3301 ContextStaticData *data = g_new0 (ContextStaticData, 1);
3302 data->gc_handle = GPOINTER_TO_UINT (gch);
3303 MONO_HANDLE_SETVAL (ctx, data, ContextStaticData*, data);
3305 context_adjust_static_data (ctx);
3306 mono_gc_reference_queue_add_handle (context_queue, ctx, data);
3308 mono_threads_unlock ();
3310 MONO_PROFILER_RAISE (context_loaded, (MONO_HANDLE_RAW (ctx)));
3313 #ifndef ENABLE_NETCORE
3314 void
3315 ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppContextHandle ctx, MonoError *error)
3317 mono_threads_register_app_context (ctx, error);
3319 #endif
3321 void
3322 mono_threads_release_app_context (MonoAppContext* ctx, MonoError *error)
3325 * NOTE: Since finalizers are unreliable for the purposes of ensuring
3326 * cleanup in exceptional circumstances, we don't actually do any
3327 * cleanup work here. We instead do this via a reference queue.
3330 //g_print ("Releasing context %d in domain %d\n", ctx->context_id, ctx->domain_id);
3332 MONO_PROFILER_RAISE (context_unloaded, (ctx));
3335 #ifndef ENABLE_NETCORE
3336 void
3337 ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContextHandle ctx, MonoError *error)
3339 mono_threads_release_app_context (MONO_HANDLE_RAW (ctx), error); /* FIXME use handles in mono_threads_release_app_context */
3341 #endif
3343 void mono_thread_init (MonoThreadStartCB start_cb,
3344 MonoThreadAttachCB attach_cb)
3346 mono_coop_mutex_init_recursive (&threads_mutex);
3348 #if SIZEOF_VOID_P == 4
3349 mono_os_mutex_init (&interlocked_mutex);
3350 #endif
3351 mono_coop_mutex_init_recursive(&joinable_threads_mutex);
3353 mono_os_event_init (&background_change_event, FALSE);
3355 mono_coop_cond_init (&pending_native_thread_join_calls_event);
3356 mono_coop_cond_init (&zero_pending_joinable_thread_event);
3358 mono_init_static_data_info (&thread_static_info);
3359 mono_init_static_data_info (&context_static_info);
3361 mono_thread_start_cb = start_cb;
3362 mono_thread_attach_cb = attach_cb;
3366 static gpointer
3367 thread_attach (MonoThreadInfo *info)
3369 return mono_gc_thread_attach (info);
3372 static void
3373 thread_detach (MonoThreadInfo *info)
3375 MonoInternalThread *internal;
3376 guint32 gchandle;
3378 /* If a delegate is passed to native code and invoked on a thread we dont
3379 * know about, marshal will register it with mono_threads_attach_coop, but
3380 * we have no way of knowing when that thread goes away. SGen has a TSD
3381 * so we assume that if the domain is still registered, we can detach
3382 * the thread */
3384 g_assert (info);
3385 g_assert (mono_thread_info_is_current (info));
3387 if (!mono_thread_info_try_get_internal_thread_gchandle (info, &gchandle))
3388 return;
3390 internal = (MonoInternalThread*) mono_gchandle_get_target_internal (gchandle);
3391 g_assert (internal);
3393 mono_thread_detach_internal (internal);
3395 mono_gc_thread_detach (info);
3398 static void
3399 thread_detach_with_lock (MonoThreadInfo *info)
3401 mono_gc_thread_detach_with_lock (info);
3404 static gboolean
3405 thread_in_critical_region (MonoThreadInfo *info)
3407 return mono_gc_thread_in_critical_region (info);
3410 static gboolean
3411 ip_in_critical_region (MonoDomain *domain, gpointer ip)
3413 MonoJitInfo *ji;
3414 MonoMethod *method;
3417 * We pass false for 'try_aot' so this becomes async safe.
3418 * It won't find aot methods whose jit info is not yet loaded,
3419 * so we preload their jit info in the JIT.
3421 ji = mono_jit_info_table_find_internal (domain, ip, FALSE, FALSE);
3422 if (!ji)
3423 return FALSE;
3425 method = mono_jit_info_get_method (ji);
3426 g_assert (method);
3428 return mono_gc_is_critical_method (method);
3431 static void
3432 thread_flags_changing (MonoThreadInfoFlags old, MonoThreadInfoFlags new_)
3434 mono_gc_skip_thread_changing (!!(new_ & MONO_THREAD_INFO_FLAGS_NO_GC));
3437 static void
3438 thread_flags_changed (MonoThreadInfoFlags old, MonoThreadInfoFlags new_)
3440 mono_gc_skip_thread_changed (!!(new_ & MONO_THREAD_INFO_FLAGS_NO_GC));
3443 void
3444 mono_thread_callbacks_init (void)
3446 MonoThreadInfoCallbacks cb;
3448 memset (&cb, 0, sizeof(cb));
3449 cb.thread_attach = thread_attach;
3450 cb.thread_detach = thread_detach;
3451 cb.thread_detach_with_lock = thread_detach_with_lock;
3452 cb.ip_in_critical_region = ip_in_critical_region;
3453 cb.thread_in_critical_region = thread_in_critical_region;
3454 cb.thread_flags_changing = thread_flags_changing;
3455 cb.thread_flags_changed = thread_flags_changed;
3456 mono_thread_info_callbacks_init (&cb);
3460 * mono_thread_cleanup:
3462 void
3463 mono_thread_cleanup (void)
3465 /* Wait for pending threads to park on joinable threads list */
3466 /* NOTE, waiting on this should be extremely rare and will only happen */
3467 /* under certain specific conditions. */
3468 gboolean wait_result = threads_wait_pending_joinable_threads (2000);
3469 if (!wait_result)
3470 g_warning ("Waiting on threads to park on joinable thread list timed out.");
3472 mono_threads_join_threads ();
3474 #if !defined(HOST_WIN32)
3475 /* The main thread must abandon any held mutexes (particularly
3476 * important for named mutexes as they are shared across
3477 * processes, see bug 74680.) This will happen when the
3478 * thread exits, but if it's not running in a subthread it
3479 * won't exit in time.
3481 if (!mono_runtime_get_no_exec ())
3482 mono_w32mutex_abandon (mono_thread_internal_current ());
3483 #endif
3485 #if 0
3486 /* This stuff needs more testing, it seems one of these
3487 * critical sections can be locked when mono_thread_cleanup is
3488 * called.
3490 mono_coop_mutex_destroy (&threads_mutex);
3491 mono_os_mutex_destroy (&interlocked_mutex);
3492 mono_os_mutex_destroy (&delayed_free_table_mutex);
3493 mono_os_mutex_destroy (&small_id_mutex);
3494 mono_coop_cond_destroy (&zero_pending_joinable_thread_event);
3495 mono_coop_cond_destroy (&pending_native_thread_join_calls_event);
3496 mono_os_event_destroy (&background_change_event);
3497 #endif
3500 void
3501 mono_threads_install_cleanup (MonoThreadCleanupFunc func)
3503 mono_thread_cleanup_fn = func;
3507 * mono_thread_set_manage_callback:
3509 void
3510 mono_thread_set_manage_callback (MonoThread *thread, MonoThreadManageCallback func)
3512 thread->internal_thread->manage_callback = func;
3515 G_GNUC_UNUSED
3516 static void print_tids (gpointer key, gpointer value, gpointer user)
3518 /* GPOINTER_TO_UINT breaks horribly if sizeof(void *) >
3519 * sizeof(uint) and a cast to uint would overflow
3521 /* Older versions of glib don't have G_GSIZE_FORMAT, so just
3522 * print this as a pointer.
3524 g_message ("Waiting for: %p", key);
3527 struct wait_data
3529 MonoThreadHandle *handles[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
3530 MonoInternalThread *threads[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
3531 guint32 num;
3534 static void
3535 wait_for_tids (struct wait_data *wait, guint32 timeout, gboolean check_state_change)
3537 guint32 i;
3538 MonoThreadInfoWaitRet ret;
3540 THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num));
3542 /* Add the thread state change event, so it wakes
3543 * up if a thread changes to background mode. */
3545 MONO_ENTER_GC_SAFE;
3546 if (check_state_change)
3547 ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, &background_change_event, FALSE, timeout, TRUE);
3548 else
3549 ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, NULL, TRUE, timeout, TRUE);
3550 MONO_EXIT_GC_SAFE;
3552 if (ret == MONO_THREAD_INFO_WAIT_RET_FAILED) {
3553 /* See the comment in build_wait_tids() */
3554 THREAD_DEBUG (g_message ("%s: Wait failed", __func__));
3555 return;
3558 for( i = 0; i < wait->num; i++)
3559 mono_threads_close_thread_handle (wait->handles [i]);
3561 if (ret >= MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 && ret < (MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + wait->num)) {
3562 MonoInternalThread *internal;
3564 internal = wait->threads [ret - MONO_THREAD_INFO_WAIT_RET_SUCCESS_0];
3566 mono_threads_lock ();
3567 if (mono_g_hash_table_lookup (threads, (gpointer) internal->tid) == internal)
3568 g_error ("%s: failed to call mono_thread_detach_internal on thread %p, InternalThread: %p", __func__, internal->tid, internal);
3569 mono_threads_unlock ();
3573 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
3575 struct wait_data *wait=(struct wait_data *)user;
3577 if(wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS - 1) {
3578 MonoInternalThread *thread=(MonoInternalThread *)value;
3580 /* Ignore background threads, we abort them later */
3581 /* Do not lock here since it is not needed and the caller holds threads_lock */
3582 if (thread->state & ThreadState_Background) {
3583 THREAD_DEBUG (g_message ("%s: ignoring background thread %" G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3584 return; /* just leave, ignore */
3587 if (mono_gc_is_finalizer_internal_thread (thread)) {
3588 THREAD_DEBUG (g_message ("%s: ignoring finalizer thread %" G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3589 return;
3592 if (thread == mono_thread_internal_current ()) {
3593 THREAD_DEBUG (g_message ("%s: ignoring current thread %" G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3594 return;
3597 if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread)) {
3598 THREAD_DEBUG (g_message ("%s: ignoring main thread %" G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3599 return;
3602 if (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE) {
3603 THREAD_DEBUG (g_message ("%s: ignoring thread %" G_GSIZE_FORMAT "with DONT_MANAGE flag set.", __func__, (gsize)thread->tid));
3604 return;
3607 THREAD_DEBUG (g_message ("%s: Invoking mono_thread_manage callback on thread %p", __func__, thread));
3608 if ((thread->manage_callback == NULL) || (thread->manage_callback (thread->root_domain_thread) == TRUE)) {
3609 wait->handles[wait->num]=mono_threads_open_thread_handle (thread->handle);
3610 wait->threads[wait->num]=thread;
3611 wait->num++;
3613 THREAD_DEBUG (g_message ("%s: adding thread %" G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3614 } else {
3615 THREAD_DEBUG (g_message ("%s: ignoring (because of callback) thread %" G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3619 } else {
3620 /* Just ignore the rest, we can't do anything with
3621 * them yet
3626 static void
3627 abort_threads (gpointer key, gpointer value, gpointer user)
3629 struct wait_data *wait=(struct wait_data *)user;
3630 MonoNativeThreadId self = mono_native_thread_id_get ();
3631 MonoInternalThread *thread = (MonoInternalThread *)value;
3633 if (wait->num >= MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS)
3634 return;
3636 if (mono_native_thread_id_equals (thread_get_tid (thread), self))
3637 return;
3638 if (mono_gc_is_finalizer_internal_thread (thread))
3639 return;
3641 if ((thread->flags & MONO_THREAD_FLAG_DONT_MANAGE))
3642 return;
3644 wait->handles[wait->num] = mono_threads_open_thread_handle (thread->handle);
3645 wait->threads[wait->num] = thread;
3646 wait->num++;
3648 THREAD_DEBUG (g_print ("%s: Aborting id: %" G_GSIZE_FORMAT "\n", __func__, (gsize)thread->tid));
3649 mono_thread_internal_abort (thread, FALSE);
3652 /**
3653 * mono_threads_set_shutting_down:
3655 * Is called by a thread that wants to shut down Mono. If the runtime is already
3656 * shutting down, the calling thread is suspended/stopped, and this function never
3657 * returns.
3659 void
3660 mono_threads_set_shutting_down (void)
3662 MonoInternalThread *current_thread = mono_thread_internal_current ();
3664 mono_threads_lock ();
3666 if (shutting_down) {
3667 mono_threads_unlock ();
3669 /* Make sure we're properly suspended/stopped */
3671 LOCK_THREAD (current_thread);
3673 if (current_thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
3674 UNLOCK_THREAD (current_thread);
3675 mono_thread_execute_interruption_void ();
3676 } else {
3677 UNLOCK_THREAD (current_thread);
3680 /*since we're killing the thread, detach it.*/
3681 mono_thread_detach_internal (current_thread);
3683 /* Wake up other threads potentially waiting for us */
3684 mono_thread_info_exit (0);
3685 } else {
3686 shutting_down = TRUE;
3688 /* Not really a background state change, but this will
3689 * interrupt the main thread if it is waiting for all
3690 * the other threads.
3692 MONO_ENTER_GC_SAFE;
3693 mono_os_event_set (&background_change_event);
3694 MONO_EXIT_GC_SAFE;
3696 mono_threads_unlock ();
3701 * mono_thread_manage_internal:
3703 void
3704 mono_thread_manage_internal (void)
3706 MONO_REQ_GC_UNSAFE_MODE;
3708 struct wait_data wait_data;
3709 struct wait_data *wait = &wait_data;
3711 memset (wait, 0, sizeof (struct wait_data));
3712 /* join each thread that's still running */
3713 THREAD_DEBUG (g_message ("%s: Joining each running thread...", __func__));
3715 mono_threads_lock ();
3716 if(threads==NULL) {
3717 THREAD_DEBUG (g_message("%s: No threads", __func__));
3718 mono_threads_unlock ();
3719 return;
3722 mono_threads_unlock ();
3724 do {
3725 mono_threads_lock ();
3726 if (shutting_down) {
3727 /* somebody else is shutting down */
3728 mono_threads_unlock ();
3729 break;
3731 THREAD_DEBUG (g_message ("%s: There are %d threads to join", __func__, mono_g_hash_table_size (threads));
3732 mono_g_hash_table_foreach (threads, print_tids, NULL));
3734 MONO_ENTER_GC_SAFE;
3735 mono_os_event_reset (&background_change_event);
3736 MONO_EXIT_GC_SAFE;
3737 wait->num=0;
3738 /* We must zero all InternalThread pointers to avoid making the GC unhappy. */
3739 memset (wait->threads, 0, sizeof (wait->threads));
3740 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
3741 mono_threads_unlock ();
3742 if (wait->num > 0)
3743 /* Something to wait for */
3744 wait_for_tids (wait, MONO_INFINITE_WAIT, TRUE);
3745 THREAD_DEBUG (g_message ("%s: I have %d threads after waiting.", __func__, wait->num));
3746 } while(wait->num>0);
3748 /* Mono is shutting down, so just wait for the end */
3749 if (!mono_runtime_try_shutdown ()) {
3750 /*FIXME mono_thread_suspend probably should call mono_thread_execute_interruption when self interrupting. */
3751 mono_thread_suspend (mono_thread_internal_current ());
3752 mono_thread_execute_interruption_void ();
3755 #ifndef ENABLE_NETCORE
3757 * Under netcore, we don't abort any threads, just exit.
3758 * This is not a problem since we don't do runtime cleanup either.
3761 * Remove everything but the finalizer thread and self.
3762 * Also abort all the background threads
3763 * */
3764 do {
3765 mono_threads_lock ();
3767 wait->num = 0;
3768 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
3769 memset (wait->threads, 0, sizeof (wait->threads));
3770 mono_g_hash_table_foreach (threads, abort_threads, wait);
3772 mono_threads_unlock ();
3774 THREAD_DEBUG (g_message ("%s: wait->num is now %d", __func__, wait->num));
3775 if (wait->num > 0) {
3776 /* Something to wait for */
3777 wait_for_tids (wait, MONO_INFINITE_WAIT, FALSE);
3779 } while (wait->num > 0);
3780 #endif
3783 * give the subthreads a chance to really quit (this is mainly needed
3784 * to get correct user and system times from getrusage/wait/time(1)).
3785 * This could be removed if we avoid pthread_detach() and use pthread_join().
3787 mono_thread_info_yield ();
3790 #ifndef ENABLE_NETCORE
3791 static void
3792 collect_threads_for_suspend (gpointer key, gpointer value, gpointer user_data)
3794 MonoInternalThread *thread = (MonoInternalThread*)value;
3795 struct wait_data *wait = (struct wait_data*)user_data;
3798 * We try to exclude threads early, to avoid running into the MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
3799 * limitation.
3800 * This needs no locking.
3802 if ((thread->state & ThreadState_Suspended) != 0 ||
3803 (thread->state & ThreadState_Stopped) != 0)
3804 return;
3806 if (wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
3807 wait->handles [wait->num] = mono_threads_open_thread_handle (thread->handle);
3808 wait->threads [wait->num] = thread;
3809 wait->num++;
3814 * mono_thread_suspend_all_other_threads:
3816 * Suspend all managed threads except the finalizer thread and this thread. It is
3817 * not possible to resume them later.
3819 void mono_thread_suspend_all_other_threads (void)
3821 struct wait_data wait_data;
3822 struct wait_data *wait = &wait_data;
3823 int i;
3824 MonoNativeThreadId self = mono_native_thread_id_get ();
3825 guint32 eventidx = 0;
3826 gboolean starting, finished;
3828 memset (wait, 0, sizeof (struct wait_data));
3830 * The other threads could be in an arbitrary state at this point, i.e.
3831 * they could be starting up, shutting down etc. This means that there could be
3832 * threads which are not even in the threads hash table yet.
3836 * First we set a barrier which will be checked by all threads before they
3837 * are added to the threads hash table, and they will exit if the flag is set.
3838 * This ensures that no threads could be added to the hash later.
3839 * We will use shutting_down as the barrier for now.
3841 g_assert (shutting_down);
3844 * We make multiple calls to WaitForMultipleObjects since:
3845 * - we can only wait for MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS threads
3846 * - some threads could exit without becoming suspended
3848 finished = FALSE;
3849 while (!finished) {
3851 * Make a copy of the hashtable since we can't do anything with
3852 * threads while threads_mutex is held.
3854 wait->num = 0;
3855 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
3856 memset (wait->threads, 0, sizeof (wait->threads));
3857 mono_threads_lock ();
3858 mono_g_hash_table_foreach (threads, collect_threads_for_suspend, wait);
3859 mono_threads_unlock ();
3861 eventidx = 0;
3862 /* Get the suspended events that we'll be waiting for */
3863 for (i = 0; i < wait->num; ++i) {
3864 MonoInternalThread *thread = wait->threads [i];
3866 if (mono_native_thread_id_equals (thread_get_tid (thread), self)
3867 || mono_gc_is_finalizer_internal_thread (thread)
3868 || (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)
3870 mono_threads_close_thread_handle (wait->handles [i]);
3871 wait->threads [i] = NULL;
3872 continue;
3875 LOCK_THREAD (thread);
3877 if (thread->state & (ThreadState_Suspended | ThreadState_Stopped)) {
3878 UNLOCK_THREAD (thread);
3879 mono_threads_close_thread_handle (wait->handles [i]);
3880 wait->threads [i] = NULL;
3881 continue;
3884 ++eventidx;
3886 /* Convert abort requests into suspend requests */
3887 if ((thread->state & ThreadState_AbortRequested) != 0)
3888 thread->state &= ~ThreadState_AbortRequested;
3890 thread->state |= ThreadState_SuspendRequested;
3891 MONO_ENTER_GC_SAFE;
3892 mono_os_event_reset (thread->suspended);
3893 MONO_EXIT_GC_SAFE;
3895 /* Signal the thread to suspend + calls UNLOCK_THREAD (thread) */
3896 async_suspend_internal (thread, TRUE);
3898 mono_threads_close_thread_handle (wait->handles [i]);
3899 wait->threads [i] = NULL;
3901 if (eventidx <= 0) {
3903 * If there are threads which are starting up, we wait until they
3904 * are suspended when they try to register in the threads hash.
3905 * This is guaranteed to finish, since the threads which can create new
3906 * threads get suspended after a while.
3907 * FIXME: The finalizer thread can still create new threads.
3909 mono_threads_lock ();
3910 if (threads_starting_up)
3911 starting = mono_g_hash_table_size (threads_starting_up) > 0;
3912 else
3913 starting = FALSE;
3914 mono_threads_unlock ();
3915 if (starting)
3916 mono_thread_info_sleep (100, NULL);
3917 else
3918 finished = TRUE;
3922 #endif
3924 typedef struct {
3925 MonoInternalThread *thread;
3926 MonoStackFrameInfo *frames;
3927 int nframes, max_frames;
3928 int nthreads, max_threads;
3929 MonoInternalThread **threads;
3930 } ThreadDumpUserData;
3932 static gboolean thread_dump_requested;
3934 /* This needs to be async safe */
3935 static gboolean
3936 collect_frame (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
3938 ThreadDumpUserData *ud = (ThreadDumpUserData *)data;
3940 if (ud->nframes < ud->max_frames) {
3941 memcpy (&ud->frames [ud->nframes], frame, sizeof (MonoStackFrameInfo));
3942 ud->nframes ++;
3945 return FALSE;
3948 /* This needs to be async safe */
3949 static SuspendThreadResult
3950 get_thread_dump (MonoThreadInfo *info, gpointer ud)
3952 ThreadDumpUserData *user_data = (ThreadDumpUserData *)ud;
3953 MonoInternalThread *thread = user_data->thread;
3955 #if 0
3956 /* This no longer works with remote unwinding */
3957 g_string_append_printf (text, " tid=0x%p this=0x%p ", (gpointer)(gsize)thread->tid, thread);
3958 mono_thread_internal_describe (thread, text);
3959 g_string_append (text, "\n");
3960 #endif
3962 if (thread == mono_thread_internal_current ())
3963 mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (collect_frame, NULL, MONO_UNWIND_SIGNAL_SAFE, ud);
3964 else
3965 mono_get_eh_callbacks ()->mono_walk_stack_with_state (collect_frame, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, ud);
3967 return MonoResumeThread;
3970 typedef struct {
3971 int nthreads, max_threads;
3973 guint32 *threads;
3974 } CollectThreadsUserData;
3976 typedef struct {
3977 int nthreads, max_threads;
3978 MonoNativeThreadId *threads;
3979 } CollectThreadIdsUserData;
3981 static void
3982 collect_thread (gpointer key, gpointer value, gpointer user)
3984 CollectThreadsUserData *ud = (CollectThreadsUserData *)user;
3985 MonoInternalThread *thread = (MonoInternalThread *)value;
3987 if (ud->nthreads < ud->max_threads)
3988 ud->threads [ud->nthreads ++] = mono_gchandle_new_internal (&thread->obj, TRUE);
3992 * Collect running threads into the THREADS array.
3993 * THREADS should be an array allocated on the stack.
3995 static int
3996 collect_threads (guint32 *thread_handles, int max_threads)
3998 CollectThreadsUserData ud;
4000 mono_memory_barrier ();
4001 if (!threads)
4002 return 0;
4004 memset (&ud, 0, sizeof (ud));
4005 /* This array contains refs, but its on the stack, so its ok */
4006 ud.threads = thread_handles;
4007 ud.max_threads = max_threads;
4009 mono_threads_lock ();
4010 mono_g_hash_table_foreach (threads, collect_thread, &ud);
4011 mono_threads_unlock ();
4013 return ud.nthreads;
4016 void
4017 mono_gstring_append_thread_name (GString* text, MonoInternalThread* thread)
4019 g_string_append (text, "\n\"");
4020 char const * const name = thread->name.chars;
4021 g_string_append (text, name ? name :
4022 thread->threadpool_thread ? "<threadpool thread>" :
4023 "<unnamed thread>");
4024 g_string_append (text, "\"");
4027 static void
4028 dump_thread (MonoInternalThread *thread, ThreadDumpUserData *ud, FILE* output_file)
4030 GString* text = g_string_new (0);
4031 int i;
4033 ud->thread = thread;
4034 ud->nframes = 0;
4036 /* Collect frames for the thread */
4037 if (thread == mono_thread_internal_current ()) {
4038 get_thread_dump (mono_thread_info_current (), ud);
4039 } else {
4040 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, get_thread_dump, ud);
4044 * Do all the non async-safe work outside of get_thread_dump.
4046 mono_gstring_append_thread_name (text, thread);
4048 for (i = 0; i < ud->nframes; ++i) {
4049 MonoStackFrameInfo *frame = &ud->frames [i];
4050 MonoMethod *method = NULL;
4052 if (frame->type == FRAME_TYPE_MANAGED)
4053 method = mono_jit_info_get_method (frame->ji);
4055 if (method) {
4056 gchar *location = mono_debug_print_stack_frame (method, frame->native_offset, frame->domain);
4057 g_string_append_printf (text, " %s\n", location);
4058 g_free (location);
4059 } else {
4060 g_string_append_printf (text, " at <unknown> <0x%05x>\n", frame->native_offset);
4064 g_fprintf (output_file, "%s", text->str);
4066 #if PLATFORM_WIN32 && TARGET_WIN32 && _DEBUG
4067 OutputDebugStringA(text->str);
4068 #endif
4070 g_string_free (text, TRUE);
4071 fflush (output_file);
4074 static void
4075 mono_get_time_of_day (struct timeval *tv) {
4076 #ifdef WIN32
4077 struct _timeb time;
4078 _ftime(&time);
4079 tv->tv_sec = time.time;
4080 tv->tv_usec = time.millitm * 1000;
4081 #else
4082 if (gettimeofday (tv, NULL) == -1) {
4083 g_error ("gettimeofday() failed; errno is %d (%s)", errno, strerror (errno));
4085 #endif
4088 static void
4089 mono_local_time (const struct timeval *tv, struct tm *tm) {
4090 #ifdef HAVE_LOCALTIME_R
4091 localtime_r(&tv->tv_sec, tm);
4092 #else
4093 time_t const tv_sec = tv->tv_sec; // Copy due to Win32/Posix contradiction.
4094 *tm = *localtime (&tv_sec);
4095 #endif
4098 void
4099 mono_threads_perform_thread_dump (void)
4101 FILE* output_file = NULL;
4102 ThreadDumpUserData ud;
4103 guint32 thread_array [128];
4104 int tindex, nthreads;
4106 if (!thread_dump_requested)
4107 return;
4109 if (thread_dump_dir != NULL) {
4110 GString* path = g_string_new (0);
4111 char time_str[80];
4112 struct timeval tv;
4113 long ms;
4114 struct tm tod;
4115 mono_get_time_of_day (&tv);
4116 mono_local_time(&tv, &tod);
4117 strftime(time_str, sizeof(time_str), MONO_STRFTIME_F "_" MONO_STRFTIME_T, &tod);
4118 ms = tv.tv_usec / 1000;
4119 g_string_append_printf (path, "%s/%s.%03ld.tdump", thread_dump_dir, time_str, ms);
4120 output_file = fopen (path->str, "w");
4121 g_string_free (path, TRUE);
4123 if (output_file == NULL) {
4124 g_print ("Full thread dump:\n");
4127 /* Make a copy of the threads hash to avoid doing work inside threads_lock () */
4128 nthreads = collect_threads (thread_array, 128);
4130 memset (&ud, 0, sizeof (ud));
4131 ud.frames = g_new0 (MonoStackFrameInfo, 256);
4132 ud.max_frames = 256;
4134 for (tindex = 0; tindex < nthreads; ++tindex) {
4135 guint32 handle = thread_array [tindex];
4136 MonoInternalThread *thread = (MonoInternalThread *) mono_gchandle_get_target_internal (handle);
4137 dump_thread (thread, &ud, output_file != NULL ? output_file : stdout);
4138 mono_gchandle_free_internal (handle);
4141 if (output_file != NULL) {
4142 fclose (output_file);
4144 g_free (ud.frames);
4146 thread_dump_requested = FALSE;
4149 #ifndef ENABLE_NETCORE
4150 /* Obtain the thread dump of all threads */
4151 void
4152 ves_icall_System_Threading_Thread_GetStackTraces (MonoArrayHandleOut out_threads_handle, MonoArrayHandleOut out_stack_frames_handle, MonoError *error)
4154 MONO_HANDLE_ASSIGN_RAW (out_threads_handle, NULL);
4155 MONO_HANDLE_ASSIGN_RAW (out_stack_frames_handle, NULL);
4157 guint32 handle = 0;
4159 MonoStackFrameHandle stack_frame_handle = MONO_HANDLE_NEW (MonoStackFrame, NULL);
4160 MonoReflectionMethodHandle reflection_method_handle = MONO_HANDLE_NEW (MonoReflectionMethod, NULL);
4161 MonoStringHandle filename_handle = MONO_HANDLE_NEW (MonoString, NULL);
4162 MonoArrayHandle thread_frames_handle = MONO_HANDLE_NEW (MonoArray, NULL);
4164 ThreadDumpUserData ud;
4165 guint32 thread_array [128];
4166 MonoDomain *domain = mono_domain_get ();
4167 MonoDebugSourceLocation *location;
4168 int tindex, nthreads;
4170 MonoArray* out_threads = NULL;
4171 MonoArray* out_stack_frames = NULL;
4173 MONO_HANDLE_ASSIGN_RAW (out_threads_handle, NULL);
4174 MONO_HANDLE_ASSIGN_RAW (out_stack_frames_handle, NULL);
4176 /* Make a copy of the threads hash to avoid doing work inside threads_lock () */
4177 nthreads = collect_threads (thread_array, 128);
4179 memset (&ud, 0, sizeof (ud));
4180 ud.frames = g_new0 (MonoStackFrameInfo, 256);
4181 ud.max_frames = 256;
4183 out_threads = mono_array_new_checked (domain, mono_defaults.thread_class, nthreads, error);
4184 goto_if_nok (error, leave);
4185 MONO_HANDLE_ASSIGN_RAW (out_threads_handle, out_threads);
4186 out_stack_frames = mono_array_new_checked (domain, mono_defaults.array_class, nthreads, error);
4187 goto_if_nok (error, leave);
4188 MONO_HANDLE_ASSIGN_RAW (out_stack_frames_handle, out_stack_frames);
4190 for (tindex = 0; tindex < nthreads; ++tindex) {
4192 mono_gchandle_free_internal (handle);
4193 handle = thread_array [tindex];
4194 MonoInternalThread *thread = (MonoInternalThread *) mono_gchandle_get_target_internal (handle);
4196 MonoArray *thread_frames;
4197 int i;
4199 ud.thread = thread;
4200 ud.nframes = 0;
4202 /* Collect frames for the thread */
4203 if (thread == mono_thread_internal_current ()) {
4204 get_thread_dump (mono_thread_info_current (), &ud);
4205 } else {
4206 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, get_thread_dump, &ud);
4209 mono_array_setref_fast (out_threads, tindex, mono_thread_current_for_thread (thread));
4211 thread_frames = mono_array_new_checked (domain, mono_defaults.stack_frame_class, ud.nframes, error);
4212 MONO_HANDLE_ASSIGN_RAW (thread_frames_handle, thread_frames);
4213 goto_if_nok (error, leave);
4214 mono_array_setref_fast (out_stack_frames, tindex, thread_frames);
4216 for (i = 0; i < ud.nframes; ++i) {
4217 MonoStackFrameInfo *frame = &ud.frames [i];
4218 MonoMethod *method = NULL;
4219 MonoStackFrame *sf = (MonoStackFrame *)mono_object_new_checked (domain, mono_defaults.stack_frame_class, error);
4220 MONO_HANDLE_ASSIGN_RAW (stack_frame_handle, sf);
4221 goto_if_nok (error, leave);
4223 sf->native_offset = frame->native_offset;
4225 if (frame->type == FRAME_TYPE_MANAGED)
4226 method = mono_jit_info_get_method (frame->ji);
4228 if (method) {
4229 sf->method_address = (gsize) frame->ji->code_start;
4231 MonoReflectionMethod *rm = mono_method_get_object_checked (domain, method, NULL, error);
4232 MONO_HANDLE_ASSIGN_RAW (reflection_method_handle, rm);
4233 goto_if_nok (error, leave);
4234 MONO_OBJECT_SETREF_INTERNAL (sf, method, rm);
4236 location = mono_debug_lookup_source_location (method, frame->native_offset, domain);
4237 if (location) {
4238 sf->il_offset = location->il_offset;
4240 if (location->source_file) {
4241 MonoString *filename = mono_string_new_checked (domain, location->source_file, error);
4242 MONO_HANDLE_ASSIGN_RAW (filename_handle, filename);
4243 goto_if_nok (error, leave);
4244 MONO_OBJECT_SETREF_INTERNAL (sf, filename, filename);
4245 sf->line = location->row;
4246 sf->column = location->column;
4248 mono_debug_free_source_location (location);
4249 } else {
4250 sf->il_offset = -1;
4253 mono_array_setref_internal (thread_frames, i, sf);
4256 mono_gchandle_free_internal (handle);
4257 handle = 0;
4260 leave:
4261 mono_gchandle_free_internal (handle);
4262 g_free (ud.frames);
4264 #endif
4267 * mono_threads_request_thread_dump:
4269 * Ask all threads except the current to print their stacktrace to stdout.
4271 void
4272 mono_threads_request_thread_dump (void)
4274 /*The new thread dump code runs out of the finalizer thread. */
4275 thread_dump_requested = TRUE;
4276 mono_gc_finalize_notify ();
4279 struct ref_stack {
4280 gpointer *refs;
4281 gint allocated; /* +1 so that refs [allocated] == NULL */
4282 gint bottom;
4285 typedef struct ref_stack RefStack;
4287 static RefStack *
4288 ref_stack_new (gint initial_size)
4290 RefStack *rs;
4292 initial_size = MAX (initial_size, 16) + 1;
4293 rs = g_new0 (RefStack, 1);
4294 rs->refs = g_new0 (gpointer, initial_size);
4295 rs->allocated = initial_size;
4296 return rs;
4299 static void
4300 ref_stack_destroy (gpointer ptr)
4302 RefStack *rs = (RefStack *)ptr;
4304 if (rs != NULL) {
4305 g_free (rs->refs);
4306 g_free (rs);
4310 static void
4311 ref_stack_push (RefStack *rs, gpointer ptr)
4313 g_assert (rs != NULL);
4315 if (rs->bottom >= rs->allocated) {
4316 rs->refs = (void **)g_realloc (rs->refs, rs->allocated * 2 * sizeof (gpointer) + 1);
4317 rs->allocated <<= 1;
4318 rs->refs [rs->allocated] = NULL;
4320 rs->refs [rs->bottom++] = ptr;
4323 static void
4324 ref_stack_pop (RefStack *rs)
4326 if (rs == NULL || rs->bottom == 0)
4327 return;
4329 rs->bottom--;
4330 rs->refs [rs->bottom] = NULL;
4333 static gboolean
4334 ref_stack_find (RefStack *rs, gpointer ptr)
4336 gpointer *refs;
4338 if (rs == NULL)
4339 return FALSE;
4341 for (refs = rs->refs; refs && *refs; refs++) {
4342 if (*refs == ptr)
4343 return TRUE;
4345 return FALSE;
4349 * mono_thread_push_appdomain_ref:
4351 * Register that the current thread may have references to objects in domain
4352 * @domain on its stack. Each call to this function should be paired with a
4353 * call to pop_appdomain_ref.
4355 void
4356 mono_thread_push_appdomain_ref (MonoDomain *domain)
4358 MonoInternalThread *thread = mono_thread_internal_current ();
4360 if (thread) {
4361 /* printf ("PUSH REF: %" G_GSIZE_FORMAT " -> %s.\n", (gsize)thread->tid, domain->friendly_name); */
4362 SPIN_LOCK (thread->lock_thread_id);
4363 if (thread->appdomain_refs == NULL)
4364 thread->appdomain_refs = ref_stack_new (16);
4365 ref_stack_push ((RefStack *)thread->appdomain_refs, domain);
4366 SPIN_UNLOCK (thread->lock_thread_id);
4370 void
4371 mono_thread_pop_appdomain_ref (void)
4373 MonoInternalThread *thread = mono_thread_internal_current ();
4375 if (thread) {
4376 /* printf ("POP REF: %" G_GSIZE_FORMAT " -> %s.\n", (gsize)thread->tid, ((MonoDomain*)(thread->appdomain_refs->data))->friendly_name); */
4377 SPIN_LOCK (thread->lock_thread_id);
4378 ref_stack_pop ((RefStack *)thread->appdomain_refs);
4379 SPIN_UNLOCK (thread->lock_thread_id);
4383 gboolean
4384 mono_thread_internal_has_appdomain_ref (MonoInternalThread *thread, MonoDomain *domain)
4386 gboolean res;
4387 SPIN_LOCK (thread->lock_thread_id);
4388 res = ref_stack_find ((RefStack *)thread->appdomain_refs, domain);
4389 SPIN_UNLOCK (thread->lock_thread_id);
4390 return res;
4393 gboolean
4394 mono_thread_has_appdomain_ref (MonoThread *thread, MonoDomain *domain)
4396 return mono_thread_internal_has_appdomain_ref (thread->internal_thread, domain);
4399 typedef struct abort_appdomain_data {
4400 struct wait_data wait;
4401 MonoDomain *domain;
4402 } abort_appdomain_data;
4404 static void
4405 collect_appdomain_thread (gpointer key, gpointer value, gpointer user_data)
4407 MonoInternalThread *thread = (MonoInternalThread*)value;
4408 abort_appdomain_data *data = (abort_appdomain_data*)user_data;
4409 MonoDomain *domain = data->domain;
4411 if (mono_thread_internal_has_appdomain_ref (thread, domain)) {
4412 /* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread->tid, domain->friendly_name); */
4414 if(data->wait.num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
4415 data->wait.handles [data->wait.num] = mono_threads_open_thread_handle (thread->handle);
4416 data->wait.threads [data->wait.num] = thread;
4417 data->wait.num++;
4418 } else {
4419 /* Just ignore the rest, we can't do anything with
4420 * them yet
4427 * mono_threads_abort_appdomain_threads:
4429 * Abort threads which has references to the given appdomain.
4431 gboolean
4432 mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
4434 abort_appdomain_data user_data;
4435 gint64 start_time;
4436 int orig_timeout = timeout;
4437 int i;
4439 THREAD_DEBUG (g_message ("%s: starting abort", __func__));
4441 start_time = mono_msec_ticks ();
4442 do {
4443 mono_threads_lock ();
4445 user_data.domain = domain;
4446 user_data.wait.num = 0;
4447 /* This shouldn't take any locks */
4448 mono_g_hash_table_foreach (threads, collect_appdomain_thread, &user_data);
4449 mono_threads_unlock ();
4451 if (user_data.wait.num > 0) {
4452 /* Abort the threads outside the threads lock */
4453 for (i = 0; i < user_data.wait.num; ++i)
4454 mono_thread_internal_abort (user_data.wait.threads [i], TRUE);
4457 * We should wait for the threads either to abort, or to leave the
4458 * domain. We can't do the latter, so we wait with a timeout.
4460 wait_for_tids (&user_data.wait, 100, FALSE);
4463 /* Update remaining time */
4464 timeout -= mono_msec_ticks () - start_time;
4465 start_time = mono_msec_ticks ();
4467 if (orig_timeout != -1 && timeout < 0)
4468 return FALSE;
4470 while (user_data.wait.num > 0);
4472 THREAD_DEBUG (g_message ("%s: abort done", __func__));
4474 return TRUE;
4477 /* This is a JIT icall. This icall is called from a finally block when
4478 * mono_install_handler_block_guard called by another thread has flipped the
4479 * finally block's exvar (see mono_find_exvar_for_offset). In that case, if
4480 * the finally is in an abort protected block, we must defer the abort
4481 * exception until we leave the abort protected block. Otherwise we proceed
4482 * with a synchronous self-abort.
4484 void
4485 ves_icall_thread_finish_async_abort (void)
4487 /* We were called from the handler block and are about to
4488 * leave it. (If we end up postponing the abort because we're
4489 * in an abort protected block, the unwinder won't run and
4490 * won't clear the handler block itself which will confuse the
4491 * unwinder if we're in a try {} catch {} and we throw again.
4492 * ie, this:
4493 * static Constructor () {
4494 * try {
4495 * try {
4496 * } finally {
4497 * icall (); // Thread.Abort landed here,
4498 * // and caused the handler block to be installed
4499 * if (exvar)
4500 * ves_icall_thread_finish_async_abort (); // we're here
4502 * throw E ();
4503 * } catch (E) {
4504 * // unwinder will get confused here and synthesize a self abort
4508 * More interestingly, this doesn't only happen with icalls - a JIT
4509 * trampoline is native code that will cause a handler to be installed.
4510 * So the above situation can happen with any code in a "finally"
4511 * clause.
4513 mono_get_eh_callbacks ()->mono_uninstall_current_handler_block_guard ();
4514 /* Just set the async interruption requested bit. Rely on the icall
4515 * wrapper of this icall to process the thread interruption, respecting
4516 * any abort protection blocks in our call stack.
4518 mono_thread_set_self_interruption_respect_abort_prot ();
4522 * mono_thread_get_undeniable_exception:
4524 * Return an exception which needs to be raised when leaving a catch clause.
4525 * This is used for undeniable exception propagation.
4527 MonoException*
4528 mono_thread_get_undeniable_exception (void)
4530 MonoInternalThread *thread = mono_thread_internal_current ();
4532 if (!(thread && thread->abort_exc && !is_running_protected_wrapper ()))
4533 return NULL;
4535 // We don't want to have our exception effect calls made by
4536 // the catching block
4538 if (!mono_get_eh_callbacks ()->mono_above_abort_threshold ())
4539 return NULL;
4542 * FIXME: Clear the abort exception and return an AppDomainUnloaded
4543 * exception if the thread no longer references a dying appdomain.
4545 thread->abort_exc->trace_ips = NULL;
4546 thread->abort_exc->stack_trace = NULL;
4547 return thread->abort_exc;
4550 #if MONO_SMALL_CONFIG
4551 #define NUM_STATIC_DATA_IDX 4
4552 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
4553 64, 256, 1024, 4096
4555 #else
4556 #define NUM_STATIC_DATA_IDX 8
4557 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
4558 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
4560 #endif
4562 static MonoBitSet *thread_reference_bitmaps [NUM_STATIC_DATA_IDX];
4563 static MonoBitSet *context_reference_bitmaps [NUM_STATIC_DATA_IDX];
4565 static void
4566 mark_slots (void *addr, MonoBitSet **bitmaps, MonoGCMarkFunc mark_func, void *gc_data)
4568 gpointer *static_data = (gpointer *)addr;
4570 for (int i = 0; i < NUM_STATIC_DATA_IDX; ++i) {
4571 void **ptr = (void **)static_data [i];
4573 if (!ptr)
4574 continue;
4576 MONO_BITSET_FOREACH (bitmaps [i], idx, {
4577 void **p = ptr + idx;
4579 if (*p)
4580 mark_func ((MonoObject**)p, gc_data);
4585 static void
4586 mark_tls_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
4588 mark_slots (addr, thread_reference_bitmaps, mark_func, gc_data);
4591 static void
4592 mark_ctx_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
4594 mark_slots (addr, context_reference_bitmaps, mark_func, gc_data);
4598 * mono_alloc_static_data
4600 * Allocate memory blocks for storing threads or context static data
4602 static void
4603 mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, void *alloc_key, gboolean threadlocal)
4605 guint idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4606 int i;
4608 gpointer* static_data = *static_data_ptr;
4609 if (!static_data) {
4610 static MonoGCDescriptor tls_desc = MONO_GC_DESCRIPTOR_NULL;
4611 static MonoGCDescriptor ctx_desc = MONO_GC_DESCRIPTOR_NULL;
4613 if (mono_gc_user_markers_supported ()) {
4614 if (tls_desc == MONO_GC_DESCRIPTOR_NULL)
4615 tls_desc = mono_gc_make_root_descr_user (mark_tls_slots);
4617 if (ctx_desc == MONO_GC_DESCRIPTOR_NULL)
4618 ctx_desc = mono_gc_make_root_descr_user (mark_ctx_slots);
4621 static_data = (void **)mono_gc_alloc_fixed (static_data_size [0], threadlocal ? tls_desc : ctx_desc,
4622 threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC,
4623 alloc_key,
4624 threadlocal ? "ThreadStatic Fields" : "ContextStatic Fields");
4625 *static_data_ptr = static_data;
4626 static_data [0] = static_data;
4629 for (i = 1; i <= idx; ++i) {
4630 if (static_data [i])
4631 continue;
4633 if (mono_gc_user_markers_supported ())
4634 static_data [i] = g_malloc0 (static_data_size [i]);
4635 else
4636 static_data [i] = mono_gc_alloc_fixed (static_data_size [i], MONO_GC_DESCRIPTOR_NULL,
4637 threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC,
4638 alloc_key,
4639 threadlocal ? "ThreadStatic Fields" : "ContextStatic Fields");
4643 static void
4644 mono_free_static_data (gpointer* static_data)
4646 int i;
4647 for (i = 1; i < NUM_STATIC_DATA_IDX; ++i) {
4648 gpointer p = static_data [i];
4649 if (!p)
4650 continue;
4652 * At this point, the static data pointer array is still registered with the
4653 * GC, so must ensure that mark_tls_slots() will not encounter any invalid
4654 * data. Freeing the individual arrays without first nulling their slots
4655 * would make it possible for mark_tls/ctx_slots() to encounter a pointer to
4656 * such an already freed array. See bug #13813.
4658 static_data [i] = NULL;
4659 mono_memory_write_barrier ();
4660 if (mono_gc_user_markers_supported ())
4661 g_free (p);
4662 else
4663 mono_gc_free_fixed (p);
4665 mono_gc_free_fixed (static_data);
4669 * mono_init_static_data_info
4671 * Initializes static data counters
4673 static void mono_init_static_data_info (StaticDataInfo *static_data)
4675 static_data->idx = 0;
4676 static_data->offset = 0;
4677 static_data->freelist = NULL;
4681 * mono_alloc_static_data_slot
4683 * Generates an offset for static data. static_data contains the counters
4684 * used to generate it.
4686 static guint32
4687 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align)
4689 if (!static_data->idx && !static_data->offset) {
4691 * we use the first chunk of the first allocation also as
4692 * an array for the rest of the data
4694 static_data->offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX;
4696 static_data->offset += align - 1;
4697 static_data->offset &= ~(align - 1);
4698 if (static_data->offset + size >= static_data_size [static_data->idx]) {
4699 static_data->idx ++;
4700 g_assert (size <= static_data_size [static_data->idx]);
4701 g_assert (static_data->idx < NUM_STATIC_DATA_IDX);
4702 static_data->offset = 0;
4704 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (static_data->idx, static_data->offset, 0);
4705 static_data->offset += size;
4706 return offset;
4710 * LOCKING: requires that threads_mutex is held
4712 static void
4713 context_adjust_static_data (MonoAppContextHandle ctx_handle)
4715 MonoAppContext *ctx = MONO_HANDLE_RAW (ctx_handle);
4716 if (context_static_info.offset || context_static_info.idx > 0) {
4717 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (context_static_info.idx, context_static_info.offset, 0);
4718 mono_alloc_static_data (&ctx->static_data, offset, ctx, FALSE);
4719 ctx->data->static_data = ctx->static_data;
4724 * LOCKING: requires that threads_mutex is held
4726 static void
4727 alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
4729 MonoInternalThread *thread = (MonoInternalThread *)value;
4730 guint32 offset = GPOINTER_TO_UINT (user);
4732 mono_alloc_static_data (&(thread->static_data), offset, (void *) MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid), TRUE);
4736 * LOCKING: requires that threads_mutex is held
4738 static void
4739 alloc_context_static_data_helper (gpointer key, gpointer value, gpointer user)
4741 MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target_internal (GPOINTER_TO_INT (key));
4743 if (!ctx)
4744 return;
4746 guint32 offset = GPOINTER_TO_UINT (user);
4747 mono_alloc_static_data (&ctx->static_data, offset, ctx, FALSE);
4748 ctx->data->static_data = ctx->static_data;
4751 static StaticDataFreeList*
4752 search_slot_in_freelist (StaticDataInfo *static_data, guint32 size, guint32 align)
4754 StaticDataFreeList* prev = NULL;
4755 StaticDataFreeList* tmp = static_data->freelist;
4756 while (tmp) {
4757 if (tmp->size == size) {
4758 if (prev)
4759 prev->next = tmp->next;
4760 else
4761 static_data->freelist = tmp->next;
4762 return tmp;
4764 prev = tmp;
4765 tmp = tmp->next;
4767 return NULL;
4770 #if SIZEOF_VOID_P == 4
4771 #define ONE_P 1
4772 #else
4773 #define ONE_P 1ll
4774 #endif
4776 static void
4777 update_reference_bitmap (MonoBitSet **sets, guint32 offset, uintptr_t *bitmap, int numbits)
4779 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4780 if (!sets [idx])
4781 sets [idx] = mono_bitset_new (static_data_size [idx] / sizeof (uintptr_t), 0);
4782 MonoBitSet *rb = sets [idx];
4783 offset = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
4784 offset /= sizeof (uintptr_t);
4785 /* offset is now the bitmap offset */
4786 for (int i = 0; i < numbits; ++i) {
4787 if (bitmap [i / sizeof (uintptr_t)] & (ONE_P << (i & (sizeof (uintptr_t) * 8 -1))))
4788 mono_bitset_set_fast (rb, offset + i);
4792 static void
4793 clear_reference_bitmap (MonoBitSet **sets, guint32 offset, guint32 size)
4795 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4796 MonoBitSet *rb = sets [idx];
4797 offset = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
4798 offset /= sizeof (uintptr_t);
4799 /* offset is now the bitmap offset */
4800 for (int i = 0; i < size / sizeof (uintptr_t); i++)
4801 mono_bitset_clear_fast (rb, offset + i);
4804 guint32
4805 mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align, uintptr_t *bitmap, int numbits)
4807 g_assert (static_type == SPECIAL_STATIC_THREAD || static_type == SPECIAL_STATIC_CONTEXT);
4809 StaticDataInfo *info;
4810 MonoBitSet **sets;
4812 if (static_type == SPECIAL_STATIC_THREAD) {
4813 info = &thread_static_info;
4814 sets = thread_reference_bitmaps;
4815 } else {
4816 info = &context_static_info;
4817 sets = context_reference_bitmaps;
4820 mono_threads_lock ();
4822 StaticDataFreeList *item = search_slot_in_freelist (info, size, align);
4823 guint32 offset;
4825 if (item) {
4826 offset = item->offset;
4827 g_free (item);
4828 } else {
4829 offset = mono_alloc_static_data_slot (info, size, align);
4832 update_reference_bitmap (sets, offset, bitmap, numbits);
4834 if (static_type == SPECIAL_STATIC_THREAD) {
4835 /* This can be called during startup */
4836 if (threads != NULL)
4837 mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
4838 } else {
4839 if (contexts != NULL)
4840 g_hash_table_foreach (contexts, alloc_context_static_data_helper, GUINT_TO_POINTER (offset));
4842 ACCESS_SPECIAL_STATIC_OFFSET (offset, type) = SPECIAL_STATIC_OFFSET_TYPE_CONTEXT;
4845 mono_threads_unlock ();
4847 return offset;
4850 gpointer
4851 mono_get_special_static_data_for_thread (MonoInternalThread *thread, guint32 offset)
4853 guint32 static_type = ACCESS_SPECIAL_STATIC_OFFSET (offset, type);
4855 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4856 return get_thread_static_data (thread, offset);
4857 } else {
4858 return get_context_static_data (thread->current_appcontext, offset);
4862 gpointer
4863 mono_get_special_static_data (guint32 offset)
4865 return mono_get_special_static_data_for_thread (mono_thread_internal_current (), offset);
4868 typedef struct {
4869 guint32 offset;
4870 guint32 size;
4871 } OffsetSize;
4874 * LOCKING: requires that threads_mutex is held
4876 static void
4877 free_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
4879 MonoInternalThread *thread = (MonoInternalThread *)value;
4880 OffsetSize *data = (OffsetSize *)user;
4881 int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index);
4882 int off = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, offset);
4883 char *ptr;
4885 if (!thread->static_data || !thread->static_data [idx])
4886 return;
4887 ptr = ((char*) thread->static_data [idx]) + off;
4888 mono_gc_bzero_atomic (ptr, data->size);
4892 * LOCKING: requires that threads_mutex is held
4894 static void
4895 free_context_static_data_helper (gpointer key, gpointer value, gpointer user)
4897 MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target_internal (GPOINTER_TO_INT (key));
4899 if (!ctx)
4900 return;
4902 OffsetSize *data = (OffsetSize *)user;
4903 int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index);
4904 int off = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, offset);
4905 char *ptr;
4907 if (!ctx->static_data || !ctx->static_data [idx])
4908 return;
4910 ptr = ((char*) ctx->static_data [idx]) + off;
4911 mono_gc_bzero_atomic (ptr, data->size);
4914 static void
4915 do_free_special_slot (guint32 offset, guint32 size)
4917 guint32 static_type = ACCESS_SPECIAL_STATIC_OFFSET (offset, type);
4918 MonoBitSet **sets;
4919 StaticDataInfo *info;
4921 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4922 info = &thread_static_info;
4923 sets = thread_reference_bitmaps;
4924 } else {
4925 info = &context_static_info;
4926 sets = context_reference_bitmaps;
4929 guint32 data_offset = offset;
4930 ACCESS_SPECIAL_STATIC_OFFSET (data_offset, type) = 0;
4931 OffsetSize data = { data_offset, size };
4933 clear_reference_bitmap (sets, data.offset, data.size);
4935 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4936 if (threads != NULL)
4937 mono_g_hash_table_foreach (threads, free_thread_static_data_helper, &data);
4938 } else {
4939 if (contexts != NULL)
4940 g_hash_table_foreach (contexts, free_context_static_data_helper, &data);
4943 if (!mono_runtime_is_shutting_down ()) {
4944 StaticDataFreeList *item = g_new0 (StaticDataFreeList, 1);
4946 item->offset = offset;
4947 item->size = size;
4949 item->next = info->freelist;
4950 info->freelist = item;
4954 static void
4955 do_free_special (gpointer key, gpointer value, gpointer data)
4957 MonoClassField *field = (MonoClassField *)key;
4958 guint32 offset = GPOINTER_TO_UINT (value);
4959 gint32 align;
4960 guint32 size;
4961 size = mono_type_size (field->type, &align);
4962 do_free_special_slot (offset, size);
4965 void
4966 mono_alloc_special_static_data_free (GHashTable *special_static_fields)
4968 mono_threads_lock ();
4970 g_hash_table_foreach (special_static_fields, do_free_special, NULL);
4972 mono_threads_unlock ();
4975 #ifdef HOST_WIN32
4976 static void
4977 flush_thread_interrupt_queue (void)
4979 /* Consume pending APC calls for current thread.*/
4980 /* Since this function get's called from interrupt handler it must use a direct */
4981 /* Win32 API call and can't go through mono_coop_win32_wait_for_single_object_ex */
4982 /* or it will detect a pending interrupt and not entering the wait call needed */
4983 /* to consume pending APC's.*/
4984 MONO_ENTER_GC_SAFE;
4985 WaitForSingleObjectEx (GetCurrentThread (), 0, TRUE);
4986 MONO_EXIT_GC_SAFE;
4988 #else
4989 static void
4990 flush_thread_interrupt_queue (void)
4993 #endif
4996 * mono_thread_execute_interruption
4998 * Performs the operation that the requested thread state requires (abort,
4999 * suspend or stop)
5001 static gboolean
5002 mono_thread_execute_interruption (MonoExceptionHandle *pexc)
5004 gboolean fexc = FALSE;
5006 // Optimize away frame if caller supplied one.
5007 if (!pexc) {
5008 HANDLE_FUNCTION_ENTER ();
5009 MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, NULL);
5010 fexc = mono_thread_execute_interruption (&exc);
5011 HANDLE_FUNCTION_RETURN_VAL (fexc);
5014 MONO_REQ_GC_UNSAFE_MODE;
5016 MonoInternalThreadHandle thread = mono_thread_internal_current_handle ();
5017 MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, NULL);
5019 lock_thread_handle (thread);
5020 gboolean unlock = TRUE;
5022 /* MonoThread::interruption_requested can only be changed with atomics */
5023 if (!mono_thread_clear_interruption_requested_handle (thread))
5024 goto exit;
5026 MonoThreadObjectHandle sys_thread;
5027 sys_thread = mono_thread_current_handle ();
5029 flush_thread_interrupt_queue ();
5031 /* Clear the interrupted flag of the thread so it can wait again */
5032 mono_thread_info_clear_self_interrupt ();
5034 /* If there's a pending exception and an AbortRequested, the pending exception takes precedence */
5035 MONO_HANDLE_GET (exc, sys_thread, pending_exception);
5036 if (!MONO_HANDLE_IS_NULL (exc)) {
5037 // sys_thread->pending_exception = NULL;
5038 MONO_HANDLE_SETRAW (sys_thread, pending_exception, NULL);
5039 fexc = TRUE;
5040 goto exit;
5041 } else if (MONO_HANDLE_GETVAL (thread, state) & ThreadState_AbortRequested) {
5042 // Does the thread already have an abort exception?
5043 // If not, create a new one and set it on demand.
5044 // exc = thread->abort_exc;
5045 MONO_HANDLE_GET (exc, thread, abort_exc);
5046 if (MONO_HANDLE_IS_NULL (exc)) {
5047 ERROR_DECL (error);
5048 exc = mono_exception_new_thread_abort (error);
5049 mono_error_assert_ok (error); // FIXME
5050 // thread->abort_exc = exc;
5051 MONO_HANDLE_SET (thread, abort_exc, exc);
5053 fexc = TRUE;
5054 } else if (MONO_HANDLE_GETVAL (thread, state) & ThreadState_SuspendRequested) {
5055 /* calls UNLOCK_THREAD (thread) */
5056 self_suspend_internal ();
5057 unlock = FALSE;
5058 } else if (MONO_HANDLE_GETVAL (thread, thread_interrupt_requested)) {
5059 // thread->thread_interrupt_requested = FALSE
5060 MONO_HANDLE_SETVAL (thread, thread_interrupt_requested, MonoBoolean, FALSE);
5061 unlock_thread_handle (thread);
5062 unlock = FALSE;
5063 ERROR_DECL (error);
5064 exc = mono_exception_new_thread_interrupted (error);
5065 mono_error_assert_ok (error); // FIXME
5066 fexc = TRUE;
5068 exit:
5069 if (unlock)
5070 unlock_thread_handle (thread);
5072 if (fexc)
5073 MONO_HANDLE_ASSIGN (*pexc, exc);
5075 return fexc;
5078 static void
5079 mono_thread_execute_interruption_void (void)
5081 (void)mono_thread_execute_interruption (NULL);
5084 static MonoException*
5085 mono_thread_execute_interruption_ptr (void)
5087 HANDLE_FUNCTION_ENTER ();
5088 MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, NULL);
5089 MonoException * const exc_raw = mono_thread_execute_interruption (&exc) ? MONO_HANDLE_RAW (exc) : NULL;
5090 HANDLE_FUNCTION_RETURN_VAL (exc_raw);
5094 * mono_thread_request_interruption_internal
5096 * A signal handler can call this method to request the interruption of a
5097 * thread. The result of the interruption will depend on the current state of
5098 * the thread. If the result is an exception that needs to be thrown, it is
5099 * provided as return value.
5101 static gboolean
5102 mono_thread_request_interruption_internal (gboolean running_managed, MonoExceptionHandle *pexc)
5104 MonoInternalThread *thread = mono_thread_internal_current ();
5106 /* The thread may already be stopping */
5107 if (thread == NULL)
5108 return FALSE;
5110 if (!mono_thread_set_interruption_requested (thread))
5111 return FALSE;
5113 if (!running_managed || is_running_protected_wrapper ()) {
5114 /* Can't stop while in unmanaged code. Increase the global interruption
5115 request count. When exiting the unmanaged method the count will be
5116 checked and the thread will be interrupted. */
5118 /* this will awake the thread if it is in WaitForSingleObject
5119 or similar */
5120 #ifdef HOST_WIN32
5121 mono_win32_interrupt_wait (thread->thread_info, thread->native_handle, (DWORD)thread->tid);
5122 #else
5123 mono_thread_info_self_interrupt ();
5124 #endif
5125 return FALSE;
5127 return mono_thread_execute_interruption (pexc);
5130 static void
5131 mono_thread_request_interruption_native (void)
5133 (void)mono_thread_request_interruption_internal (FALSE, NULL);
5136 static gboolean
5137 mono_thread_request_interruption_managed (MonoExceptionHandle *exc)
5139 return mono_thread_request_interruption_internal (TRUE, exc);
5142 /*This function should be called by a thread after it has exited all of
5143 * its handle blocks at interruption time.*/
5144 void
5145 mono_thread_resume_interruption (gboolean exec)
5147 MonoInternalThread *thread = mono_thread_internal_current ();
5148 gboolean still_aborting;
5150 /* The thread may already be stopping */
5151 if (thread == NULL)
5152 return;
5154 LOCK_THREAD (thread);
5155 still_aborting = (thread->state & (ThreadState_AbortRequested)) != 0;
5156 UNLOCK_THREAD (thread);
5158 /*This can happen if the protected block called Thread::ResetAbort*/
5159 if (!still_aborting)
5160 return;
5162 if (!mono_thread_set_interruption_requested (thread))
5163 return;
5165 mono_thread_info_self_interrupt ();
5167 if (exec) // Ignore the exception here, it will be raised later.
5168 mono_thread_execute_interruption_void ();
5171 gboolean
5172 mono_thread_interruption_requested (void)
5174 if (mono_thread_interruption_request_flag) {
5175 MonoInternalThread *thread = mono_thread_internal_current ();
5176 /* The thread may already be stopping */
5177 if (thread != NULL)
5178 return mono_thread_get_interruption_requested (thread);
5180 return FALSE;
5183 static MonoException*
5184 mono_thread_interruption_checkpoint_request (gboolean bypass_abort_protection)
5186 MonoInternalThread *thread = mono_thread_internal_current ();
5188 /* The thread may already be stopping */
5189 if (!thread)
5190 return NULL;
5191 if (!mono_thread_get_interruption_requested (thread))
5192 return NULL;
5193 if (!bypass_abort_protection && !mono_thread_current ()->pending_exception && is_running_protected_wrapper ())
5194 return NULL;
5196 return mono_thread_execute_interruption_ptr ();
5200 * Performs the interruption of the current thread, if one has been requested,
5201 * and the thread is not running a protected wrapper.
5202 * Return the exception which needs to be thrown, if any.
5204 MonoException*
5205 mono_thread_interruption_checkpoint (void)
5207 return mono_thread_interruption_checkpoint_request (FALSE);
5210 gboolean
5211 mono_thread_interruption_checkpoint_bool (void)
5213 return mono_thread_interruption_checkpoint () != NULL;
5216 void
5217 mono_thread_interruption_checkpoint_void (void)
5219 mono_thread_interruption_checkpoint ();
5223 * Performs the interruption of the current thread, if one has been requested.
5224 * Return the exception which needs to be thrown, if any.
5226 MonoException*
5227 mono_thread_force_interruption_checkpoint_noraise (void)
5229 return mono_thread_interruption_checkpoint_request (TRUE);
5233 * mono_set_pending_exception:
5235 * Set the pending exception of the current thread to EXC.
5236 * The exception will be thrown when execution returns to managed code.
5238 void
5239 mono_set_pending_exception (MonoException *exc)
5241 MonoThread *thread = mono_thread_current ();
5243 /* The thread may already be stopping */
5244 if (thread == NULL)
5245 return;
5247 MONO_OBJECT_SETREF_INTERNAL (thread, pending_exception, exc);
5249 mono_thread_request_interruption_native ();
5253 * mono_runtime_set_pending_exception:
5255 * Set the pending exception of the current thread to \p exc.
5256 * The exception will be thrown when execution returns to managed code.
5257 * Can optionally \p overwrite any existing pending exceptions (it's not supported
5258 * to overwrite any pending exceptions if the runtime is processing a thread abort request,
5259 * in which case the behavior will be undefined).
5260 * Return whether the pending exception was set or not.
5261 * It will not be set if:
5262 * * The thread or runtime is stopping or shutting down
5263 * * There already is a pending exception (and \p overwrite is false)
5265 mono_bool
5266 mono_runtime_set_pending_exception (MonoException *exc, mono_bool overwrite)
5268 MonoThread *thread = mono_thread_current ();
5270 /* The thread may already be stopping */
5271 if (thread == NULL)
5272 return FALSE;
5274 /* Don't overwrite any existing pending exceptions unless asked to */
5275 if (!overwrite && thread->pending_exception)
5276 return FALSE;
5278 MONO_OBJECT_SETREF_INTERNAL (thread, pending_exception, exc);
5280 mono_thread_request_interruption_native ();
5282 return TRUE;
5287 * mono_set_pending_exception_handle:
5289 * Set the pending exception of the current thread to EXC.
5290 * The exception will be thrown when execution returns to managed code.
5292 MONO_COLD void
5293 mono_set_pending_exception_handle (MonoExceptionHandle exc)
5295 MonoThread *thread = mono_thread_current ();
5297 /* The thread may already be stopping */
5298 if (thread == NULL)
5299 return;
5301 MONO_OBJECT_SETREF_INTERNAL (thread, pending_exception, MONO_HANDLE_RAW (exc));
5303 mono_thread_request_interruption_native ();
5306 void
5307 mono_thread_init_apartment_state (void)
5309 #ifdef HOST_WIN32
5310 MonoInternalThread* thread = mono_thread_internal_current ();
5312 /* Positive return value indicates success, either
5313 * S_OK if this is first CoInitialize call, or
5314 * S_FALSE if CoInitialize already called, but with same
5315 * threading model. A negative value indicates failure,
5316 * probably due to trying to change the threading model.
5318 if (CoInitializeEx(NULL, (thread->apartment_state == ThreadApartmentState_STA)
5319 ? COINIT_APARTMENTTHREADED
5320 : COINIT_MULTITHREADED) < 0) {
5321 thread->apartment_state = ThreadApartmentState_Unknown;
5323 #endif
5326 void
5327 mono_thread_cleanup_apartment_state (void)
5329 #ifdef HOST_WIN32
5330 MonoInternalThread* thread = mono_thread_internal_current ();
5332 if (thread && thread->apartment_state != ThreadApartmentState_Unknown) {
5333 CoUninitialize ();
5335 #endif
5338 static void
5339 mono_thread_notify_change_state (MonoThreadState old_state, MonoThreadState new_state)
5341 MonoThreadState diff = old_state ^ new_state;
5342 if (diff & ThreadState_Background) {
5343 /* If the thread changes the background mode, the main thread has to
5344 * be notified, since it has to rebuild the list of threads to
5345 * wait for.
5347 MONO_ENTER_GC_SAFE;
5348 mono_os_event_set (&background_change_event);
5349 MONO_EXIT_GC_SAFE;
5353 void
5354 mono_thread_clear_and_set_state (MonoInternalThread *thread, MonoThreadState clear, MonoThreadState set)
5356 LOCK_THREAD (thread);
5358 MonoThreadState const old_state = (MonoThreadState)thread->state;
5359 MonoThreadState const new_state = (old_state & ~clear) | set;
5360 thread->state = new_state;
5362 UNLOCK_THREAD (thread);
5364 mono_thread_notify_change_state (old_state, new_state);
5367 void
5368 mono_thread_set_state (MonoInternalThread *thread, MonoThreadState state)
5370 mono_thread_clear_and_set_state (thread, (MonoThreadState)0, state);
5374 * mono_thread_test_and_set_state:
5375 * Test if current state of \p thread include \p test. If it does not, OR \p set into the state.
5376 * \returns TRUE if \p set was OR'd in.
5378 gboolean
5379 mono_thread_test_and_set_state (MonoInternalThread *thread, MonoThreadState test, MonoThreadState set)
5381 LOCK_THREAD (thread);
5383 MonoThreadState const old_state = (MonoThreadState)thread->state;
5385 if ((old_state & test) != 0) {
5386 UNLOCK_THREAD (thread);
5387 return FALSE;
5390 MonoThreadState const new_state = old_state | set;
5391 thread->state = new_state;
5393 UNLOCK_THREAD (thread);
5395 mono_thread_notify_change_state (old_state, new_state);
5397 return TRUE;
5400 void
5401 mono_thread_clr_state (MonoInternalThread *thread, MonoThreadState state)
5403 mono_thread_clear_and_set_state (thread, state, (MonoThreadState)0);
5406 gboolean
5407 mono_thread_test_state (MonoInternalThread *thread, MonoThreadState test)
5409 LOCK_THREAD (thread);
5411 gboolean const ret = ((thread->state & test) != 0);
5413 UNLOCK_THREAD (thread);
5415 return ret;
5418 static void
5419 self_interrupt_thread (void *_unused)
5421 MonoException *exc;
5422 MonoThreadInfo *info;
5423 MonoContext ctx;
5425 exc = mono_thread_execute_interruption_ptr ();
5426 if (!exc) {
5427 if (mono_threads_are_safepoints_enabled ()) {
5428 /* We can return from an async call in coop, as
5429 * it's simply called when exiting the safepoint */
5430 /* If we're using hybrid suspend, we only self
5431 * interrupt if we were running, hence using
5432 * safepoints */
5433 return;
5436 g_error ("%s: we can't resume from an async call", __func__);
5439 info = mono_thread_info_current ();
5441 /* FIXME using thread_saved_state [ASYNC_SUSPEND_STATE_INDEX] can race with another suspend coming in. */
5442 ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
5444 mono_raise_exception_with_context (exc, &ctx);
5447 static gboolean
5448 mono_jit_info_match (MonoJitInfo *ji, gpointer ip)
5450 if (!ji)
5451 return FALSE;
5452 return ji->code_start <= ip && (char*)ip < (char*)ji->code_start + ji->code_size;
5455 static gboolean
5456 last_managed (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
5458 MonoJitInfo **dest = (MonoJitInfo **)data;
5459 *dest = frame->ji;
5460 return TRUE;
5463 static MonoJitInfo*
5464 mono_thread_info_get_last_managed (MonoThreadInfo *info)
5466 MonoJitInfo *ji = NULL;
5467 if (!info)
5468 return NULL;
5471 * The suspended thread might be holding runtime locks. Make sure we don't try taking
5472 * any runtime locks while unwinding.
5474 mono_thread_info_set_is_async_context (TRUE);
5475 mono_get_eh_callbacks ()->mono_walk_stack_with_state (last_managed, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, &ji);
5476 mono_thread_info_set_is_async_context (FALSE);
5477 return ji;
5480 typedef struct {
5481 MonoInternalThread *thread;
5482 gboolean install_async_abort;
5483 MonoThreadInfoInterruptToken *interrupt_token;
5484 } AbortThreadData;
5486 static SuspendThreadResult
5487 async_abort_critical (MonoThreadInfo *info, gpointer ud)
5489 AbortThreadData *data = (AbortThreadData *)ud;
5490 MonoInternalThread *thread = data->thread;
5491 MonoJitInfo *ji = NULL;
5492 gboolean protected_wrapper;
5493 gboolean running_managed;
5495 if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (mono_thread_info_get_suspend_state (info)))
5496 return MonoResumeThread;
5498 /*someone is already interrupting it*/
5499 if (!mono_thread_set_interruption_requested (thread))
5500 return MonoResumeThread;
5502 ji = mono_thread_info_get_last_managed (info);
5503 protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
5504 running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
5506 if (!protected_wrapper && running_managed) {
5507 /*We are in managed code*/
5508 /*Set the thread to call */
5509 if (data->install_async_abort)
5510 mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
5511 return MonoResumeThread;
5512 } else {
5514 * This will cause waits to be broken.
5515 * It will also prevent the thread from entering a wait, so if the thread returns
5516 * from the wait before it receives the abort signal, it will just spin in the wait
5517 * functions in the io-layer until the signal handler calls QueueUserAPC which will
5518 * make it return.
5520 data->interrupt_token = mono_thread_info_prepare_interrupt (info);
5522 return MonoResumeThread;
5526 static void
5527 async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort)
5529 AbortThreadData data;
5531 g_assert (thread != mono_thread_internal_current ());
5533 data.thread = thread;
5534 data.install_async_abort = install_async_abort;
5535 data.interrupt_token = NULL;
5537 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), TRUE, async_abort_critical, &data);
5538 if (data.interrupt_token)
5539 mono_thread_info_finish_interrupt (data.interrupt_token);
5540 /*FIXME we need to wait for interruption to complete -- figure out how much into interruption we should wait for here*/
5543 static void
5544 self_abort_internal (MonoError *error)
5546 HANDLE_FUNCTION_ENTER ();
5548 error_init (error);
5550 /* FIXME this is insanely broken, it doesn't cause interruption to happen synchronously
5551 * since passing FALSE to mono_thread_request_interruption makes sure it returns NULL */
5554 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.
5556 MonoExceptionHandle exc = MONO_HANDLE_NEW (MonoException, NULL);
5557 if (mono_thread_request_interruption_managed (&exc))
5558 mono_error_set_exception_handle (error, exc);
5559 else
5560 mono_thread_info_self_interrupt ();
5562 HANDLE_FUNCTION_RETURN ();
5565 typedef struct {
5566 MonoInternalThread *thread;
5567 gboolean interrupt;
5568 MonoThreadInfoInterruptToken *interrupt_token;
5569 } SuspendThreadData;
5571 static SuspendThreadResult
5572 async_suspend_critical (MonoThreadInfo *info, gpointer ud)
5574 SuspendThreadData *data = (SuspendThreadData *)ud;
5575 MonoInternalThread *thread = data->thread;
5576 MonoJitInfo *ji = NULL;
5577 gboolean protected_wrapper;
5578 gboolean running_managed;
5580 ji = mono_thread_info_get_last_managed (info);
5581 protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
5582 running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
5584 if (running_managed && !protected_wrapper) {
5585 if (mono_threads_are_safepoints_enabled ()) {
5586 mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
5587 return MonoResumeThread;
5588 } else {
5589 thread->state &= ~ThreadState_SuspendRequested;
5590 thread->state |= ThreadState_Suspended;
5591 return KeepSuspended;
5593 } else {
5594 mono_thread_set_interruption_requested (thread);
5595 if (data->interrupt)
5596 data->interrupt_token = mono_thread_info_prepare_interrupt ((MonoThreadInfo *)thread->thread_info);
5598 return MonoResumeThread;
5602 /* LOCKING: called with @thread longlived->synch_cs held, and releases it */
5603 static void
5604 async_suspend_internal (MonoInternalThread *thread, gboolean interrupt)
5606 SuspendThreadData data;
5608 g_assert (thread != mono_thread_internal_current ());
5610 // g_async_safe_printf ("ASYNC SUSPEND thread %p\n", thread_get_tid (thread));
5612 thread->self_suspended = FALSE;
5614 data.thread = thread;
5615 data.interrupt = interrupt;
5616 data.interrupt_token = NULL;
5618 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), interrupt, async_suspend_critical, &data);
5619 if (data.interrupt_token)
5620 mono_thread_info_finish_interrupt (data.interrupt_token);
5622 UNLOCK_THREAD (thread);
5625 /* LOCKING: called with @thread longlived->synch_cs held, and releases it */
5626 static void
5627 self_suspend_internal (void)
5629 MonoInternalThread *thread;
5630 MonoOSEvent *event;
5631 MonoOSEventWaitRet res;
5633 thread = mono_thread_internal_current ();
5635 // g_async_safe_printf ("SELF SUSPEND thread %p\n", thread_get_tid (thread));
5637 thread->self_suspended = TRUE;
5639 thread->state &= ~ThreadState_SuspendRequested;
5640 thread->state |= ThreadState_Suspended;
5642 UNLOCK_THREAD (thread);
5644 event = thread->suspended;
5646 MONO_ENTER_GC_SAFE;
5647 res = mono_os_event_wait_one (event, MONO_INFINITE_WAIT, TRUE);
5648 g_assert (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0 || res == MONO_OS_EVENT_WAIT_RET_ALERTED);
5649 MONO_EXIT_GC_SAFE;
5652 static void
5653 suspend_for_shutdown_async_call (gpointer unused)
5655 for (;;)
5656 mono_thread_info_yield ();
5659 static SuspendThreadResult
5660 suspend_for_shutdown_critical (MonoThreadInfo *info, gpointer unused)
5662 mono_thread_info_setup_async_call (info, suspend_for_shutdown_async_call, NULL);
5663 return MonoResumeThread;
5666 void
5667 mono_thread_internal_suspend_for_shutdown (MonoInternalThread *thread)
5669 g_assert (thread != mono_thread_internal_current ());
5671 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, suspend_for_shutdown_critical, NULL);
5675 * mono_thread_is_foreign:
5676 * \param thread the thread to query
5678 * This function allows one to determine if a thread was created by the mono runtime and has
5679 * a well defined lifecycle or it's a foreign one, created by the native environment.
5681 * \returns TRUE if \p thread was not created by the runtime.
5683 mono_bool
5684 mono_thread_is_foreign (MonoThread *thread)
5686 mono_bool result;
5687 MONO_ENTER_GC_UNSAFE;
5688 MonoThreadInfo *info = (MonoThreadInfo *)thread->internal_thread->thread_info;
5689 result = (info->runtime_thread == FALSE);
5690 MONO_EXIT_GC_UNSAFE;
5691 return result;
5694 #ifndef HOST_WIN32
5695 static void
5696 threads_native_thread_join_lock (gpointer tid, gpointer value)
5699 * Have to cast to a pointer-sized integer first, as we can't narrow
5700 * from a pointer if pthread_t is an integer smaller than a pointer.
5702 pthread_t thread = (pthread_t)(intptr_t)tid;
5703 if (thread != pthread_self ()) {
5704 MONO_ENTER_GC_SAFE;
5705 /* This shouldn't block */
5706 mono_threads_join_lock ();
5707 mono_native_thread_join (thread);
5708 mono_threads_join_unlock ();
5709 MONO_EXIT_GC_SAFE;
5712 static void
5713 threads_native_thread_join_nolock (gpointer tid, gpointer value)
5715 pthread_t thread = (pthread_t)(intptr_t)tid;
5716 MONO_ENTER_GC_SAFE;
5717 mono_native_thread_join (thread);
5718 MONO_EXIT_GC_SAFE;
5721 static void
5722 threads_add_joinable_thread_nolock (gpointer tid)
5724 g_hash_table_insert (joinable_threads, tid, tid);
5726 #else
5727 static void
5728 threads_native_thread_join_lock (gpointer tid, gpointer value)
5730 MonoNativeThreadId thread_id = (MonoNativeThreadId)(guint64)tid;
5731 HANDLE thread_handle = (HANDLE)value;
5732 if (thread_id != GetCurrentThreadId () && thread_handle != NULL && thread_handle != INVALID_HANDLE_VALUE) {
5733 MONO_ENTER_GC_SAFE;
5734 /* This shouldn't block */
5735 mono_threads_join_lock ();
5736 mono_native_thread_join_handle (thread_handle, TRUE);
5737 mono_threads_join_unlock ();
5738 MONO_EXIT_GC_SAFE;
5742 static void
5743 threads_native_thread_join_nolock (gpointer tid, gpointer value)
5745 HANDLE thread_handle = (HANDLE)value;
5746 MONO_ENTER_GC_SAFE;
5747 mono_native_thread_join_handle (thread_handle, TRUE);
5748 MONO_EXIT_GC_SAFE;
5751 static void
5752 threads_add_joinable_thread_nolock (gpointer tid)
5754 g_hash_table_insert (joinable_threads, tid, (gpointer)OpenThread (SYNCHRONIZE, TRUE, (MonoNativeThreadId)(guint64)tid));
5756 #endif
5758 static void
5759 threads_add_pending_joinable_thread (gpointer tid)
5761 joinable_threads_lock ();
5763 if (!pending_joinable_threads)
5764 pending_joinable_threads = g_hash_table_new (NULL, NULL);
5766 gpointer orig_key;
5767 gpointer value;
5769 if (!g_hash_table_lookup_extended (pending_joinable_threads, tid, &orig_key, &value)) {
5770 g_hash_table_insert (pending_joinable_threads, tid, tid);
5771 UnlockedIncrement (&pending_joinable_thread_count);
5774 joinable_threads_unlock ();
5777 static void
5778 threads_add_pending_joinable_runtime_thread (MonoThreadInfo *mono_thread_info)
5780 g_assert (mono_thread_info);
5782 if (mono_thread_info->runtime_thread) {
5783 threads_add_pending_joinable_thread ((gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (mono_thread_info_get_tid (mono_thread_info))));
5787 static void
5788 threads_remove_pending_joinable_thread_nolock (gpointer tid)
5790 gpointer orig_key;
5791 gpointer value;
5793 if (pending_joinable_threads && g_hash_table_lookup_extended (pending_joinable_threads, tid, &orig_key, &value)) {
5794 g_hash_table_remove (pending_joinable_threads, tid);
5795 if (UnlockedDecrement (&pending_joinable_thread_count) == 0)
5796 mono_coop_cond_broadcast (&zero_pending_joinable_thread_event);
5800 static gboolean
5801 threads_wait_pending_joinable_threads (uint32_t timeout)
5803 if (UnlockedRead (&pending_joinable_thread_count) > 0) {
5804 joinable_threads_lock ();
5805 if (timeout == MONO_INFINITE_WAIT) {
5806 while (UnlockedRead (&pending_joinable_thread_count) > 0)
5807 mono_coop_cond_wait (&zero_pending_joinable_thread_event, &joinable_threads_mutex);
5808 } else {
5809 gint64 start = mono_msec_ticks ();
5810 gint64 elapsed = 0;
5811 while (UnlockedRead (&pending_joinable_thread_count) > 0 && elapsed < timeout) {
5812 mono_coop_cond_timedwait (&zero_pending_joinable_thread_event, &joinable_threads_mutex, timeout - (uint32_t)elapsed);
5813 elapsed = mono_msec_ticks () - start;
5816 joinable_threads_unlock ();
5819 return UnlockedRead (&pending_joinable_thread_count) == 0;
5822 static void
5823 threads_add_unique_joinable_thread_nolock (gpointer tid)
5825 if (!joinable_threads)
5826 joinable_threads = g_hash_table_new (NULL, NULL);
5828 gpointer orig_key;
5829 gpointer value;
5831 if (!g_hash_table_lookup_extended (joinable_threads, tid, &orig_key, &value)) {
5832 threads_add_joinable_thread_nolock (tid);
5833 UnlockedIncrement (&joinable_thread_count);
5837 void
5838 mono_threads_add_joinable_runtime_thread (MonoThreadInfo *thread_info)
5840 g_assert (thread_info);
5841 MonoThreadInfo *mono_thread_info = thread_info;
5843 if (mono_thread_info->runtime_thread) {
5844 gpointer tid = (gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (mono_thread_info_get_tid (mono_thread_info)));
5846 joinable_threads_lock ();
5848 // Add to joinable thread list, if not already included.
5849 threads_add_unique_joinable_thread_nolock (tid);
5851 // Remove thread from pending joinable list, if present.
5852 threads_remove_pending_joinable_thread_nolock (tid);
5854 joinable_threads_unlock ();
5856 mono_gc_finalize_notify ();
5860 static void
5861 threads_add_pending_native_thread_join_call_nolock (gpointer tid)
5863 if (!pending_native_thread_join_calls)
5864 pending_native_thread_join_calls = g_hash_table_new (NULL, NULL);
5866 gpointer orig_key;
5867 gpointer value;
5869 if (!g_hash_table_lookup_extended (pending_native_thread_join_calls, tid, &orig_key, &value))
5870 g_hash_table_insert (pending_native_thread_join_calls, tid, tid);
5873 static void
5874 threads_remove_pending_native_thread_join_call_nolock (gpointer tid)
5876 if (pending_native_thread_join_calls)
5877 g_hash_table_remove (pending_native_thread_join_calls, tid);
5879 mono_coop_cond_broadcast (&pending_native_thread_join_calls_event);
5882 static void
5883 threads_wait_pending_native_thread_join_call_nolock (gpointer tid)
5885 gpointer orig_key;
5886 gpointer value;
5888 while (g_hash_table_lookup_extended (pending_native_thread_join_calls, tid, &orig_key, &value)) {
5889 mono_coop_cond_wait (&pending_native_thread_join_calls_event, &joinable_threads_mutex);
5894 * mono_add_joinable_thread:
5896 * Add TID to the list of joinable threads.
5897 * LOCKING: Acquires the threads lock.
5899 void
5900 mono_threads_add_joinable_thread (gpointer tid)
5903 * We cannot detach from threads because it causes problems like
5904 * 2fd16f60/r114307. So we collect them and join them when
5905 * we have time (in the finalizer thread).
5907 joinable_threads_lock ();
5908 threads_add_unique_joinable_thread_nolock (tid);
5909 joinable_threads_unlock ();
5911 mono_gc_finalize_notify ();
5915 * mono_threads_join_threads:
5917 * Join all joinable threads. This is called from the finalizer thread.
5918 * LOCKING: Acquires the threads lock.
5920 void
5921 mono_threads_join_threads (void)
5923 GHashTableIter iter;
5924 gpointer key = NULL;
5925 gpointer value = NULL;
5926 gboolean found = FALSE;
5928 /* Fastpath */
5929 if (!UnlockedRead (&joinable_thread_count))
5930 return;
5932 while (TRUE) {
5933 joinable_threads_lock ();
5934 if (found) {
5935 // Previous native thread join call completed.
5936 threads_remove_pending_native_thread_join_call_nolock (key);
5938 found = FALSE;
5939 if (g_hash_table_size (joinable_threads)) {
5940 g_hash_table_iter_init (&iter, joinable_threads);
5941 g_hash_table_iter_next (&iter, &key, (void**)&value);
5942 g_hash_table_remove (joinable_threads, key);
5943 UnlockedDecrement (&joinable_thread_count);
5944 found = TRUE;
5946 // Add to table of tid's with pending native thread join call.
5947 threads_add_pending_native_thread_join_call_nolock (key);
5949 joinable_threads_unlock ();
5950 if (found)
5951 threads_native_thread_join_lock (key, value);
5952 else
5953 break;
5958 * mono_thread_join:
5960 * Wait for thread TID to exit.
5961 * LOCKING: Acquires the threads lock.
5963 void
5964 mono_thread_join (gpointer tid)
5966 gboolean found = FALSE;
5967 gpointer orig_key;
5968 gpointer value;
5970 joinable_threads_lock ();
5971 if (!joinable_threads)
5972 joinable_threads = g_hash_table_new (NULL, NULL);
5974 if (g_hash_table_lookup_extended (joinable_threads, tid, &orig_key, &value)) {
5975 g_hash_table_remove (joinable_threads, tid);
5976 UnlockedDecrement (&joinable_thread_count);
5977 found = TRUE;
5979 // Add to table of tid's with pending native join call.
5980 threads_add_pending_native_thread_join_call_nolock (tid);
5983 if (!found) {
5984 // Wait for any pending native thread join call not yet completed for this tid.
5985 threads_wait_pending_native_thread_join_call_nolock (tid);
5988 joinable_threads_unlock ();
5990 if (!found)
5991 return;
5993 threads_native_thread_join_nolock (tid, value);
5995 joinable_threads_lock ();
5996 // Native thread join call completed for this tid.
5997 threads_remove_pending_native_thread_join_call_nolock (tid);
5998 joinable_threads_unlock ();
6001 void
6002 mono_thread_internal_unhandled_exception (MonoObject* exc)
6004 MonoClass *klass = exc->vtable->klass;
6005 if (is_threadabort_exception (klass)) {
6006 mono_thread_internal_reset_abort (mono_thread_internal_current ());
6007 } else if (!is_appdomainunloaded_exception (klass)
6008 && mono_runtime_unhandled_exception_policy_get () == MONO_UNHANDLED_POLICY_CURRENT) {
6009 mono_unhandled_exception_internal (exc);
6010 if (mono_environment_exitcode_get () == 1) {
6011 mono_environment_exitcode_set (255);
6012 mono_invoke_unhandled_exception_hook (exc);
6013 g_assert_not_reached ();
6019 * mono_threads_attach_coop_internal: called by native->managed wrappers
6021 * - @cookie:
6022 * - blocking mode: contains gc unsafe transition cookie
6023 * - non-blocking mode: contains random data
6024 * - @stackdata: semi-opaque struct: stackpointer and function_name
6025 * - @return: the original domain which needs to be restored, or NULL.
6027 MonoDomain*
6028 mono_threads_attach_coop_internal (MonoDomain *domain, gpointer *cookie, MonoStackData *stackdata)
6030 MonoDomain *orig;
6031 MonoThreadInfo *info;
6032 gboolean external = FALSE;
6034 orig = mono_domain_get ();
6036 if (!domain) {
6037 /* Happens when called from AOTed code which is only used in the root domain. */
6038 domain = mono_get_root_domain ();
6039 g_assert (domain);
6042 /* On coop, when we detached, we moved the thread from RUNNING->BLOCKING.
6043 * If we try to reattach we do a BLOCKING->RUNNING transition. If the thread
6044 * is fresh, mono_thread_attach() will do a STARTING->RUNNING transition so
6045 * we're only responsible for making the cookie. */
6046 if (mono_threads_is_blocking_transition_enabled ())
6047 external = !(info = mono_thread_info_current_unchecked ()) || !mono_thread_info_is_live (info);
6049 if (!mono_thread_internal_current ()) {
6050 mono_thread_attach (domain);
6052 // #678164
6053 mono_thread_set_state (mono_thread_internal_current (), ThreadState_Background);
6056 if (mono_threads_is_blocking_transition_enabled ()) {
6057 if (external) {
6058 /* mono_thread_attach put the thread in RUNNING mode from STARTING, but we need to
6059 * return the right cookie. */
6060 *cookie = mono_threads_enter_gc_unsafe_region_cookie ();
6061 } else {
6062 /* thread state (BLOCKING|RUNNING) -> RUNNING */
6063 *cookie = mono_threads_enter_gc_unsafe_region_unbalanced_internal (stackdata);
6067 if (orig != domain)
6068 mono_domain_set_fast (domain, TRUE);
6070 return orig;
6074 * mono_threads_attach_coop: called by native->managed wrappers
6076 * - @dummy:
6077 * - blocking mode: contains gc unsafe transition cookie
6078 * - non-blocking mode: contains random data
6079 * - a pointer to stack, used for some checks
6080 * - @return: the original domain which needs to be restored, or NULL.
6082 gpointer
6083 mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
6085 MONO_STACKDATA (stackdata);
6086 stackdata.stackpointer = dummy;
6087 return mono_threads_attach_coop_internal (domain, dummy, &stackdata);
6091 * mono_threads_detach_coop_internal: called by native->managed wrappers
6093 * - @orig: the original domain which needs to be restored, or NULL.
6094 * - @stackdata: semi-opaque struct: stackpointer and function_name
6095 * - @cookie:
6096 * - blocking mode: contains gc unsafe transition cookie
6097 * - non-blocking mode: contains random data
6099 void
6100 mono_threads_detach_coop_internal (MonoDomain *orig, gpointer cookie, MonoStackData *stackdata)
6102 MonoDomain *domain = mono_domain_get ();
6103 g_assert (domain);
6105 if (orig != domain) {
6106 if (!orig)
6107 mono_domain_unset ();
6108 else
6109 mono_domain_set_fast (orig, TRUE);
6112 if (mono_threads_is_blocking_transition_enabled ()) {
6113 /* it won't do anything if cookie is NULL
6114 * thread state RUNNING -> (RUNNING|BLOCKING) */
6115 mono_threads_exit_gc_unsafe_region_unbalanced_internal (cookie, stackdata);
6120 * mono_threads_detach_coop: called by native->managed wrappers
6122 * - @orig: the original domain which needs to be restored, or NULL.
6123 * - @dummy:
6124 * - blocking mode: contains gc unsafe transition cookie
6125 * - non-blocking mode: contains random data
6126 * - a pointer to stack, used for some checks
6128 void
6129 mono_threads_detach_coop (gpointer orig, gpointer *dummy)
6131 MONO_STACKDATA (stackdata);
6132 stackdata.stackpointer = dummy;
6133 mono_threads_detach_coop_internal ((MonoDomain*)orig, *dummy, &stackdata);
6136 #if 0
6137 /* Returns TRUE if the current thread is ready to be interrupted. */
6138 gboolean
6139 mono_threads_is_ready_to_be_interrupted (void)
6141 MonoInternalThread *thread;
6143 thread = mono_thread_internal_current ();
6144 LOCK_THREAD (thread);
6145 if (thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
6146 UNLOCK_THREAD (thread);
6147 return FALSE;
6150 if (mono_thread_get_abort_prot_block_count (thread) || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ()) {
6151 UNLOCK_THREAD (thread);
6152 return FALSE;
6155 UNLOCK_THREAD (thread);
6156 return TRUE;
6158 #endif
6160 void
6161 mono_thread_internal_describe (MonoInternalThread *internal, GString *text)
6163 g_string_append_printf (text, ", thread handle : %p", internal->handle);
6165 if (internal->thread_info) {
6166 g_string_append (text, ", state : ");
6167 mono_thread_info_describe_interrupt_token (internal->thread_info, text);
6170 if (internal->owned_mutexes) {
6171 int i;
6173 g_string_append (text, ", owns : [");
6174 for (i = 0; i < internal->owned_mutexes->len; i++)
6175 g_string_append_printf (text, i == 0 ? "%p" : ", %p", g_ptr_array_index (internal->owned_mutexes, i));
6176 g_string_append (text, "]");
6180 gboolean
6181 mono_thread_internal_is_current (MonoInternalThread *internal)
6183 g_assert (internal);
6184 return mono_native_thread_id_equals (mono_native_thread_id_get (), MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid));
6187 void
6188 mono_set_thread_dump_dir (gchar* dir) {
6189 thread_dump_dir = dir;
6192 #ifdef DISABLE_CRASH_REPORTING
6193 void
6194 mono_threads_summarize_init (void)
6198 gboolean
6199 mono_threads_summarize (MonoContext *ctx, gchar **out, MonoStackHash *hashes, gboolean silent, gboolean signal_handler_controller, gchar *mem, size_t provided_size)
6201 return FALSE;
6204 gboolean
6205 mono_threads_summarize_one (MonoThreadSummary *out, MonoContext *ctx)
6207 return FALSE;
6210 #else
6212 static gboolean
6213 mono_threads_summarize_native_self (MonoThreadSummary *out, MonoContext *ctx)
6215 if (!mono_get_eh_callbacks ()->mono_summarize_managed_stack)
6216 return FALSE;
6218 memset (out, 0, sizeof (MonoThreadSummary));
6219 out->ctx = ctx;
6221 MonoNativeThreadId current = mono_native_thread_id_get();
6222 out->native_thread_id = (intptr_t) current;
6224 mono_get_eh_callbacks ()->mono_summarize_unmanaged_stack (out);
6226 mono_native_thread_get_name (current, out->name, MONO_MAX_SUMMARY_NAME_LEN);
6228 return TRUE;
6231 // Not safe to call from signal handler
6232 gboolean
6233 mono_threads_summarize_one (MonoThreadSummary *out, MonoContext *ctx)
6235 gboolean success = mono_threads_summarize_native_self (out, ctx);
6237 // Finish this on the same thread
6239 if (success && mono_get_eh_callbacks ()->mono_summarize_managed_stack)
6240 mono_get_eh_callbacks ()->mono_summarize_managed_stack (out);
6242 return success;
6245 #define MAX_NUM_THREADS 128
6246 typedef struct {
6247 gint32 has_owner; // state of this memory
6249 MonoSemType update; // notify of addition of threads
6251 int nthreads;
6252 MonoNativeThreadId thread_array [MAX_NUM_THREADS]; // ids of threads we're dumping
6254 int nthreads_attached; // Number of threads self-registered
6255 MonoThreadSummary *all_threads [MAX_NUM_THREADS];
6257 gboolean silent; // print to stdout
6258 } SummarizerGlobalState;
6260 #if defined(HAVE_KILL) && !defined(HOST_ANDROID) && defined(HAVE_WAITPID) && defined(HAVE_EXECVE) && ((!defined(HOST_DARWIN) && defined(SYS_fork)) || HAVE_FORK)
6261 #define HAVE_MONO_SUMMARIZER_SUPERVISOR 1
6262 #endif
6264 static void
6265 summarizer_supervisor_init (void);
6267 typedef struct {
6268 MonoSemType supervisor;
6269 pid_t pid;
6270 pid_t supervisor_pid;
6271 } SummarizerSupervisorState;
6273 #ifndef HAVE_MONO_SUMMARIZER_SUPERVISOR
6275 void
6276 summarizer_supervisor_init (void)
6278 return;
6281 static pid_t
6282 summarizer_supervisor_start (SummarizerSupervisorState *state)
6284 // nonzero, so caller doesn't think it's the supervisor
6285 return (pid_t) 1;
6288 static void
6289 summarizer_supervisor_end (SummarizerSupervisorState *state)
6291 return;
6294 #else
6295 static const char *hang_watchdog_path;
6297 void
6298 summarizer_supervisor_init (void)
6300 hang_watchdog_path = g_build_filename (mono_get_config_dir (), "..", "bin", "mono-hang-watchdog", NULL);
6301 g_assert (hang_watchdog_path);
6304 static pid_t
6305 summarizer_supervisor_start (SummarizerSupervisorState *state)
6307 memset (state, 0, sizeof (*state));
6308 pid_t pid;
6310 state->pid = getpid();
6313 * glibc fork acquires some locks, so if the crash happened inside malloc/free,
6314 * it will deadlock. Call the syscall directly instead.
6316 #if defined(HOST_ANDROID)
6317 /* SYS_fork is defined to be __NR_fork which is not defined in some ndk versions */
6318 // We disable this when we set HAVE_MONO_SUMMARIZER_SUPERVISOR above
6319 g_assert_not_reached ();
6320 #elif !defined(HOST_DARWIN) && defined(SYS_fork)
6321 pid = (pid_t) syscall (SYS_fork);
6322 #elif HAVE_FORK
6323 pid = (pid_t) fork ();
6324 #else
6325 g_assert_not_reached ();
6326 #endif
6328 if (pid != 0)
6329 state->supervisor_pid = pid;
6330 else {
6331 char pid_str[20]; // pid is a uint64_t, 20 digits max in decimal form
6332 sprintf (pid_str, "%" PRIu64, (uint64_t)state->pid);
6333 const char *const args[] = { hang_watchdog_path, pid_str, NULL };
6334 execve (args[0], (char * const*)args, NULL); // run 'mono-hang-watchdog [pid]'
6335 g_async_safe_printf ("Could not exec mono-hang-watchdog, expected on path '%s' (errno %d)\n", hang_watchdog_path, errno);
6336 exit (1);
6339 return pid;
6342 static void
6343 summarizer_supervisor_end (SummarizerSupervisorState *state)
6345 #ifdef HAVE_KILL
6346 kill (state->supervisor_pid, SIGKILL);
6347 #endif
6349 #if defined (HAVE_WAITPID)
6350 // Accessed on same thread that sets it.
6351 int status;
6352 waitpid (state->supervisor_pid, &status, 0);
6353 #endif
6355 #endif
6357 static void
6358 collect_thread_id (gpointer key, gpointer value, gpointer user)
6360 CollectThreadIdsUserData *ud = (CollectThreadIdsUserData *)user;
6361 MonoInternalThread *thread = (MonoInternalThread *)value;
6363 if (ud->nthreads < ud->max_threads)
6364 ud->threads [ud->nthreads ++] = thread_get_tid (thread);
6367 static int
6368 collect_thread_ids (MonoNativeThreadId *thread_ids, int max_threads)
6370 CollectThreadIdsUserData ud;
6372 mono_memory_barrier ();
6373 if (!threads)
6374 return 0;
6376 memset (&ud, 0, sizeof (ud));
6377 /* This array contains refs, but its on the stack, so its ok */
6378 ud.threads = thread_ids;
6379 ud.max_threads = max_threads;
6381 mono_threads_lock ();
6382 mono_g_hash_table_foreach (threads, collect_thread_id, &ud);
6383 mono_threads_unlock ();
6385 return ud.nthreads;
6388 static gboolean
6389 summarizer_state_init (SummarizerGlobalState *state, MonoNativeThreadId current, int *my_index)
6391 gint32 started_state = mono_atomic_cas_i32 (&state->has_owner, 1 /* set */, 0 /* compare */);
6392 gboolean not_started = started_state == 0;
6393 if (not_started) {
6394 state->nthreads = collect_thread_ids (state->thread_array, MAX_NUM_THREADS);
6395 mono_os_sem_init (&state->update, 0);
6398 for (int i = 0; i < state->nthreads; i++) {
6399 if (state->thread_array [i] == current) {
6400 *my_index = i;
6401 break;
6405 return not_started;
6408 static void
6409 summarizer_signal_other_threads (SummarizerGlobalState *state, MonoNativeThreadId current, int current_idx)
6411 sigset_t sigset, old_sigset;
6412 sigemptyset(&sigset);
6413 sigaddset(&sigset, SIGTERM);
6415 for (int i=0; i < state->nthreads; i++) {
6416 sigprocmask (SIG_UNBLOCK, &sigset, &old_sigset);
6418 if (i == current_idx)
6419 continue;
6420 #ifdef HAVE_PTHREAD_KILL
6421 pthread_kill (state->thread_array [i], SIGTERM);
6423 if (!state->silent)
6424 g_async_safe_printf("Pkilling 0x%" G_GSIZE_FORMAT "x from 0x%" G_GSIZE_FORMAT "x\n", (gsize)MONO_NATIVE_THREAD_ID_TO_UINT (state->thread_array [i]), (gsize)MONO_NATIVE_THREAD_ID_TO_UINT (current));
6425 #else
6426 g_error ("pthread_kill () is not supported by this platform");
6427 #endif
6431 // Returns true when there are shared global references to "this_thread"
6432 static gboolean
6433 summarizer_post_dump (SummarizerGlobalState *state, MonoThreadSummary *this_thread, int current_idx)
6435 mono_memory_barrier ();
6437 gpointer old = mono_atomic_cas_ptr ((volatile gpointer *)&state->all_threads [current_idx], this_thread, NULL);
6439 if (old == GINT_TO_POINTER (-1)) {
6440 g_async_safe_printf ("Trying to register response after dumping period ended");
6441 return FALSE;
6442 } else if (old != NULL) {
6443 g_async_safe_printf ("Thread dump raced for thread slot.");
6444 return FALSE;
6447 // We added our pointer
6448 gint32 count = mono_atomic_inc_i32 ((volatile gint32 *) &state->nthreads_attached);
6449 if (count == state->nthreads)
6450 mono_os_sem_post (&state->update);
6452 return TRUE;
6455 // A lockless spinwait with a timeout
6456 // Used in environments where locks are unsafe
6458 // If set_pos is true, we wait until the expected number of threads have
6459 // responded and then count that the expected number are set. If it is not true,
6460 // then we wait for them to be unset.
6461 static void
6462 summary_timedwait (SummarizerGlobalState *state, int timeout_seconds)
6464 const gint64 milliseconds_in_second = 1000;
6465 gint64 timeout_total = milliseconds_in_second * timeout_seconds;
6467 gint64 end = mono_msec_ticks () + timeout_total;
6469 while (TRUE) {
6470 if (mono_atomic_load_i32 ((volatile gint32 *) &state->nthreads_attached) == state->nthreads)
6471 break;
6473 gint64 now = mono_msec_ticks ();
6474 gint64 remaining = end - now;
6475 if (remaining <= 0)
6476 break;
6478 mono_os_sem_timedwait (&state->update, remaining, MONO_SEM_FLAGS_NONE);
6481 return;
6484 static MonoThreadSummary *
6485 summarizer_try_read_thread (SummarizerGlobalState *state, int index)
6487 gpointer old_value = NULL;
6488 gpointer new_value = GINT_TO_POINTER(-1);
6490 do {
6491 old_value = state->all_threads [index];
6492 } while (mono_atomic_cas_ptr ((volatile gpointer *) &state->all_threads [index], new_value, old_value) != old_value);
6494 MonoThreadSummary *thread = (MonoThreadSummary *) old_value;
6495 return thread;
6498 static void
6499 summarizer_state_term (SummarizerGlobalState *state, gchar **out, gchar *mem, size_t provided_size, MonoThreadSummary *controlling)
6501 // See the array writes
6502 mono_memory_barrier ();
6504 MonoThreadSummary *threads [MAX_NUM_THREADS];
6505 memset (threads, 0, sizeof(threads));
6507 mono_summarize_timeline_phase_log (MonoSummaryManagedStacks);
6508 for (int i=0; i < state->nthreads; i++) {
6509 threads [i] = summarizer_try_read_thread (state, i);
6510 if (!threads [i])
6511 continue;
6513 // We are doing this dump on the controlling thread because this isn't
6514 // an async context sometimes. There's still some reliance on malloc here, but it's
6515 // much more stable to do it all from the controlling thread.
6517 // This is non-null, checked in mono_threads_summarize
6518 // with early exit there
6519 mono_get_eh_callbacks ()->mono_summarize_managed_stack (threads [i]);
6522 MonoStateWriter writer;
6523 memset (&writer, 0, sizeof (writer));
6525 mono_summarize_timeline_phase_log (MonoSummaryStateWriter);
6526 mono_summarize_native_state_begin (&writer, mem, provided_size);
6527 for (int i=0; i < state->nthreads; i++) {
6528 MonoThreadSummary *thread = threads [i];
6529 if (!thread)
6530 continue;
6532 mono_summarize_native_state_add_thread (&writer, thread, thread->ctx, thread == controlling);
6533 // Set non-shared state to notify the waiting thread to clean up
6534 // without having to keep our shared state alive
6535 mono_atomic_store_i32 (&thread->done, 0x1);
6536 mono_os_sem_post (&thread->done_wait);
6538 *out = mono_summarize_native_state_end (&writer);
6539 mono_summarize_timeline_phase_log (MonoSummaryStateWriterDone);
6541 mono_os_sem_destroy (&state->update);
6543 memset (state, 0, sizeof (*state));
6544 mono_atomic_store_i32 ((volatile gint32 *)&state->has_owner, 0);
6547 static void
6548 summarizer_state_wait (MonoThreadSummary *thread)
6550 gint64 milliseconds_in_second = 1000;
6552 // cond_wait can spuriously wake up, so we need to check
6553 // done
6554 while (!mono_atomic_load_i32 (&thread->done))
6555 mono_os_sem_timedwait (&thread->done_wait, milliseconds_in_second, MONO_SEM_FLAGS_NONE);
6558 static gboolean
6559 mono_threads_summarize_execute_internal (MonoContext *ctx, gchar **out, MonoStackHash *hashes, gboolean silent, gchar *working_mem, size_t provided_size, gboolean this_thread_controls)
6561 static SummarizerGlobalState state;
6563 int current_idx;
6564 MonoNativeThreadId current = mono_native_thread_id_get ();
6565 gboolean thread_given_control = summarizer_state_init (&state, current, &current_idx);
6567 g_assert (this_thread_controls == thread_given_control);
6569 if (state.nthreads == 0) {
6570 if (!silent)
6571 g_async_safe_printf("No threads attached to runtime.\n");
6572 memset (&state, 0, sizeof (state));
6573 return FALSE;
6576 if (this_thread_controls) {
6577 g_assert (working_mem);
6579 mono_summarize_timeline_phase_log (MonoSummarySuspendHandshake);
6580 state.silent = silent;
6581 summarizer_signal_other_threads (&state, current, current_idx);
6582 mono_summarize_timeline_phase_log (MonoSummaryUnmanagedStacks);
6585 MonoStateMem mem;
6586 gboolean success = mono_state_alloc_mem (&mem, (long) current, sizeof (MonoThreadSummary));
6587 if (!success)
6588 return FALSE;
6590 MonoThreadSummary *this_thread = (MonoThreadSummary *) mem.mem;
6592 if (mono_threads_summarize_native_self (this_thread, ctx)) {
6593 // Init the synchronization between the controlling thread and the
6594 // providing thread
6595 mono_os_sem_init (&this_thread->done_wait, 0);
6597 // Store a reference to our stack memory into global state
6598 gboolean success = summarizer_post_dump (&state, this_thread, current_idx);
6599 if (!success && !state.silent)
6600 g_async_safe_printf("Thread 0x%" G_GSIZE_FORMAT "x reported itself.\n", (gsize)MONO_NATIVE_THREAD_ID_TO_UINT (current));
6601 } else if (!state.silent) {
6602 g_async_safe_printf("Thread 0x%" G_GSIZE_FORMAT "x couldn't report itself.\n", (gsize)MONO_NATIVE_THREAD_ID_TO_UINT (current));
6605 // From summarizer, wait and dump.
6606 if (this_thread_controls) {
6607 if (!state.silent)
6608 g_async_safe_printf("Entering thread summarizer pause from 0x%" G_GSIZE_FORMAT "x\n", (gsize)MONO_NATIVE_THREAD_ID_TO_UINT (current));
6610 // Wait up to 2 seconds for all of the other threads to catch up
6611 summary_timedwait (&state, 2);
6613 if (!state.silent)
6614 g_async_safe_printf("Finished thread summarizer pause from 0x%" G_GSIZE_FORMAT "x.\n", (gsize)MONO_NATIVE_THREAD_ID_TO_UINT (current));
6616 // Dump and cleanup all the stack memory
6617 summarizer_state_term (&state, out, working_mem, provided_size, this_thread);
6618 } else {
6619 // Wait here, keeping our stack memory alive
6620 // for the dumper
6621 summarizer_state_wait (this_thread);
6624 // FIXME: How many threads should be counted?
6625 if (hashes)
6626 *hashes = this_thread->hashes;
6628 mono_state_free_mem (&mem);
6630 return TRUE;
6633 void
6634 mono_threads_summarize_init (void)
6636 summarizer_supervisor_init ();
6639 gboolean
6640 mono_threads_summarize_execute (MonoContext *ctx, gchar **out, MonoStackHash *hashes, gboolean silent, gchar *working_mem, size_t provided_size)
6642 gboolean result;
6643 gboolean already_async = mono_thread_info_is_async_context ();
6644 if (!already_async)
6645 mono_thread_info_set_is_async_context (TRUE);
6646 result = mono_threads_summarize_execute_internal (ctx, out, hashes, silent, working_mem, provided_size, FALSE);
6647 if (!already_async)
6648 mono_thread_info_set_is_async_context (FALSE);
6649 return result;
6652 gboolean
6653 mono_threads_summarize (MonoContext *ctx, gchar **out, MonoStackHash *hashes, gboolean silent, gboolean signal_handler_controller, gchar *mem, size_t provided_size)
6655 if (!mono_get_eh_callbacks ()->mono_summarize_managed_stack)
6656 return FALSE;
6658 // The staggered values are due to the need to use inc_i64 for the first value
6659 static gint64 next_pending_request_id = 0;
6660 static gint64 request_available_to_run = 1;
6661 gint64 this_request_id = mono_atomic_inc_i64 ((volatile gint64 *) &next_pending_request_id);
6663 // This is a global queue of summary requests.
6664 // It's not safe to signal a thread while they're in the
6665 // middle of a dump. Dladdr is not reentrant. It's the one lock
6666 // we rely on being able to take.
6668 // We don't use it in almost any other place in managed code, so
6669 // our problem is in the stack dumping code racing with the signalling code.
6671 // A dump is wait-free to the degree that it's not going to loop indefinitely.
6672 // If we're running from a crash handler block, we're not in any position to
6673 // wait for an in-flight dump to finish. If we crashed while dumping, we cannot dump.
6674 // We should simply return so we can die cleanly.
6676 // signal_handler_controller should be set only from a handler that expects itself to be the only
6677 // entry point, where the runtime already being dumping means we should just give up
6679 gboolean success = FALSE;
6681 while (TRUE) {
6682 gint64 next_request_id = mono_atomic_load_i64 ((volatile gint64 *) &request_available_to_run);
6684 if (next_request_id == this_request_id) {
6685 gboolean already_async = mono_thread_info_is_async_context ();
6686 if (!already_async)
6687 mono_thread_info_set_is_async_context (TRUE);
6689 SummarizerSupervisorState synch;
6690 if (summarizer_supervisor_start (&synch)) {
6691 g_assert (mem);
6692 success = mono_threads_summarize_execute_internal (ctx, out, hashes, silent, mem, provided_size, TRUE);
6693 summarizer_supervisor_end (&synch);
6696 if (!already_async)
6697 mono_thread_info_set_is_async_context (FALSE);
6699 // Only the thread that gets the ticket can unblock future dumpers.
6700 mono_atomic_inc_i64 ((volatile gint64 *) &request_available_to_run);
6701 break;
6702 } else if (signal_handler_controller) {
6703 // We're done. We can't do anything.
6704 g_async_safe_printf ("Attempted to dump for critical failure when already in dump. Error reporting crashed?");
6705 mono_summarize_double_fault_log ();
6706 break;
6707 } else {
6708 if (!silent)
6709 g_async_safe_printf ("Waiting for in-flight dump to complete.");
6710 sleep (2);
6714 return success;
6717 #endif
6719 #ifdef ENABLE_NETCORE
6720 void
6721 ves_icall_System_Threading_Thread_StartInternal (MonoThreadObjectHandle thread_handle, MonoError *error)
6723 MonoThread *internal = MONO_HANDLE_RAW (thread_handle);
6724 gboolean res;
6726 THREAD_DEBUG (g_message("%s: Trying to start a new thread: this (%p)", __func__, internal));
6728 #ifdef DISABLE_THREADS
6729 mono_error_set_not_supported (error, NULL);
6730 return;
6731 #endif
6733 LOCK_THREAD (internal);
6735 if ((internal->state & ThreadState_Unstarted) == 0) {
6736 UNLOCK_THREAD (internal);
6737 mono_error_set_exception_thread_state (error, "Thread has already been started.");
6738 return;
6741 if ((internal->state & ThreadState_Aborted) != 0) {
6742 UNLOCK_THREAD (internal);
6743 return;
6746 res = create_thread (internal, internal, NULL, NULL, NULL, MONO_THREAD_CREATE_FLAGS_NONE, error);
6747 if (!res) {
6748 UNLOCK_THREAD (internal);
6749 return;
6752 internal->state &= ~ThreadState_Unstarted;
6754 THREAD_DEBUG (g_message ("%s: Started thread ID %" G_GSIZE_FORMAT " (handle %p)", __func__, (gsize)internal->tid, internal->handle));
6756 UNLOCK_THREAD (internal);
6759 void
6760 ves_icall_System_Threading_Thread_InitInternal (MonoThreadObjectHandle thread_handle, MonoError *error)
6762 MonoThread *internal = MONO_HANDLE_RAW (thread_handle);
6764 // Need to initialize thread objects created from managed code
6765 init_internal_thread_object (internal);
6766 internal->state = ThreadState_Unstarted;
6767 MONO_OBJECT_SETREF_INTERNAL (internal, internal_thread, internal);
6770 guint64
6771 ves_icall_System_Threading_Thread_GetCurrentOSThreadId (MonoError *error)
6773 return mono_native_thread_os_id_get ();
6776 gint32
6777 ves_icall_System_Threading_Thread_GetCurrentProcessorNumber (MonoError *error)
6779 return mono_native_thread_processor_id_get ();
6782 gpointer
6783 ves_icall_System_Threading_LowLevelLifoSemaphore_InitInternal (void)
6785 return (gpointer)mono_lifo_semaphore_init ();
6788 void
6789 ves_icall_System_Threading_LowLevelLifoSemaphore_DeleteInternal (gpointer sem_ptr)
6791 LifoSemaphore *sem = (LifoSemaphore *)sem_ptr;
6792 mono_lifo_semaphore_delete (sem);
6795 gint32
6796 ves_icall_System_Threading_LowLevelLifoSemaphore_TimedWaitInternal (gpointer sem_ptr, gint32 timeout_ms)
6798 LifoSemaphore *sem = (LifoSemaphore *)sem_ptr;
6799 return mono_lifo_semaphore_timed_wait (sem, timeout_ms);
6802 void
6803 ves_icall_System_Threading_LowLevelLifoSemaphore_ReleaseInternal (gpointer sem_ptr, gint32 count)
6805 LifoSemaphore *sem = (LifoSemaphore *)sem_ptr;
6806 mono_lifo_semaphore_release (sem, count);
6808 #endif