[runtime] Defer async requests during abort protected blocks
[mono-project.git] / mono / metadata / threads.c
blob86c6b8173ea3d49e8b709c557cc835f613784a68
1 /*
2 * threads.c: Thread support internal calls
4 * Author:
5 * Dick Porter (dick@ximian.com)
6 * Paolo Molaro (lupus@ximian.com)
7 * Patrik Torstensson (patrik.torstensson@labs2.com)
9 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
10 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
11 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
12 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
15 #include <config.h>
17 #include <glib.h>
18 #include <string.h>
20 #include <mono/metadata/object.h>
21 #include <mono/metadata/domain-internals.h>
22 #include <mono/metadata/profiler-private.h>
23 #include <mono/metadata/threads.h>
24 #include <mono/metadata/threads-types.h>
25 #include <mono/metadata/exception.h>
26 #include <mono/metadata/environment.h>
27 #include <mono/metadata/monitor.h>
28 #include <mono/metadata/gc-internals.h>
29 #include <mono/metadata/marshal.h>
30 #include <mono/metadata/runtime.h>
31 #include <mono/metadata/object-internals.h>
32 #include <mono/metadata/debug-internals.h>
33 #include <mono/utils/monobitset.h>
34 #include <mono/utils/mono-compiler.h>
35 #include <mono/utils/mono-mmap.h>
36 #include <mono/utils/mono-membar.h>
37 #include <mono/utils/mono-time.h>
38 #include <mono/utils/mono-threads.h>
39 #include <mono/utils/mono-threads-coop.h>
40 #include <mono/utils/hazard-pointer.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/metadata/w32handle.h>
48 #include <mono/metadata/w32event.h>
49 #include <mono/metadata/w32mutex.h>
51 #include <mono/metadata/reflection-internals.h>
52 #include <mono/metadata/abi-details.h>
53 #include <mono/metadata/w32error.h>
54 #include <mono/utils/w32api.h>
56 #ifdef HAVE_SIGNAL_H
57 #include <signal.h>
58 #endif
60 #if defined(HOST_WIN32)
61 #include <objbase.h>
62 #endif
64 #if defined(PLATFORM_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
65 #define USE_TKILL_ON_ANDROID 1
66 #endif
68 #ifdef PLATFORM_ANDROID
69 #include <errno.h>
71 #ifdef USE_TKILL_ON_ANDROID
72 extern int tkill (pid_t tid, int signal);
73 #endif
74 #endif
76 /*#define THREAD_DEBUG(a) do { a; } while (0)*/
77 #define THREAD_DEBUG(a)
78 /*#define THREAD_WAIT_DEBUG(a) do { a; } while (0)*/
79 #define THREAD_WAIT_DEBUG(a)
80 /*#define LIBGC_DEBUG(a) do { a; } while (0)*/
81 #define LIBGC_DEBUG(a)
83 #define SPIN_TRYLOCK(i) (InterlockedCompareExchange (&(i), 1, 0) == 0)
84 #define SPIN_LOCK(i) do { \
85 if (SPIN_TRYLOCK (i)) \
86 break; \
87 } while (1)
89 #define SPIN_UNLOCK(i) i = 0
91 #define LOCK_THREAD(thread) lock_thread((thread))
92 #define UNLOCK_THREAD(thread) unlock_thread((thread))
94 typedef union {
95 gint32 ival;
96 gfloat fval;
97 } IntFloatUnion;
99 typedef union {
100 gint64 ival;
101 gdouble fval;
102 } LongDoubleUnion;
104 typedef struct _StaticDataFreeList StaticDataFreeList;
105 struct _StaticDataFreeList {
106 StaticDataFreeList *next;
107 guint32 offset;
108 guint32 size;
111 typedef struct {
112 int idx;
113 int offset;
114 StaticDataFreeList *freelist;
115 } StaticDataInfo;
117 /* Controls access to the 'threads' hash table */
118 static void mono_threads_lock (void);
119 static void mono_threads_unlock (void);
120 static MonoCoopMutex threads_mutex;
122 /* Controls access to the 'joinable_threads' hash table */
123 #define joinable_threads_lock() mono_os_mutex_lock (&joinable_threads_mutex)
124 #define joinable_threads_unlock() mono_os_mutex_unlock (&joinable_threads_mutex)
125 static mono_mutex_t joinable_threads_mutex;
127 /* Holds current status of static data heap */
128 static StaticDataInfo thread_static_info;
129 static StaticDataInfo context_static_info;
131 /* The hash of existing threads (key is thread ID, value is
132 * MonoInternalThread*) that need joining before exit
134 static MonoGHashTable *threads=NULL;
136 /* List of app context GC handles.
137 * Added to from ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext ().
139 static GHashTable *contexts = NULL;
141 /* Cleanup queue for contexts. */
142 static MonoReferenceQueue *context_queue;
145 * Threads which are starting up and they are not in the 'threads' hash yet.
146 * When mono_thread_attach_internal is called for a thread, it will be removed from this hash table.
147 * Protected by mono_threads_lock ().
149 static MonoGHashTable *threads_starting_up = NULL;
151 /* Contains tids */
152 /* Protected by the threads lock */
153 static GHashTable *joinable_threads;
154 static int joinable_thread_count;
156 #define SET_CURRENT_OBJECT(x) mono_tls_set_thread (x)
157 #define GET_CURRENT_OBJECT() (MonoInternalThread*) mono_tls_get_thread ()
159 /* function called at thread start */
160 static MonoThreadStartCB mono_thread_start_cb = NULL;
162 /* function called at thread attach */
163 static MonoThreadAttachCB mono_thread_attach_cb = NULL;
165 /* function called at thread cleanup */
166 static MonoThreadCleanupFunc mono_thread_cleanup_fn = NULL;
168 /* The default stack size for each thread */
169 static guint32 default_stacksize = 0;
170 #define default_stacksize_for_thread(thread) ((thread)->stack_size? (thread)->stack_size: default_stacksize)
172 static void context_adjust_static_data (MonoAppContext *ctx);
173 static void mono_free_static_data (gpointer* static_data);
174 static void mono_init_static_data_info (StaticDataInfo *static_data);
175 static guint32 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align);
176 static gboolean mono_thread_resume (MonoInternalThread* thread);
177 static void async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort);
178 static void self_abort_internal (MonoError *error);
179 static void async_suspend_internal (MonoInternalThread *thread, gboolean interrupt);
180 static void self_suspend_internal (void);
182 static MonoException* mono_thread_execute_interruption (void);
183 static void ref_stack_destroy (gpointer rs);
185 /* Spin lock for InterlockedXXX 64 bit functions */
186 #define mono_interlocked_lock() mono_os_mutex_lock (&interlocked_mutex)
187 #define mono_interlocked_unlock() mono_os_mutex_unlock (&interlocked_mutex)
188 static mono_mutex_t interlocked_mutex;
190 /* global count of thread interruptions requested */
191 static gint32 thread_interruption_requested = 0;
193 /* Event signaled when a thread changes its background mode */
194 static MonoOSEvent background_change_event;
196 static gboolean shutting_down = FALSE;
198 static gint32 managed_thread_id_counter = 0;
200 /* Class lazy loading functions */
201 static GENERATE_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, "System", "AppDomainUnloadedException")
203 static void
204 mono_threads_lock (void)
206 mono_locks_coop_acquire (&threads_mutex, ThreadsLock);
209 static void
210 mono_threads_unlock (void)
212 mono_locks_coop_release (&threads_mutex, ThreadsLock);
216 static guint32
217 get_next_managed_thread_id (void)
219 return InterlockedIncrement (&managed_thread_id_counter);
223 * We separate interruptions/exceptions into either sync (they can be processed anytime,
224 * normally as soon as they are set, and are set by the same thread) and async (they can't
225 * be processed inside abort protected blocks and are normally set by other threads). We
226 * can have both a pending sync and async interruption. In this case, the sync exception is
227 * processed first. Since we clean sync flag first, mono_thread_execute_interruption must
228 * also handle all sync type exceptions before the async type exceptions.
230 enum {
231 INTERRUPT_SYNC_REQUESTED_BIT = 0x1,
232 INTERRUPT_ASYNC_REQUESTED_BIT = 0x2,
233 INTERRUPT_REQUESTED_MASK = 0x3,
234 ABORT_PROT_BLOCK_SHIFT = 2,
235 ABORT_PROT_BLOCK_BITS = 8,
236 ABORT_PROT_BLOCK_MASK = (((1 << ABORT_PROT_BLOCK_BITS) - 1) << ABORT_PROT_BLOCK_SHIFT)
239 static int
240 mono_thread_get_abort_prot_block_count (MonoInternalThread *thread)
242 gsize state = thread->thread_state;
243 return (state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT;
246 void
247 mono_threads_begin_abort_protected_block (void)
249 MonoInternalThread *thread = mono_thread_internal_current ();
250 gsize old_state, new_state;
251 int new_val;
252 do {
253 old_state = thread->thread_state;
255 new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) + 1;
256 //bounds check abort_prot_count
257 g_assert (new_val > 0);
258 g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
260 new_state = old_state + (1 << ABORT_PROT_BLOCK_SHIFT);
261 } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
263 /* Defer async request since we won't be able to process until exiting the block */
264 if (new_val == 1 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT))
265 InterlockedDecrement (&thread_interruption_requested);
268 static gboolean
269 mono_thread_state_has_interruption (gsize state)
271 /* pending exception, self abort */
272 if (state & INTERRUPT_SYNC_REQUESTED_BIT)
273 return TRUE;
275 /* abort, interruption, suspend */
276 if ((state & INTERRUPT_ASYNC_REQUESTED_BIT) && !(state & ABORT_PROT_BLOCK_MASK))
277 return TRUE;
279 return FALSE;
282 gboolean
283 mono_threads_end_abort_protected_block (void)
285 MonoInternalThread *thread = mono_thread_internal_current ();
286 gsize old_state, new_state;
287 int new_val;
288 do {
289 old_state = thread->thread_state;
291 //bounds check abort_prot_count
292 new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) - 1;
293 g_assert (new_val >= 0);
294 g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
296 new_state = old_state - (1 << ABORT_PROT_BLOCK_SHIFT);
297 } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
299 if (new_val == 0 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT))
300 InterlockedIncrement (&thread_interruption_requested);
302 return mono_thread_state_has_interruption (new_state);
305 static gboolean
306 mono_thread_get_interruption_requested (MonoInternalThread *thread)
308 gsize state = thread->thread_state;
310 return mono_thread_state_has_interruption (state);
314 * Returns TRUE is there was a state change
315 * We clear a single interruption request, sync has priority.
317 static gboolean
318 mono_thread_clear_interruption_requested (MonoInternalThread *thread)
320 gsize old_state, new_state;
321 do {
322 old_state = thread->thread_state;
324 // no interruption to process
325 if (!(old_state & INTERRUPT_SYNC_REQUESTED_BIT) &&
326 (!(old_state & INTERRUPT_ASYNC_REQUESTED_BIT) || (old_state & ABORT_PROT_BLOCK_MASK)))
327 return FALSE;
329 if (old_state & INTERRUPT_SYNC_REQUESTED_BIT)
330 new_state = old_state & ~INTERRUPT_SYNC_REQUESTED_BIT;
331 else
332 new_state = old_state & ~INTERRUPT_ASYNC_REQUESTED_BIT;
333 } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
335 InterlockedDecrement (&thread_interruption_requested);
337 return TRUE;
340 /* Returns TRUE is there was a state change and the interruption can be processed */
341 static gboolean
342 mono_thread_set_interruption_requested (MonoInternalThread *thread)
344 //always force when the current thread is doing it to itself.
345 gboolean sync = thread == mono_thread_internal_current ();
346 gsize old_state, new_state;
347 do {
348 old_state = thread->thread_state;
350 //Already set
351 if ((sync && (old_state & INTERRUPT_SYNC_REQUESTED_BIT)) ||
352 (!sync && (old_state & INTERRUPT_ASYNC_REQUESTED_BIT)))
353 return FALSE;
355 if (sync)
356 new_state = old_state | INTERRUPT_SYNC_REQUESTED_BIT;
357 else
358 new_state = old_state | INTERRUPT_ASYNC_REQUESTED_BIT;
359 } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
361 if (sync || !(new_state & ABORT_PROT_BLOCK_MASK))
362 InterlockedIncrement (&thread_interruption_requested);
364 return sync || !(new_state & ABORT_PROT_BLOCK_MASK);
367 static inline MonoNativeThreadId
368 thread_get_tid (MonoInternalThread *thread)
370 /* We store the tid as a guint64 to keep the object layout constant between platforms */
371 return MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid);
374 static void ensure_synch_cs_set (MonoInternalThread *thread)
376 MonoCoopMutex *synch_cs;
378 if (thread->synch_cs != NULL) {
379 return;
382 synch_cs = g_new0 (MonoCoopMutex, 1);
383 mono_coop_mutex_init_recursive (synch_cs);
385 if (InterlockedCompareExchangePointer ((gpointer *)&thread->synch_cs,
386 synch_cs, NULL) != NULL) {
387 /* Another thread must have installed this CS */
388 mono_coop_mutex_destroy (synch_cs);
389 g_free (synch_cs);
393 static inline void
394 lock_thread (MonoInternalThread *thread)
396 if (!thread->synch_cs)
397 ensure_synch_cs_set (thread);
399 g_assert (thread->synch_cs);
401 mono_coop_mutex_lock (thread->synch_cs);
404 static inline void
405 unlock_thread (MonoInternalThread *thread)
407 mono_coop_mutex_unlock (thread->synch_cs);
410 static inline gboolean
411 is_appdomainunloaded_exception (MonoClass *klass)
413 return klass == mono_class_get_appdomain_unloaded_exception_class ();
416 static inline gboolean
417 is_threadabort_exception (MonoClass *klass)
419 return klass == mono_defaults.threadabortexception_class;
423 * A special static data offset (guint32) consists of 3 parts:
425 * [0] 6-bit index into the array of chunks.
426 * [6] 25-bit offset into the array.
427 * [31] Bit indicating thread or context static.
430 typedef union {
431 struct {
432 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
433 guint32 type : 1;
434 guint32 offset : 25;
435 guint32 index : 6;
436 #else
437 guint32 index : 6;
438 guint32 offset : 25;
439 guint32 type : 1;
440 #endif
441 } fields;
442 guint32 raw;
443 } SpecialStaticOffset;
445 #define SPECIAL_STATIC_OFFSET_TYPE_THREAD 0
446 #define SPECIAL_STATIC_OFFSET_TYPE_CONTEXT 1
448 #define MAKE_SPECIAL_STATIC_OFFSET(index, offset, type) \
449 ((SpecialStaticOffset) { .fields = { (index), (offset), (type) } }.raw)
450 #define ACCESS_SPECIAL_STATIC_OFFSET(x,f) \
451 (((SpecialStaticOffset *) &(x))->fields.f)
453 static gpointer
454 get_thread_static_data (MonoInternalThread *thread, guint32 offset)
456 g_assert (ACCESS_SPECIAL_STATIC_OFFSET (offset, type) == SPECIAL_STATIC_OFFSET_TYPE_THREAD);
458 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
459 int off = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
461 return ((char *) thread->static_data [idx]) + off;
464 static gpointer
465 get_context_static_data (MonoAppContext *ctx, guint32 offset)
467 g_assert (ACCESS_SPECIAL_STATIC_OFFSET (offset, type) == SPECIAL_STATIC_OFFSET_TYPE_CONTEXT);
469 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
470 int off = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
472 return ((char *) ctx->static_data [idx]) + off;
475 static MonoThread**
476 get_current_thread_ptr_for_domain (MonoDomain *domain, MonoInternalThread *thread)
478 static MonoClassField *current_thread_field = NULL;
480 guint32 offset;
482 if (!current_thread_field) {
483 current_thread_field = mono_class_get_field_from_name (mono_defaults.thread_class, "current_thread");
484 g_assert (current_thread_field);
487 mono_class_vtable (domain, mono_defaults.thread_class);
488 mono_domain_lock (domain);
489 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, current_thread_field));
490 mono_domain_unlock (domain);
491 g_assert (offset);
493 return (MonoThread **)get_thread_static_data (thread, offset);
496 static void
497 set_current_thread_for_domain (MonoDomain *domain, MonoInternalThread *thread, MonoThread *current)
499 MonoThread **current_thread_ptr = get_current_thread_ptr_for_domain (domain, thread);
501 g_assert (current->obj.vtable->domain == domain);
503 g_assert (!*current_thread_ptr);
504 *current_thread_ptr = current;
507 static MonoThread*
508 create_thread_object (MonoDomain *domain, MonoInternalThread *internal)
510 MonoThread *thread;
511 MonoVTable *vtable;
512 MonoError error;
514 vtable = mono_class_vtable (domain, mono_defaults.thread_class);
515 g_assert (vtable);
517 thread = (MonoThread*)mono_object_new_mature (vtable, &error);
518 /* only possible failure mode is OOM, from which we don't expect to recover. */
519 mono_error_assert_ok (&error);
521 MONO_OBJECT_SETREF (thread, internal_thread, internal);
523 return thread;
526 static MonoInternalThread*
527 create_internal_thread_object (void)
529 MonoError error;
530 MonoInternalThread *thread;
531 MonoVTable *vt;
533 vt = mono_class_vtable (mono_get_root_domain (), mono_defaults.internal_thread_class);
534 thread = (MonoInternalThread*) mono_object_new_mature (vt, &error);
535 /* only possible failure mode is OOM, from which we don't exect to recover */
536 mono_error_assert_ok (&error);
538 thread->synch_cs = g_new0 (MonoCoopMutex, 1);
539 mono_coop_mutex_init_recursive (thread->synch_cs);
541 thread->apartment_state = ThreadApartmentState_Unknown;
542 thread->managed_id = get_next_managed_thread_id ();
543 if (mono_gc_is_moving ()) {
544 thread->thread_pinning_ref = thread;
545 MONO_GC_REGISTER_ROOT_PINNING (thread->thread_pinning_ref, MONO_ROOT_SOURCE_THREADING, "thread pinning reference");
548 thread->priority = MONO_THREAD_PRIORITY_NORMAL;
550 thread->suspended = g_new0 (MonoOSEvent, 1);
551 mono_os_event_init (thread->suspended, TRUE);
553 return thread;
556 static void
557 mono_thread_internal_set_priority (MonoInternalThread *internal, MonoThreadPriority priority)
559 g_assert (internal);
561 g_assert (priority >= MONO_THREAD_PRIORITY_LOWEST);
562 g_assert (priority <= MONO_THREAD_PRIORITY_HIGHEST);
563 g_assert (MONO_THREAD_PRIORITY_LOWEST < MONO_THREAD_PRIORITY_HIGHEST);
565 #ifdef HOST_WIN32
566 BOOL res;
568 g_assert (internal->native_handle);
570 res = SetThreadPriority (internal->native_handle, priority - 2);
571 if (!res)
572 g_error ("%s: SetThreadPriority failed, error %d", __func__, GetLastError ());
573 #else /* HOST_WIN32 */
574 pthread_t tid;
575 int policy;
576 struct sched_param param;
577 gint res;
579 tid = thread_get_tid (internal);
581 res = pthread_getschedparam (tid, &policy, &param);
582 if (res != 0)
583 g_error ("%s: pthread_getschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
585 #ifdef _POSIX_PRIORITY_SCHEDULING
586 int max, min;
588 /* Necessary to get valid priority range */
590 min = sched_get_priority_min (policy);
591 max = sched_get_priority_max (policy);
593 if (max > 0 && min >= 0 && max > min) {
594 double srange, drange, sposition, dposition;
595 srange = MONO_THREAD_PRIORITY_HIGHEST - MONO_THREAD_PRIORITY_LOWEST;
596 drange = max - min;
597 sposition = priority - MONO_THREAD_PRIORITY_LOWEST;
598 dposition = (sposition / srange) * drange;
599 param.sched_priority = (int)(dposition + min);
600 } else
601 #endif
603 switch (policy) {
604 case SCHED_FIFO:
605 case SCHED_RR:
606 param.sched_priority = 50;
607 break;
608 #ifdef SCHED_BATCH
609 case SCHED_BATCH:
610 #endif
611 case SCHED_OTHER:
612 param.sched_priority = 0;
613 break;
614 default:
615 g_warning ("%s: unknown policy %d", __func__, policy);
616 return;
620 res = pthread_setschedparam (tid, policy, &param);
621 if (res != 0) {
622 if (res == EPERM) {
623 g_warning ("%s: pthread_setschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
624 return;
626 g_error ("%s: pthread_setschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
628 #endif /* HOST_WIN32 */
631 static void
632 mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean threadlocal);
634 static gboolean
635 mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean force_domain)
637 MonoThreadInfo *info;
638 MonoInternalThread *internal;
639 MonoDomain *domain, *root_domain;
641 g_assert (thread);
643 info = mono_thread_info_current ();
645 internal = thread->internal_thread;
646 internal->handle = mono_threads_open_thread_handle (info->handle);
647 #ifdef HOST_WIN32
648 internal->native_handle = OpenThread (THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId ());
649 #endif
650 internal->tid = MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ());
651 internal->thread_info = info;
652 internal->small_id = info->small_id;
654 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, mono_native_thread_id_get (), internal));
656 SET_CURRENT_OBJECT (internal);
658 domain = mono_object_domain (thread);
660 mono_thread_push_appdomain_ref (domain);
661 if (!mono_domain_set (domain, force_domain)) {
662 mono_thread_pop_appdomain_ref ();
663 return FALSE;
666 mono_threads_lock ();
668 if (threads_starting_up)
669 mono_g_hash_table_remove (threads_starting_up, thread);
671 if (shutting_down && !force_attach) {
672 mono_threads_unlock ();
673 mono_thread_pop_appdomain_ref ();
674 return FALSE;
677 if (!threads) {
678 threads = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_THREADING, "threads table");
681 /* We don't need to duplicate thread->handle, because it is
682 * only closed when the thread object is finalized by the GC. */
683 mono_g_hash_table_insert (threads, (gpointer)(gsize)(internal->tid), internal);
685 /* We have to do this here because mono_thread_start_cb
686 * requires that root_domain_thread is set up. */
687 if (thread_static_info.offset || thread_static_info.idx > 0) {
688 /* get the current allocated size */
689 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (thread_static_info.idx, thread_static_info.offset, 0);
690 mono_alloc_static_data (&internal->static_data, offset, TRUE);
693 mono_threads_unlock ();
695 root_domain = mono_get_root_domain ();
697 g_assert (!internal->root_domain_thread);
698 if (domain != root_domain)
699 MONO_OBJECT_SETREF (internal, root_domain_thread, create_thread_object (root_domain, internal));
700 else
701 MONO_OBJECT_SETREF (internal, root_domain_thread, thread);
703 if (domain != root_domain)
704 set_current_thread_for_domain (root_domain, internal, internal->root_domain_thread);
706 set_current_thread_for_domain (domain, internal, thread);
708 THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, internal->tid, internal->handle));
710 return TRUE;
713 typedef struct {
714 gint32 ref;
715 MonoThread *thread;
716 MonoObject *start_delegate;
717 MonoObject *start_delegate_arg;
718 MonoThreadStart start_func;
719 gpointer start_func_arg;
720 gboolean force_attach;
721 gboolean failed;
722 MonoCoopSem registered;
723 } StartInfo;
725 static guint32 WINAPI start_wrapper_internal(StartInfo *start_info, gsize *stack_ptr)
727 MonoError error;
728 MonoThreadStart start_func;
729 void *start_func_arg;
730 gsize tid;
732 * We don't create a local to hold start_info->thread, so hopefully it won't get pinned during a
733 * GC stack walk.
735 MonoThread *thread;
736 MonoInternalThread *internal;
737 MonoObject *start_delegate;
738 MonoObject *start_delegate_arg;
739 MonoDomain *domain;
741 thread = start_info->thread;
742 internal = thread->internal_thread;
743 domain = mono_object_domain (start_info->thread);
745 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper", __func__, mono_native_thread_id_get ()));
747 if (!mono_thread_attach_internal (thread, start_info->force_attach, FALSE)) {
748 start_info->failed = TRUE;
750 mono_coop_sem_post (&start_info->registered);
752 if (InterlockedDecrement (&start_info->ref) == 0) {
753 mono_coop_sem_destroy (&start_info->registered);
754 g_free (start_info);
757 return 0;
760 mono_thread_internal_set_priority (internal, internal->priority);
762 tid = internal->tid;
764 start_delegate = start_info->start_delegate;
765 start_delegate_arg = start_info->start_delegate_arg;
766 start_func = start_info->start_func;
767 start_func_arg = start_info->start_func_arg;
769 /* This MUST be called before any managed code can be
770 * executed, as it calls the callback function that (for the
771 * jit) sets the lmf marker.
774 if (mono_thread_start_cb)
775 mono_thread_start_cb (tid, stack_ptr, start_func);
777 /* On 2.0 profile (and higher), set explicitly since state might have been
778 Unknown */
779 if (internal->apartment_state == ThreadApartmentState_Unknown)
780 internal->apartment_state = ThreadApartmentState_MTA;
782 mono_thread_init_apartment_state ();
784 /* Let the thread that called Start() know we're ready */
785 mono_coop_sem_post (&start_info->registered);
787 if (InterlockedDecrement (&start_info->ref) == 0) {
788 mono_coop_sem_destroy (&start_info->registered);
789 g_free (start_info);
792 /* start_info is not valid anymore */
793 start_info = NULL;
796 * Call this after calling start_notify, since the profiler callback might want
797 * to lock the thread, and the lock is held by thread_start () which waits for
798 * start_notify.
800 mono_profiler_thread_start (tid);
802 /* if the name was set before starting, we didn't invoke the profiler callback */
803 if (internal->name) {
804 char *tname = g_utf16_to_utf8 (internal->name, internal->name_len, NULL, NULL, NULL);
805 mono_profiler_thread_name (internal->tid, tname);
806 mono_native_thread_set_name (MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid), tname);
807 g_free (tname);
810 /* start_func is set only for unmanaged start functions */
811 if (start_func) {
812 start_func (start_func_arg);
813 } else {
814 void *args [1];
816 g_assert (start_delegate != NULL);
818 /* we may want to handle the exception here. See comment below on unhandled exceptions */
819 args [0] = (gpointer) start_delegate_arg;
820 mono_runtime_delegate_invoke_checked (start_delegate, args, &error);
822 if (!mono_error_ok (&error)) {
823 MonoException *ex = mono_error_convert_to_exception (&error);
825 g_assert (ex != NULL);
826 MonoClass *klass = mono_object_get_class (&ex->object);
827 if ((mono_runtime_unhandled_exception_policy_get () != MONO_UNHANDLED_POLICY_LEGACY) &&
828 !is_threadabort_exception (klass)) {
829 mono_unhandled_exception (&ex->object);
830 mono_invoke_unhandled_exception_hook (&ex->object);
831 g_assert_not_reached ();
833 } else {
834 mono_error_cleanup (&error);
838 /* If the thread calls ExitThread at all, this remaining code
839 * will not be executed, but the main thread will eventually
840 * call mono_thread_detach_internal() on this thread's behalf.
843 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper terminating", __func__, mono_native_thread_id_get ()));
845 /* Do any cleanup needed for apartment state. This
846 * cannot be done in mono_thread_detach_internal since
847 * mono_thread_detach_internal could be called for a
848 * thread other than the current thread.
849 * mono_thread_cleanup_apartment_state cleans up apartment
850 * for the current thead */
851 mono_thread_cleanup_apartment_state ();
853 mono_thread_detach_internal (internal);
855 internal->tid = 0;
857 return(0);
860 static gsize WINAPI
861 start_wrapper (gpointer data)
863 StartInfo *start_info;
864 MonoThreadInfo *info;
865 gsize res;
867 start_info = (StartInfo*) data;
868 g_assert (start_info);
870 info = mono_thread_info_attach (&res);
871 info->runtime_thread = TRUE;
873 /* Run the actual main function of the thread */
874 res = start_wrapper_internal (start_info, &res);
876 mono_thread_info_exit (res);
878 g_assert_not_reached ();
882 * create_thread:
884 * Common thread creation code.
885 * LOCKING: Acquires the threads lock.
887 static gboolean
888 create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *start_delegate, MonoThreadStart start_func, gpointer start_func_arg,
889 MonoThreadCreateFlags flags, MonoError *error)
891 StartInfo *start_info = NULL;
892 MonoNativeThreadId tid;
893 gboolean ret;
894 gsize stack_set_size;
896 if (start_delegate)
897 g_assert (!start_func && !start_func_arg);
898 if (start_func)
899 g_assert (!start_delegate);
901 if (flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL) {
902 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER));
903 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE));
905 if (flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER) {
906 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL));
907 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE));
911 * Join joinable threads to prevent running out of threads since the finalizer
912 * thread might be blocked/backlogged.
914 mono_threads_join_threads ();
916 error_init (error);
918 mono_threads_lock ();
919 if (shutting_down && !(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE)) {
920 mono_threads_unlock ();
921 return FALSE;
923 if (threads_starting_up == NULL) {
924 threads_starting_up = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_VALUE_GC, MONO_ROOT_SOURCE_THREADING, "starting threads table");
926 mono_g_hash_table_insert (threads_starting_up, thread, thread);
927 mono_threads_unlock ();
929 internal->threadpool_thread = flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL;
930 if (internal->threadpool_thread)
931 mono_thread_set_state (internal, ThreadState_Background);
933 internal->debugger_thread = flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER;
935 start_info = g_new0 (StartInfo, 1);
936 start_info->ref = 2;
937 start_info->thread = thread;
938 start_info->start_delegate = start_delegate;
939 start_info->start_delegate_arg = thread->start_obj;
940 start_info->start_func = start_func;
941 start_info->start_func_arg = start_func_arg;
942 start_info->force_attach = flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE;
943 start_info->failed = FALSE;
944 mono_coop_sem_init (&start_info->registered, 0);
946 if (flags != MONO_THREAD_CREATE_FLAGS_SMALL_STACK)
947 stack_set_size = default_stacksize_for_thread (internal);
948 else
949 stack_set_size = 0;
951 if (!mono_thread_platform_create_thread (start_wrapper, start_info, &stack_set_size, &tid)) {
952 /* The thread couldn't be created, so set an exception */
953 mono_threads_lock ();
954 mono_g_hash_table_remove (threads_starting_up, thread);
955 mono_threads_unlock ();
956 mono_error_set_execution_engine (error, "Couldn't create thread. Error 0x%x", mono_w32error_get_last());
957 /* ref is not going to be decremented in start_wrapper_internal */
958 InterlockedDecrement (&start_info->ref);
959 ret = FALSE;
960 goto done;
963 internal->stack_size = (int) stack_set_size;
965 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Launching thread %p (%"G_GSIZE_FORMAT")", __func__, mono_native_thread_id_get (), internal, (gsize)internal->tid));
968 * Wait for the thread to set up its TLS data etc, so
969 * theres no potential race condition if someone tries
970 * to look up the data believing the thread has
971 * started
974 mono_coop_sem_wait (&start_info->registered, MONO_SEM_FLAGS_NONE);
976 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));
978 ret = !start_info->failed;
980 done:
981 if (InterlockedDecrement (&start_info->ref) == 0) {
982 mono_coop_sem_destroy (&start_info->registered);
983 g_free (start_info);
986 return ret;
989 void mono_thread_new_init (intptr_t tid, gpointer stack_start, gpointer func)
991 if (mono_thread_start_cb) {
992 mono_thread_start_cb (tid, stack_start, func);
996 void mono_threads_set_default_stacksize (guint32 stacksize)
998 default_stacksize = stacksize;
1001 guint32 mono_threads_get_default_stacksize (void)
1003 return default_stacksize;
1007 * mono_thread_create_internal:
1009 * ARG should not be a GC reference.
1011 MonoInternalThread*
1012 mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, MonoThreadCreateFlags flags, MonoError *error)
1014 MonoThread *thread;
1015 MonoInternalThread *internal;
1016 gboolean res;
1018 error_init (error);
1020 internal = create_internal_thread_object ();
1022 thread = create_thread_object (domain, internal);
1024 LOCK_THREAD (internal);
1026 res = create_thread (thread, internal, NULL, (MonoThreadStart) func, arg, flags, error);
1028 UNLOCK_THREAD (internal);
1030 return_val_if_nok (error, NULL);
1031 return internal;
1034 void
1035 mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
1037 MonoError error;
1038 if (!mono_thread_create_checked (domain, func, arg, &error))
1039 mono_error_cleanup (&error);
1042 gboolean
1043 mono_thread_create_checked (MonoDomain *domain, gpointer func, gpointer arg, MonoError *error)
1045 return (NULL != mono_thread_create_internal (domain, func, arg, MONO_THREAD_CREATE_FLAGS_NONE, error));
1048 MonoThread *
1049 mono_thread_attach (MonoDomain *domain)
1051 MonoThread *thread = mono_thread_attach_full (domain, FALSE);
1053 return thread;
1056 MonoThread *
1057 mono_thread_attach_full (MonoDomain *domain, gboolean force_attach)
1059 MonoInternalThread *internal;
1060 MonoThread *thread;
1061 MonoThreadInfo *info;
1062 MonoNativeThreadId tid;
1063 gsize stack_ptr;
1065 if (mono_thread_internal_current_is_attached ()) {
1066 if (domain != mono_domain_get ())
1067 mono_domain_set (domain, TRUE);
1068 /* Already attached */
1069 return mono_thread_current ();
1072 info = mono_thread_info_attach (&stack_ptr);
1073 g_assert (info);
1075 tid=mono_native_thread_id_get ();
1077 internal = create_internal_thread_object ();
1079 thread = create_thread_object (domain, internal);
1081 if (!mono_thread_attach_internal (thread, force_attach, TRUE)) {
1082 /* Mono is shutting down, so just wait for the end */
1083 for (;;)
1084 mono_thread_info_sleep (10000, NULL);
1087 THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, internal->handle));
1089 if (mono_thread_attach_cb) {
1090 guint8 *staddr;
1091 size_t stsize;
1093 mono_thread_info_get_stack_bounds (&staddr, &stsize);
1095 if (staddr == NULL)
1096 mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), &stack_ptr);
1097 else
1098 mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), staddr + stsize);
1101 /* Can happen when we attach the profiler helper thread in order to heapshot. */
1102 if (!mono_thread_info_current ()->tools_thread)
1103 // FIXME: Need a separate callback
1104 mono_profiler_thread_start (MONO_NATIVE_THREAD_ID_TO_UINT (tid));
1106 return thread;
1109 void
1110 mono_thread_detach_internal (MonoInternalThread *thread)
1112 gboolean removed;
1114 g_assert (thread != NULL);
1116 THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
1118 #ifndef HOST_WIN32
1119 mono_w32mutex_abandon ();
1120 #endif
1122 if (thread->abort_state_handle) {
1123 mono_gchandle_free (thread->abort_state_handle);
1124 thread->abort_state_handle = 0;
1127 thread->abort_exc = NULL;
1128 thread->current_appcontext = NULL;
1131 * thread->synch_cs can be NULL if this was called after
1132 * ves_icall_System_Threading_InternalThread_Thread_free_internal.
1133 * This can happen only during shutdown.
1134 * The shutting_down flag is not always set, so we can't assert on it.
1136 if (thread->synch_cs)
1137 LOCK_THREAD (thread);
1139 thread->state |= ThreadState_Stopped;
1140 thread->state &= ~ThreadState_Background;
1142 if (thread->synch_cs)
1143 UNLOCK_THREAD (thread);
1146 An interruption request has leaked to cleanup. Adjust the global counter.
1148 This can happen is the abort source thread finds the abortee (this) thread
1149 in unmanaged code. If this thread never trips back to managed code or check
1150 the local flag it will be left set and positively unbalance the global counter.
1152 Leaving the counter unbalanced will cause a performance degradation since all threads
1153 will now keep checking their local flags all the time.
1155 mono_thread_clear_interruption_requested (thread);
1157 mono_threads_lock ();
1159 if (!threads) {
1160 removed = FALSE;
1161 } else if (mono_g_hash_table_lookup (threads, (gpointer)thread->tid) != thread) {
1162 /* We have to check whether the thread object for the
1163 * tid is still the same in the table because the
1164 * thread might have been destroyed and the tid reused
1165 * in the meantime, in which case the tid would be in
1166 * the table, but with another thread object.
1168 removed = FALSE;
1169 } else {
1170 mono_g_hash_table_remove (threads, (gpointer)thread->tid);
1171 removed = TRUE;
1174 mono_threads_unlock ();
1176 /* Don't close the handle here, wait for the object finalizer
1177 * to do it. Otherwise, the following race condition applies:
1179 * 1) Thread exits (and mono_thread_detach_internal() closes the handle)
1181 * 2) Some other handle is reassigned the same slot
1183 * 3) Another thread tries to join the first thread, and
1184 * blocks waiting for the reassigned handle to be signalled
1185 * (which might never happen). This is possible, because the
1186 * thread calling Join() still has a reference to the first
1187 * thread's object.
1190 /* if the thread is not in the hash it has been removed already */
1191 if (!removed) {
1192 mono_domain_unset ();
1193 mono_memory_barrier ();
1195 if (mono_thread_cleanup_fn)
1196 mono_thread_cleanup_fn (thread_get_tid (thread));
1198 goto done;
1201 mono_release_type_locks (thread);
1203 /* Can happen when we attach the profiler helper thread in order to heapshot. */
1204 if (!mono_thread_info_lookup (MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid))->tools_thread)
1205 mono_profiler_thread_end (thread->tid);
1207 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
1210 * This will signal async signal handlers that the thread has exited.
1211 * The profiler callback needs this to be set, so it cannot be done earlier.
1213 mono_domain_unset ();
1214 mono_memory_barrier ();
1216 if (thread == mono_thread_internal_current ())
1217 mono_thread_pop_appdomain_ref ();
1219 mono_free_static_data (thread->static_data);
1220 thread->static_data = NULL;
1221 ref_stack_destroy (thread->appdomain_refs);
1222 thread->appdomain_refs = NULL;
1224 g_assert (thread->suspended);
1225 mono_os_event_destroy (thread->suspended);
1226 g_free (thread->suspended);
1227 thread->suspended = NULL;
1229 if (mono_thread_cleanup_fn)
1230 mono_thread_cleanup_fn (thread_get_tid (thread));
1232 mono_memory_barrier ();
1234 if (mono_gc_is_moving ()) {
1235 MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref);
1236 thread->thread_pinning_ref = NULL;
1239 done:
1240 SET_CURRENT_OBJECT (NULL);
1241 mono_domain_unset ();
1243 /* Don't need to close the handle to this thread, even though we took a
1244 * reference in mono_thread_attach (), because the GC will do it
1245 * when the Thread object is finalised.
1249 void
1250 mono_thread_detach (MonoThread *thread)
1252 if (thread)
1253 mono_thread_detach_internal (thread->internal_thread);
1257 * mono_thread_detach_if_exiting:
1259 * Detach the current thread from the runtime if it is exiting, i.e. it is running pthread dtors.
1260 * This should be used at the end of embedding code which calls into managed code, and which
1261 * can be called from pthread dtors, like dealloc: implementations in objective-c.
1263 mono_bool
1264 mono_thread_detach_if_exiting (void)
1266 if (mono_thread_info_is_exiting ()) {
1267 MonoInternalThread *thread;
1269 thread = mono_thread_internal_current ();
1270 if (thread) {
1271 mono_thread_detach_internal (thread);
1272 mono_thread_info_detach ();
1273 return TRUE;
1276 return FALSE;
1279 gboolean
1280 mono_thread_internal_current_is_attached (void)
1282 MonoInternalThread *internal;
1284 internal = GET_CURRENT_OBJECT ();
1285 if (!internal)
1286 return FALSE;
1288 return TRUE;
1291 void
1292 mono_thread_exit (void)
1294 MonoInternalThread *thread = mono_thread_internal_current ();
1296 THREAD_DEBUG (g_message ("%s: mono_thread_exit for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
1298 mono_thread_detach_internal (thread);
1300 /* we could add a callback here for embedders to use. */
1301 if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread))
1302 exit (mono_environment_exitcode_get ());
1304 mono_thread_info_exit (0);
1307 void
1308 ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this_obj)
1310 MonoInternalThread *internal;
1312 internal = create_internal_thread_object ();
1314 internal->state = ThreadState_Unstarted;
1316 InterlockedCompareExchangePointer ((volatile gpointer *)&this_obj->internal_thread, internal, NULL);
1319 MonoThread *
1320 ves_icall_System_Threading_Thread_GetCurrentThread (void)
1322 return mono_thread_current ();
1325 HANDLE
1326 ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this_obj,
1327 MonoObject *start)
1329 MonoError error;
1330 MonoInternalThread *internal;
1331 gboolean res;
1333 THREAD_DEBUG (g_message("%s: Trying to start a new thread: this (%p) start (%p)", __func__, this_obj, start));
1335 if (!this_obj->internal_thread)
1336 ves_icall_System_Threading_Thread_ConstructInternalThread (this_obj);
1337 internal = this_obj->internal_thread;
1339 LOCK_THREAD (internal);
1341 if ((internal->state & ThreadState_Unstarted) == 0) {
1342 UNLOCK_THREAD (internal);
1343 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has already been started."));
1344 return NULL;
1347 if ((internal->state & ThreadState_Aborted) != 0) {
1348 UNLOCK_THREAD (internal);
1349 return this_obj;
1352 res = create_thread (this_obj, internal, start, NULL, NULL, MONO_THREAD_CREATE_FLAGS_NONE, &error);
1353 if (!res) {
1354 mono_error_cleanup (&error);
1355 UNLOCK_THREAD (internal);
1356 return NULL;
1359 internal->state &= ~ThreadState_Unstarted;
1361 THREAD_DEBUG (g_message ("%s: Started thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread));
1363 UNLOCK_THREAD (internal);
1364 return internal->handle;
1368 * This is called from the finalizer of the internal thread object.
1370 void
1371 ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThread *this_obj)
1373 THREAD_DEBUG (g_message ("%s: Closing thread %p, handle %p", __func__, this, this_obj->handle));
1376 * Since threads keep a reference to their thread object while running, by
1377 * the time this function is called, the thread has already exited/detached,
1378 * i.e. mono_thread_detach_internal () has ran. The exception is during
1379 * shutdown, when mono_thread_detach_internal () can be called after this.
1381 if (this_obj->handle) {
1382 mono_threads_close_thread_handle (this_obj->handle);
1383 this_obj->handle = NULL;
1386 #if HOST_WIN32
1387 CloseHandle (this_obj->native_handle);
1388 #endif
1390 if (this_obj->synch_cs) {
1391 MonoCoopMutex *synch_cs = this_obj->synch_cs;
1392 this_obj->synch_cs = NULL;
1393 mono_coop_mutex_destroy (synch_cs);
1394 g_free (synch_cs);
1397 if (this_obj->name) {
1398 void *name = this_obj->name;
1399 this_obj->name = NULL;
1400 g_free (name);
1404 void
1405 ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
1407 guint32 res;
1408 MonoInternalThread *thread = mono_thread_internal_current ();
1410 THREAD_DEBUG (g_message ("%s: Sleeping for %d ms", __func__, ms));
1412 if (mono_thread_current_check_pending_interrupt ())
1413 return;
1415 while (TRUE) {
1416 gboolean alerted = FALSE;
1418 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1420 res = mono_thread_info_sleep (ms, &alerted);
1422 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1424 if (alerted) {
1425 MonoException* exc = mono_thread_execute_interruption ();
1426 if (exc) {
1427 mono_raise_exception (exc);
1428 } else {
1429 // FIXME: !MONO_INFINITE_WAIT
1430 if (ms != MONO_INFINITE_WAIT)
1431 break;
1433 } else {
1434 break;
1439 void ves_icall_System_Threading_Thread_SpinWait_nop (void)
1443 gint32
1444 ves_icall_System_Threading_Thread_GetDomainID (void)
1446 return mono_domain_get()->domain_id;
1449 gboolean
1450 ves_icall_System_Threading_Thread_Yield (void)
1452 return mono_thread_info_yield ();
1456 * mono_thread_get_name:
1458 * Return the name of the thread. NAME_LEN is set to the length of the name.
1459 * Return NULL if the thread has no name. The returned memory is owned by the
1460 * caller.
1462 gunichar2*
1463 mono_thread_get_name (MonoInternalThread *this_obj, guint32 *name_len)
1465 gunichar2 *res;
1467 LOCK_THREAD (this_obj);
1469 if (!this_obj->name) {
1470 *name_len = 0;
1471 res = NULL;
1472 } else {
1473 *name_len = this_obj->name_len;
1474 res = g_new (gunichar2, this_obj->name_len);
1475 memcpy (res, this_obj->name, sizeof (gunichar2) * this_obj->name_len);
1478 UNLOCK_THREAD (this_obj);
1480 return res;
1484 * mono_thread_get_name_utf8:
1486 * Return the name of the thread in UTF-8.
1487 * Return NULL if the thread has no name.
1488 * The returned memory is owned by the caller.
1490 char *
1491 mono_thread_get_name_utf8 (MonoThread *thread)
1493 if (thread == NULL)
1494 return NULL;
1496 MonoInternalThread *internal = thread->internal_thread;
1497 if (internal == NULL)
1498 return NULL;
1500 LOCK_THREAD (internal);
1502 char *tname = g_utf16_to_utf8 (internal->name, internal->name_len, NULL, NULL, NULL);
1504 UNLOCK_THREAD (internal);
1506 return tname;
1510 * mono_thread_get_managed_id:
1512 * Return the Thread.ManagedThreadId value of `thread`.
1513 * Returns -1 if `thread` is NULL.
1515 int32_t
1516 mono_thread_get_managed_id (MonoThread *thread)
1518 if (thread == NULL)
1519 return -1;
1521 MonoInternalThread *internal = thread->internal_thread;
1522 if (internal == NULL)
1523 return -1;
1525 int32_t id = internal->managed_id;
1527 return id;
1530 MonoString*
1531 ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThread *this_obj)
1533 MonoError error;
1534 MonoString* str;
1536 error_init (&error);
1538 LOCK_THREAD (this_obj);
1540 if (!this_obj->name)
1541 str = NULL;
1542 else
1543 str = mono_string_new_utf16_checked (mono_domain_get (), this_obj->name, this_obj->name_len, &error);
1545 UNLOCK_THREAD (this_obj);
1547 if (mono_error_set_pending_exception (&error))
1548 return NULL;
1550 return str;
1553 void
1554 mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, gboolean reset, MonoError *error)
1556 LOCK_THREAD (this_obj);
1558 error_init (error);
1560 if (reset) {
1561 this_obj->flags &= ~MONO_THREAD_FLAG_NAME_SET;
1562 } else if (this_obj->flags & MONO_THREAD_FLAG_NAME_SET) {
1563 UNLOCK_THREAD (this_obj);
1565 mono_error_set_invalid_operation (error, "Thread.Name can only be set once.");
1566 return;
1568 if (this_obj->name) {
1569 g_free (this_obj->name);
1570 this_obj->name_len = 0;
1572 if (name) {
1573 this_obj->name = g_memdup (mono_string_chars (name), mono_string_length (name) * sizeof (gunichar2));
1574 this_obj->name_len = mono_string_length (name);
1576 if (permanent)
1577 this_obj->flags |= MONO_THREAD_FLAG_NAME_SET;
1579 else
1580 this_obj->name = NULL;
1583 UNLOCK_THREAD (this_obj);
1585 if (this_obj->name && this_obj->tid) {
1586 char *tname = mono_string_to_utf8_checked (name, error);
1587 return_if_nok (error);
1588 mono_profiler_thread_name (this_obj->tid, tname);
1589 mono_native_thread_set_name (thread_get_tid (this_obj), tname);
1590 mono_free (tname);
1594 void
1595 ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj, MonoString *name)
1597 MonoError error;
1598 mono_thread_set_name_internal (this_obj, name, TRUE, FALSE, &error);
1599 mono_error_set_pending_exception (&error);
1603 * ves_icall_System_Threading_Thread_GetPriority_internal:
1604 * @param this_obj: The MonoInternalThread on which to operate.
1606 * Gets the priority of the given thread.
1607 * @return: The priority of the given thread.
1610 ves_icall_System_Threading_Thread_GetPriority (MonoThread *this_obj)
1612 gint32 priority;
1613 MonoInternalThread *internal = this_obj->internal_thread;
1615 LOCK_THREAD (internal);
1616 priority = internal->priority;
1617 UNLOCK_THREAD (internal);
1619 return priority;
1623 * ves_icall_System_Threading_Thread_SetPriority_internal:
1624 * @param this_obj: The MonoInternalThread on which to operate.
1625 * @param priority: The priority to set.
1627 * Sets the priority of the given thread.
1629 void
1630 ves_icall_System_Threading_Thread_SetPriority (MonoThread *this_obj, int priority)
1632 MonoInternalThread *internal = this_obj->internal_thread;
1634 LOCK_THREAD (internal);
1635 internal->priority = priority;
1636 if (internal->thread_info != NULL)
1637 mono_thread_internal_set_priority (internal, priority);
1638 UNLOCK_THREAD (internal);
1641 /* If the array is already in the requested domain, we just return it,
1642 otherwise we return a copy in that domain. */
1643 static MonoArray*
1644 byte_array_to_domain (MonoArray *arr, MonoDomain *domain, MonoError *error)
1646 MonoArray *copy;
1648 error_init (error);
1649 if (!arr)
1650 return NULL;
1652 if (mono_object_domain (arr) == domain)
1653 return arr;
1655 copy = mono_array_new_checked (domain, mono_defaults.byte_class, arr->max_length, error);
1656 memmove (mono_array_addr (copy, guint8, 0), mono_array_addr (arr, guint8, 0), arr->max_length);
1657 return copy;
1660 MonoArray*
1661 ves_icall_System_Threading_Thread_ByteArrayToRootDomain (MonoArray *arr)
1663 MonoError error;
1664 MonoArray *result = byte_array_to_domain (arr, mono_get_root_domain (), &error);
1665 mono_error_set_pending_exception (&error);
1666 return result;
1669 MonoArray*
1670 ves_icall_System_Threading_Thread_ByteArrayToCurrentDomain (MonoArray *arr)
1672 MonoError error;
1673 MonoArray *result = byte_array_to_domain (arr, mono_domain_get (), &error);
1674 mono_error_set_pending_exception (&error);
1675 return result;
1678 MonoThread *
1679 mono_thread_current (void)
1681 MonoDomain *domain = mono_domain_get ();
1682 MonoInternalThread *internal = mono_thread_internal_current ();
1683 MonoThread **current_thread_ptr;
1685 g_assert (internal);
1686 current_thread_ptr = get_current_thread_ptr_for_domain (domain, internal);
1688 if (!*current_thread_ptr) {
1689 g_assert (domain != mono_get_root_domain ());
1690 *current_thread_ptr = create_thread_object (domain, internal);
1692 return *current_thread_ptr;
1695 /* Return the thread object belonging to INTERNAL in the current domain */
1696 static MonoThread *
1697 mono_thread_current_for_thread (MonoInternalThread *internal)
1699 MonoDomain *domain = mono_domain_get ();
1700 MonoThread **current_thread_ptr;
1702 g_assert (internal);
1703 current_thread_ptr = get_current_thread_ptr_for_domain (domain, internal);
1705 if (!*current_thread_ptr) {
1706 g_assert (domain != mono_get_root_domain ());
1707 *current_thread_ptr = create_thread_object (domain, internal);
1709 return *current_thread_ptr;
1712 MonoInternalThread*
1713 mono_thread_internal_current (void)
1715 MonoInternalThread *res = GET_CURRENT_OBJECT ();
1716 THREAD_DEBUG (g_message ("%s: returning %p", __func__, res));
1717 return res;
1720 static MonoThreadInfoWaitRet
1721 mono_join_uninterrupted (MonoThreadHandle* thread_to_join, gint32 ms, MonoError *error)
1723 MonoException *exc;
1724 MonoThreadInfoWaitRet ret;
1725 gint64 start;
1726 gint32 diff_ms;
1727 gint32 wait = ms;
1729 error_init (error);
1731 start = (ms == -1) ? 0 : mono_msec_ticks ();
1732 for (;;) {
1733 MONO_ENTER_GC_SAFE;
1734 ret = mono_thread_info_wait_one_handle (thread_to_join, ms, TRUE);
1735 MONO_EXIT_GC_SAFE;
1737 if (ret != MONO_THREAD_INFO_WAIT_RET_ALERTED)
1738 return ret;
1740 exc = mono_thread_execute_interruption ();
1741 if (exc) {
1742 mono_error_set_exception_instance (error, exc);
1743 return ret;
1746 if (ms == -1)
1747 continue;
1749 /* Re-calculate ms according to the time passed */
1750 diff_ms = (gint32)(mono_msec_ticks () - start);
1751 if (diff_ms >= ms) {
1752 ret = MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1753 return ret;
1755 wait = ms - diff_ms;
1758 return ret;
1761 gboolean
1762 ves_icall_System_Threading_Thread_Join_internal(MonoThread *this_obj, int ms)
1764 MonoInternalThread *thread = this_obj->internal_thread;
1765 MonoThreadHandle *handle = thread->handle;
1766 MonoInternalThread *cur_thread = mono_thread_internal_current ();
1767 gboolean ret;
1768 MonoError error;
1770 if (mono_thread_current_check_pending_interrupt ())
1771 return FALSE;
1773 LOCK_THREAD (thread);
1775 if ((thread->state & ThreadState_Unstarted) != 0) {
1776 UNLOCK_THREAD (thread);
1778 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started."));
1779 return FALSE;
1782 UNLOCK_THREAD (thread);
1784 if(ms== -1) {
1785 ms=MONO_INFINITE_WAIT;
1787 THREAD_DEBUG (g_message ("%s: joining thread handle %p, %d ms", __func__, handle, ms));
1789 mono_thread_set_state (cur_thread, ThreadState_WaitSleepJoin);
1791 ret=mono_join_uninterrupted (handle, ms, &error);
1793 mono_thread_clr_state (cur_thread, ThreadState_WaitSleepJoin);
1795 mono_error_set_pending_exception (&error);
1797 if(ret==MONO_THREAD_INFO_WAIT_RET_SUCCESS_0) {
1798 THREAD_DEBUG (g_message ("%s: join successful", __func__));
1800 return(TRUE);
1803 THREAD_DEBUG (g_message ("%s: join failed", __func__));
1805 return(FALSE);
1808 #define MANAGED_WAIT_FAILED 0x7fffffff
1810 static gint32
1811 map_native_wait_result_to_managed (MonoW32HandleWaitRet val, gsize numobjects)
1813 if (val >= MONO_W32HANDLE_WAIT_RET_SUCCESS_0 && val < MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + numobjects) {
1814 return WAIT_OBJECT_0 + (val - MONO_W32HANDLE_WAIT_RET_SUCCESS_0);
1815 } else if (val >= MONO_W32HANDLE_WAIT_RET_ABANDONED_0 && val < MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + numobjects) {
1816 return WAIT_ABANDONED_0 + (val - MONO_W32HANDLE_WAIT_RET_ABANDONED_0);
1817 } else if (val == MONO_W32HANDLE_WAIT_RET_ALERTED) {
1818 return WAIT_IO_COMPLETION;
1819 } else if (val == MONO_W32HANDLE_WAIT_RET_TIMEOUT) {
1820 return WAIT_TIMEOUT;
1821 } else if (val == MONO_W32HANDLE_WAIT_RET_FAILED) {
1822 /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
1823 return MANAGED_WAIT_FAILED;
1824 } else {
1825 g_error ("%s: unknown val value %d", __func__, val);
1829 static MonoW32HandleWaitRet
1830 mono_wait_uninterrupted (MonoInternalThread *thread, guint32 numhandles, gpointer *handles, gboolean waitall, gint32 ms, MonoError *error)
1832 MonoException *exc;
1833 MonoW32HandleWaitRet ret;
1834 gint64 start;
1835 gint32 diff_ms;
1836 gint32 wait = ms;
1838 error_init (error);
1840 start = (ms == -1) ? 0 : mono_100ns_ticks ();
1841 do {
1842 MONO_ENTER_GC_SAFE;
1843 #ifdef HOST_WIN32
1844 if (numhandles != 1)
1845 ret = mono_w32handle_convert_wait_ret (WaitForMultipleObjectsEx (numhandles, handles, waitall, wait, TRUE), numhandles);
1846 else
1847 ret = mono_w32handle_convert_wait_ret (WaitForSingleObjectEx (handles [0], ms, TRUE), 1);
1848 #else
1849 /* mono_w32handle_wait_multiple optimizes the case for numhandles == 1 */
1850 ret = mono_w32handle_wait_multiple (handles, numhandles, waitall, wait, TRUE);
1851 #endif /* HOST_WIN32 */
1852 MONO_EXIT_GC_SAFE;
1854 if (ret != MONO_W32HANDLE_WAIT_RET_ALERTED)
1855 break;
1857 exc = mono_thread_execute_interruption ();
1858 if (exc) {
1859 mono_error_set_exception_instance (error, exc);
1860 break;
1863 if (ms == -1)
1864 continue;
1866 /* Re-calculate ms according to the time passed */
1867 diff_ms = (gint32)((mono_100ns_ticks () - start) / 10000);
1868 if (diff_ms >= ms) {
1869 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1870 break;
1872 wait = ms - diff_ms;
1873 } while (TRUE);
1875 return ret;
1878 gint32 ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms)
1880 MonoError error;
1881 HANDLE *handles;
1882 guint32 numhandles;
1883 MonoW32HandleWaitRet ret;
1884 guint32 i;
1885 MonoObject *waitHandle;
1886 MonoInternalThread *thread = mono_thread_internal_current ();
1888 /* Do this WaitSleepJoin check before creating objects */
1889 if (mono_thread_current_check_pending_interrupt ())
1890 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
1892 /* We fail in managed if the array has more than 64 elements */
1893 numhandles = (guint32)mono_array_length(mono_handles);
1894 handles = g_new0(HANDLE, numhandles);
1896 for(i = 0; i < numhandles; i++) {
1897 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
1898 handles [i] = mono_wait_handle_get_handle ((MonoWaitHandle *) waitHandle);
1901 if(ms== -1) {
1902 ms=MONO_INFINITE_WAIT;
1905 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1907 ret = mono_wait_uninterrupted (thread, numhandles, handles, TRUE, ms, &error);
1909 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1911 g_free(handles);
1913 mono_error_set_pending_exception (&error);
1915 return map_native_wait_result_to_managed (ret, numhandles);
1918 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms)
1920 MonoError error;
1921 HANDLE handles [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
1922 uintptr_t numhandles;
1923 MonoW32HandleWaitRet ret;
1924 guint32 i;
1925 MonoObject *waitHandle;
1926 MonoInternalThread *thread = mono_thread_internal_current ();
1928 /* Do this WaitSleepJoin check before creating objects */
1929 if (mono_thread_current_check_pending_interrupt ())
1930 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
1932 numhandles = mono_array_length(mono_handles);
1933 if (numhandles > MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS)
1934 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
1936 for(i = 0; i < numhandles; i++) {
1937 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
1938 handles [i] = mono_wait_handle_get_handle ((MonoWaitHandle *) waitHandle);
1941 if(ms== -1) {
1942 ms=MONO_INFINITE_WAIT;
1945 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1947 ret = mono_wait_uninterrupted (thread, numhandles, handles, FALSE, ms, &error);
1949 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1951 THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") returning %d", __func__, mono_native_thread_id_get (), ret));
1953 mono_error_set_pending_exception (&error);
1955 return map_native_wait_result_to_managed (ret, numhandles);
1958 gint32 ves_icall_System_Threading_WaitHandle_WaitOne_internal(HANDLE handle, gint32 ms)
1960 MonoError error;
1961 MonoW32HandleWaitRet ret;
1962 MonoInternalThread *thread = mono_thread_internal_current ();
1964 THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") waiting for %p, %d ms", __func__, mono_native_thread_id_get (), handle, ms));
1966 if(ms== -1) {
1967 ms=MONO_INFINITE_WAIT;
1970 if (mono_thread_current_check_pending_interrupt ())
1971 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
1973 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1975 ret = mono_wait_uninterrupted (thread, 1, &handle, FALSE, ms, &error);
1977 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1979 mono_error_set_pending_exception (&error);
1980 return map_native_wait_result_to_managed (ret, 1);
1983 gint32
1984 ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (HANDLE toSignal, HANDLE toWait, gint32 ms)
1986 MonoW32HandleWaitRet ret;
1987 MonoInternalThread *thread = mono_thread_internal_current ();
1989 if (ms == -1)
1990 ms = MONO_INFINITE_WAIT;
1992 if (mono_thread_current_check_pending_interrupt ())
1993 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
1995 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1997 MONO_ENTER_GC_SAFE;
1998 #ifdef HOST_WIN32
1999 ret = mono_w32handle_convert_wait_ret (SignalObjectAndWait (toSignal, toWait, ms, TRUE), 1);
2000 #else
2001 ret = mono_w32handle_signal_and_wait (toSignal, toWait, ms, TRUE);
2002 #endif
2003 MONO_EXIT_GC_SAFE;
2005 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
2007 return map_native_wait_result_to_managed (ret, 1);
2010 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
2012 return InterlockedIncrement (location);
2015 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
2017 #if SIZEOF_VOID_P == 4
2018 if (G_UNLIKELY ((size_t)location & 0x7)) {
2019 gint64 ret;
2020 mono_interlocked_lock ();
2021 (*location)++;
2022 ret = *location;
2023 mono_interlocked_unlock ();
2024 return ret;
2026 #endif
2027 return InterlockedIncrement64 (location);
2030 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
2032 return InterlockedDecrement(location);
2035 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
2037 #if SIZEOF_VOID_P == 4
2038 if (G_UNLIKELY ((size_t)location & 0x7)) {
2039 gint64 ret;
2040 mono_interlocked_lock ();
2041 (*location)--;
2042 ret = *location;
2043 mono_interlocked_unlock ();
2044 return ret;
2046 #endif
2047 return InterlockedDecrement64 (location);
2050 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location, gint32 value)
2052 return InterlockedExchange(location, value);
2055 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location, MonoObject *value)
2057 MonoObject *res;
2058 res = (MonoObject *) InterlockedExchangePointer((gpointer *) location, value);
2059 mono_gc_wbarrier_generic_nostore (location);
2060 return res;
2063 gpointer ves_icall_System_Threading_Interlocked_Exchange_IntPtr (gpointer *location, gpointer value)
2065 return InterlockedExchangePointer(location, value);
2068 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location, gfloat value)
2070 IntFloatUnion val, ret;
2072 val.fval = value;
2073 ret.ival = InterlockedExchange((gint32 *) location, val.ival);
2075 return ret.fval;
2078 gint64
2079 ves_icall_System_Threading_Interlocked_Exchange_Long (gint64 *location, gint64 value)
2081 #if SIZEOF_VOID_P == 4
2082 if (G_UNLIKELY ((size_t)location & 0x7)) {
2083 gint64 ret;
2084 mono_interlocked_lock ();
2085 ret = *location;
2086 *location = value;
2087 mono_interlocked_unlock ();
2088 return ret;
2090 #endif
2091 return InterlockedExchange64 (location, value);
2094 gdouble
2095 ves_icall_System_Threading_Interlocked_Exchange_Double (gdouble *location, gdouble value)
2097 LongDoubleUnion val, ret;
2099 val.fval = value;
2100 ret.ival = (gint64)InterlockedExchange64((gint64 *) location, val.ival);
2102 return ret.fval;
2105 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location, gint32 value, gint32 comparand)
2107 return InterlockedCompareExchange(location, value, comparand);
2110 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int_Success(gint32 *location, gint32 value, gint32 comparand, MonoBoolean *success)
2112 gint32 r = InterlockedCompareExchange(location, value, comparand);
2113 *success = r == comparand;
2114 return r;
2117 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location, MonoObject *value, MonoObject *comparand)
2119 MonoObject *res;
2120 res = (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location, value, comparand);
2121 mono_gc_wbarrier_generic_nostore (location);
2122 return res;
2125 gpointer ves_icall_System_Threading_Interlocked_CompareExchange_IntPtr(gpointer *location, gpointer value, gpointer comparand)
2127 return InterlockedCompareExchangePointer(location, value, comparand);
2130 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location, gfloat value, gfloat comparand)
2132 IntFloatUnion val, ret, cmp;
2134 val.fval = value;
2135 cmp.fval = comparand;
2136 ret.ival = InterlockedCompareExchange((gint32 *) location, val.ival, cmp.ival);
2138 return ret.fval;
2141 gdouble
2142 ves_icall_System_Threading_Interlocked_CompareExchange_Double (gdouble *location, gdouble value, gdouble comparand)
2144 #if SIZEOF_VOID_P == 8
2145 LongDoubleUnion val, comp, ret;
2147 val.fval = value;
2148 comp.fval = comparand;
2149 ret.ival = (gint64)InterlockedCompareExchangePointer((gpointer *) location, (gpointer)val.ival, (gpointer)comp.ival);
2151 return ret.fval;
2152 #else
2153 gdouble old;
2155 mono_interlocked_lock ();
2156 old = *location;
2157 if (old == comparand)
2158 *location = value;
2159 mono_interlocked_unlock ();
2161 return old;
2162 #endif
2165 gint64
2166 ves_icall_System_Threading_Interlocked_CompareExchange_Long (gint64 *location, gint64 value, gint64 comparand)
2168 #if SIZEOF_VOID_P == 4
2169 if (G_UNLIKELY ((size_t)location & 0x7)) {
2170 gint64 old;
2171 mono_interlocked_lock ();
2172 old = *location;
2173 if (old == comparand)
2174 *location = value;
2175 mono_interlocked_unlock ();
2176 return old;
2178 #endif
2179 return InterlockedCompareExchange64 (location, value, comparand);
2182 MonoObject*
2183 ves_icall_System_Threading_Interlocked_CompareExchange_T (MonoObject **location, MonoObject *value, MonoObject *comparand)
2185 MonoObject *res;
2186 res = (MonoObject *)InterlockedCompareExchangePointer ((volatile gpointer *)location, value, comparand);
2187 mono_gc_wbarrier_generic_nostore (location);
2188 return res;
2191 MonoObject*
2192 ves_icall_System_Threading_Interlocked_Exchange_T (MonoObject **location, MonoObject *value)
2194 MonoObject *res;
2195 MONO_CHECK_NULL (location, NULL);
2196 res = (MonoObject *)InterlockedExchangePointer ((volatile gpointer *)location, value);
2197 mono_gc_wbarrier_generic_nostore (location);
2198 return res;
2201 gint32
2202 ves_icall_System_Threading_Interlocked_Add_Int (gint32 *location, gint32 value)
2204 return InterlockedAdd (location, value);
2207 gint64
2208 ves_icall_System_Threading_Interlocked_Add_Long (gint64 *location, gint64 value)
2210 #if SIZEOF_VOID_P == 4
2211 if (G_UNLIKELY ((size_t)location & 0x7)) {
2212 gint64 ret;
2213 mono_interlocked_lock ();
2214 *location += value;
2215 ret = *location;
2216 mono_interlocked_unlock ();
2217 return ret;
2219 #endif
2220 return InterlockedAdd64 (location, value);
2223 gint64
2224 ves_icall_System_Threading_Interlocked_Read_Long (gint64 *location)
2226 #if SIZEOF_VOID_P == 4
2227 if (G_UNLIKELY ((size_t)location & 0x7)) {
2228 gint64 ret;
2229 mono_interlocked_lock ();
2230 ret = *location;
2231 mono_interlocked_unlock ();
2232 return ret;
2234 #endif
2235 return InterlockedRead64 (location);
2238 void
2239 ves_icall_System_Threading_Thread_MemoryBarrier (void)
2241 mono_memory_barrier ();
2244 void
2245 ves_icall_System_Threading_Thread_ClrState (MonoInternalThread* this_obj, guint32 state)
2247 mono_thread_clr_state (this_obj, (MonoThreadState)state);
2249 if (state & ThreadState_Background) {
2250 /* If the thread changes the background mode, the main thread has to
2251 * be notified, since it has to rebuild the list of threads to
2252 * wait for.
2254 mono_os_event_set (&background_change_event);
2258 void
2259 ves_icall_System_Threading_Thread_SetState (MonoInternalThread* this_obj, guint32 state)
2261 mono_thread_set_state (this_obj, (MonoThreadState)state);
2263 if (state & ThreadState_Background) {
2264 /* If the thread changes the background mode, the main thread has to
2265 * be notified, since it has to rebuild the list of threads to
2266 * wait for.
2268 mono_os_event_set (&background_change_event);
2272 guint32
2273 ves_icall_System_Threading_Thread_GetState (MonoInternalThread* this_obj)
2275 guint32 state;
2277 LOCK_THREAD (this_obj);
2279 state = this_obj->state;
2281 UNLOCK_THREAD (this_obj);
2283 return state;
2286 void ves_icall_System_Threading_Thread_Interrupt_internal (MonoThread *this_obj)
2288 MonoInternalThread *current;
2289 gboolean throw_;
2290 MonoInternalThread *thread = this_obj->internal_thread;
2292 LOCK_THREAD (thread);
2294 current = mono_thread_internal_current ();
2296 thread->thread_interrupt_requested = TRUE;
2297 throw_ = current != thread && (thread->state & ThreadState_WaitSleepJoin);
2299 UNLOCK_THREAD (thread);
2301 if (throw_) {
2302 async_abort_internal (thread, FALSE);
2307 * mono_thread_current_check_pending_interrupt:
2309 * Checks if there's a interruption request and set the pending exception if so.
2311 * @returns true if a pending exception was set
2313 gboolean
2314 mono_thread_current_check_pending_interrupt (void)
2316 MonoInternalThread *thread = mono_thread_internal_current ();
2317 gboolean throw_ = FALSE;
2319 LOCK_THREAD (thread);
2321 if (thread->thread_interrupt_requested) {
2322 throw_ = TRUE;
2323 thread->thread_interrupt_requested = FALSE;
2326 UNLOCK_THREAD (thread);
2328 if (throw_)
2329 mono_set_pending_exception (mono_get_exception_thread_interrupted ());
2330 return throw_;
2333 static gboolean
2334 request_thread_abort (MonoInternalThread *thread, MonoObject *state)
2336 LOCK_THREAD (thread);
2338 if (thread->state & (ThreadState_AbortRequested | ThreadState_Stopped))
2340 UNLOCK_THREAD (thread);
2341 return FALSE;
2344 if ((thread->state & ThreadState_Unstarted) != 0) {
2345 thread->state |= ThreadState_Aborted;
2346 UNLOCK_THREAD (thread);
2347 return FALSE;
2350 thread->state |= ThreadState_AbortRequested;
2351 if (thread->abort_state_handle)
2352 mono_gchandle_free (thread->abort_state_handle);
2353 if (state) {
2354 thread->abort_state_handle = mono_gchandle_new (state, FALSE);
2355 g_assert (thread->abort_state_handle);
2356 } else {
2357 thread->abort_state_handle = 0;
2359 thread->abort_exc = NULL;
2361 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));
2363 /* During shutdown, we can't wait for other threads */
2364 if (!shutting_down)
2365 /* Make sure the thread is awake */
2366 mono_thread_resume (thread);
2368 UNLOCK_THREAD (thread);
2369 return TRUE;
2372 void
2373 ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject *state)
2375 if (!request_thread_abort (thread, state))
2376 return;
2378 if (thread == mono_thread_internal_current ()) {
2379 MonoError error;
2380 self_abort_internal (&error);
2381 mono_error_set_pending_exception (&error);
2382 } else {
2383 async_abort_internal (thread, TRUE);
2388 * mono_thread_internal_abort:
2390 * Request thread @thread to be aborted.
2392 * @thread MUST NOT be the current thread.
2394 void
2395 mono_thread_internal_abort (MonoInternalThread *thread)
2397 g_assert (thread != mono_thread_internal_current ());
2399 if (!request_thread_abort (thread, NULL))
2400 return;
2401 async_abort_internal (thread, TRUE);
2404 void
2405 ves_icall_System_Threading_Thread_ResetAbort (MonoThread *this_obj)
2407 MonoInternalThread *thread = mono_thread_internal_current ();
2408 gboolean was_aborting;
2410 LOCK_THREAD (thread);
2411 was_aborting = thread->state & ThreadState_AbortRequested;
2412 thread->state &= ~ThreadState_AbortRequested;
2413 UNLOCK_THREAD (thread);
2415 if (!was_aborting) {
2416 const char *msg = "Unable to reset abort because no abort was requested";
2417 mono_set_pending_exception (mono_get_exception_thread_state (msg));
2418 return;
2421 mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
2422 thread->abort_exc = NULL;
2423 if (thread->abort_state_handle) {
2424 mono_gchandle_free (thread->abort_state_handle);
2425 /* This is actually not necessary - the handle
2426 only counts if the exception is set */
2427 thread->abort_state_handle = 0;
2431 void
2432 mono_thread_internal_reset_abort (MonoInternalThread *thread)
2434 LOCK_THREAD (thread);
2436 thread->state &= ~ThreadState_AbortRequested;
2438 if (thread->abort_exc) {
2439 mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
2440 thread->abort_exc = NULL;
2441 if (thread->abort_state_handle) {
2442 mono_gchandle_free (thread->abort_state_handle);
2443 /* This is actually not necessary - the handle
2444 only counts if the exception is set */
2445 thread->abort_state_handle = 0;
2449 UNLOCK_THREAD (thread);
2452 MonoObject*
2453 ves_icall_System_Threading_Thread_GetAbortExceptionState (MonoThread *this_obj)
2455 MonoError error;
2456 MonoInternalThread *thread = this_obj->internal_thread;
2457 MonoObject *state, *deserialized = NULL;
2458 MonoDomain *domain;
2460 if (!thread->abort_state_handle)
2461 return NULL;
2463 state = mono_gchandle_get_target (thread->abort_state_handle);
2464 g_assert (state);
2466 domain = mono_domain_get ();
2467 if (mono_object_domain (state) == domain)
2468 return state;
2470 deserialized = mono_object_xdomain_representation (state, domain, &error);
2472 if (!deserialized) {
2473 MonoException *invalid_op_exc = mono_get_exception_invalid_operation ("Thread.ExceptionState cannot access an ExceptionState from a different AppDomain");
2474 if (!is_ok (&error)) {
2475 MonoObject *exc = (MonoObject*)mono_error_convert_to_exception (&error);
2476 MONO_OBJECT_SETREF (invalid_op_exc, inner_ex, exc);
2478 mono_set_pending_exception (invalid_op_exc);
2479 return NULL;
2482 return deserialized;
2485 static gboolean
2486 mono_thread_suspend (MonoInternalThread *thread)
2488 LOCK_THREAD (thread);
2490 if (thread->state & (ThreadState_Unstarted | ThreadState_Aborted | ThreadState_Stopped))
2492 UNLOCK_THREAD (thread);
2493 return FALSE;
2496 if (thread->state & (ThreadState_Suspended | ThreadState_SuspendRequested | ThreadState_AbortRequested))
2498 UNLOCK_THREAD (thread);
2499 return TRUE;
2502 thread->state |= ThreadState_SuspendRequested;
2503 mono_os_event_reset (thread->suspended);
2505 if (thread == mono_thread_internal_current ()) {
2506 /* calls UNLOCK_THREAD (thread) */
2507 self_suspend_internal ();
2508 } else {
2509 /* calls UNLOCK_THREAD (thread) */
2510 async_suspend_internal (thread, FALSE);
2513 return TRUE;
2516 void
2517 ves_icall_System_Threading_Thread_Suspend (MonoThread *this_obj)
2519 if (!mono_thread_suspend (this_obj->internal_thread)) {
2520 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
2521 return;
2525 /* LOCKING: LOCK_THREAD(thread) must be held */
2526 static gboolean
2527 mono_thread_resume (MonoInternalThread *thread)
2529 if ((thread->state & ThreadState_SuspendRequested) != 0) {
2530 // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (1) thread %p\n", thread_get_tid (thread));
2531 thread->state &= ~ThreadState_SuspendRequested;
2532 mono_os_event_set (thread->suspended);
2533 return TRUE;
2536 if ((thread->state & ThreadState_Suspended) == 0 ||
2537 (thread->state & ThreadState_Unstarted) != 0 ||
2538 (thread->state & ThreadState_Aborted) != 0 ||
2539 (thread->state & ThreadState_Stopped) != 0)
2541 // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (2) thread %p\n", thread_get_tid (thread));
2542 return FALSE;
2545 // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (3) thread %p\n", thread_get_tid (thread));
2547 mono_os_event_set (thread->suspended);
2549 if (!thread->self_suspended) {
2550 UNLOCK_THREAD (thread);
2552 /* Awake the thread */
2553 if (!mono_thread_info_resume (thread_get_tid (thread)))
2554 return FALSE;
2556 LOCK_THREAD (thread);
2559 thread->state &= ~ThreadState_Suspended;
2561 return TRUE;
2564 void
2565 ves_icall_System_Threading_Thread_Resume (MonoThread *thread)
2567 if (!thread->internal_thread) {
2568 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
2569 } else {
2570 LOCK_THREAD (thread->internal_thread);
2571 if (!mono_thread_resume (thread->internal_thread))
2572 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
2573 UNLOCK_THREAD (thread->internal_thread);
2577 static gboolean
2578 mono_threads_is_critical_method (MonoMethod *method)
2580 switch (method->wrapper_type) {
2581 case MONO_WRAPPER_RUNTIME_INVOKE:
2582 case MONO_WRAPPER_XDOMAIN_INVOKE:
2583 case MONO_WRAPPER_XDOMAIN_DISPATCH:
2584 return TRUE;
2586 return FALSE;
2589 static gboolean
2590 find_wrapper (MonoMethod *m, gint no, gint ilo, gboolean managed, gpointer data)
2592 if (managed)
2593 return TRUE;
2595 if (mono_threads_is_critical_method (m)) {
2596 *((gboolean*)data) = TRUE;
2597 return TRUE;
2599 return FALSE;
2602 static gboolean
2603 is_running_protected_wrapper (void)
2605 gboolean found = FALSE;
2606 mono_stack_walk (find_wrapper, &found);
2607 return found;
2610 void
2611 mono_thread_stop (MonoThread *thread)
2613 MonoInternalThread *internal = thread->internal_thread;
2615 if (!request_thread_abort (internal, NULL))
2616 return;
2618 if (internal == mono_thread_internal_current ()) {
2619 MonoError error;
2620 self_abort_internal (&error);
2622 This function is part of the embeding API and has no way to return the exception
2623 to be thrown. So what we do is keep the old behavior and raise the exception.
2625 mono_error_raise_exception (&error); /* OK to throw, see note */
2626 } else {
2627 async_abort_internal (internal, TRUE);
2631 gint8
2632 ves_icall_System_Threading_Thread_VolatileRead1 (void *ptr)
2634 gint8 tmp = *(volatile gint8 *)ptr;
2635 mono_memory_barrier ();
2636 return tmp;
2639 gint16
2640 ves_icall_System_Threading_Thread_VolatileRead2 (void *ptr)
2642 gint16 tmp = *(volatile gint16 *)ptr;
2643 mono_memory_barrier ();
2644 return tmp;
2647 gint32
2648 ves_icall_System_Threading_Thread_VolatileRead4 (void *ptr)
2650 gint32 tmp = *(volatile gint32 *)ptr;
2651 mono_memory_barrier ();
2652 return tmp;
2655 gint64
2656 ves_icall_System_Threading_Thread_VolatileRead8 (void *ptr)
2658 gint64 tmp = *(volatile gint64 *)ptr;
2659 mono_memory_barrier ();
2660 return tmp;
2663 void *
2664 ves_icall_System_Threading_Thread_VolatileReadIntPtr (void *ptr)
2666 volatile void *tmp = *(volatile void **)ptr;
2667 mono_memory_barrier ();
2668 return (void *) tmp;
2671 void *
2672 ves_icall_System_Threading_Thread_VolatileReadObject (void *ptr)
2674 volatile MonoObject *tmp = *(volatile MonoObject **)ptr;
2675 mono_memory_barrier ();
2676 return (MonoObject *) tmp;
2679 double
2680 ves_icall_System_Threading_Thread_VolatileReadDouble (void *ptr)
2682 double tmp = *(volatile double *)ptr;
2683 mono_memory_barrier ();
2684 return tmp;
2687 float
2688 ves_icall_System_Threading_Thread_VolatileReadFloat (void *ptr)
2690 float tmp = *(volatile float *)ptr;
2691 mono_memory_barrier ();
2692 return tmp;
2695 gint8
2696 ves_icall_System_Threading_Volatile_Read1 (void *ptr)
2698 return InterlockedRead8 ((volatile gint8 *)ptr);
2701 gint16
2702 ves_icall_System_Threading_Volatile_Read2 (void *ptr)
2704 return InterlockedRead16 ((volatile gint16 *)ptr);
2707 gint32
2708 ves_icall_System_Threading_Volatile_Read4 (void *ptr)
2710 return InterlockedRead ((volatile gint32 *)ptr);
2713 gint64
2714 ves_icall_System_Threading_Volatile_Read8 (void *ptr)
2716 #if SIZEOF_VOID_P == 4
2717 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2718 gint64 val;
2719 mono_interlocked_lock ();
2720 val = *(gint64*)ptr;
2721 mono_interlocked_unlock ();
2722 return val;
2724 #endif
2725 return InterlockedRead64 ((volatile gint64 *)ptr);
2728 void *
2729 ves_icall_System_Threading_Volatile_ReadIntPtr (void *ptr)
2731 return InterlockedReadPointer ((volatile gpointer *)ptr);
2734 double
2735 ves_icall_System_Threading_Volatile_ReadDouble (void *ptr)
2737 LongDoubleUnion u;
2739 #if SIZEOF_VOID_P == 4
2740 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2741 double val;
2742 mono_interlocked_lock ();
2743 val = *(double*)ptr;
2744 mono_interlocked_unlock ();
2745 return val;
2747 #endif
2749 u.ival = InterlockedRead64 ((volatile gint64 *)ptr);
2751 return u.fval;
2754 float
2755 ves_icall_System_Threading_Volatile_ReadFloat (void *ptr)
2757 IntFloatUnion u;
2759 u.ival = InterlockedRead ((volatile gint32 *)ptr);
2761 return u.fval;
2764 MonoObject*
2765 ves_icall_System_Threading_Volatile_Read_T (void *ptr)
2767 return (MonoObject *)InterlockedReadPointer ((volatile gpointer *)ptr);
2770 void
2771 ves_icall_System_Threading_Thread_VolatileWrite1 (void *ptr, gint8 value)
2773 mono_memory_barrier ();
2774 *(volatile gint8 *)ptr = value;
2777 void
2778 ves_icall_System_Threading_Thread_VolatileWrite2 (void *ptr, gint16 value)
2780 mono_memory_barrier ();
2781 *(volatile gint16 *)ptr = value;
2784 void
2785 ves_icall_System_Threading_Thread_VolatileWrite4 (void *ptr, gint32 value)
2787 mono_memory_barrier ();
2788 *(volatile gint32 *)ptr = value;
2791 void
2792 ves_icall_System_Threading_Thread_VolatileWrite8 (void *ptr, gint64 value)
2794 mono_memory_barrier ();
2795 *(volatile gint64 *)ptr = value;
2798 void
2799 ves_icall_System_Threading_Thread_VolatileWriteIntPtr (void *ptr, void *value)
2801 mono_memory_barrier ();
2802 *(volatile void **)ptr = value;
2805 void
2806 ves_icall_System_Threading_Thread_VolatileWriteObject (void *ptr, MonoObject *value)
2808 mono_memory_barrier ();
2809 mono_gc_wbarrier_generic_store (ptr, value);
2812 void
2813 ves_icall_System_Threading_Thread_VolatileWriteDouble (void *ptr, double value)
2815 mono_memory_barrier ();
2816 *(volatile double *)ptr = value;
2819 void
2820 ves_icall_System_Threading_Thread_VolatileWriteFloat (void *ptr, float value)
2822 mono_memory_barrier ();
2823 *(volatile float *)ptr = value;
2826 void
2827 ves_icall_System_Threading_Volatile_Write1 (void *ptr, gint8 value)
2829 InterlockedWrite8 ((volatile gint8 *)ptr, value);
2832 void
2833 ves_icall_System_Threading_Volatile_Write2 (void *ptr, gint16 value)
2835 InterlockedWrite16 ((volatile gint16 *)ptr, value);
2838 void
2839 ves_icall_System_Threading_Volatile_Write4 (void *ptr, gint32 value)
2841 InterlockedWrite ((volatile gint32 *)ptr, value);
2844 void
2845 ves_icall_System_Threading_Volatile_Write8 (void *ptr, gint64 value)
2847 #if SIZEOF_VOID_P == 4
2848 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2849 mono_interlocked_lock ();
2850 *(gint64*)ptr = value;
2851 mono_interlocked_unlock ();
2852 return;
2854 #endif
2856 InterlockedWrite64 ((volatile gint64 *)ptr, value);
2859 void
2860 ves_icall_System_Threading_Volatile_WriteIntPtr (void *ptr, void *value)
2862 InterlockedWritePointer ((volatile gpointer *)ptr, value);
2865 void
2866 ves_icall_System_Threading_Volatile_WriteDouble (void *ptr, double value)
2868 LongDoubleUnion u;
2870 #if SIZEOF_VOID_P == 4
2871 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2872 mono_interlocked_lock ();
2873 *(double*)ptr = value;
2874 mono_interlocked_unlock ();
2875 return;
2877 #endif
2879 u.fval = value;
2881 InterlockedWrite64 ((volatile gint64 *)ptr, u.ival);
2884 void
2885 ves_icall_System_Threading_Volatile_WriteFloat (void *ptr, float value)
2887 IntFloatUnion u;
2889 u.fval = value;
2891 InterlockedWrite ((volatile gint32 *)ptr, u.ival);
2894 void
2895 ves_icall_System_Threading_Volatile_Write_T (void *ptr, MonoObject *value)
2897 mono_gc_wbarrier_generic_store_atomic (ptr, value);
2900 static void
2901 free_context (void *user_data)
2903 ContextStaticData *data = user_data;
2905 mono_threads_lock ();
2908 * There is no guarantee that, by the point this reference queue callback
2909 * has been invoked, the GC handle associated with the object will fail to
2910 * resolve as one might expect. So if we don't free and remove the GC
2911 * handle here, free_context_static_data_helper () could end up resolving
2912 * a GC handle to an actually-dead context which would contain a pointer
2913 * to an already-freed static data segment, resulting in a crash when
2914 * accessing it.
2916 g_hash_table_remove (contexts, GUINT_TO_POINTER (data->gc_handle));
2918 mono_threads_unlock ();
2920 mono_gchandle_free (data->gc_handle);
2921 mono_free_static_data (data->static_data);
2922 g_free (data);
2925 void
2926 ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppContext *ctx)
2928 mono_threads_lock ();
2930 //g_print ("Registering context %d in domain %d\n", ctx->context_id, ctx->domain_id);
2932 if (!contexts)
2933 contexts = g_hash_table_new (NULL, NULL);
2935 if (!context_queue)
2936 context_queue = mono_gc_reference_queue_new (free_context);
2938 gpointer gch = GUINT_TO_POINTER (mono_gchandle_new_weakref (&ctx->obj, FALSE));
2939 g_hash_table_insert (contexts, gch, gch);
2942 * We use this intermediate structure to contain a duplicate pointer to
2943 * the static data because we can't rely on being able to resolve the GC
2944 * handle in the reference queue callback.
2946 ContextStaticData *data = g_new0 (ContextStaticData, 1);
2947 data->gc_handle = GPOINTER_TO_UINT (gch);
2948 ctx->data = data;
2950 context_adjust_static_data (ctx);
2951 mono_gc_reference_queue_add (context_queue, &ctx->obj, data);
2953 mono_threads_unlock ();
2955 mono_profiler_context_loaded (ctx);
2958 void
2959 ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContext *ctx)
2962 * NOTE: Since finalizers are unreliable for the purposes of ensuring
2963 * cleanup in exceptional circumstances, we don't actually do any
2964 * cleanup work here. We instead do this via a reference queue.
2967 //g_print ("Releasing context %d in domain %d\n", ctx->context_id, ctx->domain_id);
2969 mono_profiler_context_unloaded (ctx);
2972 void mono_thread_init (MonoThreadStartCB start_cb,
2973 MonoThreadAttachCB attach_cb)
2975 mono_coop_mutex_init_recursive (&threads_mutex);
2977 mono_os_mutex_init_recursive(&interlocked_mutex);
2978 mono_os_mutex_init_recursive(&joinable_threads_mutex);
2980 mono_os_event_init (&background_change_event, FALSE);
2982 mono_init_static_data_info (&thread_static_info);
2983 mono_init_static_data_info (&context_static_info);
2985 mono_thread_start_cb = start_cb;
2986 mono_thread_attach_cb = attach_cb;
2989 void mono_thread_cleanup (void)
2991 #if !defined(RUN_IN_SUBTHREAD) && !defined(HOST_WIN32)
2992 /* The main thread must abandon any held mutexes (particularly
2993 * important for named mutexes as they are shared across
2994 * processes, see bug 74680.) This will happen when the
2995 * thread exits, but if it's not running in a subthread it
2996 * won't exit in time.
2998 mono_w32mutex_abandon ();
2999 #endif
3001 #if 0
3002 /* This stuff needs more testing, it seems one of these
3003 * critical sections can be locked when mono_thread_cleanup is
3004 * called.
3006 mono_coop_mutex_destroy (&threads_mutex);
3007 mono_os_mutex_destroy (&interlocked_mutex);
3008 mono_os_mutex_destroy (&delayed_free_table_mutex);
3009 mono_os_mutex_destroy (&small_id_mutex);
3010 mono_os_event_destroy (&background_change_event);
3011 #endif
3014 void
3015 mono_threads_install_cleanup (MonoThreadCleanupFunc func)
3017 mono_thread_cleanup_fn = func;
3020 void
3021 mono_thread_set_manage_callback (MonoThread *thread, MonoThreadManageCallback func)
3023 thread->internal_thread->manage_callback = func;
3026 G_GNUC_UNUSED
3027 static void print_tids (gpointer key, gpointer value, gpointer user)
3029 /* GPOINTER_TO_UINT breaks horribly if sizeof(void *) >
3030 * sizeof(uint) and a cast to uint would overflow
3032 /* Older versions of glib don't have G_GSIZE_FORMAT, so just
3033 * print this as a pointer.
3035 g_message ("Waiting for: %p", key);
3038 struct wait_data
3040 MonoThreadHandle *handles[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
3041 MonoInternalThread *threads[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
3042 guint32 num;
3045 static void
3046 wait_for_tids (struct wait_data *wait, guint32 timeout, gboolean check_state_change)
3048 guint32 i;
3049 MonoThreadInfoWaitRet ret;
3051 THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num));
3053 /* Add the thread state change event, so it wakes
3054 * up if a thread changes to background mode. */
3056 MONO_ENTER_GC_SAFE;
3057 if (check_state_change)
3058 ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, &background_change_event, FALSE, timeout, TRUE);
3059 else
3060 ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, NULL, TRUE, timeout, TRUE);
3061 MONO_EXIT_GC_SAFE;
3063 if (ret == MONO_THREAD_INFO_WAIT_RET_FAILED) {
3064 /* See the comment in build_wait_tids() */
3065 THREAD_DEBUG (g_message ("%s: Wait failed", __func__));
3066 return;
3069 for( i = 0; i < wait->num; i++)
3070 mono_threads_close_thread_handle (wait->handles [i]);
3072 if (ret == MONO_THREAD_INFO_WAIT_RET_TIMEOUT)
3073 return;
3075 if (ret < wait->num) {
3076 MonoInternalThread *internal;
3078 internal = wait->threads [ret];
3080 mono_threads_lock ();
3081 if (mono_g_hash_table_lookup (threads, (gpointer) internal->tid) == internal)
3082 g_error ("%s: failed to call mono_thread_detach_internal on thread %p, InternalThread: %p", __func__, internal->tid, internal);
3083 mono_threads_unlock ();
3087 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
3089 struct wait_data *wait=(struct wait_data *)user;
3091 if(wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS - 1) {
3092 MonoInternalThread *thread=(MonoInternalThread *)value;
3094 /* Ignore background threads, we abort them later */
3095 /* Do not lock here since it is not needed and the caller holds threads_lock */
3096 if (thread->state & ThreadState_Background) {
3097 THREAD_DEBUG (g_message ("%s: ignoring background thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3098 return; /* just leave, ignore */
3101 if (mono_gc_is_finalizer_internal_thread (thread)) {
3102 THREAD_DEBUG (g_message ("%s: ignoring finalizer thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3103 return;
3106 if (thread == mono_thread_internal_current ()) {
3107 THREAD_DEBUG (g_message ("%s: ignoring current thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3108 return;
3111 if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread)) {
3112 THREAD_DEBUG (g_message ("%s: ignoring main thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3113 return;
3116 if (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE) {
3117 THREAD_DEBUG (g_message ("%s: ignoring thread %" G_GSIZE_FORMAT "with DONT_MANAGE flag set.", __func__, (gsize)thread->tid));
3118 return;
3121 THREAD_DEBUG (g_message ("%s: Invoking mono_thread_manage callback on thread %p", __func__, thread));
3122 if ((thread->manage_callback == NULL) || (thread->manage_callback (thread->root_domain_thread) == TRUE)) {
3123 wait->handles[wait->num]=mono_threads_open_thread_handle (thread->handle);
3124 wait->threads[wait->num]=thread;
3125 wait->num++;
3127 THREAD_DEBUG (g_message ("%s: adding thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3128 } else {
3129 THREAD_DEBUG (g_message ("%s: ignoring (because of callback) thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3133 } else {
3134 /* Just ignore the rest, we can't do anything with
3135 * them yet
3140 static gboolean
3141 remove_and_abort_threads (gpointer key, gpointer value, gpointer user)
3143 struct wait_data *wait=(struct wait_data *)user;
3144 MonoNativeThreadId self = mono_native_thread_id_get ();
3145 MonoInternalThread *thread = (MonoInternalThread *)value;
3147 if (wait->num >= MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS)
3148 return FALSE;
3150 if (mono_native_thread_id_equals (thread_get_tid (thread), self))
3151 return FALSE;
3152 if (mono_gc_is_finalizer_internal_thread (thread))
3153 return FALSE;
3155 if ((thread->state & ThreadState_Background) && !(thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)) {
3156 wait->handles[wait->num] = mono_threads_open_thread_handle (thread->handle);
3157 wait->threads[wait->num] = thread;
3158 wait->num++;
3160 THREAD_DEBUG (g_print ("%s: Aborting id: %"G_GSIZE_FORMAT"\n", __func__, (gsize)thread->tid));
3161 mono_thread_internal_abort (thread);
3164 return TRUE;
3167 /**
3168 * mono_threads_set_shutting_down:
3170 * Is called by a thread that wants to shut down Mono. If the runtime is already
3171 * shutting down, the calling thread is suspended/stopped, and this function never
3172 * returns.
3174 void
3175 mono_threads_set_shutting_down (void)
3177 MonoInternalThread *current_thread = mono_thread_internal_current ();
3179 mono_threads_lock ();
3181 if (shutting_down) {
3182 mono_threads_unlock ();
3184 /* Make sure we're properly suspended/stopped */
3186 LOCK_THREAD (current_thread);
3188 if (current_thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
3189 UNLOCK_THREAD (current_thread);
3190 mono_thread_execute_interruption ();
3191 } else {
3192 UNLOCK_THREAD (current_thread);
3195 /*since we're killing the thread, detach it.*/
3196 mono_thread_detach_internal (current_thread);
3198 /* Wake up other threads potentially waiting for us */
3199 mono_thread_info_exit (0);
3200 } else {
3201 shutting_down = TRUE;
3203 /* Not really a background state change, but this will
3204 * interrupt the main thread if it is waiting for all
3205 * the other threads.
3207 mono_os_event_set (&background_change_event);
3209 mono_threads_unlock ();
3213 void mono_thread_manage (void)
3215 struct wait_data wait_data;
3216 struct wait_data *wait = &wait_data;
3218 memset (wait, 0, sizeof (struct wait_data));
3219 /* join each thread that's still running */
3220 THREAD_DEBUG (g_message ("%s: Joining each running thread...", __func__));
3222 mono_threads_lock ();
3223 if(threads==NULL) {
3224 THREAD_DEBUG (g_message("%s: No threads", __func__));
3225 mono_threads_unlock ();
3226 return;
3228 mono_threads_unlock ();
3230 do {
3231 mono_threads_lock ();
3232 if (shutting_down) {
3233 /* somebody else is shutting down */
3234 mono_threads_unlock ();
3235 break;
3237 THREAD_DEBUG (g_message ("%s: There are %d threads to join", __func__, mono_g_hash_table_size (threads));
3238 mono_g_hash_table_foreach (threads, print_tids, NULL));
3240 mono_os_event_reset (&background_change_event);
3241 wait->num=0;
3242 /* We must zero all InternalThread pointers to avoid making the GC unhappy. */
3243 memset (wait->threads, 0, MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
3244 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
3245 mono_threads_unlock ();
3246 if (wait->num > 0)
3247 /* Something to wait for */
3248 wait_for_tids (wait, MONO_INFINITE_WAIT, TRUE);
3249 THREAD_DEBUG (g_message ("%s: I have %d threads after waiting.", __func__, wait->num));
3250 } while(wait->num>0);
3252 /* Mono is shutting down, so just wait for the end */
3253 if (!mono_runtime_try_shutdown ()) {
3254 /*FIXME mono_thread_suspend probably should call mono_thread_execute_interruption when self interrupting. */
3255 mono_thread_suspend (mono_thread_internal_current ());
3256 mono_thread_execute_interruption ();
3260 * Remove everything but the finalizer thread and self.
3261 * Also abort all the background threads
3262 * */
3263 do {
3264 mono_threads_lock ();
3266 wait->num = 0;
3267 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
3268 memset (wait->threads, 0, MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
3269 mono_g_hash_table_foreach_remove (threads, remove_and_abort_threads, wait);
3271 mono_threads_unlock ();
3273 THREAD_DEBUG (g_message ("%s: wait->num is now %d", __func__, wait->num));
3274 if (wait->num > 0) {
3275 /* Something to wait for */
3276 wait_for_tids (wait, MONO_INFINITE_WAIT, FALSE);
3278 } while (wait->num > 0);
3281 * give the subthreads a chance to really quit (this is mainly needed
3282 * to get correct user and system times from getrusage/wait/time(1)).
3283 * This could be removed if we avoid pthread_detach() and use pthread_join().
3285 mono_thread_info_yield ();
3288 static void
3289 collect_threads_for_suspend (gpointer key, gpointer value, gpointer user_data)
3291 MonoInternalThread *thread = (MonoInternalThread*)value;
3292 struct wait_data *wait = (struct wait_data*)user_data;
3295 * We try to exclude threads early, to avoid running into the MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
3296 * limitation.
3297 * This needs no locking.
3299 if ((thread->state & ThreadState_Suspended) != 0 ||
3300 (thread->state & ThreadState_Stopped) != 0)
3301 return;
3303 if (wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
3304 wait->handles [wait->num] = mono_threads_open_thread_handle (thread->handle);
3305 wait->threads [wait->num] = thread;
3306 wait->num++;
3311 * mono_thread_suspend_all_other_threads:
3313 * Suspend all managed threads except the finalizer thread and this thread. It is
3314 * not possible to resume them later.
3316 void mono_thread_suspend_all_other_threads (void)
3318 struct wait_data wait_data;
3319 struct wait_data *wait = &wait_data;
3320 int i;
3321 MonoNativeThreadId self = mono_native_thread_id_get ();
3322 guint32 eventidx = 0;
3323 gboolean starting, finished;
3325 memset (wait, 0, sizeof (struct wait_data));
3327 * The other threads could be in an arbitrary state at this point, i.e.
3328 * they could be starting up, shutting down etc. This means that there could be
3329 * threads which are not even in the threads hash table yet.
3333 * First we set a barrier which will be checked by all threads before they
3334 * are added to the threads hash table, and they will exit if the flag is set.
3335 * This ensures that no threads could be added to the hash later.
3336 * We will use shutting_down as the barrier for now.
3338 g_assert (shutting_down);
3341 * We make multiple calls to WaitForMultipleObjects since:
3342 * - we can only wait for MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS threads
3343 * - some threads could exit without becoming suspended
3345 finished = FALSE;
3346 while (!finished) {
3348 * Make a copy of the hashtable since we can't do anything with
3349 * threads while threads_mutex is held.
3351 wait->num = 0;
3352 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
3353 memset (wait->threads, 0, MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
3354 mono_threads_lock ();
3355 mono_g_hash_table_foreach (threads, collect_threads_for_suspend, wait);
3356 mono_threads_unlock ();
3358 eventidx = 0;
3359 /* Get the suspended events that we'll be waiting for */
3360 for (i = 0; i < wait->num; ++i) {
3361 MonoInternalThread *thread = wait->threads [i];
3363 if (mono_native_thread_id_equals (thread_get_tid (thread), self)
3364 || mono_gc_is_finalizer_internal_thread (thread)
3365 || (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)
3367 mono_threads_close_thread_handle (wait->handles [i]);
3368 wait->threads [i] = NULL;
3369 continue;
3372 LOCK_THREAD (thread);
3374 if (thread->state & (ThreadState_Suspended | ThreadState_Stopped)) {
3375 UNLOCK_THREAD (thread);
3376 mono_threads_close_thread_handle (wait->handles [i]);
3377 wait->threads [i] = NULL;
3378 continue;
3381 ++eventidx;
3383 /* Convert abort requests into suspend requests */
3384 if ((thread->state & ThreadState_AbortRequested) != 0)
3385 thread->state &= ~ThreadState_AbortRequested;
3387 thread->state |= ThreadState_SuspendRequested;
3388 mono_os_event_reset (thread->suspended);
3390 /* Signal the thread to suspend + calls UNLOCK_THREAD (thread) */
3391 async_suspend_internal (thread, TRUE);
3393 mono_threads_close_thread_handle (wait->handles [i]);
3394 wait->threads [i] = NULL;
3396 if (eventidx <= 0) {
3398 * If there are threads which are starting up, we wait until they
3399 * are suspended when they try to register in the threads hash.
3400 * This is guaranteed to finish, since the threads which can create new
3401 * threads get suspended after a while.
3402 * FIXME: The finalizer thread can still create new threads.
3404 mono_threads_lock ();
3405 if (threads_starting_up)
3406 starting = mono_g_hash_table_size (threads_starting_up) > 0;
3407 else
3408 starting = FALSE;
3409 mono_threads_unlock ();
3410 if (starting)
3411 mono_thread_info_sleep (100, NULL);
3412 else
3413 finished = TRUE;
3418 typedef struct {
3419 MonoInternalThread *thread;
3420 MonoStackFrameInfo *frames;
3421 int nframes, max_frames;
3422 int nthreads, max_threads;
3423 MonoInternalThread **threads;
3424 } ThreadDumpUserData;
3426 static gboolean thread_dump_requested;
3428 /* This needs to be async safe */
3429 static gboolean
3430 collect_frame (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
3432 ThreadDumpUserData *ud = (ThreadDumpUserData *)data;
3434 if (ud->nframes < ud->max_frames) {
3435 memcpy (&ud->frames [ud->nframes], frame, sizeof (MonoStackFrameInfo));
3436 ud->nframes ++;
3439 return FALSE;
3442 /* This needs to be async safe */
3443 static SuspendThreadResult
3444 get_thread_dump (MonoThreadInfo *info, gpointer ud)
3446 ThreadDumpUserData *user_data = (ThreadDumpUserData *)ud;
3447 MonoInternalThread *thread = user_data->thread;
3449 #if 0
3450 /* This no longer works with remote unwinding */
3451 g_string_append_printf (text, " tid=0x%p this=0x%p ", (gpointer)(gsize)thread->tid, thread);
3452 mono_thread_internal_describe (thread, text);
3453 g_string_append (text, "\n");
3454 #endif
3456 if (thread == mono_thread_internal_current ())
3457 mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (collect_frame, NULL, MONO_UNWIND_SIGNAL_SAFE, ud);
3458 else
3459 mono_get_eh_callbacks ()->mono_walk_stack_with_state (collect_frame, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, ud);
3461 return MonoResumeThread;
3464 typedef struct {
3465 int nthreads, max_threads;
3466 MonoInternalThread **threads;
3467 } CollectThreadsUserData;
3469 static void
3470 collect_thread (gpointer key, gpointer value, gpointer user)
3472 CollectThreadsUserData *ud = (CollectThreadsUserData *)user;
3473 MonoInternalThread *thread = (MonoInternalThread *)value;
3475 if (ud->nthreads < ud->max_threads)
3476 ud->threads [ud->nthreads ++] = thread;
3480 * Collect running threads into the THREADS array.
3481 * THREADS should be an array allocated on the stack.
3483 static int
3484 collect_threads (MonoInternalThread **thread_array, int max_threads)
3486 CollectThreadsUserData ud;
3488 memset (&ud, 0, sizeof (ud));
3489 /* This array contains refs, but its on the stack, so its ok */
3490 ud.threads = thread_array;
3491 ud.max_threads = max_threads;
3493 mono_threads_lock ();
3494 mono_g_hash_table_foreach (threads, collect_thread, &ud);
3495 mono_threads_unlock ();
3497 return ud.nthreads;
3500 static void
3501 dump_thread (MonoInternalThread *thread, ThreadDumpUserData *ud)
3503 GString* text = g_string_new (0);
3504 char *name;
3505 GError *error = NULL;
3506 int i;
3508 ud->thread = thread;
3509 ud->nframes = 0;
3511 /* Collect frames for the thread */
3512 if (thread == mono_thread_internal_current ()) {
3513 get_thread_dump (mono_thread_info_current (), ud);
3514 } else {
3515 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, get_thread_dump, ud);
3519 * Do all the non async-safe work outside of get_thread_dump.
3521 if (thread->name) {
3522 name = g_utf16_to_utf8 (thread->name, thread->name_len, NULL, NULL, &error);
3523 g_assert (!error);
3524 g_string_append_printf (text, "\n\"%s\"", name);
3525 g_free (name);
3527 else if (thread->threadpool_thread) {
3528 g_string_append (text, "\n\"<threadpool thread>\"");
3529 } else {
3530 g_string_append (text, "\n\"<unnamed thread>\"");
3533 for (i = 0; i < ud->nframes; ++i) {
3534 MonoStackFrameInfo *frame = &ud->frames [i];
3535 MonoMethod *method = NULL;
3537 if (frame->type == FRAME_TYPE_MANAGED)
3538 method = mono_jit_info_get_method (frame->ji);
3540 if (method) {
3541 gchar *location = mono_debug_print_stack_frame (method, frame->native_offset, frame->domain);
3542 g_string_append_printf (text, " %s\n", location);
3543 g_free (location);
3544 } else {
3545 g_string_append_printf (text, " at <unknown> <0x%05x>\n", frame->native_offset);
3549 fprintf (stdout, "%s", text->str);
3551 #if PLATFORM_WIN32 && TARGET_WIN32 && _DEBUG
3552 OutputDebugStringA(text->str);
3553 #endif
3555 g_string_free (text, TRUE);
3556 fflush (stdout);
3559 void
3560 mono_threads_perform_thread_dump (void)
3562 ThreadDumpUserData ud;
3563 MonoInternalThread *thread_array [128];
3564 int tindex, nthreads;
3566 if (!thread_dump_requested)
3567 return;
3569 printf ("Full thread dump:\n");
3571 /* Make a copy of the threads hash to avoid doing work inside threads_lock () */
3572 nthreads = collect_threads (thread_array, 128);
3574 memset (&ud, 0, sizeof (ud));
3575 ud.frames = g_new0 (MonoStackFrameInfo, 256);
3576 ud.max_frames = 256;
3578 for (tindex = 0; tindex < nthreads; ++tindex)
3579 dump_thread (thread_array [tindex], &ud);
3581 g_free (ud.frames);
3583 thread_dump_requested = FALSE;
3586 /* Obtain the thread dump of all threads */
3587 static gboolean
3588 mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_frames, MonoError *error)
3591 ThreadDumpUserData ud;
3592 MonoInternalThread *thread_array [128];
3593 MonoDomain *domain = mono_domain_get ();
3594 MonoDebugSourceLocation *location;
3595 int tindex, nthreads;
3597 error_init (error);
3599 *out_threads = NULL;
3600 *out_stack_frames = NULL;
3602 /* Make a copy of the threads hash to avoid doing work inside threads_lock () */
3603 nthreads = collect_threads (thread_array, 128);
3605 memset (&ud, 0, sizeof (ud));
3606 ud.frames = g_new0 (MonoStackFrameInfo, 256);
3607 ud.max_frames = 256;
3609 *out_threads = mono_array_new_checked (domain, mono_defaults.thread_class, nthreads, error);
3610 if (!is_ok (error))
3611 goto leave;
3612 *out_stack_frames = mono_array_new_checked (domain, mono_defaults.array_class, nthreads, error);
3613 if (!is_ok (error))
3614 goto leave;
3616 for (tindex = 0; tindex < nthreads; ++tindex) {
3617 MonoInternalThread *thread = thread_array [tindex];
3618 MonoArray *thread_frames;
3619 int i;
3621 ud.thread = thread;
3622 ud.nframes = 0;
3624 /* Collect frames for the thread */
3625 if (thread == mono_thread_internal_current ()) {
3626 get_thread_dump (mono_thread_info_current (), &ud);
3627 } else {
3628 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, get_thread_dump, &ud);
3631 mono_array_setref_fast (*out_threads, tindex, mono_thread_current_for_thread (thread));
3633 thread_frames = mono_array_new_checked (domain, mono_defaults.stack_frame_class, ud.nframes, error);
3634 if (!is_ok (error))
3635 goto leave;
3636 mono_array_setref_fast (*out_stack_frames, tindex, thread_frames);
3638 for (i = 0; i < ud.nframes; ++i) {
3639 MonoStackFrameInfo *frame = &ud.frames [i];
3640 MonoMethod *method = NULL;
3641 MonoStackFrame *sf = (MonoStackFrame *)mono_object_new_checked (domain, mono_defaults.stack_frame_class, error);
3642 if (!is_ok (error))
3643 goto leave;
3645 sf->native_offset = frame->native_offset;
3647 if (frame->type == FRAME_TYPE_MANAGED)
3648 method = mono_jit_info_get_method (frame->ji);
3650 if (method) {
3651 sf->method_address = (gsize) frame->ji->code_start;
3653 MonoReflectionMethod *rm = mono_method_get_object_checked (domain, method, NULL, error);
3654 if (!is_ok (error))
3655 goto leave;
3656 MONO_OBJECT_SETREF (sf, method, rm);
3658 location = mono_debug_lookup_source_location (method, frame->native_offset, domain);
3659 if (location) {
3660 sf->il_offset = location->il_offset;
3662 if (location && location->source_file) {
3663 MONO_OBJECT_SETREF (sf, filename, mono_string_new (domain, location->source_file));
3664 sf->line = location->row;
3665 sf->column = location->column;
3667 mono_debug_free_source_location (location);
3668 } else {
3669 sf->il_offset = -1;
3672 mono_array_setref (thread_frames, i, sf);
3676 leave:
3677 g_free (ud.frames);
3678 return is_ok (error);
3682 * mono_threads_request_thread_dump:
3684 * Ask all threads except the current to print their stacktrace to stdout.
3686 void
3687 mono_threads_request_thread_dump (void)
3689 /*The new thread dump code runs out of the finalizer thread. */
3690 thread_dump_requested = TRUE;
3691 mono_gc_finalize_notify ();
3694 struct ref_stack {
3695 gpointer *refs;
3696 gint allocated; /* +1 so that refs [allocated] == NULL */
3697 gint bottom;
3700 typedef struct ref_stack RefStack;
3702 static RefStack *
3703 ref_stack_new (gint initial_size)
3705 RefStack *rs;
3707 initial_size = MAX (initial_size, 16) + 1;
3708 rs = g_new0 (RefStack, 1);
3709 rs->refs = g_new0 (gpointer, initial_size);
3710 rs->allocated = initial_size;
3711 return rs;
3714 static void
3715 ref_stack_destroy (gpointer ptr)
3717 RefStack *rs = (RefStack *)ptr;
3719 if (rs != NULL) {
3720 g_free (rs->refs);
3721 g_free (rs);
3725 static void
3726 ref_stack_push (RefStack *rs, gpointer ptr)
3728 g_assert (rs != NULL);
3730 if (rs->bottom >= rs->allocated) {
3731 rs->refs = (void **)g_realloc (rs->refs, rs->allocated * 2 * sizeof (gpointer) + 1);
3732 rs->allocated <<= 1;
3733 rs->refs [rs->allocated] = NULL;
3735 rs->refs [rs->bottom++] = ptr;
3738 static void
3739 ref_stack_pop (RefStack *rs)
3741 if (rs == NULL || rs->bottom == 0)
3742 return;
3744 rs->bottom--;
3745 rs->refs [rs->bottom] = NULL;
3748 static gboolean
3749 ref_stack_find (RefStack *rs, gpointer ptr)
3751 gpointer *refs;
3753 if (rs == NULL)
3754 return FALSE;
3756 for (refs = rs->refs; refs && *refs; refs++) {
3757 if (*refs == ptr)
3758 return TRUE;
3760 return FALSE;
3764 * mono_thread_push_appdomain_ref:
3766 * Register that the current thread may have references to objects in domain
3767 * @domain on its stack. Each call to this function should be paired with a
3768 * call to pop_appdomain_ref.
3770 void
3771 mono_thread_push_appdomain_ref (MonoDomain *domain)
3773 MonoInternalThread *thread = mono_thread_internal_current ();
3775 if (thread) {
3776 /* printf ("PUSH REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, domain->friendly_name); */
3777 SPIN_LOCK (thread->lock_thread_id);
3778 if (thread->appdomain_refs == NULL)
3779 thread->appdomain_refs = ref_stack_new (16);
3780 ref_stack_push ((RefStack *)thread->appdomain_refs, domain);
3781 SPIN_UNLOCK (thread->lock_thread_id);
3785 void
3786 mono_thread_pop_appdomain_ref (void)
3788 MonoInternalThread *thread = mono_thread_internal_current ();
3790 if (thread) {
3791 /* printf ("POP REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, ((MonoDomain*)(thread->appdomain_refs->data))->friendly_name); */
3792 SPIN_LOCK (thread->lock_thread_id);
3793 ref_stack_pop ((RefStack *)thread->appdomain_refs);
3794 SPIN_UNLOCK (thread->lock_thread_id);
3798 gboolean
3799 mono_thread_internal_has_appdomain_ref (MonoInternalThread *thread, MonoDomain *domain)
3801 gboolean res;
3802 SPIN_LOCK (thread->lock_thread_id);
3803 res = ref_stack_find ((RefStack *)thread->appdomain_refs, domain);
3804 SPIN_UNLOCK (thread->lock_thread_id);
3805 return res;
3808 gboolean
3809 mono_thread_has_appdomain_ref (MonoThread *thread, MonoDomain *domain)
3811 return mono_thread_internal_has_appdomain_ref (thread->internal_thread, domain);
3814 typedef struct abort_appdomain_data {
3815 struct wait_data wait;
3816 MonoDomain *domain;
3817 } abort_appdomain_data;
3819 static void
3820 collect_appdomain_thread (gpointer key, gpointer value, gpointer user_data)
3822 MonoInternalThread *thread = (MonoInternalThread*)value;
3823 abort_appdomain_data *data = (abort_appdomain_data*)user_data;
3824 MonoDomain *domain = data->domain;
3826 if (mono_thread_internal_has_appdomain_ref (thread, domain)) {
3827 /* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread->tid, domain->friendly_name); */
3829 if(data->wait.num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
3830 data->wait.handles [data->wait.num] = mono_threads_open_thread_handle (thread->handle);
3831 data->wait.threads [data->wait.num] = thread;
3832 data->wait.num++;
3833 } else {
3834 /* Just ignore the rest, we can't do anything with
3835 * them yet
3842 * mono_threads_abort_appdomain_threads:
3844 * Abort threads which has references to the given appdomain.
3846 gboolean
3847 mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
3849 #ifdef __native_client__
3850 return FALSE;
3851 #endif
3853 abort_appdomain_data user_data;
3854 gint64 start_time;
3855 int orig_timeout = timeout;
3856 int i;
3858 THREAD_DEBUG (g_message ("%s: starting abort", __func__));
3860 start_time = mono_msec_ticks ();
3861 do {
3862 mono_threads_lock ();
3864 user_data.domain = domain;
3865 user_data.wait.num = 0;
3866 /* This shouldn't take any locks */
3867 mono_g_hash_table_foreach (threads, collect_appdomain_thread, &user_data);
3868 mono_threads_unlock ();
3870 if (user_data.wait.num > 0) {
3871 /* Abort the threads outside the threads lock */
3872 for (i = 0; i < user_data.wait.num; ++i)
3873 mono_thread_internal_abort (user_data.wait.threads [i]);
3876 * We should wait for the threads either to abort, or to leave the
3877 * domain. We can't do the latter, so we wait with a timeout.
3879 wait_for_tids (&user_data.wait, 100, FALSE);
3882 /* Update remaining time */
3883 timeout -= mono_msec_ticks () - start_time;
3884 start_time = mono_msec_ticks ();
3886 if (orig_timeout != -1 && timeout < 0)
3887 return FALSE;
3889 while (user_data.wait.num > 0);
3891 THREAD_DEBUG (g_message ("%s: abort done", __func__));
3893 return TRUE;
3897 * mono_thread_get_undeniable_exception:
3899 * Return an exception which needs to be raised when leaving a catch clause.
3900 * This is used for undeniable exception propagation.
3902 MonoException*
3903 mono_thread_get_undeniable_exception (void)
3905 MonoInternalThread *thread = mono_thread_internal_current ();
3907 if (!(thread && thread->abort_exc && !is_running_protected_wrapper ()))
3908 return NULL;
3910 // We don't want to have our exception effect calls made by
3911 // the catching block
3913 if (!mono_get_eh_callbacks ()->mono_above_abort_threshold ())
3914 return NULL;
3917 * FIXME: Clear the abort exception and return an AppDomainUnloaded
3918 * exception if the thread no longer references a dying appdomain.
3920 thread->abort_exc->trace_ips = NULL;
3921 thread->abort_exc->stack_trace = NULL;
3922 return thread->abort_exc;
3925 #if MONO_SMALL_CONFIG
3926 #define NUM_STATIC_DATA_IDX 4
3927 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
3928 64, 256, 1024, 4096
3930 #else
3931 #define NUM_STATIC_DATA_IDX 8
3932 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
3933 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
3935 #endif
3937 static MonoBitSet *thread_reference_bitmaps [NUM_STATIC_DATA_IDX];
3938 static MonoBitSet *context_reference_bitmaps [NUM_STATIC_DATA_IDX];
3940 static void
3941 mark_slots (void *addr, MonoBitSet **bitmaps, MonoGCMarkFunc mark_func, void *gc_data)
3943 gpointer *static_data = (gpointer *)addr;
3945 for (int i = 0; i < NUM_STATIC_DATA_IDX; ++i) {
3946 void **ptr = (void **)static_data [i];
3948 if (!ptr)
3949 continue;
3951 MONO_BITSET_FOREACH (bitmaps [i], idx, {
3952 void **p = ptr + idx;
3954 if (*p)
3955 mark_func ((MonoObject**)p, gc_data);
3960 static void
3961 mark_tls_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
3963 mark_slots (addr, thread_reference_bitmaps, mark_func, gc_data);
3966 static void
3967 mark_ctx_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
3969 mark_slots (addr, context_reference_bitmaps, mark_func, gc_data);
3973 * mono_alloc_static_data
3975 * Allocate memory blocks for storing threads or context static data
3977 static void
3978 mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean threadlocal)
3980 guint idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
3981 int i;
3983 gpointer* static_data = *static_data_ptr;
3984 if (!static_data) {
3985 static MonoGCDescriptor tls_desc = MONO_GC_DESCRIPTOR_NULL;
3986 static MonoGCDescriptor ctx_desc = MONO_GC_DESCRIPTOR_NULL;
3988 if (mono_gc_user_markers_supported ()) {
3989 if (tls_desc == MONO_GC_DESCRIPTOR_NULL)
3990 tls_desc = mono_gc_make_root_descr_user (mark_tls_slots);
3992 if (ctx_desc == MONO_GC_DESCRIPTOR_NULL)
3993 ctx_desc = mono_gc_make_root_descr_user (mark_ctx_slots);
3996 static_data = (void **)mono_gc_alloc_fixed (static_data_size [0], threadlocal ? tls_desc : ctx_desc,
3997 threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC,
3998 threadlocal ? "managed thread-static variables" : "managed context-static variables");
3999 *static_data_ptr = static_data;
4000 static_data [0] = static_data;
4003 for (i = 1; i <= idx; ++i) {
4004 if (static_data [i])
4005 continue;
4007 if (mono_gc_user_markers_supported ())
4008 static_data [i] = g_malloc0 (static_data_size [i]);
4009 else
4010 static_data [i] = mono_gc_alloc_fixed (static_data_size [i], MONO_GC_DESCRIPTOR_NULL,
4011 threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC,
4012 threadlocal ? "managed thread-static variables" : "managed context-static variables");
4016 static void
4017 mono_free_static_data (gpointer* static_data)
4019 int i;
4020 for (i = 1; i < NUM_STATIC_DATA_IDX; ++i) {
4021 gpointer p = static_data [i];
4022 if (!p)
4023 continue;
4025 * At this point, the static data pointer array is still registered with the
4026 * GC, so must ensure that mark_tls_slots() will not encounter any invalid
4027 * data. Freeing the individual arrays without first nulling their slots
4028 * would make it possible for mark_tls/ctx_slots() to encounter a pointer to
4029 * such an already freed array. See bug #13813.
4031 static_data [i] = NULL;
4032 mono_memory_write_barrier ();
4033 if (mono_gc_user_markers_supported ())
4034 g_free (p);
4035 else
4036 mono_gc_free_fixed (p);
4038 mono_gc_free_fixed (static_data);
4042 * mono_init_static_data_info
4044 * Initializes static data counters
4046 static void mono_init_static_data_info (StaticDataInfo *static_data)
4048 static_data->idx = 0;
4049 static_data->offset = 0;
4050 static_data->freelist = NULL;
4054 * mono_alloc_static_data_slot
4056 * Generates an offset for static data. static_data contains the counters
4057 * used to generate it.
4059 static guint32
4060 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align)
4062 if (!static_data->idx && !static_data->offset) {
4064 * we use the first chunk of the first allocation also as
4065 * an array for the rest of the data
4067 static_data->offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX;
4069 static_data->offset += align - 1;
4070 static_data->offset &= ~(align - 1);
4071 if (static_data->offset + size >= static_data_size [static_data->idx]) {
4072 static_data->idx ++;
4073 g_assert (size <= static_data_size [static_data->idx]);
4074 g_assert (static_data->idx < NUM_STATIC_DATA_IDX);
4075 static_data->offset = 0;
4077 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (static_data->idx, static_data->offset, 0);
4078 static_data->offset += size;
4079 return offset;
4083 * LOCKING: requires that threads_mutex is held
4085 static void
4086 context_adjust_static_data (MonoAppContext *ctx)
4088 if (context_static_info.offset || context_static_info.idx > 0) {
4089 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (context_static_info.idx, context_static_info.offset, 0);
4090 mono_alloc_static_data (&ctx->static_data, offset, FALSE);
4091 ctx->data->static_data = ctx->static_data;
4096 * LOCKING: requires that threads_mutex is held
4098 static void
4099 alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
4101 MonoInternalThread *thread = (MonoInternalThread *)value;
4102 guint32 offset = GPOINTER_TO_UINT (user);
4104 mono_alloc_static_data (&(thread->static_data), offset, TRUE);
4108 * LOCKING: requires that threads_mutex is held
4110 static void
4111 alloc_context_static_data_helper (gpointer key, gpointer value, gpointer user)
4113 MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (GPOINTER_TO_INT (key));
4115 if (!ctx)
4116 return;
4118 guint32 offset = GPOINTER_TO_UINT (user);
4119 mono_alloc_static_data (&ctx->static_data, offset, FALSE);
4120 ctx->data->static_data = ctx->static_data;
4123 static StaticDataFreeList*
4124 search_slot_in_freelist (StaticDataInfo *static_data, guint32 size, guint32 align)
4126 StaticDataFreeList* prev = NULL;
4127 StaticDataFreeList* tmp = static_data->freelist;
4128 while (tmp) {
4129 if (tmp->size == size) {
4130 if (prev)
4131 prev->next = tmp->next;
4132 else
4133 static_data->freelist = tmp->next;
4134 return tmp;
4136 prev = tmp;
4137 tmp = tmp->next;
4139 return NULL;
4142 #if SIZEOF_VOID_P == 4
4143 #define ONE_P 1
4144 #else
4145 #define ONE_P 1ll
4146 #endif
4148 static void
4149 update_reference_bitmap (MonoBitSet **sets, guint32 offset, uintptr_t *bitmap, int numbits)
4151 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4152 if (!sets [idx])
4153 sets [idx] = mono_bitset_new (static_data_size [idx] / sizeof (uintptr_t), 0);
4154 MonoBitSet *rb = sets [idx];
4155 offset = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
4156 offset /= sizeof (uintptr_t);
4157 /* offset is now the bitmap offset */
4158 for (int i = 0; i < numbits; ++i) {
4159 if (bitmap [i / sizeof (uintptr_t)] & (ONE_P << (i & (sizeof (uintptr_t) * 8 -1))))
4160 mono_bitset_set_fast (rb, offset + i);
4164 static void
4165 clear_reference_bitmap (MonoBitSet **sets, guint32 offset, guint32 size)
4167 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4168 MonoBitSet *rb = sets [idx];
4169 offset = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
4170 offset /= sizeof (uintptr_t);
4171 /* offset is now the bitmap offset */
4172 for (int i = 0; i < size / sizeof (uintptr_t); i++)
4173 mono_bitset_clear_fast (rb, offset + i);
4176 guint32
4177 mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align, uintptr_t *bitmap, int numbits)
4179 g_assert (static_type == SPECIAL_STATIC_THREAD || static_type == SPECIAL_STATIC_CONTEXT);
4181 StaticDataInfo *info;
4182 MonoBitSet **sets;
4184 if (static_type == SPECIAL_STATIC_THREAD) {
4185 info = &thread_static_info;
4186 sets = thread_reference_bitmaps;
4187 } else {
4188 info = &context_static_info;
4189 sets = context_reference_bitmaps;
4192 mono_threads_lock ();
4194 StaticDataFreeList *item = search_slot_in_freelist (info, size, align);
4195 guint32 offset;
4197 if (item) {
4198 offset = item->offset;
4199 g_free (item);
4200 } else {
4201 offset = mono_alloc_static_data_slot (info, size, align);
4204 update_reference_bitmap (sets, offset, bitmap, numbits);
4206 if (static_type == SPECIAL_STATIC_THREAD) {
4207 /* This can be called during startup */
4208 if (threads != NULL)
4209 mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
4210 } else {
4211 if (contexts != NULL)
4212 g_hash_table_foreach (contexts, alloc_context_static_data_helper, GUINT_TO_POINTER (offset));
4214 ACCESS_SPECIAL_STATIC_OFFSET (offset, type) = SPECIAL_STATIC_OFFSET_TYPE_CONTEXT;
4217 mono_threads_unlock ();
4219 return offset;
4222 gpointer
4223 mono_get_special_static_data_for_thread (MonoInternalThread *thread, guint32 offset)
4225 guint32 static_type = ACCESS_SPECIAL_STATIC_OFFSET (offset, type);
4227 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4228 return get_thread_static_data (thread, offset);
4229 } else {
4230 return get_context_static_data (thread->current_appcontext, offset);
4234 gpointer
4235 mono_get_special_static_data (guint32 offset)
4237 return mono_get_special_static_data_for_thread (mono_thread_internal_current (), offset);
4240 typedef struct {
4241 guint32 offset;
4242 guint32 size;
4243 } OffsetSize;
4246 * LOCKING: requires that threads_mutex is held
4248 static void
4249 free_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
4251 MonoInternalThread *thread = (MonoInternalThread *)value;
4252 OffsetSize *data = (OffsetSize *)user;
4253 int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index);
4254 int off = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, offset);
4255 char *ptr;
4257 if (!thread->static_data || !thread->static_data [idx])
4258 return;
4259 ptr = ((char*) thread->static_data [idx]) + off;
4260 mono_gc_bzero_atomic (ptr, data->size);
4264 * LOCKING: requires that threads_mutex is held
4266 static void
4267 free_context_static_data_helper (gpointer key, gpointer value, gpointer user)
4269 MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (GPOINTER_TO_INT (key));
4271 if (!ctx)
4272 return;
4274 OffsetSize *data = (OffsetSize *)user;
4275 int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index);
4276 int off = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, offset);
4277 char *ptr;
4279 if (!ctx->static_data || !ctx->static_data [idx])
4280 return;
4282 ptr = ((char*) ctx->static_data [idx]) + off;
4283 mono_gc_bzero_atomic (ptr, data->size);
4286 static void
4287 do_free_special_slot (guint32 offset, guint32 size)
4289 guint32 static_type = ACCESS_SPECIAL_STATIC_OFFSET (offset, type);
4290 MonoBitSet **sets;
4291 StaticDataInfo *info;
4293 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4294 info = &thread_static_info;
4295 sets = thread_reference_bitmaps;
4296 } else {
4297 info = &context_static_info;
4298 sets = context_reference_bitmaps;
4301 guint32 data_offset = offset;
4302 ACCESS_SPECIAL_STATIC_OFFSET (data_offset, type) = 0;
4303 OffsetSize data = { data_offset, size };
4305 clear_reference_bitmap (sets, data.offset, data.size);
4307 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4308 if (threads != NULL)
4309 mono_g_hash_table_foreach (threads, free_thread_static_data_helper, &data);
4310 } else {
4311 if (contexts != NULL)
4312 g_hash_table_foreach (contexts, free_context_static_data_helper, &data);
4315 if (!mono_runtime_is_shutting_down ()) {
4316 StaticDataFreeList *item = g_new0 (StaticDataFreeList, 1);
4318 item->offset = offset;
4319 item->size = size;
4321 item->next = info->freelist;
4322 info->freelist = item;
4326 static void
4327 do_free_special (gpointer key, gpointer value, gpointer data)
4329 MonoClassField *field = (MonoClassField *)key;
4330 guint32 offset = GPOINTER_TO_UINT (value);
4331 gint32 align;
4332 guint32 size;
4333 size = mono_type_size (field->type, &align);
4334 do_free_special_slot (offset, size);
4337 void
4338 mono_alloc_special_static_data_free (GHashTable *special_static_fields)
4340 mono_threads_lock ();
4342 g_hash_table_foreach (special_static_fields, do_free_special, NULL);
4344 mono_threads_unlock ();
4347 #ifdef HOST_WIN32
4348 static void CALLBACK dummy_apc (ULONG_PTR param)
4351 #endif
4354 * mono_thread_execute_interruption
4356 * Performs the operation that the requested thread state requires (abort,
4357 * suspend or stop)
4359 static MonoException*
4360 mono_thread_execute_interruption (void)
4362 MonoInternalThread *thread = mono_thread_internal_current ();
4363 MonoThread *sys_thread = mono_thread_current ();
4365 LOCK_THREAD (thread);
4367 /* MonoThread::interruption_requested can only be changed with atomics */
4368 if (mono_thread_clear_interruption_requested (thread)) {
4369 /* this will consume pending APC calls */
4370 #ifdef HOST_WIN32
4371 WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
4372 #endif
4373 /* Clear the interrupted flag of the thread so it can wait again */
4374 mono_thread_info_clear_self_interrupt ();
4377 /* If there's a pending exception and an AbortRequested, the pending exception takes precedence */
4378 if (sys_thread->pending_exception) {
4379 MonoException *exc;
4381 exc = sys_thread->pending_exception;
4382 sys_thread->pending_exception = NULL;
4384 UNLOCK_THREAD (thread);
4385 return exc;
4386 } else if (thread->state & (ThreadState_AbortRequested)) {
4387 UNLOCK_THREAD (thread);
4388 g_assert (sys_thread->pending_exception == NULL);
4389 if (thread->abort_exc == NULL) {
4391 * This might be racy, but it has to be called outside the lock
4392 * since it calls managed code.
4394 MONO_OBJECT_SETREF (thread, abort_exc, mono_get_exception_thread_abort ());
4396 return thread->abort_exc;
4397 } else if (thread->state & (ThreadState_SuspendRequested)) {
4398 /* calls UNLOCK_THREAD (thread) */
4399 self_suspend_internal ();
4400 return NULL;
4401 } else if (thread->thread_interrupt_requested) {
4403 thread->thread_interrupt_requested = FALSE;
4404 UNLOCK_THREAD (thread);
4406 return(mono_get_exception_thread_interrupted ());
4409 UNLOCK_THREAD (thread);
4411 return NULL;
4415 * mono_thread_request_interruption
4417 * A signal handler can call this method to request the interruption of a
4418 * thread. The result of the interruption will depend on the current state of
4419 * the thread. If the result is an exception that needs to be throw, it is
4420 * provided as return value.
4422 MonoException*
4423 mono_thread_request_interruption (gboolean running_managed)
4425 MonoInternalThread *thread = mono_thread_internal_current ();
4427 /* The thread may already be stopping */
4428 if (thread == NULL)
4429 return NULL;
4431 if (!mono_thread_set_interruption_requested (thread))
4432 return NULL;
4434 if (!running_managed || is_running_protected_wrapper ()) {
4435 /* Can't stop while in unmanaged code. Increase the global interruption
4436 request count. When exiting the unmanaged method the count will be
4437 checked and the thread will be interrupted. */
4439 /* this will awake the thread if it is in WaitForSingleObject
4440 or similar */
4441 /* Our implementation of this function ignores the func argument */
4442 #ifdef HOST_WIN32
4443 QueueUserAPC ((PAPCFUNC)dummy_apc, thread->native_handle, (ULONG_PTR)NULL);
4444 #else
4445 mono_thread_info_self_interrupt ();
4446 #endif
4447 return NULL;
4449 else {
4450 return mono_thread_execute_interruption ();
4454 /*This function should be called by a thread after it has exited all of
4455 * its handle blocks at interruption time.*/
4456 MonoException*
4457 mono_thread_resume_interruption (gboolean exec)
4459 MonoInternalThread *thread = mono_thread_internal_current ();
4460 gboolean still_aborting;
4462 /* The thread may already be stopping */
4463 if (thread == NULL)
4464 return NULL;
4466 LOCK_THREAD (thread);
4467 still_aborting = (thread->state & (ThreadState_AbortRequested)) != 0;
4468 UNLOCK_THREAD (thread);
4470 /*This can happen if the protected block called Thread::ResetAbort*/
4471 if (!still_aborting)
4472 return NULL;
4474 if (!mono_thread_set_interruption_requested (thread))
4475 return NULL;
4477 mono_thread_info_self_interrupt ();
4479 if (exec)
4480 return mono_thread_execute_interruption ();
4481 else
4482 return NULL;
4485 gboolean mono_thread_interruption_requested ()
4487 if (thread_interruption_requested) {
4488 MonoInternalThread *thread = mono_thread_internal_current ();
4489 /* The thread may already be stopping */
4490 if (thread != NULL)
4491 return mono_thread_get_interruption_requested (thread);
4493 return FALSE;
4496 static MonoException*
4497 mono_thread_interruption_checkpoint_request (gboolean bypass_abort_protection)
4499 MonoInternalThread *thread = mono_thread_internal_current ();
4501 /* The thread may already be stopping */
4502 if (!thread)
4503 return NULL;
4504 if (!mono_thread_get_interruption_requested (thread))
4505 return NULL;
4506 if (!bypass_abort_protection && is_running_protected_wrapper ())
4507 return NULL;
4509 return mono_thread_execute_interruption ();
4513 * Performs the interruption of the current thread, if one has been requested,
4514 * and the thread is not running a protected wrapper.
4515 * Return the exception which needs to be thrown, if any.
4517 MonoException*
4518 mono_thread_interruption_checkpoint (void)
4520 return mono_thread_interruption_checkpoint_request (FALSE);
4524 * Performs the interruption of the current thread, if one has been requested.
4525 * Return the exception which needs to be thrown, if any.
4527 MonoException*
4528 mono_thread_force_interruption_checkpoint_noraise (void)
4530 return mono_thread_interruption_checkpoint_request (TRUE);
4534 * mono_set_pending_exception:
4536 * Set the pending exception of the current thread to EXC.
4537 * The exception will be thrown when execution returns to managed code.
4539 void
4540 mono_set_pending_exception (MonoException *exc)
4542 MonoThread *thread = mono_thread_current ();
4544 /* The thread may already be stopping */
4545 if (thread == NULL)
4546 return;
4548 MONO_OBJECT_SETREF (thread, pending_exception, exc);
4550 mono_thread_request_interruption (FALSE);
4554 * mono_thread_interruption_request_flag:
4556 * Returns the address of a flag that will be non-zero if an interruption has
4557 * been requested for a thread. The thread to interrupt may not be the current
4558 * thread, so an additional call to mono_thread_interruption_requested() or
4559 * mono_thread_interruption_checkpoint() is allways needed if the flag is not
4560 * zero.
4562 gint32* mono_thread_interruption_request_flag ()
4564 return &thread_interruption_requested;
4567 void
4568 mono_thread_init_apartment_state (void)
4570 #ifdef HOST_WIN32
4571 MonoInternalThread* thread = mono_thread_internal_current ();
4573 /* Positive return value indicates success, either
4574 * S_OK if this is first CoInitialize call, or
4575 * S_FALSE if CoInitialize already called, but with same
4576 * threading model. A negative value indicates failure,
4577 * probably due to trying to change the threading model.
4579 if (CoInitializeEx(NULL, (thread->apartment_state == ThreadApartmentState_STA)
4580 ? COINIT_APARTMENTTHREADED
4581 : COINIT_MULTITHREADED) < 0) {
4582 thread->apartment_state = ThreadApartmentState_Unknown;
4584 #endif
4587 void
4588 mono_thread_cleanup_apartment_state (void)
4590 #ifdef HOST_WIN32
4591 MonoInternalThread* thread = mono_thread_internal_current ();
4593 if (thread && thread->apartment_state != ThreadApartmentState_Unknown) {
4594 CoUninitialize ();
4596 #endif
4599 void
4600 mono_thread_set_state (MonoInternalThread *thread, MonoThreadState state)
4602 LOCK_THREAD (thread);
4603 thread->state |= state;
4604 UNLOCK_THREAD (thread);
4608 * mono_thread_test_and_set_state:
4610 * Test if current state of @thread include @test. If it does not, OR @set into the state.
4612 * Returns TRUE is @set was OR'd in.
4614 gboolean
4615 mono_thread_test_and_set_state (MonoInternalThread *thread, MonoThreadState test, MonoThreadState set)
4617 LOCK_THREAD (thread);
4619 if ((thread->state & test) != 0) {
4620 UNLOCK_THREAD (thread);
4621 return FALSE;
4624 thread->state |= set;
4625 UNLOCK_THREAD (thread);
4627 return TRUE;
4630 void
4631 mono_thread_clr_state (MonoInternalThread *thread, MonoThreadState state)
4633 LOCK_THREAD (thread);
4634 thread->state &= ~state;
4635 UNLOCK_THREAD (thread);
4638 gboolean
4639 mono_thread_test_state (MonoInternalThread *thread, MonoThreadState test)
4641 gboolean ret = FALSE;
4643 LOCK_THREAD (thread);
4645 if ((thread->state & test) != 0) {
4646 ret = TRUE;
4649 UNLOCK_THREAD (thread);
4651 return ret;
4654 static void
4655 self_interrupt_thread (void *_unused)
4657 MonoException *exc;
4658 MonoThreadInfo *info;
4660 exc = mono_thread_execute_interruption ();
4661 if (!exc) {
4662 if (mono_threads_is_coop_enabled ()) {
4663 /* We can return from an async call in coop, as
4664 * it's simply called when exiting the safepoint */
4665 return;
4668 g_error ("%s: we can't resume from an async call", __func__);
4671 info = mono_thread_info_current ();
4673 /* We must use _with_context since we didn't trampoline into the runtime */
4674 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. */
4677 static gboolean
4678 mono_jit_info_match (MonoJitInfo *ji, gpointer ip)
4680 if (!ji)
4681 return FALSE;
4682 return ji->code_start <= ip && (char*)ip < (char*)ji->code_start + ji->code_size;
4685 static gboolean
4686 last_managed (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
4688 MonoJitInfo **dest = (MonoJitInfo **)data;
4689 *dest = frame->ji;
4690 return TRUE;
4693 static MonoJitInfo*
4694 mono_thread_info_get_last_managed (MonoThreadInfo *info)
4696 MonoJitInfo *ji = NULL;
4697 if (!info)
4698 return NULL;
4701 * The suspended thread might be holding runtime locks. Make sure we don't try taking
4702 * any runtime locks while unwinding. In coop case we shouldn't safepoint in regions
4703 * where we hold runtime locks.
4705 if (!mono_threads_is_coop_enabled ())
4706 mono_thread_info_set_is_async_context (TRUE);
4707 mono_get_eh_callbacks ()->mono_walk_stack_with_state (last_managed, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, &ji);
4708 if (!mono_threads_is_coop_enabled ())
4709 mono_thread_info_set_is_async_context (FALSE);
4710 return ji;
4713 typedef struct {
4714 MonoInternalThread *thread;
4715 gboolean install_async_abort;
4716 MonoThreadInfoInterruptToken *interrupt_token;
4717 } AbortThreadData;
4719 static SuspendThreadResult
4720 async_abort_critical (MonoThreadInfo *info, gpointer ud)
4722 AbortThreadData *data = (AbortThreadData *)ud;
4723 MonoInternalThread *thread = data->thread;
4724 MonoJitInfo *ji = NULL;
4725 gboolean protected_wrapper;
4726 gboolean running_managed;
4728 if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (mono_thread_info_get_suspend_state (info)))
4729 return MonoResumeThread;
4731 /*someone is already interrupting it*/
4732 if (!mono_thread_set_interruption_requested (thread))
4733 return MonoResumeThread;
4735 ji = mono_thread_info_get_last_managed (info);
4736 protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
4737 running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
4739 if (!protected_wrapper && running_managed) {
4740 /*We are in managed code*/
4741 /*Set the thread to call */
4742 if (data->install_async_abort)
4743 mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
4744 return MonoResumeThread;
4745 } else {
4747 * This will cause waits to be broken.
4748 * It will also prevent the thread from entering a wait, so if the thread returns
4749 * from the wait before it receives the abort signal, it will just spin in the wait
4750 * functions in the io-layer until the signal handler calls QueueUserAPC which will
4751 * make it return.
4753 data->interrupt_token = mono_thread_info_prepare_interrupt (info);
4755 return MonoResumeThread;
4759 static void
4760 async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort)
4762 AbortThreadData data;
4764 g_assert (thread != mono_thread_internal_current ());
4766 data.thread = thread;
4767 data.install_async_abort = install_async_abort;
4768 data.interrupt_token = NULL;
4770 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), TRUE, async_abort_critical, &data);
4771 if (data.interrupt_token)
4772 mono_thread_info_finish_interrupt (data.interrupt_token);
4773 /*FIXME we need to wait for interruption to complete -- figure out how much into interruption we should wait for here*/
4776 static void
4777 self_abort_internal (MonoError *error)
4779 MonoException *exc;
4781 error_init (error);
4783 /* FIXME this is insanely broken, it doesn't cause interruption to happen synchronously
4784 * since passing FALSE to mono_thread_request_interruption makes sure it returns NULL */
4787 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.
4789 exc = mono_thread_request_interruption (TRUE);
4790 if (exc)
4791 mono_error_set_exception_instance (error, exc);
4792 else
4793 mono_thread_info_self_interrupt ();
4796 typedef struct {
4797 MonoInternalThread *thread;
4798 gboolean interrupt;
4799 MonoThreadInfoInterruptToken *interrupt_token;
4800 } SuspendThreadData;
4802 static SuspendThreadResult
4803 async_suspend_critical (MonoThreadInfo *info, gpointer ud)
4805 SuspendThreadData *data = (SuspendThreadData *)ud;
4806 MonoInternalThread *thread = data->thread;
4807 MonoJitInfo *ji = NULL;
4808 gboolean protected_wrapper;
4809 gboolean running_managed;
4811 ji = mono_thread_info_get_last_managed (info);
4812 protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
4813 running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
4815 if (running_managed && !protected_wrapper) {
4816 if (mono_threads_is_coop_enabled ()) {
4817 mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
4818 return MonoResumeThread;
4819 } else {
4820 thread->state &= ~ThreadState_SuspendRequested;
4821 thread->state |= ThreadState_Suspended;
4822 return KeepSuspended;
4824 } else {
4825 mono_thread_set_interruption_requested (thread);
4826 if (data->interrupt)
4827 data->interrupt_token = mono_thread_info_prepare_interrupt ((MonoThreadInfo *)thread->thread_info);
4829 return MonoResumeThread;
4833 /* LOCKING: called with @thread synch_cs held, and releases it */
4834 static void
4835 async_suspend_internal (MonoInternalThread *thread, gboolean interrupt)
4837 SuspendThreadData data;
4839 g_assert (thread != mono_thread_internal_current ());
4841 // MOSTLY_ASYNC_SAFE_PRINTF ("ASYNC SUSPEND thread %p\n", thread_get_tid (thread));
4843 thread->self_suspended = FALSE;
4845 data.thread = thread;
4846 data.interrupt = interrupt;
4847 data.interrupt_token = NULL;
4849 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), interrupt, async_suspend_critical, &data);
4850 if (data.interrupt_token)
4851 mono_thread_info_finish_interrupt (data.interrupt_token);
4853 UNLOCK_THREAD (thread);
4856 /* LOCKING: called with @thread synch_cs held, and releases it */
4857 static void
4858 self_suspend_internal (void)
4860 MonoInternalThread *thread;
4861 MonoOSEvent *event;
4862 MonoOSEventWaitRet res;
4864 thread = mono_thread_internal_current ();
4866 // MOSTLY_ASYNC_SAFE_PRINTF ("SELF SUSPEND thread %p\n", thread_get_tid (thread));
4868 thread->self_suspended = TRUE;
4870 thread->state &= ~ThreadState_SuspendRequested;
4871 thread->state |= ThreadState_Suspended;
4873 UNLOCK_THREAD (thread);
4875 event = thread->suspended;
4877 MONO_ENTER_GC_SAFE;
4878 res = mono_os_event_wait_one (event, MONO_INFINITE_WAIT, TRUE);
4879 g_assert (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0 || res == MONO_OS_EVENT_WAIT_RET_ALERTED);
4880 MONO_EXIT_GC_SAFE;
4883 static void
4884 suspend_for_shutdown_async_call (gpointer unused)
4886 for (;;)
4887 mono_thread_info_yield ();
4890 static SuspendThreadResult
4891 suspend_for_shutdown_critical (MonoThreadInfo *info, gpointer unused)
4893 mono_thread_info_setup_async_call (info, suspend_for_shutdown_async_call, NULL);
4894 return MonoResumeThread;
4897 void
4898 mono_thread_internal_suspend_for_shutdown (MonoInternalThread *thread)
4900 g_assert (thread != mono_thread_internal_current ());
4902 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, suspend_for_shutdown_critical, NULL);
4906 * mono_thread_is_foreign:
4907 * @thread: the thread to query
4909 * This function allows one to determine if a thread was created by the mono runtime and has
4910 * a well defined lifecycle or it's a foreigh one, created by the native environment.
4912 * Returns: TRUE if @thread was not created by the runtime.
4914 mono_bool
4915 mono_thread_is_foreign (MonoThread *thread)
4917 MonoThreadInfo *info = (MonoThreadInfo *)thread->internal_thread->thread_info;
4918 return info->runtime_thread == FALSE;
4922 * mono_add_joinable_thread:
4924 * Add TID to the list of joinable threads.
4925 * LOCKING: Acquires the threads lock.
4927 void
4928 mono_threads_add_joinable_thread (gpointer tid)
4930 #ifndef HOST_WIN32
4932 * We cannot detach from threads because it causes problems like
4933 * 2fd16f60/r114307. So we collect them and join them when
4934 * we have time (in he finalizer thread).
4936 joinable_threads_lock ();
4937 if (!joinable_threads)
4938 joinable_threads = g_hash_table_new (NULL, NULL);
4939 g_hash_table_insert (joinable_threads, tid, tid);
4940 joinable_thread_count ++;
4941 joinable_threads_unlock ();
4943 mono_gc_finalize_notify ();
4944 #endif
4948 * mono_threads_join_threads:
4950 * Join all joinable threads. This is called from the finalizer thread.
4951 * LOCKING: Acquires the threads lock.
4953 void
4954 mono_threads_join_threads (void)
4956 #ifndef HOST_WIN32
4957 GHashTableIter iter;
4958 gpointer key;
4959 gpointer tid;
4960 pthread_t thread;
4961 gboolean found;
4963 /* Fastpath */
4964 if (!joinable_thread_count)
4965 return;
4967 while (TRUE) {
4968 joinable_threads_lock ();
4969 found = FALSE;
4970 if (g_hash_table_size (joinable_threads)) {
4971 g_hash_table_iter_init (&iter, joinable_threads);
4972 g_hash_table_iter_next (&iter, &key, (void**)&tid);
4973 thread = (pthread_t)tid;
4974 g_hash_table_remove (joinable_threads, key);
4975 joinable_thread_count --;
4976 found = TRUE;
4978 joinable_threads_unlock ();
4979 if (found) {
4980 if (thread != pthread_self ()) {
4981 MONO_ENTER_GC_SAFE;
4982 /* This shouldn't block */
4983 mono_threads_join_lock ();
4984 mono_native_thread_join (thread);
4985 mono_threads_join_unlock ();
4986 MONO_EXIT_GC_SAFE;
4988 } else {
4989 break;
4992 #endif
4996 * mono_thread_join:
4998 * Wait for thread TID to exit.
4999 * LOCKING: Acquires the threads lock.
5001 void
5002 mono_thread_join (gpointer tid)
5004 #ifndef HOST_WIN32
5005 pthread_t thread;
5006 gboolean found = FALSE;
5008 joinable_threads_lock ();
5009 if (!joinable_threads)
5010 joinable_threads = g_hash_table_new (NULL, NULL);
5011 if (g_hash_table_lookup (joinable_threads, tid)) {
5012 g_hash_table_remove (joinable_threads, tid);
5013 joinable_thread_count --;
5014 found = TRUE;
5016 joinable_threads_unlock ();
5017 if (!found)
5018 return;
5019 thread = (pthread_t)tid;
5020 MONO_ENTER_GC_SAFE;
5021 mono_native_thread_join (thread);
5022 MONO_EXIT_GC_SAFE;
5023 #endif
5026 void
5027 mono_thread_internal_unhandled_exception (MonoObject* exc)
5029 MonoClass *klass = exc->vtable->klass;
5030 if (is_threadabort_exception (klass)) {
5031 mono_thread_internal_reset_abort (mono_thread_internal_current ());
5032 } else if (!is_appdomainunloaded_exception (klass)
5033 && mono_runtime_unhandled_exception_policy_get () == MONO_UNHANDLED_POLICY_CURRENT) {
5034 mono_unhandled_exception (exc);
5035 if (mono_environment_exitcode_get () == 1) {
5036 mono_environment_exitcode_set (255);
5037 mono_invoke_unhandled_exception_hook (exc);
5038 g_assert_not_reached ();
5043 void
5044 ves_icall_System_Threading_Thread_GetStackTraces (MonoArray **out_threads, MonoArray **out_stack_traces)
5046 MonoError error;
5047 mono_threads_get_thread_dump (out_threads, out_stack_traces, &error);
5048 mono_error_set_pending_exception (&error);
5052 * mono_threads_attach_coop: called by native->managed wrappers
5054 * In non-coop mode:
5055 * - @dummy: is NULL
5056 * - @return: the original domain which needs to be restored, or NULL.
5058 * In coop mode:
5059 * - @dummy: contains the original domain
5060 * - @return: a cookie containing current MonoThreadInfo*.
5062 gpointer
5063 mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
5065 MonoDomain *orig;
5066 gboolean fresh_thread = FALSE;
5068 if (!domain) {
5069 /* Happens when called from AOTed code which is only used in the root domain. */
5070 domain = mono_get_root_domain ();
5073 g_assert (domain);
5075 /* On coop, when we detached, we moved the thread from RUNNING->BLOCKING.
5076 * If we try to reattach we do a BLOCKING->RUNNING transition. If the thread
5077 * is fresh, mono_thread_attach() will do a STARTING->RUNNING transition so
5078 * we're only responsible for making the cookie. */
5079 if (mono_threads_is_coop_enabled ()) {
5080 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
5081 fresh_thread = !info || !mono_thread_info_is_live (info);
5084 if (!mono_thread_internal_current ()) {
5085 mono_thread_attach_full (domain, FALSE);
5087 // #678164
5088 mono_thread_set_state (mono_thread_internal_current (), ThreadState_Background);
5091 orig = mono_domain_get ();
5092 if (orig != domain)
5093 mono_domain_set (domain, TRUE);
5095 if (!mono_threads_is_coop_enabled ())
5096 return orig != domain ? orig : NULL;
5098 if (fresh_thread) {
5099 *dummy = NULL;
5100 /* mono_thread_attach put the thread in RUNNING mode from STARTING, but we need to
5101 * return the right cookie. */
5102 return mono_threads_enter_gc_unsafe_region_cookie ();
5103 } else {
5104 *dummy = orig;
5105 /* thread state (BLOCKING|RUNNING) -> RUNNING */
5106 return mono_threads_enter_gc_unsafe_region (dummy);
5111 * mono_threads_detach_coop: called by native->managed wrappers
5113 * In non-coop mode:
5114 * - @cookie: the original domain which needs to be restored, or NULL.
5115 * - @dummy: is NULL
5117 * In coop mode:
5118 * - @cookie: contains current MonoThreadInfo* if it was in BLOCKING mode, NULL otherwise
5119 * - @dummy: contains the original domain
5121 void
5122 mono_threads_detach_coop (gpointer cookie, gpointer *dummy)
5124 MonoDomain *domain, *orig;
5126 if (!mono_threads_is_coop_enabled ()) {
5127 orig = (MonoDomain*) cookie;
5128 if (orig)
5129 mono_domain_set (orig, TRUE);
5130 } else {
5131 orig = (MonoDomain*) *dummy;
5133 domain = mono_domain_get ();
5134 g_assert (domain);
5136 /* it won't do anything if cookie is NULL
5137 * thread state RUNNING -> (RUNNING|BLOCKING) */
5138 mono_threads_exit_gc_unsafe_region (cookie, dummy);
5140 if (orig != domain) {
5141 if (!orig)
5142 mono_domain_unset ();
5143 else
5144 mono_domain_set (orig, TRUE);
5149 #if 0
5150 /* Returns TRUE if the current thread is ready to be interrupted. */
5151 gboolean
5152 mono_threads_is_ready_to_be_interrupted (void)
5154 MonoInternalThread *thread;
5156 thread = mono_thread_internal_current ();
5157 LOCK_THREAD (thread);
5158 if (thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
5159 UNLOCK_THREAD (thread);
5160 return FALSE;
5163 if (mono_thread_get_abort_prot_block_count (thread) || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ()) {
5164 UNLOCK_THREAD (thread);
5165 return FALSE;
5168 UNLOCK_THREAD (thread);
5169 return TRUE;
5171 #endif
5173 void
5174 mono_thread_internal_describe (MonoInternalThread *internal, GString *text)
5176 g_string_append_printf (text, ", thread handle : %p", internal->handle);
5178 if (internal->thread_info) {
5179 g_string_append (text, ", state : ");
5180 mono_thread_info_describe_interrupt_token ((MonoThreadInfo*) internal->thread_info, text);
5183 if (internal->owned_mutexes) {
5184 int i;
5186 g_string_append (text, ", owns : [");
5187 for (i = 0; i < internal->owned_mutexes->len; i++)
5188 g_string_append_printf (text, i == 0 ? "%p" : ", %p", g_ptr_array_index (internal->owned_mutexes, i));
5189 g_string_append (text, "]");
5193 gboolean
5194 mono_thread_internal_is_current (MonoInternalThread *internal)
5196 g_assert (internal);
5197 return mono_native_thread_id_equals (mono_native_thread_id_get (), MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid));