Convert thread get/set/clear state to coop/handle. (#6389)
[mono-project.git] / mono / metadata / threads.c
blob65dad823d21341fbb0a177f5571e357c53d701c1
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/gc-internals.h>
30 #include <mono/metadata/marshal.h>
31 #include <mono/metadata/runtime.h>
32 #include <mono/metadata/object-internals.h>
33 #include <mono/metadata/debug-internals.h>
34 #include <mono/utils/monobitset.h>
35 #include <mono/utils/mono-compiler.h>
36 #include <mono/utils/mono-mmap.h>
37 #include <mono/utils/mono-membar.h>
38 #include <mono/utils/mono-time.h>
39 #include <mono/utils/mono-threads.h>
40 #include <mono/utils/mono-threads-coop.h>
41 #include <mono/utils/mono-tls.h>
42 #include <mono/utils/atomic.h>
43 #include <mono/utils/mono-memory-model.h>
44 #include <mono/utils/mono-error-internals.h>
45 #include <mono/utils/os-event.h>
46 #include <mono/utils/mono-threads-debug.h>
47 #include <mono/utils/unlocked.h>
48 #include <mono/metadata/w32handle.h>
49 #include <mono/metadata/w32event.h>
50 #include <mono/metadata/w32mutex.h>
52 #include <mono/metadata/reflection-internals.h>
53 #include <mono/metadata/abi-details.h>
54 #include <mono/metadata/w32error.h>
55 #include <mono/utils/w32api.h>
56 #include <mono/utils/mono-os-wait.h>
58 #ifdef HAVE_SIGNAL_H
59 #include <signal.h>
60 #endif
62 #if defined(HOST_WIN32)
63 #include <objbase.h>
65 extern gboolean
66 mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle);
67 #endif
69 #if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
70 #define USE_TKILL_ON_ANDROID 1
71 #endif
73 #ifdef HOST_ANDROID
74 #include <errno.h>
76 #ifdef USE_TKILL_ON_ANDROID
77 extern int tkill (pid_t tid, int signal);
78 #endif
79 #endif
81 /*#define THREAD_DEBUG(a) do { a; } while (0)*/
82 #define THREAD_DEBUG(a)
83 /*#define THREAD_WAIT_DEBUG(a) do { a; } while (0)*/
84 #define THREAD_WAIT_DEBUG(a)
85 /*#define LIBGC_DEBUG(a) do { a; } while (0)*/
86 #define LIBGC_DEBUG(a)
88 #define SPIN_TRYLOCK(i) (mono_atomic_cas_i32 (&(i), 1, 0) == 0)
89 #define SPIN_LOCK(i) do { \
90 if (SPIN_TRYLOCK (i)) \
91 break; \
92 } while (1)
94 #define SPIN_UNLOCK(i) i = 0
96 #define LOCK_THREAD(thread) lock_thread((thread))
97 #define UNLOCK_THREAD(thread) unlock_thread((thread))
99 typedef union {
100 gint32 ival;
101 gfloat fval;
102 } IntFloatUnion;
104 typedef union {
105 gint64 ival;
106 gdouble fval;
107 } LongDoubleUnion;
109 typedef struct _StaticDataFreeList StaticDataFreeList;
110 struct _StaticDataFreeList {
111 StaticDataFreeList *next;
112 guint32 offset;
113 guint32 size;
116 typedef struct {
117 int idx;
118 int offset;
119 StaticDataFreeList *freelist;
120 } StaticDataInfo;
122 /* Controls access to the 'threads' hash table */
123 static void mono_threads_lock (void);
124 static void mono_threads_unlock (void);
125 static MonoCoopMutex threads_mutex;
127 /* Controls access to the 'joinable_threads' hash table */
128 #define joinable_threads_lock() mono_os_mutex_lock (&joinable_threads_mutex)
129 #define joinable_threads_unlock() mono_os_mutex_unlock (&joinable_threads_mutex)
130 static mono_mutex_t joinable_threads_mutex;
132 /* Holds current status of static data heap */
133 static StaticDataInfo thread_static_info;
134 static StaticDataInfo context_static_info;
136 /* The hash of existing threads (key is thread ID, value is
137 * MonoInternalThread*) that need joining before exit
139 static MonoGHashTable *threads=NULL;
141 /* List of app context GC handles.
142 * Added to from mono_threads_register_app_context ().
144 static GHashTable *contexts = NULL;
146 /* Cleanup queue for contexts. */
147 static MonoReferenceQueue *context_queue;
150 * Threads which are starting up and they are not in the 'threads' hash yet.
151 * When mono_thread_attach_internal is called for a thread, it will be removed from this hash table.
152 * Protected by mono_threads_lock ().
154 static MonoGHashTable *threads_starting_up = NULL;
156 /* Contains tids */
157 /* Protected by the threads lock */
158 static GHashTable *joinable_threads;
159 static gint32 joinable_thread_count;
161 static GHashTable *pending_joinable_threads;
162 static gint32 pending_joinable_thread_count;
164 static mono_cond_t zero_pending_joinable_thread_event;
166 static void threads_add_pending_joinable_runtime_thread (MonoThreadInfo *mono_thread_info);
167 static gboolean threads_wait_pending_joinable_threads (uint32_t timeout);
169 #define SET_CURRENT_OBJECT(x) mono_tls_set_thread (x)
170 #define GET_CURRENT_OBJECT() (MonoInternalThread*) mono_tls_get_thread ()
172 /* function called at thread start */
173 static MonoThreadStartCB mono_thread_start_cb = NULL;
175 /* function called at thread attach */
176 static MonoThreadAttachCB mono_thread_attach_cb = NULL;
178 /* function called at thread cleanup */
179 static MonoThreadCleanupFunc mono_thread_cleanup_fn = NULL;
181 /* The default stack size for each thread */
182 static guint32 default_stacksize = 0;
183 #define default_stacksize_for_thread(thread) ((thread)->stack_size? (thread)->stack_size: default_stacksize)
185 static void context_adjust_static_data (MonoAppContext *ctx);
186 static void mono_free_static_data (gpointer* static_data);
187 static void mono_init_static_data_info (StaticDataInfo *static_data);
188 static guint32 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align);
189 static gboolean mono_thread_resume (MonoInternalThread* thread);
190 static void async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort);
191 static void self_abort_internal (MonoError *error);
192 static void async_suspend_internal (MonoInternalThread *thread, gboolean interrupt);
193 static void self_suspend_internal (void);
195 static MonoException* mono_thread_execute_interruption (void);
196 static void ref_stack_destroy (gpointer rs);
198 /* Spin lock for InterlockedXXX 64 bit functions */
199 #define mono_interlocked_lock() mono_os_mutex_lock (&interlocked_mutex)
200 #define mono_interlocked_unlock() mono_os_mutex_unlock (&interlocked_mutex)
201 static mono_mutex_t interlocked_mutex;
203 /* global count of thread interruptions requested */
204 static gint32 thread_interruption_requested = 0;
206 /* Event signaled when a thread changes its background mode */
207 static MonoOSEvent background_change_event;
209 static gboolean shutting_down = FALSE;
211 static gint32 managed_thread_id_counter = 0;
213 static void
214 mono_threads_lock (void)
216 mono_locks_coop_acquire (&threads_mutex, ThreadsLock);
219 static void
220 mono_threads_unlock (void)
222 mono_locks_coop_release (&threads_mutex, ThreadsLock);
226 static guint32
227 get_next_managed_thread_id (void)
229 return mono_atomic_inc_i32 (&managed_thread_id_counter);
233 * We separate interruptions/exceptions into either sync (they can be processed anytime,
234 * normally as soon as they are set, and are set by the same thread) and async (they can't
235 * be processed inside abort protected blocks and are normally set by other threads). We
236 * can have both a pending sync and async interruption. In this case, the sync exception is
237 * processed first. Since we clean sync flag first, mono_thread_execute_interruption must
238 * also handle all sync type exceptions before the async type exceptions.
240 enum {
241 INTERRUPT_SYNC_REQUESTED_BIT = 0x1,
242 INTERRUPT_ASYNC_REQUESTED_BIT = 0x2,
243 INTERRUPT_REQUESTED_MASK = 0x3,
244 ABORT_PROT_BLOCK_SHIFT = 2,
245 ABORT_PROT_BLOCK_BITS = 8,
246 ABORT_PROT_BLOCK_MASK = (((1 << ABORT_PROT_BLOCK_BITS) - 1) << ABORT_PROT_BLOCK_SHIFT)
249 static int
250 mono_thread_get_abort_prot_block_count (MonoInternalThread *thread)
252 gsize state = thread->thread_state;
253 return (state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT;
256 void
257 mono_threads_begin_abort_protected_block (void)
259 MonoInternalThread *thread = mono_thread_internal_current ();
260 gsize old_state, new_state;
261 int new_val;
262 do {
263 old_state = thread->thread_state;
265 new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) + 1;
266 //bounds check abort_prot_count
267 g_assert (new_val > 0);
268 g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
270 new_state = old_state + (1 << ABORT_PROT_BLOCK_SHIFT);
271 } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
273 /* Defer async request since we won't be able to process until exiting the block */
274 if (new_val == 1 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT)) {
275 mono_atomic_dec_i32 (&thread_interruption_requested);
276 THREADS_INTERRUPT_DEBUG ("[%d] begin abort protected block old_state %ld new_state %ld, defer tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
277 if (thread_interruption_requested < 0)
278 g_warning ("bad thread_interruption_requested state");
279 } else {
280 THREADS_INTERRUPT_DEBUG ("[%d] begin abort protected block old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
284 static gboolean
285 mono_thread_state_has_interruption (gsize state)
287 /* pending exception, self abort */
288 if (state & INTERRUPT_SYNC_REQUESTED_BIT)
289 return TRUE;
291 /* abort, interruption, suspend */
292 if ((state & INTERRUPT_ASYNC_REQUESTED_BIT) && !(state & ABORT_PROT_BLOCK_MASK))
293 return TRUE;
295 return FALSE;
298 gboolean
299 mono_threads_end_abort_protected_block (void)
301 MonoInternalThread *thread = mono_thread_internal_current ();
302 gsize old_state, new_state;
303 int new_val;
304 do {
305 old_state = thread->thread_state;
307 //bounds check abort_prot_count
308 new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) - 1;
309 g_assert (new_val >= 0);
310 g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
312 new_state = old_state - (1 << ABORT_PROT_BLOCK_SHIFT);
313 } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
315 if (new_val == 0 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT)) {
316 mono_atomic_inc_i32 (&thread_interruption_requested);
317 THREADS_INTERRUPT_DEBUG ("[%d] end abort protected block old_state %ld new_state %ld, restore tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
318 } else {
319 THREADS_INTERRUPT_DEBUG ("[%d] end abort protected block old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
322 return mono_thread_state_has_interruption (new_state);
325 static gboolean
326 mono_thread_get_interruption_requested (MonoInternalThread *thread)
328 gsize state = thread->thread_state;
330 return mono_thread_state_has_interruption (state);
334 * Returns TRUE is there was a state change
335 * We clear a single interruption request, sync has priority.
337 static gboolean
338 mono_thread_clear_interruption_requested (MonoInternalThread *thread)
340 gsize old_state, new_state;
341 do {
342 old_state = thread->thread_state;
344 // no interruption to process
345 if (!(old_state & INTERRUPT_SYNC_REQUESTED_BIT) &&
346 (!(old_state & INTERRUPT_ASYNC_REQUESTED_BIT) || (old_state & ABORT_PROT_BLOCK_MASK)))
347 return FALSE;
349 if (old_state & INTERRUPT_SYNC_REQUESTED_BIT)
350 new_state = old_state & ~INTERRUPT_SYNC_REQUESTED_BIT;
351 else
352 new_state = old_state & ~INTERRUPT_ASYNC_REQUESTED_BIT;
353 } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
355 mono_atomic_dec_i32 (&thread_interruption_requested);
356 THREADS_INTERRUPT_DEBUG ("[%d] clear interruption old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
357 if (thread_interruption_requested < 0)
358 g_warning ("bad thread_interruption_requested state");
359 return TRUE;
362 /* Returns TRUE is there was a state change and the interruption can be processed */
363 static gboolean
364 mono_thread_set_interruption_requested (MonoInternalThread *thread)
366 //always force when the current thread is doing it to itself.
367 gboolean sync = thread == mono_thread_internal_current ();
368 gsize old_state, new_state;
369 do {
370 old_state = thread->thread_state;
372 //Already set
373 if ((sync && (old_state & INTERRUPT_SYNC_REQUESTED_BIT)) ||
374 (!sync && (old_state & INTERRUPT_ASYNC_REQUESTED_BIT)))
375 return FALSE;
377 if (sync)
378 new_state = old_state | INTERRUPT_SYNC_REQUESTED_BIT;
379 else
380 new_state = old_state | INTERRUPT_ASYNC_REQUESTED_BIT;
381 } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
383 if (sync || !(new_state & ABORT_PROT_BLOCK_MASK)) {
384 mono_atomic_inc_i32 (&thread_interruption_requested);
385 THREADS_INTERRUPT_DEBUG ("[%d] set interruption on [%d] old_state %ld new_state %ld, tir %d\n", mono_thread_internal_current ()->small_id, thread->small_id, old_state, new_state, thread_interruption_requested);
386 } else {
387 THREADS_INTERRUPT_DEBUG ("[%d] set interruption on [%d] old_state %ld new_state %ld, tir deferred %d\n", mono_thread_internal_current ()->small_id, thread->small_id, old_state, new_state, thread_interruption_requested);
390 return sync || !(new_state & ABORT_PROT_BLOCK_MASK);
393 static inline MonoNativeThreadId
394 thread_get_tid (MonoInternalThread *thread)
396 /* We store the tid as a guint64 to keep the object layout constant between platforms */
397 return MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid);
400 static void ensure_synch_cs_set (MonoInternalThread *thread)
402 MonoCoopMutex *synch_cs;
404 if (thread->synch_cs != NULL) {
405 return;
408 synch_cs = g_new0 (MonoCoopMutex, 1);
409 mono_coop_mutex_init_recursive (synch_cs);
411 if (mono_atomic_cas_ptr ((gpointer *)&thread->synch_cs,
412 synch_cs, NULL) != NULL) {
413 /* Another thread must have installed this CS */
414 mono_coop_mutex_destroy (synch_cs);
415 g_free (synch_cs);
419 static inline void
420 lock_thread (MonoInternalThread *thread)
422 if (!thread->synch_cs)
423 ensure_synch_cs_set (thread);
425 g_assert (thread->synch_cs);
427 mono_coop_mutex_lock (thread->synch_cs);
430 static inline void
431 unlock_thread (MonoInternalThread *thread)
433 mono_coop_mutex_unlock (thread->synch_cs);
436 static inline gboolean
437 is_appdomainunloaded_exception (MonoClass *klass)
439 return klass == mono_class_get_appdomain_unloaded_exception_class ();
442 static inline gboolean
443 is_threadabort_exception (MonoClass *klass)
445 return klass == mono_defaults.threadabortexception_class;
449 * A special static data offset (guint32) consists of 3 parts:
451 * [0] 6-bit index into the array of chunks.
452 * [6] 25-bit offset into the array.
453 * [31] Bit indicating thread or context static.
456 typedef union {
457 struct {
458 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
459 guint32 type : 1;
460 guint32 offset : 25;
461 guint32 index : 6;
462 #else
463 guint32 index : 6;
464 guint32 offset : 25;
465 guint32 type : 1;
466 #endif
467 } fields;
468 guint32 raw;
469 } SpecialStaticOffset;
471 #define SPECIAL_STATIC_OFFSET_TYPE_THREAD 0
472 #define SPECIAL_STATIC_OFFSET_TYPE_CONTEXT 1
474 #define MAKE_SPECIAL_STATIC_OFFSET(idx, off, ty) \
475 ((SpecialStaticOffset) { .fields = { .index = (idx), .offset = (off), .type = (ty) } }.raw)
476 #define ACCESS_SPECIAL_STATIC_OFFSET(x,f) \
477 (((SpecialStaticOffset *) &(x))->fields.f)
479 static gpointer
480 get_thread_static_data (MonoInternalThread *thread, guint32 offset)
482 g_assert (ACCESS_SPECIAL_STATIC_OFFSET (offset, type) == SPECIAL_STATIC_OFFSET_TYPE_THREAD);
484 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
485 int off = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
487 return ((char *) thread->static_data [idx]) + off;
490 static gpointer
491 get_context_static_data (MonoAppContext *ctx, guint32 offset)
493 g_assert (ACCESS_SPECIAL_STATIC_OFFSET (offset, type) == SPECIAL_STATIC_OFFSET_TYPE_CONTEXT);
495 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
496 int off = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
498 return ((char *) ctx->static_data [idx]) + off;
501 static MonoThread**
502 get_current_thread_ptr_for_domain (MonoDomain *domain, MonoInternalThread *thread)
504 static MonoClassField *current_thread_field = NULL;
506 guint32 offset;
508 if (!current_thread_field) {
509 current_thread_field = mono_class_get_field_from_name (mono_defaults.thread_class, "current_thread");
510 g_assert (current_thread_field);
513 ERROR_DECL_VALUE (thread_vt_error);
514 mono_class_vtable_checked (domain, mono_defaults.thread_class, &thread_vt_error);
515 mono_error_assert_ok (&thread_vt_error);
516 mono_domain_lock (domain);
517 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, current_thread_field));
518 mono_domain_unlock (domain);
519 g_assert (offset);
521 return (MonoThread **)get_thread_static_data (thread, offset);
524 static void
525 set_current_thread_for_domain (MonoDomain *domain, MonoInternalThread *thread, MonoThread *current)
527 MonoThread **current_thread_ptr = get_current_thread_ptr_for_domain (domain, thread);
529 g_assert (current->obj.vtable->domain == domain);
531 g_assert (!*current_thread_ptr);
532 *current_thread_ptr = current;
535 static MonoThread*
536 create_thread_object (MonoDomain *domain, MonoInternalThread *internal)
538 MonoThread *thread;
539 MonoVTable *vtable;
540 ERROR_DECL (error);
542 vtable = mono_class_vtable_checked (domain, mono_defaults.thread_class, error);
543 mono_error_assert_ok (error);
545 thread = (MonoThread*)mono_object_new_mature (vtable, error);
546 /* only possible failure mode is OOM, from which we don't expect to recover. */
547 mono_error_assert_ok (error);
549 MONO_OBJECT_SETREF (thread, internal_thread, internal);
551 return thread;
554 static MonoInternalThread*
555 create_internal_thread_object (void)
557 ERROR_DECL (error);
558 MonoInternalThread *thread;
559 MonoVTable *vt;
561 vt = mono_class_vtable_checked (mono_get_root_domain (), mono_defaults.internal_thread_class, error);
562 mono_error_assert_ok (error);
563 thread = (MonoInternalThread*) mono_object_new_mature (vt, error);
564 /* only possible failure mode is OOM, from which we don't exect to recover */
565 mono_error_assert_ok (error);
567 thread->synch_cs = g_new0 (MonoCoopMutex, 1);
568 mono_coop_mutex_init_recursive (thread->synch_cs);
570 thread->apartment_state = ThreadApartmentState_Unknown;
571 thread->managed_id = get_next_managed_thread_id ();
572 if (mono_gc_is_moving ()) {
573 thread->thread_pinning_ref = thread;
574 MONO_GC_REGISTER_ROOT_PINNING (thread->thread_pinning_ref, MONO_ROOT_SOURCE_THREADING, NULL, "Thread Pinning Reference");
577 thread->priority = MONO_THREAD_PRIORITY_NORMAL;
579 thread->suspended = g_new0 (MonoOSEvent, 1);
580 mono_os_event_init (thread->suspended, TRUE);
582 return thread;
585 static void
586 mono_thread_internal_set_priority (MonoInternalThread *internal, MonoThreadPriority priority)
588 g_assert (internal);
590 g_assert (priority >= MONO_THREAD_PRIORITY_LOWEST);
591 g_assert (priority <= MONO_THREAD_PRIORITY_HIGHEST);
592 g_assert (MONO_THREAD_PRIORITY_LOWEST < MONO_THREAD_PRIORITY_HIGHEST);
594 #ifdef HOST_WIN32
595 BOOL res;
597 g_assert (internal->native_handle);
599 res = SetThreadPriority (internal->native_handle, priority - 2);
600 if (!res)
601 g_error ("%s: SetThreadPriority failed, error %d", __func__, GetLastError ());
602 #else /* HOST_WIN32 */
603 pthread_t tid;
604 int policy;
605 struct sched_param param;
606 gint res;
608 tid = thread_get_tid (internal);
610 res = pthread_getschedparam (tid, &policy, &param);
611 if (res != 0)
612 g_error ("%s: pthread_getschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
614 #ifdef _POSIX_PRIORITY_SCHEDULING
615 int max, min;
617 /* Necessary to get valid priority range */
619 min = sched_get_priority_min (policy);
620 max = sched_get_priority_max (policy);
622 if (max > 0 && min >= 0 && max > min) {
623 double srange, drange, sposition, dposition;
624 srange = MONO_THREAD_PRIORITY_HIGHEST - MONO_THREAD_PRIORITY_LOWEST;
625 drange = max - min;
626 sposition = priority - MONO_THREAD_PRIORITY_LOWEST;
627 dposition = (sposition / srange) * drange;
628 param.sched_priority = (int)(dposition + min);
629 } else
630 #endif
632 switch (policy) {
633 case SCHED_FIFO:
634 case SCHED_RR:
635 param.sched_priority = 50;
636 break;
637 #ifdef SCHED_BATCH
638 case SCHED_BATCH:
639 #endif
640 case SCHED_OTHER:
641 param.sched_priority = 0;
642 break;
643 default:
644 g_warning ("%s: unknown policy %d", __func__, policy);
645 return;
649 res = pthread_setschedparam (tid, policy, &param);
650 if (res != 0) {
651 if (res == EPERM) {
652 g_warning ("%s: pthread_setschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
653 return;
655 g_error ("%s: pthread_setschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
657 #endif /* HOST_WIN32 */
660 static void
661 mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, void *alloc_key, gboolean threadlocal);
663 static gboolean
664 mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean force_domain)
666 MonoThreadInfo *info;
667 MonoInternalThread *internal;
668 MonoDomain *domain, *root_domain;
669 guint32 gchandle;
671 g_assert (thread);
673 info = mono_thread_info_current ();
674 g_assert (info);
676 internal = thread->internal_thread;
677 g_assert (internal);
679 /* It is needed to store the MonoInternalThread on the MonoThreadInfo, because of the following case:
680 * - the MonoInternalThread TLS key is destroyed: set it to NULL
681 * - the MonoThreadInfo TLS key is destroyed: calls mono_thread_info_detach
682 * - it calls MonoThreadInfoCallbacks.thread_detach
683 * - mono_thread_internal_current returns NULL -> fails to detach the MonoInternalThread. */
684 mono_thread_info_set_internal_thread_gchandle (info, mono_gchandle_new ((MonoObject*) internal, FALSE));
686 internal->handle = mono_threads_open_thread_handle (info->handle);
687 #ifdef HOST_WIN32
688 internal->native_handle = OpenThread (THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId ());
689 #endif
690 internal->tid = MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ());
691 internal->thread_info = info;
692 internal->small_id = info->small_id;
694 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, mono_native_thread_id_get (), internal));
696 SET_CURRENT_OBJECT (internal);
698 domain = mono_object_domain (thread);
700 mono_thread_push_appdomain_ref (domain);
701 if (!mono_domain_set (domain, force_domain)) {
702 mono_thread_pop_appdomain_ref ();
703 goto fail;
706 mono_threads_lock ();
708 if (shutting_down && !force_attach) {
709 mono_threads_unlock ();
710 mono_thread_pop_appdomain_ref ();
711 goto fail;
714 if (threads_starting_up)
715 mono_g_hash_table_remove (threads_starting_up, thread);
717 if (!threads) {
718 threads = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_THREADING, NULL, "Thread Table");
721 /* We don't need to duplicate thread->handle, because it is
722 * only closed when the thread object is finalized by the GC. */
723 mono_g_hash_table_insert (threads, (gpointer)(gsize)(internal->tid), internal);
725 /* We have to do this here because mono_thread_start_cb
726 * requires that root_domain_thread is set up. */
727 if (thread_static_info.offset || thread_static_info.idx > 0) {
728 /* get the current allocated size */
729 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (thread_static_info.idx, thread_static_info.offset, 0);
730 mono_alloc_static_data (&internal->static_data, offset, (void *) MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid), TRUE);
733 mono_threads_unlock ();
735 root_domain = mono_get_root_domain ();
737 g_assert (!internal->root_domain_thread);
738 if (domain != root_domain)
739 MONO_OBJECT_SETREF (internal, root_domain_thread, create_thread_object (root_domain, internal));
740 else
741 MONO_OBJECT_SETREF (internal, root_domain_thread, thread);
743 if (domain != root_domain)
744 set_current_thread_for_domain (root_domain, internal, internal->root_domain_thread);
746 set_current_thread_for_domain (domain, internal, thread);
748 THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, internal->tid, internal->handle));
750 return TRUE;
752 fail:
753 mono_threads_lock ();
754 if (threads_starting_up)
755 mono_g_hash_table_remove (threads_starting_up, thread);
756 mono_threads_unlock ();
758 if (!mono_thread_info_try_get_internal_thread_gchandle (info, &gchandle))
759 g_error ("%s: failed to get gchandle, info %p", __func__, info);
761 mono_gchandle_free (gchandle);
763 mono_thread_info_unset_internal_thread_gchandle (info);
765 SET_CURRENT_OBJECT(NULL);
767 return FALSE;
770 static void
771 mono_thread_detach_internal (MonoInternalThread *thread)
773 MonoThreadInfo *info;
774 MonoInternalThread *value;
775 gboolean removed;
776 guint32 gchandle;
778 g_assert (mono_thread_internal_is_current (thread));
780 g_assert (thread != NULL);
781 SET_CURRENT_OBJECT (thread);
783 info = (MonoThreadInfo*) thread->thread_info;
784 g_assert (info);
786 THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
788 MONO_PROFILER_RAISE (thread_stopping, (thread->tid));
791 * Prevent race condition between thread shutdown and runtime shutdown.
792 * Including all runtime threads in the pending joinable count will make
793 * sure shutdown will wait for it to get onto the joinable thread list before
794 * critical resources have been cleanup (like GC memory). Threads getting onto
795 * the joinable thread list should just about to exit and not blocking a potential
796 * join call. Owner of threads attached to the runtime but not identified as runtime
797 * threads needs to make sure thread detach calls won't race with runtime shutdown.
799 threads_add_pending_joinable_runtime_thread (info);
801 #ifndef HOST_WIN32
802 mono_w32mutex_abandon (thread);
803 #endif
805 if (thread->abort_state_handle) {
806 mono_gchandle_free (thread->abort_state_handle);
807 thread->abort_state_handle = 0;
810 thread->abort_exc = NULL;
811 thread->current_appcontext = NULL;
814 * thread->synch_cs can be NULL if this was called after
815 * ves_icall_System_Threading_InternalThread_Thread_free_internal.
816 * This can happen only during shutdown.
817 * The shutting_down flag is not always set, so we can't assert on it.
819 if (thread->synch_cs)
820 LOCK_THREAD (thread);
822 thread->state |= ThreadState_Stopped;
823 thread->state &= ~ThreadState_Background;
825 if (thread->synch_cs)
826 UNLOCK_THREAD (thread);
829 An interruption request has leaked to cleanup. Adjust the global counter.
831 This can happen is the abort source thread finds the abortee (this) thread
832 in unmanaged code. If this thread never trips back to managed code or check
833 the local flag it will be left set and positively unbalance the global counter.
835 Leaving the counter unbalanced will cause a performance degradation since all threads
836 will now keep checking their local flags all the time.
838 mono_thread_clear_interruption_requested (thread);
840 mono_threads_lock ();
842 g_assert (threads);
844 if (!mono_g_hash_table_lookup_extended (threads, (gpointer)thread->tid, NULL, (gpointer*) &value)) {
845 g_error ("%s: thread %p (tid: %p) should not have been removed yet from threads", __func__, thread, thread->tid);
846 } else if (thread != value) {
847 /* We have to check whether the thread object for the tid is still the same in the table because the
848 * thread might have been destroyed and the tid reused in the meantime, in which case the tid would be in
849 * the table, but with another thread object. */
850 g_error ("%s: thread %p (tid: %p) do not match with value %p (tid: %p)", __func__, thread, thread->tid, value, value->tid);
853 removed = mono_g_hash_table_remove (threads, (gpointer)thread->tid);
854 g_assert (removed);
856 mono_threads_unlock ();
858 /* Don't close the handle here, wait for the object finalizer
859 * to do it. Otherwise, the following race condition applies:
861 * 1) Thread exits (and mono_thread_detach_internal() closes the handle)
863 * 2) Some other handle is reassigned the same slot
865 * 3) Another thread tries to join the first thread, and
866 * blocks waiting for the reassigned handle to be signalled
867 * (which might never happen). This is possible, because the
868 * thread calling Join() still has a reference to the first
869 * thread's object.
872 mono_release_type_locks (thread);
874 MONO_PROFILER_RAISE (thread_stopped, (thread->tid));
875 MONO_PROFILER_RAISE (gc_root_unregister, (info->stack_start_limit));
876 MONO_PROFILER_RAISE (gc_root_unregister, (info->handle_stack));
879 * This will signal async signal handlers that the thread has exited.
880 * The profiler callback needs this to be set, so it cannot be done earlier.
882 mono_domain_unset ();
883 mono_memory_barrier ();
885 mono_thread_pop_appdomain_ref ();
887 mono_free_static_data (thread->static_data);
888 thread->static_data = NULL;
889 ref_stack_destroy (thread->appdomain_refs);
890 thread->appdomain_refs = NULL;
892 g_assert (thread->suspended);
893 mono_os_event_destroy (thread->suspended);
894 g_free (thread->suspended);
895 thread->suspended = NULL;
897 if (mono_thread_cleanup_fn)
898 mono_thread_cleanup_fn (thread_get_tid (thread));
900 mono_memory_barrier ();
902 if (mono_gc_is_moving ()) {
903 MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref);
904 thread->thread_pinning_ref = NULL;
907 /* There is no more any guarantee that `thread` is alive */
908 mono_memory_barrier ();
910 SET_CURRENT_OBJECT (NULL);
911 mono_domain_unset ();
913 if (!mono_thread_info_try_get_internal_thread_gchandle (info, &gchandle))
914 g_error ("%s: failed to get gchandle, info = %p", __func__, info);
916 mono_gchandle_free (gchandle);
918 mono_thread_info_unset_internal_thread_gchandle (info);
920 MONO_PROFILER_RAISE (thread_exited, (thread->tid));
922 /* Don't need to close the handle to this thread, even though we took a
923 * reference in mono_thread_attach (), because the GC will do it
924 * when the Thread object is finalised.
928 typedef struct {
929 gint32 ref;
930 MonoThread *thread;
931 MonoObject *start_delegate;
932 MonoObject *start_delegate_arg;
933 MonoThreadStart start_func;
934 gpointer start_func_arg;
935 gboolean force_attach;
936 gboolean failed;
937 MonoCoopSem registered;
938 } StartInfo;
940 static void
941 fire_attach_profiler_events (MonoNativeThreadId tid)
943 MONO_PROFILER_RAISE (thread_started, ((uintptr_t) tid));
945 MonoThreadInfo *info = mono_thread_info_current ();
947 MONO_PROFILER_RAISE (gc_root_register, (
948 info->stack_start_limit,
949 (char *) info->stack_end - (char *) info->stack_start_limit,
950 MONO_ROOT_SOURCE_STACK,
951 (void *) tid,
952 "Thread Stack"));
954 // The handle stack is a pseudo-root similar to the finalizer queues.
955 MONO_PROFILER_RAISE (gc_root_register, (
956 info->handle_stack,
958 MONO_ROOT_SOURCE_HANDLE,
959 (void *) tid,
960 "Handle Stack"));
963 static guint32 WINAPI start_wrapper_internal(StartInfo *start_info, gsize *stack_ptr)
965 ERROR_DECL (error);
966 MonoThreadStart start_func;
967 void *start_func_arg;
968 gsize tid;
970 * We don't create a local to hold start_info->thread, so hopefully it won't get pinned during a
971 * GC stack walk.
973 MonoThread *thread;
974 MonoInternalThread *internal;
975 MonoObject *start_delegate;
976 MonoObject *start_delegate_arg;
977 MonoDomain *domain;
979 thread = start_info->thread;
980 internal = thread->internal_thread;
981 domain = mono_object_domain (start_info->thread);
983 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper", __func__, mono_native_thread_id_get ()));
985 if (!mono_thread_attach_internal (thread, start_info->force_attach, FALSE)) {
986 start_info->failed = TRUE;
988 mono_coop_sem_post (&start_info->registered);
990 if (mono_atomic_dec_i32 (&start_info->ref) == 0) {
991 mono_coop_sem_destroy (&start_info->registered);
992 g_free (start_info);
995 return 0;
998 mono_thread_internal_set_priority (internal, internal->priority);
1000 tid = internal->tid;
1002 start_delegate = start_info->start_delegate;
1003 start_delegate_arg = start_info->start_delegate_arg;
1004 start_func = start_info->start_func;
1005 start_func_arg = start_info->start_func_arg;
1007 /* This MUST be called before any managed code can be
1008 * executed, as it calls the callback function that (for the
1009 * jit) sets the lmf marker.
1012 if (mono_thread_start_cb)
1013 mono_thread_start_cb (tid, stack_ptr, start_func);
1015 /* On 2.0 profile (and higher), set explicitly since state might have been
1016 Unknown */
1017 if (internal->apartment_state == ThreadApartmentState_Unknown)
1018 internal->apartment_state = ThreadApartmentState_MTA;
1020 mono_thread_init_apartment_state ();
1022 /* Let the thread that called Start() know we're ready */
1023 mono_coop_sem_post (&start_info->registered);
1025 if (mono_atomic_dec_i32 (&start_info->ref) == 0) {
1026 mono_coop_sem_destroy (&start_info->registered);
1027 g_free (start_info);
1030 /* start_info is not valid anymore */
1031 start_info = NULL;
1034 * Call this after calling start_notify, since the profiler callback might want
1035 * to lock the thread, and the lock is held by thread_start () which waits for
1036 * start_notify.
1038 fire_attach_profiler_events ((MonoNativeThreadId) tid);
1040 /* if the name was set before starting, we didn't invoke the profiler callback */
1041 if (internal->name) {
1042 char *tname = g_utf16_to_utf8 (internal->name, internal->name_len, NULL, NULL, NULL);
1043 MONO_PROFILER_RAISE (thread_name, (internal->tid, tname));
1044 mono_native_thread_set_name (MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid), tname);
1045 g_free (tname);
1048 /* start_func is set only for unmanaged start functions */
1049 if (start_func) {
1050 start_func (start_func_arg);
1051 } else {
1052 void *args [1];
1054 g_assert (start_delegate != NULL);
1056 /* we may want to handle the exception here. See comment below on unhandled exceptions */
1057 args [0] = (gpointer) start_delegate_arg;
1058 mono_runtime_delegate_invoke_checked (start_delegate, args, error);
1060 if (!mono_error_ok (error)) {
1061 MonoException *ex = mono_error_convert_to_exception (error);
1063 g_assert (ex != NULL);
1064 MonoClass *klass = mono_object_get_class (&ex->object);
1065 if ((mono_runtime_unhandled_exception_policy_get () != MONO_UNHANDLED_POLICY_LEGACY) &&
1066 !is_threadabort_exception (klass)) {
1067 mono_unhandled_exception (&ex->object);
1068 mono_invoke_unhandled_exception_hook (&ex->object);
1069 g_assert_not_reached ();
1071 } else {
1072 mono_error_cleanup (error);
1076 /* If the thread calls ExitThread at all, this remaining code
1077 * will not be executed, but the main thread will eventually
1078 * call mono_thread_detach_internal() on this thread's behalf.
1081 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper terminating", __func__, mono_native_thread_id_get ()));
1083 /* Do any cleanup needed for apartment state. This
1084 * cannot be done in mono_thread_detach_internal since
1085 * mono_thread_detach_internal could be called for a
1086 * thread other than the current thread.
1087 * mono_thread_cleanup_apartment_state cleans up apartment
1088 * for the current thead */
1089 mono_thread_cleanup_apartment_state ();
1091 mono_thread_detach_internal (internal);
1093 return 0;
1096 static gsize WINAPI
1097 start_wrapper (gpointer data)
1099 StartInfo *start_info;
1100 MonoThreadInfo *info;
1101 gsize res;
1103 start_info = (StartInfo*) data;
1104 g_assert (start_info);
1106 info = mono_thread_info_attach ();
1107 info->runtime_thread = TRUE;
1109 /* Run the actual main function of the thread */
1110 res = start_wrapper_internal (start_info, info->stack_end);
1112 mono_thread_info_exit (res);
1114 g_assert_not_reached ();
1118 * create_thread:
1120 * Common thread creation code.
1121 * LOCKING: Acquires the threads lock.
1123 static gboolean
1124 create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *start_delegate, MonoThreadStart start_func, gpointer start_func_arg,
1125 MonoThreadCreateFlags flags, MonoError *error)
1127 StartInfo *start_info = NULL;
1128 MonoNativeThreadId tid;
1129 gboolean ret;
1130 gsize stack_set_size;
1132 if (start_delegate)
1133 g_assert (!start_func && !start_func_arg);
1134 if (start_func)
1135 g_assert (!start_delegate);
1137 if (flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL) {
1138 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER));
1139 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE));
1141 if (flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER) {
1142 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL));
1143 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE));
1147 * Join joinable threads to prevent running out of threads since the finalizer
1148 * thread might be blocked/backlogged.
1150 mono_threads_join_threads ();
1152 error_init (error);
1154 mono_threads_lock ();
1155 if (shutting_down && !(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE)) {
1156 mono_threads_unlock ();
1157 return FALSE;
1159 if (threads_starting_up == NULL) {
1160 threads_starting_up = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_VALUE_GC, MONO_ROOT_SOURCE_THREADING, NULL, "Thread Starting Table");
1162 mono_g_hash_table_insert (threads_starting_up, thread, thread);
1163 mono_threads_unlock ();
1165 internal->threadpool_thread = flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL;
1166 if (internal->threadpool_thread)
1167 mono_thread_set_state (internal, ThreadState_Background);
1169 internal->debugger_thread = flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER;
1171 start_info = g_new0 (StartInfo, 1);
1172 start_info->ref = 2;
1173 start_info->thread = thread;
1174 start_info->start_delegate = start_delegate;
1175 start_info->start_delegate_arg = thread->start_obj;
1176 start_info->start_func = start_func;
1177 start_info->start_func_arg = start_func_arg;
1178 start_info->force_attach = flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE;
1179 start_info->failed = FALSE;
1180 mono_coop_sem_init (&start_info->registered, 0);
1182 if (flags != MONO_THREAD_CREATE_FLAGS_SMALL_STACK)
1183 stack_set_size = default_stacksize_for_thread (internal);
1184 else
1185 stack_set_size = 0;
1187 if (!mono_thread_platform_create_thread ((MonoThreadStart)start_wrapper, start_info, &stack_set_size, &tid)) {
1188 /* The thread couldn't be created, so set an exception */
1189 mono_threads_lock ();
1190 mono_g_hash_table_remove (threads_starting_up, thread);
1191 mono_threads_unlock ();
1192 mono_error_set_execution_engine (error, "Couldn't create thread. Error 0x%x", mono_w32error_get_last());
1193 /* ref is not going to be decremented in start_wrapper_internal */
1194 mono_atomic_dec_i32 (&start_info->ref);
1195 ret = FALSE;
1196 goto done;
1199 internal->stack_size = (int) stack_set_size;
1201 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Launching thread %p (%"G_GSIZE_FORMAT")", __func__, mono_native_thread_id_get (), internal, (gsize)internal->tid));
1204 * Wait for the thread to set up its TLS data etc, so
1205 * theres no potential race condition if someone tries
1206 * to look up the data believing the thread has
1207 * started
1210 mono_coop_sem_wait (&start_info->registered, MONO_SEM_FLAGS_NONE);
1212 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));
1214 ret = !start_info->failed;
1216 done:
1217 if (mono_atomic_dec_i32 (&start_info->ref) == 0) {
1218 mono_coop_sem_destroy (&start_info->registered);
1219 g_free (start_info);
1222 return ret;
1226 * mono_thread_new_init:
1228 void
1229 mono_thread_new_init (intptr_t tid, gpointer stack_start, gpointer func)
1231 if (mono_thread_start_cb) {
1232 mono_thread_start_cb (tid, stack_start, func);
1237 * mono_threads_set_default_stacksize:
1239 void
1240 mono_threads_set_default_stacksize (guint32 stacksize)
1242 default_stacksize = stacksize;
1246 * mono_threads_get_default_stacksize:
1248 guint32
1249 mono_threads_get_default_stacksize (void)
1251 return default_stacksize;
1255 * mono_thread_create_internal:
1257 * ARG should not be a GC reference.
1259 MonoInternalThread*
1260 mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, MonoThreadCreateFlags flags, MonoError *error)
1262 MonoThread *thread;
1263 MonoInternalThread *internal;
1264 gboolean res;
1266 error_init (error);
1268 internal = create_internal_thread_object ();
1270 thread = create_thread_object (domain, internal);
1272 LOCK_THREAD (internal);
1274 res = create_thread (thread, internal, NULL, (MonoThreadStart) func, arg, flags, error);
1276 UNLOCK_THREAD (internal);
1278 return_val_if_nok (error, NULL);
1279 return internal;
1283 * mono_thread_create:
1285 void
1286 mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
1288 ERROR_DECL (error);
1289 if (!mono_thread_create_checked (domain, func, arg, error))
1290 mono_error_cleanup (error);
1293 gboolean
1294 mono_thread_create_checked (MonoDomain *domain, gpointer func, gpointer arg, MonoError *error)
1296 return (NULL != mono_thread_create_internal (domain, func, arg, MONO_THREAD_CREATE_FLAGS_NONE, error));
1300 * mono_thread_attach:
1302 MonoThread *
1303 mono_thread_attach (MonoDomain *domain)
1305 MonoInternalThread *internal;
1306 MonoThread *thread;
1307 MonoThreadInfo *info;
1308 MonoNativeThreadId tid;
1310 if (mono_thread_internal_current_is_attached ()) {
1311 if (domain != mono_domain_get ())
1312 mono_domain_set (domain, TRUE);
1313 /* Already attached */
1314 return mono_thread_current ();
1317 info = mono_thread_info_attach ();
1318 g_assert (info);
1320 tid=mono_native_thread_id_get ();
1322 internal = create_internal_thread_object ();
1324 thread = create_thread_object (domain, internal);
1326 if (!mono_thread_attach_internal (thread, FALSE, TRUE)) {
1327 /* Mono is shutting down, so just wait for the end */
1328 for (;;)
1329 mono_thread_info_sleep (10000, NULL);
1332 THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, internal->handle));
1334 if (mono_thread_attach_cb)
1335 mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), info->stack_end);
1337 fire_attach_profiler_events (tid);
1339 return thread;
1343 * mono_thread_detach:
1345 void
1346 mono_thread_detach (MonoThread *thread)
1348 if (thread)
1349 mono_thread_detach_internal (thread->internal_thread);
1353 * mono_thread_detach_if_exiting:
1355 * Detach the current thread from the runtime if it is exiting, i.e. it is running pthread dtors.
1356 * This should be used at the end of embedding code which calls into managed code, and which
1357 * can be called from pthread dtors, like <code>dealloc:</code> implementations in Objective-C.
1359 mono_bool
1360 mono_thread_detach_if_exiting (void)
1362 if (mono_thread_info_is_exiting ()) {
1363 MonoInternalThread *thread;
1365 thread = mono_thread_internal_current ();
1366 if (thread) {
1367 mono_thread_detach_internal (thread);
1368 mono_thread_info_detach ();
1369 return TRUE;
1372 return FALSE;
1375 gboolean
1376 mono_thread_internal_current_is_attached (void)
1378 MonoInternalThread *internal;
1380 internal = GET_CURRENT_OBJECT ();
1381 if (!internal)
1382 return FALSE;
1384 return TRUE;
1388 * mono_thread_exit:
1390 void
1391 mono_thread_exit (void)
1393 MonoInternalThread *thread = mono_thread_internal_current ();
1395 THREAD_DEBUG (g_message ("%s: mono_thread_exit for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
1397 mono_thread_detach_internal (thread);
1399 /* we could add a callback here for embedders to use. */
1400 if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread))
1401 exit (mono_environment_exitcode_get ());
1403 mono_thread_info_exit (0);
1406 void
1407 ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this_obj)
1409 MonoInternalThread *internal;
1411 internal = create_internal_thread_object ();
1413 internal->state = ThreadState_Unstarted;
1415 mono_atomic_cas_ptr ((volatile gpointer *)&this_obj->internal_thread, internal, NULL);
1418 MonoThreadObjectHandle
1419 ves_icall_System_Threading_Thread_GetCurrentThread (MonoError *error)
1421 return MONO_HANDLE_NEW (MonoThreadObject, mono_thread_current ());
1424 HANDLE
1425 ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this_obj,
1426 MonoObject *start)
1428 ERROR_DECL (error);
1429 MonoInternalThread *internal;
1430 gboolean res;
1432 THREAD_DEBUG (g_message("%s: Trying to start a new thread: this (%p) start (%p)", __func__, this_obj, start));
1434 if (!this_obj->internal_thread)
1435 ves_icall_System_Threading_Thread_ConstructInternalThread (this_obj);
1436 internal = this_obj->internal_thread;
1438 LOCK_THREAD (internal);
1440 if ((internal->state & ThreadState_Unstarted) == 0) {
1441 UNLOCK_THREAD (internal);
1442 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has already been started."));
1443 return NULL;
1446 if ((internal->state & ThreadState_Aborted) != 0) {
1447 UNLOCK_THREAD (internal);
1448 return this_obj;
1451 res = create_thread (this_obj, internal, start, NULL, NULL, MONO_THREAD_CREATE_FLAGS_NONE, error);
1452 if (!res) {
1453 mono_error_cleanup (error);
1454 UNLOCK_THREAD (internal);
1455 return NULL;
1458 internal->state &= ~ThreadState_Unstarted;
1460 THREAD_DEBUG (g_message ("%s: Started thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread));
1462 UNLOCK_THREAD (internal);
1463 return internal->handle;
1467 * This is called from the finalizer of the internal thread object.
1469 void
1470 ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThread *this_obj)
1472 THREAD_DEBUG (g_message ("%s: Closing thread %p, handle %p", __func__, this, this_obj->handle));
1475 * Since threads keep a reference to their thread object while running, by
1476 * the time this function is called, the thread has already exited/detached,
1477 * i.e. mono_thread_detach_internal () has ran. The exception is during
1478 * shutdown, when mono_thread_detach_internal () can be called after this.
1480 if (this_obj->handle) {
1481 mono_threads_close_thread_handle (this_obj->handle);
1482 this_obj->handle = NULL;
1485 #if HOST_WIN32
1486 CloseHandle (this_obj->native_handle);
1487 #endif
1489 if (this_obj->synch_cs) {
1490 MonoCoopMutex *synch_cs = this_obj->synch_cs;
1491 this_obj->synch_cs = NULL;
1492 mono_coop_mutex_destroy (synch_cs);
1493 g_free (synch_cs);
1496 if (this_obj->name) {
1497 void *name = this_obj->name;
1498 this_obj->name = NULL;
1499 g_free (name);
1503 void
1504 ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
1506 guint32 res;
1507 MonoInternalThread *thread = mono_thread_internal_current ();
1509 THREAD_DEBUG (g_message ("%s: Sleeping for %d ms", __func__, ms));
1511 if (mono_thread_current_check_pending_interrupt ())
1512 return;
1514 while (TRUE) {
1515 gboolean alerted = FALSE;
1517 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1519 res = mono_thread_info_sleep (ms, &alerted);
1521 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1523 if (alerted) {
1524 MonoException* exc = mono_thread_execute_interruption ();
1525 if (exc) {
1526 mono_set_pending_exception (exc);
1527 return;
1528 } else {
1529 // FIXME: !MONO_INFINITE_WAIT
1530 if (ms != MONO_INFINITE_WAIT)
1531 break;
1533 } else {
1534 break;
1539 void ves_icall_System_Threading_Thread_SpinWait_nop (void)
1543 gint32
1544 ves_icall_System_Threading_Thread_GetDomainID (void)
1546 return mono_domain_get()->domain_id;
1549 gboolean
1550 ves_icall_System_Threading_Thread_Yield (void)
1552 return mono_thread_info_yield ();
1556 * mono_thread_get_name:
1558 * Return the name of the thread. NAME_LEN is set to the length of the name.
1559 * Return NULL if the thread has no name. The returned memory is owned by the
1560 * caller.
1562 gunichar2*
1563 mono_thread_get_name (MonoInternalThread *this_obj, guint32 *name_len)
1565 gunichar2 *res;
1567 LOCK_THREAD (this_obj);
1569 if (!this_obj->name) {
1570 *name_len = 0;
1571 res = NULL;
1572 } else {
1573 *name_len = this_obj->name_len;
1574 res = g_new (gunichar2, this_obj->name_len);
1575 memcpy (res, this_obj->name, sizeof (gunichar2) * this_obj->name_len);
1578 UNLOCK_THREAD (this_obj);
1580 return res;
1584 * mono_thread_get_name_utf8:
1585 * \returns the name of the thread in UTF-8.
1586 * Return NULL if the thread has no name.
1587 * The returned memory is owned by the caller.
1589 char *
1590 mono_thread_get_name_utf8 (MonoThread *thread)
1592 if (thread == NULL)
1593 return NULL;
1595 MonoInternalThread *internal = thread->internal_thread;
1596 if (internal == NULL)
1597 return NULL;
1599 LOCK_THREAD (internal);
1601 char *tname = g_utf16_to_utf8 (internal->name, internal->name_len, NULL, NULL, NULL);
1603 UNLOCK_THREAD (internal);
1605 return tname;
1609 * mono_thread_get_managed_id:
1610 * \returns the \c Thread.ManagedThreadId value of \p thread.
1611 * Returns \c -1 if \p thread is NULL.
1613 int32_t
1614 mono_thread_get_managed_id (MonoThread *thread)
1616 if (thread == NULL)
1617 return -1;
1619 MonoInternalThread *internal = thread->internal_thread;
1620 if (internal == NULL)
1621 return -1;
1623 int32_t id = internal->managed_id;
1625 return id;
1628 MonoString*
1629 ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThread *this_obj)
1631 ERROR_DECL (error);
1632 MonoString* str;
1634 error_init (error);
1636 LOCK_THREAD (this_obj);
1638 if (!this_obj->name)
1639 str = NULL;
1640 else
1641 str = mono_string_new_utf16_checked (mono_domain_get (), this_obj->name, this_obj->name_len, error);
1643 UNLOCK_THREAD (this_obj);
1645 if (mono_error_set_pending_exception (error))
1646 return NULL;
1648 return str;
1651 void
1652 mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, gboolean reset, MonoError *error)
1654 MonoNativeThreadId tid = 0;
1656 LOCK_THREAD (this_obj);
1658 error_init (error);
1660 if (reset) {
1661 this_obj->flags &= ~MONO_THREAD_FLAG_NAME_SET;
1662 } else if (this_obj->flags & MONO_THREAD_FLAG_NAME_SET) {
1663 UNLOCK_THREAD (this_obj);
1665 mono_error_set_invalid_operation (error, "Thread.Name can only be set once.");
1666 return;
1668 if (this_obj->name) {
1669 g_free (this_obj->name);
1670 this_obj->name_len = 0;
1672 if (name) {
1673 this_obj->name = g_memdup (mono_string_chars (name), mono_string_length (name) * sizeof (gunichar2));
1674 this_obj->name_len = mono_string_length (name);
1676 if (permanent)
1677 this_obj->flags |= MONO_THREAD_FLAG_NAME_SET;
1679 else
1680 this_obj->name = NULL;
1682 if (!(this_obj->state & ThreadState_Stopped))
1683 tid = thread_get_tid (this_obj);
1685 UNLOCK_THREAD (this_obj);
1687 if (this_obj->name && tid) {
1688 char *tname = mono_string_to_utf8_checked (name, error);
1689 return_if_nok (error);
1690 MONO_PROFILER_RAISE (thread_name, ((uintptr_t)tid, tname));
1691 mono_native_thread_set_name (tid, tname);
1692 mono_free (tname);
1696 void
1697 ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj, MonoString *name)
1699 ERROR_DECL (error);
1700 mono_thread_set_name_internal (this_obj, name, TRUE, FALSE, error);
1701 mono_error_set_pending_exception (error);
1705 * ves_icall_System_Threading_Thread_GetPriority_internal:
1706 * @param this_obj: The MonoInternalThread on which to operate.
1708 * Gets the priority of the given thread.
1709 * @return: The priority of the given thread.
1712 ves_icall_System_Threading_Thread_GetPriority (MonoThreadObjectHandle this_obj, MonoError *error)
1714 gint32 priority;
1716 // InternalThreads are always pinned.
1717 MonoInternalThread *internal = MONO_HANDLE_GETVAL(this_obj, internal_thread);
1719 LOCK_THREAD (internal);
1720 priority = internal->priority;
1721 UNLOCK_THREAD (internal);
1723 return priority;
1727 * ves_icall_System_Threading_Thread_SetPriority_internal:
1728 * @param this_obj: The MonoInternalThread on which to operate.
1729 * @param priority: The priority to set.
1731 * Sets the priority of the given thread.
1733 void
1734 ves_icall_System_Threading_Thread_SetPriority (MonoThreadObjectHandle this_obj, int priority, MonoError *error)
1736 // InternalThreads are always pinned.
1737 MonoInternalThread *internal = MONO_HANDLE_GETVAL(this_obj, internal_thread);
1739 LOCK_THREAD (internal);
1740 internal->priority = priority;
1741 if (internal->thread_info != NULL)
1742 mono_thread_internal_set_priority (internal, priority);
1743 UNLOCK_THREAD (internal);
1746 /* If the array is already in the requested domain, we just return it,
1747 otherwise we return a copy in that domain. */
1748 static MonoArray*
1749 byte_array_to_domain (MonoArray *arr, MonoDomain *domain, MonoError *error)
1751 MonoArray *copy;
1753 error_init (error);
1754 if (!arr)
1755 return NULL;
1757 if (mono_object_domain (arr) == domain)
1758 return arr;
1760 copy = mono_array_new_checked (domain, mono_defaults.byte_class, arr->max_length, error);
1761 memmove (mono_array_addr (copy, guint8, 0), mono_array_addr (arr, guint8, 0), arr->max_length);
1762 return copy;
1765 MonoArray*
1766 ves_icall_System_Threading_Thread_ByteArrayToRootDomain (MonoArray *arr)
1768 ERROR_DECL (error);
1769 MonoArray *result = byte_array_to_domain (arr, mono_get_root_domain (), error);
1770 mono_error_set_pending_exception (error);
1771 return result;
1774 MonoArray*
1775 ves_icall_System_Threading_Thread_ByteArrayToCurrentDomain (MonoArray *arr)
1777 ERROR_DECL (error);
1778 MonoArray *result = byte_array_to_domain (arr, mono_domain_get (), error);
1779 mono_error_set_pending_exception (error);
1780 return result;
1784 * mono_thread_current:
1786 MonoThread *
1787 mono_thread_current (void)
1789 MonoDomain *domain = mono_domain_get ();
1790 MonoInternalThread *internal = mono_thread_internal_current ();
1791 MonoThread **current_thread_ptr;
1793 g_assert (internal);
1794 current_thread_ptr = get_current_thread_ptr_for_domain (domain, internal);
1796 if (!*current_thread_ptr) {
1797 g_assert (domain != mono_get_root_domain ());
1798 *current_thread_ptr = create_thread_object (domain, internal);
1800 return *current_thread_ptr;
1803 /* Return the thread object belonging to INTERNAL in the current domain */
1804 static MonoThread *
1805 mono_thread_current_for_thread (MonoInternalThread *internal)
1807 MonoDomain *domain = mono_domain_get ();
1808 MonoThread **current_thread_ptr;
1810 g_assert (internal);
1811 current_thread_ptr = get_current_thread_ptr_for_domain (domain, internal);
1813 if (!*current_thread_ptr) {
1814 g_assert (domain != mono_get_root_domain ());
1815 *current_thread_ptr = create_thread_object (domain, internal);
1817 return *current_thread_ptr;
1820 MonoInternalThread*
1821 mono_thread_internal_current (void)
1823 MonoInternalThread *res = GET_CURRENT_OBJECT ();
1824 THREAD_DEBUG (g_message ("%s: returning %p", __func__, res));
1825 return res;
1828 static MonoThreadInfoWaitRet
1829 mono_join_uninterrupted (MonoThreadHandle* thread_to_join, gint32 ms, MonoError *error)
1831 MonoException *exc;
1832 MonoThreadInfoWaitRet ret;
1833 gint64 start;
1834 gint32 diff_ms;
1835 gint32 wait = ms;
1837 error_init (error);
1839 start = (ms == -1) ? 0 : mono_msec_ticks ();
1840 for (;;) {
1841 MONO_ENTER_GC_SAFE;
1842 ret = mono_thread_info_wait_one_handle (thread_to_join, ms, TRUE);
1843 MONO_EXIT_GC_SAFE;
1845 if (ret != MONO_THREAD_INFO_WAIT_RET_ALERTED)
1846 return ret;
1848 exc = mono_thread_execute_interruption ();
1849 if (exc) {
1850 mono_error_set_exception_instance (error, exc);
1851 return ret;
1854 if (ms == -1)
1855 continue;
1857 /* Re-calculate ms according to the time passed */
1858 diff_ms = (gint32)(mono_msec_ticks () - start);
1859 if (diff_ms >= ms) {
1860 ret = MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1861 return ret;
1863 wait = ms - diff_ms;
1866 return ret;
1869 gboolean
1870 ves_icall_System_Threading_Thread_Join_internal (MonoThread *this_obj, int ms)
1872 MonoInternalThread *thread = this_obj->internal_thread;
1873 MonoThreadHandle *handle = thread->handle;
1874 MonoInternalThread *cur_thread = mono_thread_internal_current ();
1875 gboolean ret;
1876 ERROR_DECL (error);
1878 if (mono_thread_current_check_pending_interrupt ())
1879 return FALSE;
1881 LOCK_THREAD (thread);
1883 if ((thread->state & ThreadState_Unstarted) != 0) {
1884 UNLOCK_THREAD (thread);
1886 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started."));
1887 return FALSE;
1890 UNLOCK_THREAD (thread);
1892 if (ms == -1)
1893 ms = MONO_INFINITE_WAIT;
1894 THREAD_DEBUG (g_message ("%s: joining thread handle %p, %d ms", __func__, handle, ms));
1896 mono_thread_set_state (cur_thread, ThreadState_WaitSleepJoin);
1898 ret = mono_join_uninterrupted (handle, ms, error);
1900 mono_thread_clr_state (cur_thread, ThreadState_WaitSleepJoin);
1902 mono_error_set_pending_exception (error);
1904 if (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0) {
1905 THREAD_DEBUG (g_message ("%s: join successful", __func__));
1907 /* Wait for the thread to really exit */
1908 MonoNativeThreadId tid = thread_get_tid (thread);
1909 mono_thread_join (tid);
1911 return TRUE;
1914 THREAD_DEBUG (g_message ("%s: join failed", __func__));
1916 return FALSE;
1919 #define MANAGED_WAIT_FAILED 0x7fffffff
1921 static gint32
1922 map_native_wait_result_to_managed (MonoW32HandleWaitRet val, gsize numobjects)
1924 if (val >= MONO_W32HANDLE_WAIT_RET_SUCCESS_0 && val < MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + numobjects) {
1925 return WAIT_OBJECT_0 + (val - MONO_W32HANDLE_WAIT_RET_SUCCESS_0);
1926 } else if (val >= MONO_W32HANDLE_WAIT_RET_ABANDONED_0 && val < MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + numobjects) {
1927 return WAIT_ABANDONED_0 + (val - MONO_W32HANDLE_WAIT_RET_ABANDONED_0);
1928 } else if (val == MONO_W32HANDLE_WAIT_RET_ALERTED) {
1929 return WAIT_IO_COMPLETION;
1930 } else if (val == MONO_W32HANDLE_WAIT_RET_TIMEOUT) {
1931 return WAIT_TIMEOUT;
1932 } else if (val == MONO_W32HANDLE_WAIT_RET_FAILED) {
1933 /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
1934 return MANAGED_WAIT_FAILED;
1935 } else {
1936 g_error ("%s: unknown val value %d", __func__, val);
1940 gint32
1941 ves_icall_System_Threading_WaitHandle_Wait_internal (gpointer *handles, gint32 numhandles, MonoBoolean waitall, gint32 timeout, MonoError *error)
1943 MonoW32HandleWaitRet ret;
1944 MonoInternalThread *thread;
1945 MonoException *exc;
1946 gint64 start;
1947 guint32 timeoutLeft;
1949 /* Do this WaitSleepJoin check before creating objects */
1950 if (mono_thread_current_check_pending_interrupt ())
1951 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
1953 thread = mono_thread_internal_current ();
1955 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1957 if (timeout == -1)
1958 timeout = MONO_INFINITE_WAIT;
1959 if (timeout != MONO_INFINITE_WAIT)
1960 start = mono_msec_ticks ();
1962 timeoutLeft = timeout;
1964 for (;;) {
1965 #ifdef HOST_WIN32
1966 MONO_ENTER_GC_SAFE;
1967 if (numhandles != 1)
1968 ret = mono_w32handle_convert_wait_ret (mono_win32_wait_for_multiple_objects_ex(numhandles, handles, waitall, timeoutLeft, TRUE), numhandles);
1969 else
1970 ret = mono_w32handle_convert_wait_ret (mono_win32_wait_for_single_object_ex (handles [0], timeoutLeft, TRUE), 1);
1971 MONO_EXIT_GC_SAFE;
1972 #else
1973 /* mono_w32handle_wait_multiple optimizes the case for numhandles == 1 */
1974 ret = mono_w32handle_wait_multiple (handles, numhandles, waitall, timeoutLeft, TRUE);
1975 #endif /* HOST_WIN32 */
1977 if (ret != MONO_W32HANDLE_WAIT_RET_ALERTED)
1978 break;
1980 exc = mono_thread_execute_interruption ();
1981 if (exc) {
1982 mono_error_set_exception_instance (error, exc);
1983 break;
1986 if (timeout != MONO_INFINITE_WAIT) {
1987 gint64 elapsed;
1989 elapsed = mono_msec_ticks () - start;
1990 if (elapsed >= timeout) {
1991 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1992 break;
1995 timeoutLeft = timeout - elapsed;
1999 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
2001 return map_native_wait_result_to_managed (ret, numhandles);
2004 gint32
2005 ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (gpointer toSignal, gpointer toWait, gint32 ms, MonoError *error)
2007 MonoW32HandleWaitRet ret;
2008 MonoInternalThread *thread = mono_thread_internal_current ();
2010 if (ms == -1)
2011 ms = MONO_INFINITE_WAIT;
2013 if (mono_thread_current_check_pending_interrupt ())
2014 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
2016 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
2018 MONO_ENTER_GC_SAFE;
2019 #ifdef HOST_WIN32
2020 ret = mono_w32handle_convert_wait_ret (mono_win32_signal_object_and_wait (toSignal, toWait, ms, TRUE), 1);
2021 #else
2022 ret = mono_w32handle_signal_and_wait (toSignal, toWait, ms, TRUE);
2023 #endif
2024 MONO_EXIT_GC_SAFE;
2026 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
2028 return map_native_wait_result_to_managed (ret, 1);
2031 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
2033 return mono_atomic_inc_i32 (location);
2036 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
2038 #if SIZEOF_VOID_P == 4
2039 if (G_UNLIKELY ((size_t)location & 0x7)) {
2040 gint64 ret;
2041 mono_interlocked_lock ();
2042 (*location)++;
2043 ret = *location;
2044 mono_interlocked_unlock ();
2045 return ret;
2047 #endif
2048 return mono_atomic_inc_i64 (location);
2051 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
2053 return mono_atomic_dec_i32(location);
2056 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
2058 #if SIZEOF_VOID_P == 4
2059 if (G_UNLIKELY ((size_t)location & 0x7)) {
2060 gint64 ret;
2061 mono_interlocked_lock ();
2062 (*location)--;
2063 ret = *location;
2064 mono_interlocked_unlock ();
2065 return ret;
2067 #endif
2068 return mono_atomic_dec_i64 (location);
2071 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location, gint32 value)
2073 return mono_atomic_xchg_i32(location, value);
2076 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location, MonoObject *value)
2078 MonoObject *res;
2079 res = (MonoObject *) mono_atomic_xchg_ptr((gpointer *) location, value);
2080 mono_gc_wbarrier_generic_nostore (location);
2081 return res;
2084 gpointer ves_icall_System_Threading_Interlocked_Exchange_IntPtr (gpointer *location, gpointer value)
2086 return mono_atomic_xchg_ptr(location, value);
2089 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location, gfloat value)
2091 IntFloatUnion val, ret;
2093 val.fval = value;
2094 ret.ival = mono_atomic_xchg_i32((gint32 *) location, val.ival);
2096 return ret.fval;
2099 gint64
2100 ves_icall_System_Threading_Interlocked_Exchange_Long (gint64 *location, gint64 value)
2102 #if SIZEOF_VOID_P == 4
2103 if (G_UNLIKELY ((size_t)location & 0x7)) {
2104 gint64 ret;
2105 mono_interlocked_lock ();
2106 ret = *location;
2107 *location = value;
2108 mono_interlocked_unlock ();
2109 return ret;
2111 #endif
2112 return mono_atomic_xchg_i64 (location, value);
2115 gdouble
2116 ves_icall_System_Threading_Interlocked_Exchange_Double (gdouble *location, gdouble value)
2118 LongDoubleUnion val, ret;
2120 val.fval = value;
2121 ret.ival = (gint64)mono_atomic_xchg_i64((gint64 *) location, val.ival);
2123 return ret.fval;
2126 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location, gint32 value, gint32 comparand)
2128 return mono_atomic_cas_i32(location, value, comparand);
2131 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int_Success(gint32 *location, gint32 value, gint32 comparand, MonoBoolean *success)
2133 gint32 r = mono_atomic_cas_i32(location, value, comparand);
2134 *success = r == comparand;
2135 return r;
2138 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location, MonoObject *value, MonoObject *comparand)
2140 MonoObject *res;
2141 res = (MonoObject *) mono_atomic_cas_ptr((gpointer *) location, value, comparand);
2142 mono_gc_wbarrier_generic_nostore (location);
2143 return res;
2146 gpointer ves_icall_System_Threading_Interlocked_CompareExchange_IntPtr(gpointer *location, gpointer value, gpointer comparand)
2148 return mono_atomic_cas_ptr(location, value, comparand);
2151 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location, gfloat value, gfloat comparand)
2153 IntFloatUnion val, ret, cmp;
2155 val.fval = value;
2156 cmp.fval = comparand;
2157 ret.ival = mono_atomic_cas_i32((gint32 *) location, val.ival, cmp.ival);
2159 return ret.fval;
2162 gdouble
2163 ves_icall_System_Threading_Interlocked_CompareExchange_Double (gdouble *location, gdouble value, gdouble comparand)
2165 #if SIZEOF_VOID_P == 8
2166 LongDoubleUnion val, comp, ret;
2168 val.fval = value;
2169 comp.fval = comparand;
2170 ret.ival = (gint64)mono_atomic_cas_ptr((gpointer *) location, (gpointer)val.ival, (gpointer)comp.ival);
2172 return ret.fval;
2173 #else
2174 gdouble old;
2176 mono_interlocked_lock ();
2177 old = *location;
2178 if (old == comparand)
2179 *location = value;
2180 mono_interlocked_unlock ();
2182 return old;
2183 #endif
2186 gint64
2187 ves_icall_System_Threading_Interlocked_CompareExchange_Long (gint64 *location, gint64 value, gint64 comparand)
2189 #if SIZEOF_VOID_P == 4
2190 if (G_UNLIKELY ((size_t)location & 0x7)) {
2191 gint64 old;
2192 mono_interlocked_lock ();
2193 old = *location;
2194 if (old == comparand)
2195 *location = value;
2196 mono_interlocked_unlock ();
2197 return old;
2199 #endif
2200 return mono_atomic_cas_i64 (location, value, comparand);
2203 MonoObject*
2204 ves_icall_System_Threading_Interlocked_CompareExchange_T (MonoObject **location, MonoObject *value, MonoObject *comparand)
2206 MonoObject *res;
2207 res = (MonoObject *)mono_atomic_cas_ptr ((volatile gpointer *)location, value, comparand);
2208 mono_gc_wbarrier_generic_nostore (location);
2209 return res;
2212 MonoObject*
2213 ves_icall_System_Threading_Interlocked_Exchange_T (MonoObject **location, MonoObject *value)
2215 MonoObject *res;
2216 MONO_CHECK_NULL (location, NULL);
2217 res = (MonoObject *)mono_atomic_xchg_ptr ((volatile gpointer *)location, value);
2218 mono_gc_wbarrier_generic_nostore (location);
2219 return res;
2222 gint32
2223 ves_icall_System_Threading_Interlocked_Add_Int (gint32 *location, gint32 value)
2225 return mono_atomic_add_i32 (location, value);
2228 gint64
2229 ves_icall_System_Threading_Interlocked_Add_Long (gint64 *location, gint64 value)
2231 #if SIZEOF_VOID_P == 4
2232 if (G_UNLIKELY ((size_t)location & 0x7)) {
2233 gint64 ret;
2234 mono_interlocked_lock ();
2235 *location += value;
2236 ret = *location;
2237 mono_interlocked_unlock ();
2238 return ret;
2240 #endif
2241 return mono_atomic_add_i64 (location, value);
2244 gint64
2245 ves_icall_System_Threading_Interlocked_Read_Long (gint64 *location)
2247 #if SIZEOF_VOID_P == 4
2248 if (G_UNLIKELY ((size_t)location & 0x7)) {
2249 gint64 ret;
2250 mono_interlocked_lock ();
2251 ret = *location;
2252 mono_interlocked_unlock ();
2253 return ret;
2255 #endif
2256 return mono_atomic_load_i64 (location);
2259 void
2260 ves_icall_System_Threading_Thread_MemoryBarrier (void)
2262 mono_memory_barrier ();
2265 void
2266 ves_icall_System_Threading_Thread_ClrState (MonoInternalThreadHandle this_obj, guint32 state, MonoError *error)
2268 // InternalThreads are always pinned, so shallowly coop-handleize.
2269 mono_thread_clr_state (mono_internal_thread_handle_ptr (this_obj), state);
2272 void
2273 ves_icall_System_Threading_Thread_SetState (MonoInternalThreadHandle thread_handle, guint32 state, MonoError *error)
2275 // InternalThreads are always pinned, so shallowly coop-handleize.
2276 mono_thread_set_state (mono_internal_thread_handle_ptr (thread_handle), state);
2279 guint32
2280 ves_icall_System_Threading_Thread_GetState (MonoInternalThreadHandle thread_handle, MonoError *error)
2282 // InternalThreads are always pinned, so shallowly coop-handleize.
2283 MonoInternalThread *this_obj = mono_internal_thread_handle_ptr (thread_handle);
2285 guint32 state;
2287 LOCK_THREAD (this_obj);
2289 state = this_obj->state;
2291 UNLOCK_THREAD (this_obj);
2293 return state;
2296 void ves_icall_System_Threading_Thread_Interrupt_internal (MonoThread *this_obj)
2298 MonoInternalThread *current;
2299 gboolean throw_;
2300 MonoInternalThread *thread = this_obj->internal_thread;
2302 LOCK_THREAD (thread);
2304 current = mono_thread_internal_current ();
2306 thread->thread_interrupt_requested = TRUE;
2307 throw_ = current != thread && (thread->state & ThreadState_WaitSleepJoin);
2309 UNLOCK_THREAD (thread);
2311 if (throw_) {
2312 async_abort_internal (thread, FALSE);
2317 * mono_thread_current_check_pending_interrupt:
2318 * Checks if there's a interruption request and set the pending exception if so.
2319 * \returns true if a pending exception was set
2321 gboolean
2322 mono_thread_current_check_pending_interrupt (void)
2324 MonoInternalThread *thread = mono_thread_internal_current ();
2325 gboolean throw_ = FALSE;
2327 LOCK_THREAD (thread);
2329 if (thread->thread_interrupt_requested) {
2330 throw_ = TRUE;
2331 thread->thread_interrupt_requested = FALSE;
2334 UNLOCK_THREAD (thread);
2336 if (throw_)
2337 mono_set_pending_exception (mono_get_exception_thread_interrupted ());
2338 return throw_;
2341 static gboolean
2342 request_thread_abort (MonoInternalThread *thread, MonoObject *state, gboolean appdomain_unload)
2344 LOCK_THREAD (thread);
2346 if (thread->state & (ThreadState_AbortRequested | ThreadState_Stopped))
2348 UNLOCK_THREAD (thread);
2349 return FALSE;
2352 if ((thread->state & ThreadState_Unstarted) != 0) {
2353 thread->state |= ThreadState_Aborted;
2354 UNLOCK_THREAD (thread);
2355 return FALSE;
2358 thread->state |= ThreadState_AbortRequested;
2359 if (appdomain_unload)
2360 thread->flags |= MONO_THREAD_FLAG_APPDOMAIN_ABORT;
2361 else
2362 thread->flags &= ~MONO_THREAD_FLAG_APPDOMAIN_ABORT;
2364 if (thread->abort_state_handle)
2365 mono_gchandle_free (thread->abort_state_handle);
2366 if (state) {
2367 thread->abort_state_handle = mono_gchandle_new (state, FALSE);
2368 g_assert (thread->abort_state_handle);
2369 } else {
2370 thread->abort_state_handle = 0;
2372 thread->abort_exc = NULL;
2374 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));
2376 /* During shutdown, we can't wait for other threads */
2377 if (!shutting_down)
2378 /* Make sure the thread is awake */
2379 mono_thread_resume (thread);
2381 UNLOCK_THREAD (thread);
2382 return TRUE;
2385 void
2386 ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject *state)
2388 if (!request_thread_abort (thread, state, FALSE))
2389 return;
2391 if (thread == mono_thread_internal_current ()) {
2392 ERROR_DECL (error);
2393 self_abort_internal (error);
2394 mono_error_set_pending_exception (error);
2395 } else {
2396 async_abort_internal (thread, TRUE);
2401 * mono_thread_internal_abort:
2402 * Request thread \p thread to be aborted.
2403 * \p thread MUST NOT be the current thread.
2405 void
2406 mono_thread_internal_abort (MonoInternalThread *thread, gboolean appdomain_unload)
2408 g_assert (thread != mono_thread_internal_current ());
2410 if (!request_thread_abort (thread, NULL, appdomain_unload))
2411 return;
2412 async_abort_internal (thread, TRUE);
2415 void
2416 ves_icall_System_Threading_Thread_ResetAbort (MonoThread *this_obj)
2418 MonoInternalThread *thread = mono_thread_internal_current ();
2419 gboolean was_aborting, is_domain_abort;
2421 LOCK_THREAD (thread);
2422 was_aborting = thread->state & ThreadState_AbortRequested;
2423 is_domain_abort = thread->flags & MONO_THREAD_FLAG_APPDOMAIN_ABORT;
2425 if (was_aborting && !is_domain_abort)
2426 thread->state &= ~ThreadState_AbortRequested;
2427 UNLOCK_THREAD (thread);
2429 if (!was_aborting) {
2430 const char *msg = "Unable to reset abort because no abort was requested";
2431 mono_set_pending_exception (mono_get_exception_thread_state (msg));
2432 return;
2433 } else if (is_domain_abort) {
2434 /* Silently ignore abort resets in unloading appdomains */
2435 return;
2438 mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
2439 thread->abort_exc = NULL;
2440 if (thread->abort_state_handle) {
2441 mono_gchandle_free (thread->abort_state_handle);
2442 /* This is actually not necessary - the handle
2443 only counts if the exception is set */
2444 thread->abort_state_handle = 0;
2448 void
2449 mono_thread_internal_reset_abort (MonoInternalThread *thread)
2451 LOCK_THREAD (thread);
2453 thread->state &= ~ThreadState_AbortRequested;
2455 if (thread->abort_exc) {
2456 mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
2457 thread->abort_exc = NULL;
2458 if (thread->abort_state_handle) {
2459 mono_gchandle_free (thread->abort_state_handle);
2460 /* This is actually not necessary - the handle
2461 only counts if the exception is set */
2462 thread->abort_state_handle = 0;
2466 UNLOCK_THREAD (thread);
2469 MonoObject*
2470 ves_icall_System_Threading_Thread_GetAbortExceptionState (MonoThread *this_obj)
2472 ERROR_DECL (error);
2473 MonoInternalThread *thread = this_obj->internal_thread;
2474 MonoObject *state, *deserialized = NULL;
2475 MonoDomain *domain;
2477 if (!thread->abort_state_handle)
2478 return NULL;
2480 state = mono_gchandle_get_target (thread->abort_state_handle);
2481 g_assert (state);
2483 domain = mono_domain_get ();
2484 if (mono_object_domain (state) == domain)
2485 return state;
2487 deserialized = mono_object_xdomain_representation (state, domain, error);
2489 if (!deserialized) {
2490 MonoException *invalid_op_exc = mono_get_exception_invalid_operation ("Thread.ExceptionState cannot access an ExceptionState from a different AppDomain");
2491 if (!is_ok (error)) {
2492 MonoObject *exc = (MonoObject*)mono_error_convert_to_exception (error);
2493 MONO_OBJECT_SETREF (invalid_op_exc, inner_ex, exc);
2495 mono_set_pending_exception (invalid_op_exc);
2496 return NULL;
2499 return deserialized;
2502 static gboolean
2503 mono_thread_suspend (MonoInternalThread *thread)
2505 LOCK_THREAD (thread);
2507 if (thread->state & (ThreadState_Unstarted | ThreadState_Aborted | ThreadState_Stopped))
2509 UNLOCK_THREAD (thread);
2510 return FALSE;
2513 if (thread->state & (ThreadState_Suspended | ThreadState_SuspendRequested | ThreadState_AbortRequested))
2515 UNLOCK_THREAD (thread);
2516 return TRUE;
2519 thread->state |= ThreadState_SuspendRequested;
2520 mono_os_event_reset (thread->suspended);
2522 if (thread == mono_thread_internal_current ()) {
2523 /* calls UNLOCK_THREAD (thread) */
2524 self_suspend_internal ();
2525 } else {
2526 /* calls UNLOCK_THREAD (thread) */
2527 async_suspend_internal (thread, FALSE);
2530 return TRUE;
2533 void
2534 ves_icall_System_Threading_Thread_Suspend (MonoThread *this_obj)
2536 if (!mono_thread_suspend (this_obj->internal_thread)) {
2537 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
2538 return;
2542 /* LOCKING: LOCK_THREAD(thread) must be held */
2543 static gboolean
2544 mono_thread_resume (MonoInternalThread *thread)
2546 if ((thread->state & ThreadState_SuspendRequested) != 0) {
2547 // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (1) thread %p\n", thread_get_tid (thread));
2548 thread->state &= ~ThreadState_SuspendRequested;
2549 mono_os_event_set (thread->suspended);
2550 return TRUE;
2553 if ((thread->state & ThreadState_Suspended) == 0 ||
2554 (thread->state & ThreadState_Unstarted) != 0 ||
2555 (thread->state & ThreadState_Aborted) != 0 ||
2556 (thread->state & ThreadState_Stopped) != 0)
2558 // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (2) thread %p\n", thread_get_tid (thread));
2559 return FALSE;
2562 // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (3) thread %p\n", thread_get_tid (thread));
2564 mono_os_event_set (thread->suspended);
2566 if (!thread->self_suspended) {
2567 UNLOCK_THREAD (thread);
2569 /* Awake the thread */
2570 if (!mono_thread_info_resume (thread_get_tid (thread)))
2571 return FALSE;
2573 LOCK_THREAD (thread);
2576 thread->state &= ~ThreadState_Suspended;
2578 return TRUE;
2581 void
2582 ves_icall_System_Threading_Thread_Resume (MonoThread *thread)
2584 if (!thread->internal_thread) {
2585 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
2586 } else {
2587 LOCK_THREAD (thread->internal_thread);
2588 if (!mono_thread_resume (thread->internal_thread))
2589 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
2590 UNLOCK_THREAD (thread->internal_thread);
2594 static gboolean
2595 mono_threads_is_critical_method (MonoMethod *method)
2597 switch (method->wrapper_type) {
2598 case MONO_WRAPPER_RUNTIME_INVOKE:
2599 case MONO_WRAPPER_XDOMAIN_INVOKE:
2600 case MONO_WRAPPER_XDOMAIN_DISPATCH:
2601 return TRUE;
2603 return FALSE;
2606 static gboolean
2607 find_wrapper (MonoMethod *m, gint no, gint ilo, gboolean managed, gpointer data)
2609 if (managed)
2610 return TRUE;
2612 if (mono_threads_is_critical_method (m)) {
2613 *((gboolean*)data) = TRUE;
2614 return TRUE;
2616 return FALSE;
2619 static gboolean
2620 is_running_protected_wrapper (void)
2622 gboolean found = FALSE;
2623 mono_stack_walk (find_wrapper, &found);
2624 return found;
2628 * mono_thread_stop:
2630 void
2631 mono_thread_stop (MonoThread *thread)
2633 MonoInternalThread *internal = thread->internal_thread;
2635 if (!request_thread_abort (internal, NULL, FALSE))
2636 return;
2638 if (internal == mono_thread_internal_current ()) {
2639 ERROR_DECL (error);
2640 self_abort_internal (error);
2642 This function is part of the embeding API and has no way to return the exception
2643 to be thrown. So what we do is keep the old behavior and raise the exception.
2645 mono_error_raise_exception_deprecated (error); /* OK to throw, see note */
2646 } else {
2647 async_abort_internal (internal, TRUE);
2651 gint8
2652 ves_icall_System_Threading_Thread_VolatileRead1 (void *ptr)
2654 gint8 tmp = *(volatile gint8 *)ptr;
2655 mono_memory_barrier ();
2656 return tmp;
2659 gint16
2660 ves_icall_System_Threading_Thread_VolatileRead2 (void *ptr)
2662 gint16 tmp = *(volatile gint16 *)ptr;
2663 mono_memory_barrier ();
2664 return tmp;
2667 gint32
2668 ves_icall_System_Threading_Thread_VolatileRead4 (void *ptr)
2670 gint32 tmp = *(volatile gint32 *)ptr;
2671 mono_memory_barrier ();
2672 return tmp;
2675 gint64
2676 ves_icall_System_Threading_Thread_VolatileRead8 (void *ptr)
2678 gint64 tmp = *(volatile gint64 *)ptr;
2679 mono_memory_barrier ();
2680 return tmp;
2683 void *
2684 ves_icall_System_Threading_Thread_VolatileReadIntPtr (void *ptr)
2686 volatile void *tmp = *(volatile void **)ptr;
2687 mono_memory_barrier ();
2688 return (void *) tmp;
2691 void *
2692 ves_icall_System_Threading_Thread_VolatileReadObject (void *ptr)
2694 volatile MonoObject *tmp = *(volatile MonoObject **)ptr;
2695 mono_memory_barrier ();
2696 return (MonoObject *) tmp;
2699 double
2700 ves_icall_System_Threading_Thread_VolatileReadDouble (void *ptr)
2702 double tmp = *(volatile double *)ptr;
2703 mono_memory_barrier ();
2704 return tmp;
2707 float
2708 ves_icall_System_Threading_Thread_VolatileReadFloat (void *ptr)
2710 float tmp = *(volatile float *)ptr;
2711 mono_memory_barrier ();
2712 return tmp;
2715 gint8
2716 ves_icall_System_Threading_Volatile_Read1 (void *ptr)
2718 return mono_atomic_load_i8 ((volatile gint8 *)ptr);
2721 gint16
2722 ves_icall_System_Threading_Volatile_Read2 (void *ptr)
2724 return mono_atomic_load_i16 ((volatile gint16 *)ptr);
2727 gint32
2728 ves_icall_System_Threading_Volatile_Read4 (void *ptr)
2730 return mono_atomic_load_i32 ((volatile gint32 *)ptr);
2733 gint64
2734 ves_icall_System_Threading_Volatile_Read8 (void *ptr)
2736 #if SIZEOF_VOID_P == 4
2737 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2738 gint64 val;
2739 mono_interlocked_lock ();
2740 val = *(gint64*)ptr;
2741 mono_interlocked_unlock ();
2742 return val;
2744 #endif
2745 return mono_atomic_load_i64 ((volatile gint64 *)ptr);
2748 void *
2749 ves_icall_System_Threading_Volatile_ReadIntPtr (void *ptr)
2751 return mono_atomic_load_ptr ((volatile gpointer *)ptr);
2754 double
2755 ves_icall_System_Threading_Volatile_ReadDouble (void *ptr)
2757 LongDoubleUnion u;
2759 #if SIZEOF_VOID_P == 4
2760 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2761 double val;
2762 mono_interlocked_lock ();
2763 val = *(double*)ptr;
2764 mono_interlocked_unlock ();
2765 return val;
2767 #endif
2769 u.ival = mono_atomic_load_i64 ((volatile gint64 *)ptr);
2771 return u.fval;
2774 float
2775 ves_icall_System_Threading_Volatile_ReadFloat (void *ptr)
2777 IntFloatUnion u;
2779 u.ival = mono_atomic_load_i32 ((volatile gint32 *)ptr);
2781 return u.fval;
2784 MonoObject*
2785 ves_icall_System_Threading_Volatile_Read_T (void *ptr)
2787 return (MonoObject *)mono_atomic_load_ptr ((volatile gpointer *)ptr);
2790 void
2791 ves_icall_System_Threading_Thread_VolatileWrite1 (void *ptr, gint8 value)
2793 mono_memory_barrier ();
2794 *(volatile gint8 *)ptr = value;
2797 void
2798 ves_icall_System_Threading_Thread_VolatileWrite2 (void *ptr, gint16 value)
2800 mono_memory_barrier ();
2801 *(volatile gint16 *)ptr = value;
2804 void
2805 ves_icall_System_Threading_Thread_VolatileWrite4 (void *ptr, gint32 value)
2807 mono_memory_barrier ();
2808 *(volatile gint32 *)ptr = value;
2811 void
2812 ves_icall_System_Threading_Thread_VolatileWrite8 (void *ptr, gint64 value)
2814 mono_memory_barrier ();
2815 *(volatile gint64 *)ptr = value;
2818 void
2819 ves_icall_System_Threading_Thread_VolatileWriteIntPtr (void *ptr, void *value)
2821 mono_memory_barrier ();
2822 *(volatile void **)ptr = value;
2825 void
2826 ves_icall_System_Threading_Thread_VolatileWriteObject (void *ptr, MonoObject *value)
2828 mono_memory_barrier ();
2829 mono_gc_wbarrier_generic_store (ptr, value);
2832 void
2833 ves_icall_System_Threading_Thread_VolatileWriteDouble (void *ptr, double value)
2835 mono_memory_barrier ();
2836 *(volatile double *)ptr = value;
2839 void
2840 ves_icall_System_Threading_Thread_VolatileWriteFloat (void *ptr, float value)
2842 mono_memory_barrier ();
2843 *(volatile float *)ptr = value;
2846 void
2847 ves_icall_System_Threading_Volatile_Write1 (void *ptr, gint8 value)
2849 mono_atomic_store_i8 ((volatile gint8 *)ptr, value);
2852 void
2853 ves_icall_System_Threading_Volatile_Write2 (void *ptr, gint16 value)
2855 mono_atomic_store_i16 ((volatile gint16 *)ptr, value);
2858 void
2859 ves_icall_System_Threading_Volatile_Write4 (void *ptr, gint32 value)
2861 mono_atomic_store_i32 ((volatile gint32 *)ptr, value);
2864 void
2865 ves_icall_System_Threading_Volatile_Write8 (void *ptr, gint64 value)
2867 #if SIZEOF_VOID_P == 4
2868 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2869 mono_interlocked_lock ();
2870 *(gint64*)ptr = value;
2871 mono_interlocked_unlock ();
2872 return;
2874 #endif
2876 mono_atomic_store_i64 ((volatile gint64 *)ptr, value);
2879 void
2880 ves_icall_System_Threading_Volatile_WriteIntPtr (void *ptr, void *value)
2882 mono_atomic_store_ptr ((volatile gpointer *)ptr, value);
2885 void
2886 ves_icall_System_Threading_Volatile_WriteDouble (void *ptr, double value)
2888 LongDoubleUnion u;
2890 #if SIZEOF_VOID_P == 4
2891 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2892 mono_interlocked_lock ();
2893 *(double*)ptr = value;
2894 mono_interlocked_unlock ();
2895 return;
2897 #endif
2899 u.fval = value;
2901 mono_atomic_store_i64 ((volatile gint64 *)ptr, u.ival);
2904 void
2905 ves_icall_System_Threading_Volatile_WriteFloat (void *ptr, float value)
2907 IntFloatUnion u;
2909 u.fval = value;
2911 mono_atomic_store_i32 ((volatile gint32 *)ptr, u.ival);
2914 void
2915 ves_icall_System_Threading_Volatile_Write_T (void *ptr, MonoObject *value)
2917 mono_gc_wbarrier_generic_store_atomic (ptr, value);
2920 static void
2921 free_context (void *user_data)
2923 ContextStaticData *data = user_data;
2925 mono_threads_lock ();
2928 * There is no guarantee that, by the point this reference queue callback
2929 * has been invoked, the GC handle associated with the object will fail to
2930 * resolve as one might expect. So if we don't free and remove the GC
2931 * handle here, free_context_static_data_helper () could end up resolving
2932 * a GC handle to an actually-dead context which would contain a pointer
2933 * to an already-freed static data segment, resulting in a crash when
2934 * accessing it.
2936 g_hash_table_remove (contexts, GUINT_TO_POINTER (data->gc_handle));
2938 mono_threads_unlock ();
2940 mono_gchandle_free (data->gc_handle);
2941 mono_free_static_data (data->static_data);
2942 g_free (data);
2945 void
2946 mono_threads_register_app_context (MonoAppContext *ctx, MonoError *error)
2948 error_init (error);
2949 mono_threads_lock ();
2951 //g_print ("Registering context %d in domain %d\n", ctx->context_id, ctx->domain_id);
2953 if (!contexts)
2954 contexts = g_hash_table_new (NULL, NULL);
2956 if (!context_queue)
2957 context_queue = mono_gc_reference_queue_new (free_context);
2959 gpointer gch = GUINT_TO_POINTER (mono_gchandle_new_weakref (&ctx->obj, FALSE));
2960 g_hash_table_insert (contexts, gch, gch);
2963 * We use this intermediate structure to contain a duplicate pointer to
2964 * the static data because we can't rely on being able to resolve the GC
2965 * handle in the reference queue callback.
2967 ContextStaticData *data = g_new0 (ContextStaticData, 1);
2968 data->gc_handle = GPOINTER_TO_UINT (gch);
2969 ctx->data = data;
2971 context_adjust_static_data (ctx);
2972 mono_gc_reference_queue_add (context_queue, &ctx->obj, data);
2974 mono_threads_unlock ();
2976 MONO_PROFILER_RAISE (context_loaded, (ctx));
2979 void
2980 ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppContextHandle ctx, MonoError *error)
2982 error_init (error);
2983 mono_threads_register_app_context (MONO_HANDLE_RAW (ctx), error); /* FIXME use handles in mono_threads_register_app_context */
2986 void
2987 mono_threads_release_app_context (MonoAppContext* ctx, MonoError *error)
2990 * NOTE: Since finalizers are unreliable for the purposes of ensuring
2991 * cleanup in exceptional circumstances, we don't actually do any
2992 * cleanup work here. We instead do this via a reference queue.
2995 //g_print ("Releasing context %d in domain %d\n", ctx->context_id, ctx->domain_id);
2997 MONO_PROFILER_RAISE (context_unloaded, (ctx));
3000 void
3001 ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContextHandle ctx, MonoError *error)
3003 error_init (error);
3004 mono_threads_release_app_context (MONO_HANDLE_RAW (ctx), error); /* FIXME use handles in mono_threads_release_app_context */
3007 void mono_thread_init (MonoThreadStartCB start_cb,
3008 MonoThreadAttachCB attach_cb)
3010 mono_coop_mutex_init_recursive (&threads_mutex);
3012 mono_os_mutex_init_recursive(&interlocked_mutex);
3013 mono_os_mutex_init_recursive(&joinable_threads_mutex);
3015 mono_os_event_init (&background_change_event, FALSE);
3017 mono_os_cond_init (&zero_pending_joinable_thread_event);
3019 mono_init_static_data_info (&thread_static_info);
3020 mono_init_static_data_info (&context_static_info);
3022 mono_thread_start_cb = start_cb;
3023 mono_thread_attach_cb = attach_cb;
3026 static gpointer
3027 thread_attach (MonoThreadInfo *info)
3029 return mono_gc_thread_attach (info);
3032 static void
3033 thread_detach (MonoThreadInfo *info)
3035 MonoInternalThread *internal;
3036 guint32 gchandle;
3038 /* If a delegate is passed to native code and invoked on a thread we dont
3039 * know about, marshal will register it with mono_threads_attach_coop, but
3040 * we have no way of knowing when that thread goes away. SGen has a TSD
3041 * so we assume that if the domain is still registered, we can detach
3042 * the thread */
3044 g_assert (info);
3046 if (!mono_thread_info_try_get_internal_thread_gchandle (info, &gchandle))
3047 return;
3049 internal = (MonoInternalThread*) mono_gchandle_get_target (gchandle);
3050 g_assert (internal);
3052 mono_gchandle_free (gchandle);
3054 mono_thread_detach_internal (internal);
3057 static void
3058 thread_detach_with_lock (MonoThreadInfo *info)
3060 mono_gc_thread_detach_with_lock (info);
3063 static gboolean
3064 thread_in_critical_region (MonoThreadInfo *info)
3066 return mono_gc_thread_in_critical_region (info);
3069 static gboolean
3070 ip_in_critical_region (MonoDomain *domain, gpointer ip)
3072 MonoJitInfo *ji;
3073 MonoMethod *method;
3076 * We pass false for 'try_aot' so this becomes async safe.
3077 * It won't find aot methods whose jit info is not yet loaded,
3078 * so we preload their jit info in the JIT.
3080 ji = mono_jit_info_table_find_internal (domain, ip, FALSE, FALSE);
3081 if (!ji)
3082 return FALSE;
3084 method = mono_jit_info_get_method (ji);
3085 g_assert (method);
3087 return mono_gc_is_critical_method (method);
3090 void
3091 mono_thread_callbacks_init (void)
3093 MonoThreadInfoCallbacks cb;
3095 memset (&cb, 0, sizeof(cb));
3096 cb.thread_attach = thread_attach;
3097 cb.thread_detach = thread_detach;
3098 cb.thread_detach_with_lock = thread_detach_with_lock;
3099 cb.ip_in_critical_region = ip_in_critical_region;
3100 cb.thread_in_critical_region = thread_in_critical_region;
3101 mono_thread_info_callbacks_init (&cb);
3105 * mono_thread_cleanup:
3107 void
3108 mono_thread_cleanup (void)
3110 /* Wait for pending threads to park on joinable threads list */
3111 /* NOTE, waiting on this should be extremely rare and will only happen */
3112 /* under certain specific conditions. */
3113 gboolean wait_result = threads_wait_pending_joinable_threads (2000);
3114 if (!wait_result)
3115 g_warning ("Waiting on threads to park on joinable thread list timed out.");
3117 mono_threads_join_threads ();
3119 #if !defined(RUN_IN_SUBTHREAD) && !defined(HOST_WIN32)
3120 /* The main thread must abandon any held mutexes (particularly
3121 * important for named mutexes as they are shared across
3122 * processes, see bug 74680.) This will happen when the
3123 * thread exits, but if it's not running in a subthread it
3124 * won't exit in time.
3126 mono_w32mutex_abandon (mono_thread_internal_current ());
3127 #endif
3129 #if 0
3130 /* This stuff needs more testing, it seems one of these
3131 * critical sections can be locked when mono_thread_cleanup is
3132 * called.
3134 mono_coop_mutex_destroy (&threads_mutex);
3135 mono_os_mutex_destroy (&interlocked_mutex);
3136 mono_os_mutex_destroy (&delayed_free_table_mutex);
3137 mono_os_mutex_destroy (&small_id_mutex);
3138 mono_os_cond_destroy (&zero_pending_joinable_runtime_thread_event);
3139 mono_os_event_destroy (&background_change_event);
3140 #endif
3143 void
3144 mono_threads_install_cleanup (MonoThreadCleanupFunc func)
3146 mono_thread_cleanup_fn = func;
3150 * mono_thread_set_manage_callback:
3152 void
3153 mono_thread_set_manage_callback (MonoThread *thread, MonoThreadManageCallback func)
3155 thread->internal_thread->manage_callback = func;
3158 G_GNUC_UNUSED
3159 static void print_tids (gpointer key, gpointer value, gpointer user)
3161 /* GPOINTER_TO_UINT breaks horribly if sizeof(void *) >
3162 * sizeof(uint) and a cast to uint would overflow
3164 /* Older versions of glib don't have G_GSIZE_FORMAT, so just
3165 * print this as a pointer.
3167 g_message ("Waiting for: %p", key);
3170 struct wait_data
3172 MonoThreadHandle *handles[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
3173 MonoInternalThread *threads[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
3174 guint32 num;
3177 static void
3178 wait_for_tids (struct wait_data *wait, guint32 timeout, gboolean check_state_change)
3180 guint32 i;
3181 MonoThreadInfoWaitRet ret;
3183 THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num));
3185 /* Add the thread state change event, so it wakes
3186 * up if a thread changes to background mode. */
3188 MONO_ENTER_GC_SAFE;
3189 if (check_state_change)
3190 ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, &background_change_event, FALSE, timeout, TRUE);
3191 else
3192 ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, NULL, TRUE, timeout, TRUE);
3193 MONO_EXIT_GC_SAFE;
3195 if (ret == MONO_THREAD_INFO_WAIT_RET_FAILED) {
3196 /* See the comment in build_wait_tids() */
3197 THREAD_DEBUG (g_message ("%s: Wait failed", __func__));
3198 return;
3201 for( i = 0; i < wait->num; i++)
3202 mono_threads_close_thread_handle (wait->handles [i]);
3204 if (ret == MONO_THREAD_INFO_WAIT_RET_TIMEOUT)
3205 return;
3207 if (ret < wait->num) {
3208 MonoInternalThread *internal;
3210 internal = wait->threads [ret];
3212 mono_threads_lock ();
3213 if (mono_g_hash_table_lookup (threads, (gpointer) internal->tid) == internal)
3214 g_error ("%s: failed to call mono_thread_detach_internal on thread %p, InternalThread: %p", __func__, internal->tid, internal);
3215 mono_threads_unlock ();
3219 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
3221 struct wait_data *wait=(struct wait_data *)user;
3223 if(wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS - 1) {
3224 MonoInternalThread *thread=(MonoInternalThread *)value;
3226 /* Ignore background threads, we abort them later */
3227 /* Do not lock here since it is not needed and the caller holds threads_lock */
3228 if (thread->state & ThreadState_Background) {
3229 THREAD_DEBUG (g_message ("%s: ignoring background thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3230 return; /* just leave, ignore */
3233 if (mono_gc_is_finalizer_internal_thread (thread)) {
3234 THREAD_DEBUG (g_message ("%s: ignoring finalizer thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3235 return;
3238 if (thread == mono_thread_internal_current ()) {
3239 THREAD_DEBUG (g_message ("%s: ignoring current thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3240 return;
3243 if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread)) {
3244 THREAD_DEBUG (g_message ("%s: ignoring main thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3245 return;
3248 if (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE) {
3249 THREAD_DEBUG (g_message ("%s: ignoring thread %" G_GSIZE_FORMAT "with DONT_MANAGE flag set.", __func__, (gsize)thread->tid));
3250 return;
3253 THREAD_DEBUG (g_message ("%s: Invoking mono_thread_manage callback on thread %p", __func__, thread));
3254 if ((thread->manage_callback == NULL) || (thread->manage_callback (thread->root_domain_thread) == TRUE)) {
3255 wait->handles[wait->num]=mono_threads_open_thread_handle (thread->handle);
3256 wait->threads[wait->num]=thread;
3257 wait->num++;
3259 THREAD_DEBUG (g_message ("%s: adding thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3260 } else {
3261 THREAD_DEBUG (g_message ("%s: ignoring (because of callback) thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3265 } else {
3266 /* Just ignore the rest, we can't do anything with
3267 * them yet
3272 static void
3273 abort_threads (gpointer key, gpointer value, gpointer user)
3275 struct wait_data *wait=(struct wait_data *)user;
3276 MonoNativeThreadId self = mono_native_thread_id_get ();
3277 MonoInternalThread *thread = (MonoInternalThread *)value;
3279 if (wait->num >= MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS)
3280 return;
3282 if (mono_native_thread_id_equals (thread_get_tid (thread), self))
3283 return;
3284 if (mono_gc_is_finalizer_internal_thread (thread))
3285 return;
3287 if ((thread->flags & MONO_THREAD_FLAG_DONT_MANAGE))
3288 return;
3290 wait->handles[wait->num] = mono_threads_open_thread_handle (thread->handle);
3291 wait->threads[wait->num] = thread;
3292 wait->num++;
3294 THREAD_DEBUG (g_print ("%s: Aborting id: %"G_GSIZE_FORMAT"\n", __func__, (gsize)thread->tid));
3295 mono_thread_internal_abort (thread, FALSE);
3298 /**
3299 * mono_threads_set_shutting_down:
3301 * Is called by a thread that wants to shut down Mono. If the runtime is already
3302 * shutting down, the calling thread is suspended/stopped, and this function never
3303 * returns.
3305 void
3306 mono_threads_set_shutting_down (void)
3308 MonoInternalThread *current_thread = mono_thread_internal_current ();
3310 mono_threads_lock ();
3312 if (shutting_down) {
3313 mono_threads_unlock ();
3315 /* Make sure we're properly suspended/stopped */
3317 LOCK_THREAD (current_thread);
3319 if (current_thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
3320 UNLOCK_THREAD (current_thread);
3321 mono_thread_execute_interruption ();
3322 } else {
3323 UNLOCK_THREAD (current_thread);
3326 /*since we're killing the thread, detach it.*/
3327 mono_thread_detach_internal (current_thread);
3329 /* Wake up other threads potentially waiting for us */
3330 mono_thread_info_exit (0);
3331 } else {
3332 shutting_down = TRUE;
3334 /* Not really a background state change, but this will
3335 * interrupt the main thread if it is waiting for all
3336 * the other threads.
3338 mono_os_event_set (&background_change_event);
3340 mono_threads_unlock ();
3345 * mono_thread_manage:
3347 void
3348 mono_thread_manage (void)
3350 struct wait_data wait_data;
3351 struct wait_data *wait = &wait_data;
3353 memset (wait, 0, sizeof (struct wait_data));
3354 /* join each thread that's still running */
3355 THREAD_DEBUG (g_message ("%s: Joining each running thread...", __func__));
3357 mono_threads_lock ();
3358 if(threads==NULL) {
3359 THREAD_DEBUG (g_message("%s: No threads", __func__));
3360 mono_threads_unlock ();
3361 return;
3364 mono_threads_unlock ();
3366 do {
3367 mono_threads_lock ();
3368 if (shutting_down) {
3369 /* somebody else is shutting down */
3370 mono_threads_unlock ();
3371 break;
3373 THREAD_DEBUG (g_message ("%s: There are %d threads to join", __func__, mono_g_hash_table_size (threads));
3374 mono_g_hash_table_foreach (threads, print_tids, NULL));
3376 mono_os_event_reset (&background_change_event);
3377 wait->num=0;
3378 /* We must zero all InternalThread pointers to avoid making the GC unhappy. */
3379 memset (wait->threads, 0, MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
3380 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
3381 mono_threads_unlock ();
3382 if (wait->num > 0)
3383 /* Something to wait for */
3384 wait_for_tids (wait, MONO_INFINITE_WAIT, TRUE);
3385 THREAD_DEBUG (g_message ("%s: I have %d threads after waiting.", __func__, wait->num));
3386 } while(wait->num>0);
3388 /* Mono is shutting down, so just wait for the end */
3389 if (!mono_runtime_try_shutdown ()) {
3390 /*FIXME mono_thread_suspend probably should call mono_thread_execute_interruption when self interrupting. */
3391 mono_thread_suspend (mono_thread_internal_current ());
3392 mono_thread_execute_interruption ();
3396 * Remove everything but the finalizer thread and self.
3397 * Also abort all the background threads
3398 * */
3399 do {
3400 mono_threads_lock ();
3402 wait->num = 0;
3403 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
3404 memset (wait->threads, 0, MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
3405 mono_g_hash_table_foreach (threads, abort_threads, wait);
3407 mono_threads_unlock ();
3409 THREAD_DEBUG (g_message ("%s: wait->num is now %d", __func__, wait->num));
3410 if (wait->num > 0) {
3411 /* Something to wait for */
3412 wait_for_tids (wait, MONO_INFINITE_WAIT, FALSE);
3414 } while (wait->num > 0);
3417 * give the subthreads a chance to really quit (this is mainly needed
3418 * to get correct user and system times from getrusage/wait/time(1)).
3419 * This could be removed if we avoid pthread_detach() and use pthread_join().
3421 mono_thread_info_yield ();
3424 static void
3425 collect_threads_for_suspend (gpointer key, gpointer value, gpointer user_data)
3427 MonoInternalThread *thread = (MonoInternalThread*)value;
3428 struct wait_data *wait = (struct wait_data*)user_data;
3431 * We try to exclude threads early, to avoid running into the MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
3432 * limitation.
3433 * This needs no locking.
3435 if ((thread->state & ThreadState_Suspended) != 0 ||
3436 (thread->state & ThreadState_Stopped) != 0)
3437 return;
3439 if (wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
3440 wait->handles [wait->num] = mono_threads_open_thread_handle (thread->handle);
3441 wait->threads [wait->num] = thread;
3442 wait->num++;
3447 * mono_thread_suspend_all_other_threads:
3449 * Suspend all managed threads except the finalizer thread and this thread. It is
3450 * not possible to resume them later.
3452 void mono_thread_suspend_all_other_threads (void)
3454 struct wait_data wait_data;
3455 struct wait_data *wait = &wait_data;
3456 int i;
3457 MonoNativeThreadId self = mono_native_thread_id_get ();
3458 guint32 eventidx = 0;
3459 gboolean starting, finished;
3461 memset (wait, 0, sizeof (struct wait_data));
3463 * The other threads could be in an arbitrary state at this point, i.e.
3464 * they could be starting up, shutting down etc. This means that there could be
3465 * threads which are not even in the threads hash table yet.
3469 * First we set a barrier which will be checked by all threads before they
3470 * are added to the threads hash table, and they will exit if the flag is set.
3471 * This ensures that no threads could be added to the hash later.
3472 * We will use shutting_down as the barrier for now.
3474 g_assert (shutting_down);
3477 * We make multiple calls to WaitForMultipleObjects since:
3478 * - we can only wait for MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS threads
3479 * - some threads could exit without becoming suspended
3481 finished = FALSE;
3482 while (!finished) {
3484 * Make a copy of the hashtable since we can't do anything with
3485 * threads while threads_mutex is held.
3487 wait->num = 0;
3488 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
3489 memset (wait->threads, 0, MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
3490 mono_threads_lock ();
3491 mono_g_hash_table_foreach (threads, collect_threads_for_suspend, wait);
3492 mono_threads_unlock ();
3494 eventidx = 0;
3495 /* Get the suspended events that we'll be waiting for */
3496 for (i = 0; i < wait->num; ++i) {
3497 MonoInternalThread *thread = wait->threads [i];
3499 if (mono_native_thread_id_equals (thread_get_tid (thread), self)
3500 || mono_gc_is_finalizer_internal_thread (thread)
3501 || (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)
3503 mono_threads_close_thread_handle (wait->handles [i]);
3504 wait->threads [i] = NULL;
3505 continue;
3508 LOCK_THREAD (thread);
3510 if (thread->state & (ThreadState_Suspended | ThreadState_Stopped)) {
3511 UNLOCK_THREAD (thread);
3512 mono_threads_close_thread_handle (wait->handles [i]);
3513 wait->threads [i] = NULL;
3514 continue;
3517 ++eventidx;
3519 /* Convert abort requests into suspend requests */
3520 if ((thread->state & ThreadState_AbortRequested) != 0)
3521 thread->state &= ~ThreadState_AbortRequested;
3523 thread->state |= ThreadState_SuspendRequested;
3524 mono_os_event_reset (thread->suspended);
3526 /* Signal the thread to suspend + calls UNLOCK_THREAD (thread) */
3527 async_suspend_internal (thread, TRUE);
3529 mono_threads_close_thread_handle (wait->handles [i]);
3530 wait->threads [i] = NULL;
3532 if (eventidx <= 0) {
3534 * If there are threads which are starting up, we wait until they
3535 * are suspended when they try to register in the threads hash.
3536 * This is guaranteed to finish, since the threads which can create new
3537 * threads get suspended after a while.
3538 * FIXME: The finalizer thread can still create new threads.
3540 mono_threads_lock ();
3541 if (threads_starting_up)
3542 starting = mono_g_hash_table_size (threads_starting_up) > 0;
3543 else
3544 starting = FALSE;
3545 mono_threads_unlock ();
3546 if (starting)
3547 mono_thread_info_sleep (100, NULL);
3548 else
3549 finished = TRUE;
3554 typedef struct {
3555 MonoInternalThread *thread;
3556 MonoStackFrameInfo *frames;
3557 int nframes, max_frames;
3558 int nthreads, max_threads;
3559 MonoInternalThread **threads;
3560 } ThreadDumpUserData;
3562 static gboolean thread_dump_requested;
3564 /* This needs to be async safe */
3565 static gboolean
3566 collect_frame (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
3568 ThreadDumpUserData *ud = (ThreadDumpUserData *)data;
3570 if (ud->nframes < ud->max_frames) {
3571 memcpy (&ud->frames [ud->nframes], frame, sizeof (MonoStackFrameInfo));
3572 ud->nframes ++;
3575 return FALSE;
3578 /* This needs to be async safe */
3579 static SuspendThreadResult
3580 get_thread_dump (MonoThreadInfo *info, gpointer ud)
3582 ThreadDumpUserData *user_data = (ThreadDumpUserData *)ud;
3583 MonoInternalThread *thread = user_data->thread;
3585 #if 0
3586 /* This no longer works with remote unwinding */
3587 g_string_append_printf (text, " tid=0x%p this=0x%p ", (gpointer)(gsize)thread->tid, thread);
3588 mono_thread_internal_describe (thread, text);
3589 g_string_append (text, "\n");
3590 #endif
3592 if (thread == mono_thread_internal_current ())
3593 mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (collect_frame, NULL, MONO_UNWIND_SIGNAL_SAFE, ud);
3594 else
3595 mono_get_eh_callbacks ()->mono_walk_stack_with_state (collect_frame, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, ud);
3597 return MonoResumeThread;
3600 typedef struct {
3601 int nthreads, max_threads;
3602 MonoInternalThread **threads;
3603 } CollectThreadsUserData;
3605 static void
3606 collect_thread (gpointer key, gpointer value, gpointer user)
3608 CollectThreadsUserData *ud = (CollectThreadsUserData *)user;
3609 MonoInternalThread *thread = (MonoInternalThread *)value;
3611 if (ud->nthreads < ud->max_threads)
3612 ud->threads [ud->nthreads ++] = thread;
3616 * Collect running threads into the THREADS array.
3617 * THREADS should be an array allocated on the stack.
3619 static int
3620 collect_threads (MonoInternalThread **thread_array, int max_threads)
3622 CollectThreadsUserData ud;
3624 memset (&ud, 0, sizeof (ud));
3625 /* This array contains refs, but its on the stack, so its ok */
3626 ud.threads = thread_array;
3627 ud.max_threads = max_threads;
3629 mono_threads_lock ();
3630 mono_g_hash_table_foreach (threads, collect_thread, &ud);
3631 mono_threads_unlock ();
3633 return ud.nthreads;
3636 static void
3637 dump_thread (MonoInternalThread *thread, ThreadDumpUserData *ud)
3639 GString* text = g_string_new (0);
3640 char *name;
3641 GError *gerror = NULL;
3642 int i;
3644 ud->thread = thread;
3645 ud->nframes = 0;
3647 /* Collect frames for the thread */
3648 if (thread == mono_thread_internal_current ()) {
3649 get_thread_dump (mono_thread_info_current (), ud);
3650 } else {
3651 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, get_thread_dump, ud);
3655 * Do all the non async-safe work outside of get_thread_dump.
3657 if (thread->name) {
3658 name = g_utf16_to_utf8 (thread->name, thread->name_len, NULL, NULL, &gerror);
3659 g_assert (!gerror);
3660 g_string_append_printf (text, "\n\"%s\"", name);
3661 g_free (name);
3663 else if (thread->threadpool_thread) {
3664 g_string_append (text, "\n\"<threadpool thread>\"");
3665 } else {
3666 g_string_append (text, "\n\"<unnamed thread>\"");
3669 for (i = 0; i < ud->nframes; ++i) {
3670 MonoStackFrameInfo *frame = &ud->frames [i];
3671 MonoMethod *method = NULL;
3673 if (frame->type == FRAME_TYPE_MANAGED)
3674 method = mono_jit_info_get_method (frame->ji);
3676 if (method) {
3677 gchar *location = mono_debug_print_stack_frame (method, frame->native_offset, frame->domain);
3678 g_string_append_printf (text, " %s\n", location);
3679 g_free (location);
3680 } else {
3681 g_string_append_printf (text, " at <unknown> <0x%05x>\n", frame->native_offset);
3685 fprintf (stdout, "%s", text->str);
3687 #if PLATFORM_WIN32 && TARGET_WIN32 && _DEBUG
3688 OutputDebugStringA(text->str);
3689 #endif
3691 g_string_free (text, TRUE);
3692 fflush (stdout);
3695 void
3696 mono_threads_perform_thread_dump (void)
3698 ThreadDumpUserData ud;
3699 MonoInternalThread *thread_array [128];
3700 int tindex, nthreads;
3702 if (!thread_dump_requested)
3703 return;
3705 printf ("Full thread dump:\n");
3707 /* Make a copy of the threads hash to avoid doing work inside threads_lock () */
3708 nthreads = collect_threads (thread_array, 128);
3710 memset (&ud, 0, sizeof (ud));
3711 ud.frames = g_new0 (MonoStackFrameInfo, 256);
3712 ud.max_frames = 256;
3714 for (tindex = 0; tindex < nthreads; ++tindex)
3715 dump_thread (thread_array [tindex], &ud);
3717 g_free (ud.frames);
3719 thread_dump_requested = FALSE;
3722 /* Obtain the thread dump of all threads */
3723 static gboolean
3724 mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_frames, MonoError *error)
3727 ThreadDumpUserData ud;
3728 MonoInternalThread *thread_array [128];
3729 MonoDomain *domain = mono_domain_get ();
3730 MonoDebugSourceLocation *location;
3731 int tindex, nthreads;
3733 error_init (error);
3735 *out_threads = NULL;
3736 *out_stack_frames = NULL;
3738 /* Make a copy of the threads hash to avoid doing work inside threads_lock () */
3739 nthreads = collect_threads (thread_array, 128);
3741 memset (&ud, 0, sizeof (ud));
3742 ud.frames = g_new0 (MonoStackFrameInfo, 256);
3743 ud.max_frames = 256;
3745 *out_threads = mono_array_new_checked (domain, mono_defaults.thread_class, nthreads, error);
3746 goto_if_nok (error, leave);
3747 *out_stack_frames = mono_array_new_checked (domain, mono_defaults.array_class, nthreads, error);
3748 goto_if_nok (error, leave);
3750 for (tindex = 0; tindex < nthreads; ++tindex) {
3751 MonoInternalThread *thread = thread_array [tindex];
3752 MonoArray *thread_frames;
3753 int i;
3755 ud.thread = thread;
3756 ud.nframes = 0;
3758 /* Collect frames for the thread */
3759 if (thread == mono_thread_internal_current ()) {
3760 get_thread_dump (mono_thread_info_current (), &ud);
3761 } else {
3762 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, get_thread_dump, &ud);
3765 mono_array_setref_fast (*out_threads, tindex, mono_thread_current_for_thread (thread));
3767 thread_frames = mono_array_new_checked (domain, mono_defaults.stack_frame_class, ud.nframes, error);
3768 goto_if_nok (error, leave);
3769 mono_array_setref_fast (*out_stack_frames, tindex, thread_frames);
3771 for (i = 0; i < ud.nframes; ++i) {
3772 MonoStackFrameInfo *frame = &ud.frames [i];
3773 MonoMethod *method = NULL;
3774 MonoStackFrame *sf = (MonoStackFrame *)mono_object_new_checked (domain, mono_defaults.stack_frame_class, error);
3775 goto_if_nok (error, leave);
3777 sf->native_offset = frame->native_offset;
3779 if (frame->type == FRAME_TYPE_MANAGED)
3780 method = mono_jit_info_get_method (frame->ji);
3782 if (method) {
3783 sf->method_address = (gsize) frame->ji->code_start;
3785 MonoReflectionMethod *rm = mono_method_get_object_checked (domain, method, NULL, error);
3786 goto_if_nok (error, leave);
3787 MONO_OBJECT_SETREF (sf, method, rm);
3789 location = mono_debug_lookup_source_location (method, frame->native_offset, domain);
3790 if (location) {
3791 sf->il_offset = location->il_offset;
3793 if (location && location->source_file) {
3794 MonoString *filename = mono_string_new_checked (domain, location->source_file, error);
3795 goto_if_nok (error, leave);
3796 MONO_OBJECT_SETREF (sf, filename, filename);
3797 sf->line = location->row;
3798 sf->column = location->column;
3800 mono_debug_free_source_location (location);
3801 } else {
3802 sf->il_offset = -1;
3805 mono_array_setref (thread_frames, i, sf);
3809 leave:
3810 g_free (ud.frames);
3811 return is_ok (error);
3815 * mono_threads_request_thread_dump:
3817 * Ask all threads except the current to print their stacktrace to stdout.
3819 void
3820 mono_threads_request_thread_dump (void)
3822 /*The new thread dump code runs out of the finalizer thread. */
3823 thread_dump_requested = TRUE;
3824 mono_gc_finalize_notify ();
3827 struct ref_stack {
3828 gpointer *refs;
3829 gint allocated; /* +1 so that refs [allocated] == NULL */
3830 gint bottom;
3833 typedef struct ref_stack RefStack;
3835 static RefStack *
3836 ref_stack_new (gint initial_size)
3838 RefStack *rs;
3840 initial_size = MAX (initial_size, 16) + 1;
3841 rs = g_new0 (RefStack, 1);
3842 rs->refs = g_new0 (gpointer, initial_size);
3843 rs->allocated = initial_size;
3844 return rs;
3847 static void
3848 ref_stack_destroy (gpointer ptr)
3850 RefStack *rs = (RefStack *)ptr;
3852 if (rs != NULL) {
3853 g_free (rs->refs);
3854 g_free (rs);
3858 static void
3859 ref_stack_push (RefStack *rs, gpointer ptr)
3861 g_assert (rs != NULL);
3863 if (rs->bottom >= rs->allocated) {
3864 rs->refs = (void **)g_realloc (rs->refs, rs->allocated * 2 * sizeof (gpointer) + 1);
3865 rs->allocated <<= 1;
3866 rs->refs [rs->allocated] = NULL;
3868 rs->refs [rs->bottom++] = ptr;
3871 static void
3872 ref_stack_pop (RefStack *rs)
3874 if (rs == NULL || rs->bottom == 0)
3875 return;
3877 rs->bottom--;
3878 rs->refs [rs->bottom] = NULL;
3881 static gboolean
3882 ref_stack_find (RefStack *rs, gpointer ptr)
3884 gpointer *refs;
3886 if (rs == NULL)
3887 return FALSE;
3889 for (refs = rs->refs; refs && *refs; refs++) {
3890 if (*refs == ptr)
3891 return TRUE;
3893 return FALSE;
3897 * mono_thread_push_appdomain_ref:
3899 * Register that the current thread may have references to objects in domain
3900 * @domain on its stack. Each call to this function should be paired with a
3901 * call to pop_appdomain_ref.
3903 void
3904 mono_thread_push_appdomain_ref (MonoDomain *domain)
3906 MonoInternalThread *thread = mono_thread_internal_current ();
3908 if (thread) {
3909 /* printf ("PUSH REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, domain->friendly_name); */
3910 SPIN_LOCK (thread->lock_thread_id);
3911 if (thread->appdomain_refs == NULL)
3912 thread->appdomain_refs = ref_stack_new (16);
3913 ref_stack_push ((RefStack *)thread->appdomain_refs, domain);
3914 SPIN_UNLOCK (thread->lock_thread_id);
3918 void
3919 mono_thread_pop_appdomain_ref (void)
3921 MonoInternalThread *thread = mono_thread_internal_current ();
3923 if (thread) {
3924 /* printf ("POP REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, ((MonoDomain*)(thread->appdomain_refs->data))->friendly_name); */
3925 SPIN_LOCK (thread->lock_thread_id);
3926 ref_stack_pop ((RefStack *)thread->appdomain_refs);
3927 SPIN_UNLOCK (thread->lock_thread_id);
3931 gboolean
3932 mono_thread_internal_has_appdomain_ref (MonoInternalThread *thread, MonoDomain *domain)
3934 gboolean res;
3935 SPIN_LOCK (thread->lock_thread_id);
3936 res = ref_stack_find ((RefStack *)thread->appdomain_refs, domain);
3937 SPIN_UNLOCK (thread->lock_thread_id);
3938 return res;
3941 gboolean
3942 mono_thread_has_appdomain_ref (MonoThread *thread, MonoDomain *domain)
3944 return mono_thread_internal_has_appdomain_ref (thread->internal_thread, domain);
3947 typedef struct abort_appdomain_data {
3948 struct wait_data wait;
3949 MonoDomain *domain;
3950 } abort_appdomain_data;
3952 static void
3953 collect_appdomain_thread (gpointer key, gpointer value, gpointer user_data)
3955 MonoInternalThread *thread = (MonoInternalThread*)value;
3956 abort_appdomain_data *data = (abort_appdomain_data*)user_data;
3957 MonoDomain *domain = data->domain;
3959 if (mono_thread_internal_has_appdomain_ref (thread, domain)) {
3960 /* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread->tid, domain->friendly_name); */
3962 if(data->wait.num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
3963 data->wait.handles [data->wait.num] = mono_threads_open_thread_handle (thread->handle);
3964 data->wait.threads [data->wait.num] = thread;
3965 data->wait.num++;
3966 } else {
3967 /* Just ignore the rest, we can't do anything with
3968 * them yet
3975 * mono_threads_abort_appdomain_threads:
3977 * Abort threads which has references to the given appdomain.
3979 gboolean
3980 mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
3982 abort_appdomain_data user_data;
3983 gint64 start_time;
3984 int orig_timeout = timeout;
3985 int i;
3987 THREAD_DEBUG (g_message ("%s: starting abort", __func__));
3989 start_time = mono_msec_ticks ();
3990 do {
3991 mono_threads_lock ();
3993 user_data.domain = domain;
3994 user_data.wait.num = 0;
3995 /* This shouldn't take any locks */
3996 mono_g_hash_table_foreach (threads, collect_appdomain_thread, &user_data);
3997 mono_threads_unlock ();
3999 if (user_data.wait.num > 0) {
4000 /* Abort the threads outside the threads lock */
4001 for (i = 0; i < user_data.wait.num; ++i)
4002 mono_thread_internal_abort (user_data.wait.threads [i], TRUE);
4005 * We should wait for the threads either to abort, or to leave the
4006 * domain. We can't do the latter, so we wait with a timeout.
4008 wait_for_tids (&user_data.wait, 100, FALSE);
4011 /* Update remaining time */
4012 timeout -= mono_msec_ticks () - start_time;
4013 start_time = mono_msec_ticks ();
4015 if (orig_timeout != -1 && timeout < 0)
4016 return FALSE;
4018 while (user_data.wait.num > 0);
4020 THREAD_DEBUG (g_message ("%s: abort done", __func__));
4022 return TRUE;
4025 void
4026 mono_thread_self_abort (void)
4028 ERROR_DECL (error);
4029 self_abort_internal (error);
4030 mono_error_set_pending_exception (error);
4034 * mono_thread_get_undeniable_exception:
4036 * Return an exception which needs to be raised when leaving a catch clause.
4037 * This is used for undeniable exception propagation.
4039 MonoException*
4040 mono_thread_get_undeniable_exception (void)
4042 MonoInternalThread *thread = mono_thread_internal_current ();
4044 if (!(thread && thread->abort_exc && !is_running_protected_wrapper ()))
4045 return NULL;
4047 // We don't want to have our exception effect calls made by
4048 // the catching block
4050 if (!mono_get_eh_callbacks ()->mono_above_abort_threshold ())
4051 return NULL;
4054 * FIXME: Clear the abort exception and return an AppDomainUnloaded
4055 * exception if the thread no longer references a dying appdomain.
4057 thread->abort_exc->trace_ips = NULL;
4058 thread->abort_exc->stack_trace = NULL;
4059 return thread->abort_exc;
4062 #if MONO_SMALL_CONFIG
4063 #define NUM_STATIC_DATA_IDX 4
4064 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
4065 64, 256, 1024, 4096
4067 #else
4068 #define NUM_STATIC_DATA_IDX 8
4069 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
4070 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
4072 #endif
4074 static MonoBitSet *thread_reference_bitmaps [NUM_STATIC_DATA_IDX];
4075 static MonoBitSet *context_reference_bitmaps [NUM_STATIC_DATA_IDX];
4077 static void
4078 mark_slots (void *addr, MonoBitSet **bitmaps, MonoGCMarkFunc mark_func, void *gc_data)
4080 gpointer *static_data = (gpointer *)addr;
4082 for (int i = 0; i < NUM_STATIC_DATA_IDX; ++i) {
4083 void **ptr = (void **)static_data [i];
4085 if (!ptr)
4086 continue;
4088 MONO_BITSET_FOREACH (bitmaps [i], idx, {
4089 void **p = ptr + idx;
4091 if (*p)
4092 mark_func ((MonoObject**)p, gc_data);
4097 static void
4098 mark_tls_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
4100 mark_slots (addr, thread_reference_bitmaps, mark_func, gc_data);
4103 static void
4104 mark_ctx_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
4106 mark_slots (addr, context_reference_bitmaps, mark_func, gc_data);
4110 * mono_alloc_static_data
4112 * Allocate memory blocks for storing threads or context static data
4114 static void
4115 mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, void *alloc_key, gboolean threadlocal)
4117 guint idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4118 int i;
4120 gpointer* static_data = *static_data_ptr;
4121 if (!static_data) {
4122 static MonoGCDescriptor tls_desc = MONO_GC_DESCRIPTOR_NULL;
4123 static MonoGCDescriptor ctx_desc = MONO_GC_DESCRIPTOR_NULL;
4125 if (mono_gc_user_markers_supported ()) {
4126 if (tls_desc == MONO_GC_DESCRIPTOR_NULL)
4127 tls_desc = mono_gc_make_root_descr_user (mark_tls_slots);
4129 if (ctx_desc == MONO_GC_DESCRIPTOR_NULL)
4130 ctx_desc = mono_gc_make_root_descr_user (mark_ctx_slots);
4133 static_data = (void **)mono_gc_alloc_fixed (static_data_size [0], threadlocal ? tls_desc : ctx_desc,
4134 threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC,
4135 alloc_key,
4136 threadlocal ? "ThreadStatic Fields" : "ContextStatic Fields");
4137 *static_data_ptr = static_data;
4138 static_data [0] = static_data;
4141 for (i = 1; i <= idx; ++i) {
4142 if (static_data [i])
4143 continue;
4145 if (mono_gc_user_markers_supported ())
4146 static_data [i] = g_malloc0 (static_data_size [i]);
4147 else
4148 static_data [i] = mono_gc_alloc_fixed (static_data_size [i], MONO_GC_DESCRIPTOR_NULL,
4149 threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC,
4150 alloc_key,
4151 threadlocal ? "ThreadStatic Fields" : "ContextStatic Fields");
4155 static void
4156 mono_free_static_data (gpointer* static_data)
4158 int i;
4159 for (i = 1; i < NUM_STATIC_DATA_IDX; ++i) {
4160 gpointer p = static_data [i];
4161 if (!p)
4162 continue;
4164 * At this point, the static data pointer array is still registered with the
4165 * GC, so must ensure that mark_tls_slots() will not encounter any invalid
4166 * data. Freeing the individual arrays without first nulling their slots
4167 * would make it possible for mark_tls/ctx_slots() to encounter a pointer to
4168 * such an already freed array. See bug #13813.
4170 static_data [i] = NULL;
4171 mono_memory_write_barrier ();
4172 if (mono_gc_user_markers_supported ())
4173 g_free (p);
4174 else
4175 mono_gc_free_fixed (p);
4177 mono_gc_free_fixed (static_data);
4181 * mono_init_static_data_info
4183 * Initializes static data counters
4185 static void mono_init_static_data_info (StaticDataInfo *static_data)
4187 static_data->idx = 0;
4188 static_data->offset = 0;
4189 static_data->freelist = NULL;
4193 * mono_alloc_static_data_slot
4195 * Generates an offset for static data. static_data contains the counters
4196 * used to generate it.
4198 static guint32
4199 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align)
4201 if (!static_data->idx && !static_data->offset) {
4203 * we use the first chunk of the first allocation also as
4204 * an array for the rest of the data
4206 static_data->offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX;
4208 static_data->offset += align - 1;
4209 static_data->offset &= ~(align - 1);
4210 if (static_data->offset + size >= static_data_size [static_data->idx]) {
4211 static_data->idx ++;
4212 g_assert (size <= static_data_size [static_data->idx]);
4213 g_assert (static_data->idx < NUM_STATIC_DATA_IDX);
4214 static_data->offset = 0;
4216 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (static_data->idx, static_data->offset, 0);
4217 static_data->offset += size;
4218 return offset;
4222 * LOCKING: requires that threads_mutex is held
4224 static void
4225 context_adjust_static_data (MonoAppContext *ctx)
4227 if (context_static_info.offset || context_static_info.idx > 0) {
4228 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (context_static_info.idx, context_static_info.offset, 0);
4229 mono_alloc_static_data (&ctx->static_data, offset, ctx, FALSE);
4230 ctx->data->static_data = ctx->static_data;
4235 * LOCKING: requires that threads_mutex is held
4237 static void
4238 alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
4240 MonoInternalThread *thread = (MonoInternalThread *)value;
4241 guint32 offset = GPOINTER_TO_UINT (user);
4243 mono_alloc_static_data (&(thread->static_data), offset, (void *) MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid), TRUE);
4247 * LOCKING: requires that threads_mutex is held
4249 static void
4250 alloc_context_static_data_helper (gpointer key, gpointer value, gpointer user)
4252 MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (GPOINTER_TO_INT (key));
4254 if (!ctx)
4255 return;
4257 guint32 offset = GPOINTER_TO_UINT (user);
4258 mono_alloc_static_data (&ctx->static_data, offset, ctx, FALSE);
4259 ctx->data->static_data = ctx->static_data;
4262 static StaticDataFreeList*
4263 search_slot_in_freelist (StaticDataInfo *static_data, guint32 size, guint32 align)
4265 StaticDataFreeList* prev = NULL;
4266 StaticDataFreeList* tmp = static_data->freelist;
4267 while (tmp) {
4268 if (tmp->size == size) {
4269 if (prev)
4270 prev->next = tmp->next;
4271 else
4272 static_data->freelist = tmp->next;
4273 return tmp;
4275 prev = tmp;
4276 tmp = tmp->next;
4278 return NULL;
4281 #if SIZEOF_VOID_P == 4
4282 #define ONE_P 1
4283 #else
4284 #define ONE_P 1ll
4285 #endif
4287 static void
4288 update_reference_bitmap (MonoBitSet **sets, guint32 offset, uintptr_t *bitmap, int numbits)
4290 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4291 if (!sets [idx])
4292 sets [idx] = mono_bitset_new (static_data_size [idx] / sizeof (uintptr_t), 0);
4293 MonoBitSet *rb = sets [idx];
4294 offset = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
4295 offset /= sizeof (uintptr_t);
4296 /* offset is now the bitmap offset */
4297 for (int i = 0; i < numbits; ++i) {
4298 if (bitmap [i / sizeof (uintptr_t)] & (ONE_P << (i & (sizeof (uintptr_t) * 8 -1))))
4299 mono_bitset_set_fast (rb, offset + i);
4303 static void
4304 clear_reference_bitmap (MonoBitSet **sets, guint32 offset, guint32 size)
4306 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4307 MonoBitSet *rb = sets [idx];
4308 offset = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
4309 offset /= sizeof (uintptr_t);
4310 /* offset is now the bitmap offset */
4311 for (int i = 0; i < size / sizeof (uintptr_t); i++)
4312 mono_bitset_clear_fast (rb, offset + i);
4315 guint32
4316 mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align, uintptr_t *bitmap, int numbits)
4318 g_assert (static_type == SPECIAL_STATIC_THREAD || static_type == SPECIAL_STATIC_CONTEXT);
4320 StaticDataInfo *info;
4321 MonoBitSet **sets;
4323 if (static_type == SPECIAL_STATIC_THREAD) {
4324 info = &thread_static_info;
4325 sets = thread_reference_bitmaps;
4326 } else {
4327 info = &context_static_info;
4328 sets = context_reference_bitmaps;
4331 mono_threads_lock ();
4333 StaticDataFreeList *item = search_slot_in_freelist (info, size, align);
4334 guint32 offset;
4336 if (item) {
4337 offset = item->offset;
4338 g_free (item);
4339 } else {
4340 offset = mono_alloc_static_data_slot (info, size, align);
4343 update_reference_bitmap (sets, offset, bitmap, numbits);
4345 if (static_type == SPECIAL_STATIC_THREAD) {
4346 /* This can be called during startup */
4347 if (threads != NULL)
4348 mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
4349 } else {
4350 if (contexts != NULL)
4351 g_hash_table_foreach (contexts, alloc_context_static_data_helper, GUINT_TO_POINTER (offset));
4353 ACCESS_SPECIAL_STATIC_OFFSET (offset, type) = SPECIAL_STATIC_OFFSET_TYPE_CONTEXT;
4356 mono_threads_unlock ();
4358 return offset;
4361 gpointer
4362 mono_get_special_static_data_for_thread (MonoInternalThread *thread, guint32 offset)
4364 guint32 static_type = ACCESS_SPECIAL_STATIC_OFFSET (offset, type);
4366 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4367 return get_thread_static_data (thread, offset);
4368 } else {
4369 return get_context_static_data (thread->current_appcontext, offset);
4373 gpointer
4374 mono_get_special_static_data (guint32 offset)
4376 return mono_get_special_static_data_for_thread (mono_thread_internal_current (), offset);
4379 typedef struct {
4380 guint32 offset;
4381 guint32 size;
4382 } OffsetSize;
4385 * LOCKING: requires that threads_mutex is held
4387 static void
4388 free_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
4390 MonoInternalThread *thread = (MonoInternalThread *)value;
4391 OffsetSize *data = (OffsetSize *)user;
4392 int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index);
4393 int off = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, offset);
4394 char *ptr;
4396 if (!thread->static_data || !thread->static_data [idx])
4397 return;
4398 ptr = ((char*) thread->static_data [idx]) + off;
4399 mono_gc_bzero_atomic (ptr, data->size);
4403 * LOCKING: requires that threads_mutex is held
4405 static void
4406 free_context_static_data_helper (gpointer key, gpointer value, gpointer user)
4408 MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (GPOINTER_TO_INT (key));
4410 if (!ctx)
4411 return;
4413 OffsetSize *data = (OffsetSize *)user;
4414 int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index);
4415 int off = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, offset);
4416 char *ptr;
4418 if (!ctx->static_data || !ctx->static_data [idx])
4419 return;
4421 ptr = ((char*) ctx->static_data [idx]) + off;
4422 mono_gc_bzero_atomic (ptr, data->size);
4425 static void
4426 do_free_special_slot (guint32 offset, guint32 size)
4428 guint32 static_type = ACCESS_SPECIAL_STATIC_OFFSET (offset, type);
4429 MonoBitSet **sets;
4430 StaticDataInfo *info;
4432 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4433 info = &thread_static_info;
4434 sets = thread_reference_bitmaps;
4435 } else {
4436 info = &context_static_info;
4437 sets = context_reference_bitmaps;
4440 guint32 data_offset = offset;
4441 ACCESS_SPECIAL_STATIC_OFFSET (data_offset, type) = 0;
4442 OffsetSize data = { data_offset, size };
4444 clear_reference_bitmap (sets, data.offset, data.size);
4446 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4447 if (threads != NULL)
4448 mono_g_hash_table_foreach (threads, free_thread_static_data_helper, &data);
4449 } else {
4450 if (contexts != NULL)
4451 g_hash_table_foreach (contexts, free_context_static_data_helper, &data);
4454 if (!mono_runtime_is_shutting_down ()) {
4455 StaticDataFreeList *item = g_new0 (StaticDataFreeList, 1);
4457 item->offset = offset;
4458 item->size = size;
4460 item->next = info->freelist;
4461 info->freelist = item;
4465 static void
4466 do_free_special (gpointer key, gpointer value, gpointer data)
4468 MonoClassField *field = (MonoClassField *)key;
4469 guint32 offset = GPOINTER_TO_UINT (value);
4470 gint32 align;
4471 guint32 size;
4472 size = mono_type_size (field->type, &align);
4473 do_free_special_slot (offset, size);
4476 void
4477 mono_alloc_special_static_data_free (GHashTable *special_static_fields)
4479 mono_threads_lock ();
4481 g_hash_table_foreach (special_static_fields, do_free_special, NULL);
4483 mono_threads_unlock ();
4486 #ifdef HOST_WIN32
4487 static void CALLBACK dummy_apc (ULONG_PTR param)
4490 #endif
4493 * mono_thread_execute_interruption
4495 * Performs the operation that the requested thread state requires (abort,
4496 * suspend or stop)
4498 static MonoException*
4499 mono_thread_execute_interruption (void)
4501 MonoInternalThread *thread = mono_thread_internal_current ();
4502 MonoThread *sys_thread = mono_thread_current ();
4504 LOCK_THREAD (thread);
4506 /* MonoThread::interruption_requested can only be changed with atomics */
4507 if (!mono_thread_clear_interruption_requested (thread)) {
4508 UNLOCK_THREAD (thread);
4509 return NULL;
4512 /* this will consume pending APC calls */
4513 #ifdef HOST_WIN32
4514 MONO_ENTER_GC_SAFE;
4515 mono_win32_wait_for_single_object_ex (GetCurrentThread (), 0, TRUE);
4516 MONO_EXIT_GC_SAFE;
4517 #endif
4519 /* Clear the interrupted flag of the thread so it can wait again */
4520 mono_thread_info_clear_self_interrupt ();
4522 /* If there's a pending exception and an AbortRequested, the pending exception takes precedence */
4523 if (sys_thread->pending_exception) {
4524 MonoException *exc;
4526 exc = sys_thread->pending_exception;
4527 sys_thread->pending_exception = NULL;
4529 UNLOCK_THREAD (thread);
4530 return exc;
4531 } else if (thread->state & (ThreadState_AbortRequested)) {
4532 UNLOCK_THREAD (thread);
4533 g_assert (sys_thread->pending_exception == NULL);
4534 if (thread->abort_exc == NULL) {
4536 * This might be racy, but it has to be called outside the lock
4537 * since it calls managed code.
4539 MONO_OBJECT_SETREF (thread, abort_exc, mono_get_exception_thread_abort ());
4541 return thread->abort_exc;
4542 } else if (thread->state & (ThreadState_SuspendRequested)) {
4543 /* calls UNLOCK_THREAD (thread) */
4544 self_suspend_internal ();
4545 return NULL;
4546 } else if (thread->thread_interrupt_requested) {
4548 thread->thread_interrupt_requested = FALSE;
4549 UNLOCK_THREAD (thread);
4551 return(mono_get_exception_thread_interrupted ());
4554 UNLOCK_THREAD (thread);
4556 return NULL;
4560 * mono_thread_request_interruption
4562 * A signal handler can call this method to request the interruption of a
4563 * thread. The result of the interruption will depend on the current state of
4564 * the thread. If the result is an exception that needs to be throw, it is
4565 * provided as return value.
4567 MonoException*
4568 mono_thread_request_interruption (gboolean running_managed)
4570 MonoInternalThread *thread = mono_thread_internal_current ();
4572 /* The thread may already be stopping */
4573 if (thread == NULL)
4574 return NULL;
4576 if (!mono_thread_set_interruption_requested (thread))
4577 return NULL;
4579 if (!running_managed || is_running_protected_wrapper ()) {
4580 /* Can't stop while in unmanaged code. Increase the global interruption
4581 request count. When exiting the unmanaged method the count will be
4582 checked and the thread will be interrupted. */
4584 /* this will awake the thread if it is in WaitForSingleObject
4585 or similar */
4586 #ifdef HOST_WIN32
4587 mono_win32_interrupt_wait (thread->thread_info, thread->native_handle, (DWORD)thread->tid);
4588 #else
4589 mono_thread_info_self_interrupt ();
4590 #endif
4591 return NULL;
4593 else {
4594 return mono_thread_execute_interruption ();
4598 /*This function should be called by a thread after it has exited all of
4599 * its handle blocks at interruption time.*/
4600 MonoException*
4601 mono_thread_resume_interruption (gboolean exec)
4603 MonoInternalThread *thread = mono_thread_internal_current ();
4604 gboolean still_aborting;
4606 /* The thread may already be stopping */
4607 if (thread == NULL)
4608 return NULL;
4610 LOCK_THREAD (thread);
4611 still_aborting = (thread->state & (ThreadState_AbortRequested)) != 0;
4612 UNLOCK_THREAD (thread);
4614 /*This can happen if the protected block called Thread::ResetAbort*/
4615 if (!still_aborting)
4616 return NULL;
4618 if (!mono_thread_set_interruption_requested (thread))
4619 return NULL;
4621 mono_thread_info_self_interrupt ();
4623 if (exec)
4624 return mono_thread_execute_interruption ();
4625 else
4626 return NULL;
4629 gboolean mono_thread_interruption_requested ()
4631 if (thread_interruption_requested) {
4632 MonoInternalThread *thread = mono_thread_internal_current ();
4633 /* The thread may already be stopping */
4634 if (thread != NULL)
4635 return mono_thread_get_interruption_requested (thread);
4637 return FALSE;
4640 static MonoException*
4641 mono_thread_interruption_checkpoint_request (gboolean bypass_abort_protection)
4643 MonoInternalThread *thread = mono_thread_internal_current ();
4645 /* The thread may already be stopping */
4646 if (!thread)
4647 return NULL;
4648 if (!mono_thread_get_interruption_requested (thread))
4649 return NULL;
4650 if (!bypass_abort_protection && !mono_thread_current ()->pending_exception && is_running_protected_wrapper ())
4651 return NULL;
4653 return mono_thread_execute_interruption ();
4657 * Performs the interruption of the current thread, if one has been requested,
4658 * and the thread is not running a protected wrapper.
4659 * Return the exception which needs to be thrown, if any.
4661 MonoException*
4662 mono_thread_interruption_checkpoint (void)
4664 return mono_thread_interruption_checkpoint_request (FALSE);
4668 * Performs the interruption of the current thread, if one has been requested.
4669 * Return the exception which needs to be thrown, if any.
4671 MonoException*
4672 mono_thread_force_interruption_checkpoint_noraise (void)
4674 return mono_thread_interruption_checkpoint_request (TRUE);
4678 * mono_set_pending_exception:
4680 * Set the pending exception of the current thread to EXC.
4681 * The exception will be thrown when execution returns to managed code.
4683 void
4684 mono_set_pending_exception (MonoException *exc)
4686 MonoThread *thread = mono_thread_current ();
4688 /* The thread may already be stopping */
4689 if (thread == NULL)
4690 return;
4692 MONO_OBJECT_SETREF (thread, pending_exception, exc);
4694 mono_thread_request_interruption (FALSE);
4698 * mono_thread_interruption_request_flag:
4700 * Returns the address of a flag that will be non-zero if an interruption has
4701 * been requested for a thread. The thread to interrupt may not be the current
4702 * thread, so an additional call to mono_thread_interruption_requested() or
4703 * mono_thread_interruption_checkpoint() is allways needed if the flag is not
4704 * zero.
4706 gint32* mono_thread_interruption_request_flag ()
4708 return &thread_interruption_requested;
4711 void
4712 mono_thread_init_apartment_state (void)
4714 #ifdef HOST_WIN32
4715 MonoInternalThread* thread = mono_thread_internal_current ();
4717 /* Positive return value indicates success, either
4718 * S_OK if this is first CoInitialize call, or
4719 * S_FALSE if CoInitialize already called, but with same
4720 * threading model. A negative value indicates failure,
4721 * probably due to trying to change the threading model.
4723 if (CoInitializeEx(NULL, (thread->apartment_state == ThreadApartmentState_STA)
4724 ? COINIT_APARTMENTTHREADED
4725 : COINIT_MULTITHREADED) < 0) {
4726 thread->apartment_state = ThreadApartmentState_Unknown;
4728 #endif
4731 void
4732 mono_thread_cleanup_apartment_state (void)
4734 #ifdef HOST_WIN32
4735 MonoInternalThread* thread = mono_thread_internal_current ();
4737 if (thread && thread->apartment_state != ThreadApartmentState_Unknown) {
4738 CoUninitialize ();
4740 #endif
4743 static void
4744 mono_thread_notify_change_state (MonoThreadState old_state, MonoThreadState new_state)
4746 MonoThreadState diff = old_state ^ new_state;
4747 if (diff & ThreadState_Background) {
4748 /* If the thread changes the background mode, the main thread has to
4749 * be notified, since it has to rebuild the list of threads to
4750 * wait for.
4752 mono_os_event_set (&background_change_event);
4756 void
4757 mono_thread_clear_and_set_state (MonoInternalThread *thread, MonoThreadState clear, MonoThreadState set)
4759 LOCK_THREAD (thread);
4761 MonoThreadState const old_state = thread->state;
4762 MonoThreadState const new_state = (old_state & ~clear) | set;
4763 thread->state = new_state;
4765 UNLOCK_THREAD (thread);
4767 mono_thread_notify_change_state (old_state, new_state);
4770 void
4771 mono_thread_set_state (MonoInternalThread *thread, MonoThreadState state)
4773 mono_thread_clear_and_set_state (thread, 0, state);
4777 * mono_thread_test_and_set_state:
4778 * Test if current state of \p thread include \p test. If it does not, OR \p set into the state.
4779 * \returns TRUE if \p set was OR'd in.
4781 gboolean
4782 mono_thread_test_and_set_state (MonoInternalThread *thread, MonoThreadState test, MonoThreadState set)
4784 LOCK_THREAD (thread);
4786 MonoThreadState const old_state = thread->state;
4788 if ((old_state & test) != 0) {
4789 UNLOCK_THREAD (thread);
4790 return FALSE;
4793 MonoThreadState const new_state = old_state | set;
4794 thread->state = new_state;
4796 UNLOCK_THREAD (thread);
4798 mono_thread_notify_change_state (old_state, new_state);
4800 return TRUE;
4803 void
4804 mono_thread_clr_state (MonoInternalThread *thread, MonoThreadState state)
4806 mono_thread_clear_and_set_state (thread, state, 0);
4809 gboolean
4810 mono_thread_test_state (MonoInternalThread *thread, MonoThreadState test)
4812 LOCK_THREAD (thread);
4814 gboolean const ret = ((thread->state & test) != 0);
4816 UNLOCK_THREAD (thread);
4818 return ret;
4821 static void
4822 self_interrupt_thread (void *_unused)
4824 MonoException *exc;
4825 MonoThreadInfo *info;
4827 exc = mono_thread_execute_interruption ();
4828 if (!exc) {
4829 if (mono_threads_is_coop_enabled ()) {
4830 /* We can return from an async call in coop, as
4831 * it's simply called when exiting the safepoint */
4832 return;
4835 g_error ("%s: we can't resume from an async call", __func__);
4838 info = mono_thread_info_current ();
4840 mono_raise_exception_with_context (exc, &info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx); /* FIXME using thread_saved_state [ASYNC_SUSPEND_STATE_INDEX] can race with another suspend coming in. */
4843 static gboolean
4844 mono_jit_info_match (MonoJitInfo *ji, gpointer ip)
4846 if (!ji)
4847 return FALSE;
4848 return ji->code_start <= ip && (char*)ip < (char*)ji->code_start + ji->code_size;
4851 static gboolean
4852 last_managed (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
4854 MonoJitInfo **dest = (MonoJitInfo **)data;
4855 *dest = frame->ji;
4856 return TRUE;
4859 static MonoJitInfo*
4860 mono_thread_info_get_last_managed (MonoThreadInfo *info)
4862 MonoJitInfo *ji = NULL;
4863 if (!info)
4864 return NULL;
4867 * The suspended thread might be holding runtime locks. Make sure we don't try taking
4868 * any runtime locks while unwinding. In coop case we shouldn't safepoint in regions
4869 * where we hold runtime locks.
4871 if (!mono_threads_is_coop_enabled ())
4872 mono_thread_info_set_is_async_context (TRUE);
4873 mono_get_eh_callbacks ()->mono_walk_stack_with_state (last_managed, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, &ji);
4874 if (!mono_threads_is_coop_enabled ())
4875 mono_thread_info_set_is_async_context (FALSE);
4876 return ji;
4879 typedef struct {
4880 MonoInternalThread *thread;
4881 gboolean install_async_abort;
4882 MonoThreadInfoInterruptToken *interrupt_token;
4883 } AbortThreadData;
4885 static SuspendThreadResult
4886 async_abort_critical (MonoThreadInfo *info, gpointer ud)
4888 AbortThreadData *data = (AbortThreadData *)ud;
4889 MonoInternalThread *thread = data->thread;
4890 MonoJitInfo *ji = NULL;
4891 gboolean protected_wrapper;
4892 gboolean running_managed;
4894 if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (mono_thread_info_get_suspend_state (info)))
4895 return MonoResumeThread;
4897 /*someone is already interrupting it*/
4898 if (!mono_thread_set_interruption_requested (thread))
4899 return MonoResumeThread;
4901 ji = mono_thread_info_get_last_managed (info);
4902 protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
4903 running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
4905 if (!protected_wrapper && running_managed) {
4906 /*We are in managed code*/
4907 /*Set the thread to call */
4908 if (data->install_async_abort)
4909 mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
4910 return MonoResumeThread;
4911 } else {
4913 * This will cause waits to be broken.
4914 * It will also prevent the thread from entering a wait, so if the thread returns
4915 * from the wait before it receives the abort signal, it will just spin in the wait
4916 * functions in the io-layer until the signal handler calls QueueUserAPC which will
4917 * make it return.
4919 data->interrupt_token = mono_thread_info_prepare_interrupt (info);
4921 return MonoResumeThread;
4925 static void
4926 async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort)
4928 AbortThreadData data;
4930 g_assert (thread != mono_thread_internal_current ());
4932 data.thread = thread;
4933 data.install_async_abort = install_async_abort;
4934 data.interrupt_token = NULL;
4936 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), TRUE, async_abort_critical, &data);
4937 if (data.interrupt_token)
4938 mono_thread_info_finish_interrupt (data.interrupt_token);
4939 /*FIXME we need to wait for interruption to complete -- figure out how much into interruption we should wait for here*/
4942 static void
4943 self_abort_internal (MonoError *error)
4945 MonoException *exc;
4947 error_init (error);
4949 /* FIXME this is insanely broken, it doesn't cause interruption to happen synchronously
4950 * since passing FALSE to mono_thread_request_interruption makes sure it returns NULL */
4953 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.
4955 exc = mono_thread_request_interruption (TRUE);
4956 if (exc)
4957 mono_error_set_exception_instance (error, exc);
4958 else
4959 mono_thread_info_self_interrupt ();
4962 typedef struct {
4963 MonoInternalThread *thread;
4964 gboolean interrupt;
4965 MonoThreadInfoInterruptToken *interrupt_token;
4966 } SuspendThreadData;
4968 static SuspendThreadResult
4969 async_suspend_critical (MonoThreadInfo *info, gpointer ud)
4971 SuspendThreadData *data = (SuspendThreadData *)ud;
4972 MonoInternalThread *thread = data->thread;
4973 MonoJitInfo *ji = NULL;
4974 gboolean protected_wrapper;
4975 gboolean running_managed;
4977 ji = mono_thread_info_get_last_managed (info);
4978 protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
4979 running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
4981 if (running_managed && !protected_wrapper) {
4982 if (mono_threads_is_coop_enabled ()) {
4983 mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
4984 return MonoResumeThread;
4985 } else {
4986 thread->state &= ~ThreadState_SuspendRequested;
4987 thread->state |= ThreadState_Suspended;
4988 return KeepSuspended;
4990 } else {
4991 mono_thread_set_interruption_requested (thread);
4992 if (data->interrupt)
4993 data->interrupt_token = mono_thread_info_prepare_interrupt ((MonoThreadInfo *)thread->thread_info);
4995 return MonoResumeThread;
4999 /* LOCKING: called with @thread synch_cs held, and releases it */
5000 static void
5001 async_suspend_internal (MonoInternalThread *thread, gboolean interrupt)
5003 SuspendThreadData data;
5005 g_assert (thread != mono_thread_internal_current ());
5007 // MOSTLY_ASYNC_SAFE_PRINTF ("ASYNC SUSPEND thread %p\n", thread_get_tid (thread));
5009 thread->self_suspended = FALSE;
5011 data.thread = thread;
5012 data.interrupt = interrupt;
5013 data.interrupt_token = NULL;
5015 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), interrupt, async_suspend_critical, &data);
5016 if (data.interrupt_token)
5017 mono_thread_info_finish_interrupt (data.interrupt_token);
5019 UNLOCK_THREAD (thread);
5022 /* LOCKING: called with @thread synch_cs held, and releases it */
5023 static void
5024 self_suspend_internal (void)
5026 MonoInternalThread *thread;
5027 MonoOSEvent *event;
5028 MonoOSEventWaitRet res;
5030 thread = mono_thread_internal_current ();
5032 // MOSTLY_ASYNC_SAFE_PRINTF ("SELF SUSPEND thread %p\n", thread_get_tid (thread));
5034 thread->self_suspended = TRUE;
5036 thread->state &= ~ThreadState_SuspendRequested;
5037 thread->state |= ThreadState_Suspended;
5039 UNLOCK_THREAD (thread);
5041 event = thread->suspended;
5043 MONO_ENTER_GC_SAFE;
5044 res = mono_os_event_wait_one (event, MONO_INFINITE_WAIT, TRUE);
5045 g_assert (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0 || res == MONO_OS_EVENT_WAIT_RET_ALERTED);
5046 MONO_EXIT_GC_SAFE;
5049 static void
5050 suspend_for_shutdown_async_call (gpointer unused)
5052 for (;;)
5053 mono_thread_info_yield ();
5056 static SuspendThreadResult
5057 suspend_for_shutdown_critical (MonoThreadInfo *info, gpointer unused)
5059 mono_thread_info_setup_async_call (info, suspend_for_shutdown_async_call, NULL);
5060 return MonoResumeThread;
5063 void
5064 mono_thread_internal_suspend_for_shutdown (MonoInternalThread *thread)
5066 g_assert (thread != mono_thread_internal_current ());
5068 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, suspend_for_shutdown_critical, NULL);
5072 * mono_thread_is_foreign:
5073 * \param thread the thread to query
5075 * This function allows one to determine if a thread was created by the mono runtime and has
5076 * a well defined lifecycle or it's a foreign one, created by the native environment.
5078 * \returns TRUE if \p thread was not created by the runtime.
5080 mono_bool
5081 mono_thread_is_foreign (MonoThread *thread)
5083 MonoThreadInfo *info = (MonoThreadInfo *)thread->internal_thread->thread_info;
5084 return info->runtime_thread == FALSE;
5087 #ifndef HOST_WIN32
5088 static void
5089 threads_native_thread_join_lock (gpointer tid, gpointer value)
5091 pthread_t thread = (pthread_t)tid;
5092 if (thread != pthread_self ()) {
5093 MONO_ENTER_GC_SAFE;
5094 /* This shouldn't block */
5095 mono_threads_join_lock ();
5096 mono_native_thread_join (thread);
5097 mono_threads_join_unlock ();
5098 MONO_EXIT_GC_SAFE;
5101 static void
5102 threads_native_thread_join_nolock (gpointer tid, gpointer value)
5104 pthread_t thread = (pthread_t)tid;
5105 MONO_ENTER_GC_SAFE;
5106 mono_native_thread_join (thread);
5107 MONO_EXIT_GC_SAFE;
5110 static void
5111 threads_add_joinable_thread_nolock (gpointer tid)
5113 g_hash_table_insert (joinable_threads, tid, tid);
5115 #else
5116 static void
5117 threads_native_thread_join_lock (gpointer tid, gpointer value)
5119 MonoNativeThreadId thread_id = (MonoNativeThreadId)(guint64)tid;
5120 HANDLE thread_handle = (HANDLE)value;
5121 if (thread_id != GetCurrentThreadId () && thread_handle != NULL && thread_handle != INVALID_HANDLE_VALUE) {
5122 MONO_ENTER_GC_SAFE;
5123 /* This shouldn't block */
5124 mono_threads_join_lock ();
5125 mono_native_thread_join_handle (thread_handle, TRUE);
5126 mono_threads_join_unlock ();
5127 MONO_EXIT_GC_SAFE;
5131 static void
5132 threads_native_thread_join_nolock (gpointer tid, gpointer value)
5134 HANDLE thread_handle = (HANDLE)value;
5135 MONO_ENTER_GC_SAFE;
5136 mono_native_thread_join_handle (thread_handle, TRUE);
5137 MONO_EXIT_GC_SAFE;
5140 static void
5141 threads_add_joinable_thread_nolock (gpointer tid)
5143 g_hash_table_insert (joinable_threads, tid, (gpointer)OpenThread (SYNCHRONIZE, TRUE, (MonoNativeThreadId)(guint64)tid));
5145 #endif
5147 static void
5148 threads_add_pending_joinable_thread (gpointer tid)
5150 joinable_threads_lock ();
5152 if (!pending_joinable_threads)
5153 pending_joinable_threads = g_hash_table_new (NULL, NULL);
5155 gpointer orig_key;
5156 gpointer value;
5158 if (!g_hash_table_lookup_extended (pending_joinable_threads, tid, &orig_key, &value)) {
5159 g_hash_table_insert (pending_joinable_threads, tid, tid);
5160 UnlockedIncrement (&pending_joinable_thread_count);
5163 joinable_threads_unlock ();
5166 static void
5167 threads_add_pending_joinable_runtime_thread (MonoThreadInfo *mono_thread_info)
5169 g_assert (mono_thread_info);
5171 if (mono_thread_info->runtime_thread) {
5172 threads_add_pending_joinable_thread ((gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (mono_thread_info_get_tid (mono_thread_info))));
5176 static void
5177 threads_remove_pending_joinable_thread_nolock (gpointer tid)
5179 gpointer orig_key;
5180 gpointer value;
5182 if (pending_joinable_threads && g_hash_table_lookup_extended (pending_joinable_threads, tid, &orig_key, &value)) {
5183 g_hash_table_remove (pending_joinable_threads, tid);
5184 if (UnlockedDecrement (&pending_joinable_thread_count) == 0)
5185 mono_os_cond_broadcast (&zero_pending_joinable_thread_event);
5189 static gboolean
5190 threads_wait_pending_joinable_threads (uint32_t timeout)
5192 if (UnlockedRead (&pending_joinable_thread_count) > 0) {
5193 joinable_threads_lock ();
5194 if (timeout == MONO_INFINITE_WAIT) {
5195 while (UnlockedRead (&pending_joinable_thread_count) > 0)
5196 mono_os_cond_wait (&zero_pending_joinable_thread_event, &joinable_threads_mutex);
5197 } else {
5198 gint64 start = mono_msec_ticks ();
5199 gint64 elapsed = 0;
5200 while (UnlockedRead (&pending_joinable_thread_count) > 0 && elapsed < timeout) {
5201 mono_os_cond_timedwait (&zero_pending_joinable_thread_event, &joinable_threads_mutex, timeout - (uint32_t)elapsed);
5202 elapsed = mono_msec_ticks () - start;
5205 joinable_threads_unlock ();
5208 return UnlockedRead (&pending_joinable_thread_count) == 0;
5211 static void
5212 threads_add_unique_joinable_thread_nolock (gpointer tid)
5214 if (!joinable_threads)
5215 joinable_threads = g_hash_table_new (NULL, NULL);
5217 gpointer orig_key;
5218 gpointer value;
5220 if (!g_hash_table_lookup_extended (joinable_threads, tid, &orig_key, &value)) {
5221 threads_add_joinable_thread_nolock (tid);
5222 UnlockedIncrement (&joinable_thread_count);
5226 void
5227 mono_threads_add_joinable_runtime_thread (gpointer thread_info)
5229 g_assert (thread_info);
5230 MonoThreadInfo *mono_thread_info = (MonoThreadInfo*)thread_info;
5232 if (mono_thread_info->runtime_thread) {
5233 gpointer tid = (gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (mono_thread_info_get_tid (mono_thread_info)));
5235 joinable_threads_lock ();
5237 // Add to joinable thread list, if not already included.
5238 threads_add_unique_joinable_thread_nolock (tid);
5240 // Remove thread from pending joinable list, if present.
5241 threads_remove_pending_joinable_thread_nolock (tid);
5243 joinable_threads_unlock ();
5245 mono_gc_finalize_notify ();
5250 * mono_add_joinable_thread:
5252 * Add TID to the list of joinable threads.
5253 * LOCKING: Acquires the threads lock.
5255 void
5256 mono_threads_add_joinable_thread (gpointer tid)
5259 * We cannot detach from threads because it causes problems like
5260 * 2fd16f60/r114307. So we collect them and join them when
5261 * we have time (in the finalizer thread).
5263 joinable_threads_lock ();
5264 threads_add_unique_joinable_thread_nolock (tid);
5265 joinable_threads_unlock ();
5267 mono_gc_finalize_notify ();
5271 * mono_threads_join_threads:
5273 * Join all joinable threads. This is called from the finalizer thread.
5274 * LOCKING: Acquires the threads lock.
5276 void
5277 mono_threads_join_threads (void)
5279 GHashTableIter iter;
5280 gpointer key;
5281 gpointer value;
5282 gboolean found;
5284 /* Fastpath */
5285 if (!UnlockedRead (&joinable_thread_count))
5286 return;
5288 while (TRUE) {
5289 joinable_threads_lock ();
5290 found = FALSE;
5291 if (g_hash_table_size (joinable_threads)) {
5292 g_hash_table_iter_init (&iter, joinable_threads);
5293 g_hash_table_iter_next (&iter, &key, (void**)&value);
5294 g_hash_table_remove (joinable_threads, key);
5295 UnlockedDecrement (&joinable_thread_count);
5296 found = TRUE;
5298 joinable_threads_unlock ();
5299 if (found)
5300 threads_native_thread_join_lock (key, value);
5301 else
5302 break;
5307 * mono_thread_join:
5309 * Wait for thread TID to exit.
5310 * LOCKING: Acquires the threads lock.
5312 void
5313 mono_thread_join (gpointer tid)
5315 gboolean found = FALSE;
5316 gpointer orig_key;
5317 gpointer value;
5319 joinable_threads_lock ();
5320 if (!joinable_threads)
5321 joinable_threads = g_hash_table_new (NULL, NULL);
5323 if (g_hash_table_lookup_extended (joinable_threads, tid, &orig_key, &value)) {
5324 g_hash_table_remove (joinable_threads, tid);
5325 UnlockedDecrement (&joinable_thread_count);
5326 found = TRUE;
5328 joinable_threads_unlock ();
5330 if (!found)
5331 return;
5333 threads_native_thread_join_nolock (tid, value);
5336 void
5337 mono_thread_internal_unhandled_exception (MonoObject* exc)
5339 MonoClass *klass = exc->vtable->klass;
5340 if (is_threadabort_exception (klass)) {
5341 mono_thread_internal_reset_abort (mono_thread_internal_current ());
5342 } else if (!is_appdomainunloaded_exception (klass)
5343 && mono_runtime_unhandled_exception_policy_get () == MONO_UNHANDLED_POLICY_CURRENT) {
5344 mono_unhandled_exception (exc);
5345 if (mono_environment_exitcode_get () == 1) {
5346 mono_environment_exitcode_set (255);
5347 mono_invoke_unhandled_exception_hook (exc);
5348 g_assert_not_reached ();
5353 void
5354 ves_icall_System_Threading_Thread_GetStackTraces (MonoArray **out_threads, MonoArray **out_stack_traces)
5356 ERROR_DECL (error);
5357 mono_threads_get_thread_dump (out_threads, out_stack_traces, error);
5358 mono_error_set_pending_exception (error);
5362 * mono_threads_attach_coop: called by native->managed wrappers
5364 * - @dummy:
5365 * - blocking mode: contains gc unsafe transition cookie
5366 * - non-blocking mode: contains random data
5367 * - @return: the original domain which needs to be restored, or NULL.
5369 gpointer
5370 mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
5372 MonoDomain *orig;
5373 MonoThreadInfo *info;
5374 gboolean external;
5376 orig = mono_domain_get ();
5378 if (!domain) {
5379 /* Happens when called from AOTed code which is only used in the root domain. */
5380 domain = mono_get_root_domain ();
5381 g_assert (domain);
5384 /* On coop, when we detached, we moved the thread from RUNNING->BLOCKING.
5385 * If we try to reattach we do a BLOCKING->RUNNING transition. If the thread
5386 * is fresh, mono_thread_attach() will do a STARTING->RUNNING transition so
5387 * we're only responsible for making the cookie. */
5388 if (mono_threads_is_blocking_transition_enabled ())
5389 external = !(info = mono_thread_info_current_unchecked ()) || !mono_thread_info_is_live (info);
5391 if (!mono_thread_internal_current ()) {
5392 mono_thread_attach (domain);
5394 // #678164
5395 mono_thread_set_state (mono_thread_internal_current (), ThreadState_Background);
5398 if (orig != domain)
5399 mono_domain_set (domain, TRUE);
5401 if (mono_threads_is_blocking_transition_enabled ()) {
5402 if (external) {
5403 /* mono_thread_attach put the thread in RUNNING mode from STARTING, but we need to
5404 * return the right cookie. */
5405 *dummy = mono_threads_enter_gc_unsafe_region_cookie ();
5406 } else {
5407 /* thread state (BLOCKING|RUNNING) -> RUNNING */
5408 *dummy = mono_threads_enter_gc_unsafe_region (dummy);
5412 return orig;
5416 * mono_threads_detach_coop: called by native->managed wrappers
5418 * - @cookie: the original domain which needs to be restored, or NULL.
5419 * - @dummy:
5420 * - blocking mode: contains gc unsafe transition cookie
5421 * - non-blocking mode: contains random data
5423 void
5424 mono_threads_detach_coop (gpointer cookie, gpointer *dummy)
5426 MonoDomain *domain, *orig;
5428 orig = (MonoDomain*) cookie;
5430 domain = mono_domain_get ();
5431 g_assert (domain);
5433 if (mono_threads_is_blocking_transition_enabled ()) {
5434 /* it won't do anything if cookie is NULL
5435 * thread state RUNNING -> (RUNNING|BLOCKING) */
5436 mono_threads_exit_gc_unsafe_region (*dummy, dummy);
5439 if (orig != domain) {
5440 if (!orig)
5441 mono_domain_unset ();
5442 else
5443 mono_domain_set (orig, TRUE);
5447 #if 0
5448 /* Returns TRUE if the current thread is ready to be interrupted. */
5449 gboolean
5450 mono_threads_is_ready_to_be_interrupted (void)
5452 MonoInternalThread *thread;
5454 thread = mono_thread_internal_current ();
5455 LOCK_THREAD (thread);
5456 if (thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
5457 UNLOCK_THREAD (thread);
5458 return FALSE;
5461 if (mono_thread_get_abort_prot_block_count (thread) || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ()) {
5462 UNLOCK_THREAD (thread);
5463 return FALSE;
5466 UNLOCK_THREAD (thread);
5467 return TRUE;
5469 #endif
5471 void
5472 mono_thread_internal_describe (MonoInternalThread *internal, GString *text)
5474 g_string_append_printf (text, ", thread handle : %p", internal->handle);
5476 if (internal->thread_info) {
5477 g_string_append (text, ", state : ");
5478 mono_thread_info_describe_interrupt_token ((MonoThreadInfo*) internal->thread_info, text);
5481 if (internal->owned_mutexes) {
5482 int i;
5484 g_string_append (text, ", owns : [");
5485 for (i = 0; i < internal->owned_mutexes->len; i++)
5486 g_string_append_printf (text, i == 0 ? "%p" : ", %p", g_ptr_array_index (internal->owned_mutexes, i));
5487 g_string_append (text, "]");
5491 gboolean
5492 mono_thread_internal_is_current (MonoInternalThread *internal)
5494 g_assert (internal);
5495 return mono_native_thread_id_equals (mono_native_thread_id_get (), MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid));