[runtime] Rework abort deferring
[mono-project.git] / mono / metadata / threads.c
blob2ca989bea0fe9e62f10294ff76257355db71ea00
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);
264 static gboolean
265 mono_thread_state_has_interruption (gsize state)
267 /* pending exception, self abort */
268 if (state & INTERRUPT_SYNC_REQUESTED_BIT)
269 return TRUE;
271 /* abort, interruption, suspend */
272 if ((state & INTERRUPT_ASYNC_REQUESTED_BIT) && !(state & ABORT_PROT_BLOCK_MASK))
273 return TRUE;
275 return FALSE;
278 gboolean
279 mono_threads_end_abort_protected_block (void)
281 MonoInternalThread *thread = mono_thread_internal_current ();
282 gsize old_state, new_state;
283 do {
284 old_state = thread->thread_state;
286 //bounds check abort_prot_count
287 int new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) - 1;
288 g_assert (new_val >= 0);
289 g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
291 new_state = old_state - (1 << ABORT_PROT_BLOCK_SHIFT);
292 } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
294 return mono_thread_state_has_interruption (new_state);
297 static gboolean
298 mono_thread_get_interruption_requested (MonoInternalThread *thread)
300 gsize state = thread->thread_state;
302 return mono_thread_state_has_interruption (state);
306 * Returns TRUE is there was a state change
307 * We clear a single interruption request, sync has priority.
309 static gboolean
310 mono_thread_clear_interruption_requested (MonoInternalThread *thread)
312 gsize old_state, new_state;
313 do {
314 old_state = thread->thread_state;
316 // no interruption to process
317 if (!(old_state & INTERRUPT_SYNC_REQUESTED_BIT) &&
318 (!(old_state & INTERRUPT_ASYNC_REQUESTED_BIT) || (old_state & ABORT_PROT_BLOCK_MASK)))
319 return FALSE;
321 if (old_state & INTERRUPT_SYNC_REQUESTED_BIT)
322 new_state = old_state & ~INTERRUPT_SYNC_REQUESTED_BIT;
323 else
324 new_state = old_state & ~INTERRUPT_ASYNC_REQUESTED_BIT;
325 } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
326 return TRUE;
329 /* Returns TRUE is there was a state change and the interruption can be processed */
330 static gboolean
331 mono_thread_set_interruption_requested (MonoInternalThread *thread)
333 //always force when the current thread is doing it to itself.
334 gboolean sync = thread == mono_thread_internal_current ();
335 gsize old_state, new_state;
336 do {
337 old_state = thread->thread_state;
339 //Already set
340 if ((sync && (old_state & INTERRUPT_SYNC_REQUESTED_BIT)) ||
341 (!sync && (old_state & INTERRUPT_ASYNC_REQUESTED_BIT)))
342 return FALSE;
344 if (sync)
345 new_state = old_state | INTERRUPT_SYNC_REQUESTED_BIT;
346 else
347 new_state = old_state | INTERRUPT_ASYNC_REQUESTED_BIT;
348 } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
350 return sync || !(new_state & ABORT_PROT_BLOCK_MASK);
353 static inline MonoNativeThreadId
354 thread_get_tid (MonoInternalThread *thread)
356 /* We store the tid as a guint64 to keep the object layout constant between platforms */
357 return MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid);
360 static void ensure_synch_cs_set (MonoInternalThread *thread)
362 MonoCoopMutex *synch_cs;
364 if (thread->synch_cs != NULL) {
365 return;
368 synch_cs = g_new0 (MonoCoopMutex, 1);
369 mono_coop_mutex_init_recursive (synch_cs);
371 if (InterlockedCompareExchangePointer ((gpointer *)&thread->synch_cs,
372 synch_cs, NULL) != NULL) {
373 /* Another thread must have installed this CS */
374 mono_coop_mutex_destroy (synch_cs);
375 g_free (synch_cs);
379 static inline void
380 lock_thread (MonoInternalThread *thread)
382 if (!thread->synch_cs)
383 ensure_synch_cs_set (thread);
385 g_assert (thread->synch_cs);
387 mono_coop_mutex_lock (thread->synch_cs);
390 static inline void
391 unlock_thread (MonoInternalThread *thread)
393 mono_coop_mutex_unlock (thread->synch_cs);
396 static inline gboolean
397 is_appdomainunloaded_exception (MonoClass *klass)
399 return klass == mono_class_get_appdomain_unloaded_exception_class ();
402 static inline gboolean
403 is_threadabort_exception (MonoClass *klass)
405 return klass == mono_defaults.threadabortexception_class;
409 * A special static data offset (guint32) consists of 3 parts:
411 * [0] 6-bit index into the array of chunks.
412 * [6] 25-bit offset into the array.
413 * [31] Bit indicating thread or context static.
416 typedef union {
417 struct {
418 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
419 guint32 type : 1;
420 guint32 offset : 25;
421 guint32 index : 6;
422 #else
423 guint32 index : 6;
424 guint32 offset : 25;
425 guint32 type : 1;
426 #endif
427 } fields;
428 guint32 raw;
429 } SpecialStaticOffset;
431 #define SPECIAL_STATIC_OFFSET_TYPE_THREAD 0
432 #define SPECIAL_STATIC_OFFSET_TYPE_CONTEXT 1
434 #define MAKE_SPECIAL_STATIC_OFFSET(index, offset, type) \
435 ((SpecialStaticOffset) { .fields = { (index), (offset), (type) } }.raw)
436 #define ACCESS_SPECIAL_STATIC_OFFSET(x,f) \
437 (((SpecialStaticOffset *) &(x))->fields.f)
439 static gpointer
440 get_thread_static_data (MonoInternalThread *thread, guint32 offset)
442 g_assert (ACCESS_SPECIAL_STATIC_OFFSET (offset, type) == SPECIAL_STATIC_OFFSET_TYPE_THREAD);
444 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
445 int off = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
447 return ((char *) thread->static_data [idx]) + off;
450 static gpointer
451 get_context_static_data (MonoAppContext *ctx, guint32 offset)
453 g_assert (ACCESS_SPECIAL_STATIC_OFFSET (offset, type) == SPECIAL_STATIC_OFFSET_TYPE_CONTEXT);
455 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
456 int off = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
458 return ((char *) ctx->static_data [idx]) + off;
461 static MonoThread**
462 get_current_thread_ptr_for_domain (MonoDomain *domain, MonoInternalThread *thread)
464 static MonoClassField *current_thread_field = NULL;
466 guint32 offset;
468 if (!current_thread_field) {
469 current_thread_field = mono_class_get_field_from_name (mono_defaults.thread_class, "current_thread");
470 g_assert (current_thread_field);
473 mono_class_vtable (domain, mono_defaults.thread_class);
474 mono_domain_lock (domain);
475 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, current_thread_field));
476 mono_domain_unlock (domain);
477 g_assert (offset);
479 return (MonoThread **)get_thread_static_data (thread, offset);
482 static void
483 set_current_thread_for_domain (MonoDomain *domain, MonoInternalThread *thread, MonoThread *current)
485 MonoThread **current_thread_ptr = get_current_thread_ptr_for_domain (domain, thread);
487 g_assert (current->obj.vtable->domain == domain);
489 g_assert (!*current_thread_ptr);
490 *current_thread_ptr = current;
493 static MonoThread*
494 create_thread_object (MonoDomain *domain, MonoInternalThread *internal)
496 MonoThread *thread;
497 MonoVTable *vtable;
498 MonoError error;
500 vtable = mono_class_vtable (domain, mono_defaults.thread_class);
501 g_assert (vtable);
503 thread = (MonoThread*)mono_object_new_mature (vtable, &error);
504 /* only possible failure mode is OOM, from which we don't expect to recover. */
505 mono_error_assert_ok (&error);
507 MONO_OBJECT_SETREF (thread, internal_thread, internal);
509 return thread;
512 static MonoInternalThread*
513 create_internal_thread_object (void)
515 MonoError error;
516 MonoInternalThread *thread;
517 MonoVTable *vt;
519 vt = mono_class_vtable (mono_get_root_domain (), mono_defaults.internal_thread_class);
520 thread = (MonoInternalThread*) mono_object_new_mature (vt, &error);
521 /* only possible failure mode is OOM, from which we don't exect to recover */
522 mono_error_assert_ok (&error);
524 thread->synch_cs = g_new0 (MonoCoopMutex, 1);
525 mono_coop_mutex_init_recursive (thread->synch_cs);
527 thread->apartment_state = ThreadApartmentState_Unknown;
528 thread->managed_id = get_next_managed_thread_id ();
529 if (mono_gc_is_moving ()) {
530 thread->thread_pinning_ref = thread;
531 MONO_GC_REGISTER_ROOT_PINNING (thread->thread_pinning_ref, MONO_ROOT_SOURCE_THREADING, "thread pinning reference");
534 thread->priority = MONO_THREAD_PRIORITY_NORMAL;
536 thread->suspended = g_new0 (MonoOSEvent, 1);
537 mono_os_event_init (thread->suspended, TRUE);
539 return thread;
542 static void
543 mono_thread_internal_set_priority (MonoInternalThread *internal, MonoThreadPriority priority)
545 g_assert (internal);
547 g_assert (priority >= MONO_THREAD_PRIORITY_LOWEST);
548 g_assert (priority <= MONO_THREAD_PRIORITY_HIGHEST);
549 g_assert (MONO_THREAD_PRIORITY_LOWEST < MONO_THREAD_PRIORITY_HIGHEST);
551 #ifdef HOST_WIN32
552 BOOL res;
554 g_assert (internal->native_handle);
556 res = SetThreadPriority (internal->native_handle, priority - 2);
557 if (!res)
558 g_error ("%s: SetThreadPriority failed, error %d", __func__, GetLastError ());
559 #else /* HOST_WIN32 */
560 pthread_t tid;
561 int policy;
562 struct sched_param param;
563 gint res;
565 tid = thread_get_tid (internal);
567 res = pthread_getschedparam (tid, &policy, &param);
568 if (res != 0)
569 g_error ("%s: pthread_getschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
571 #ifdef _POSIX_PRIORITY_SCHEDULING
572 int max, min;
574 /* Necessary to get valid priority range */
576 min = sched_get_priority_min (policy);
577 max = sched_get_priority_max (policy);
579 if (max > 0 && min >= 0 && max > min) {
580 double srange, drange, sposition, dposition;
581 srange = MONO_THREAD_PRIORITY_HIGHEST - MONO_THREAD_PRIORITY_LOWEST;
582 drange = max - min;
583 sposition = priority - MONO_THREAD_PRIORITY_LOWEST;
584 dposition = (sposition / srange) * drange;
585 param.sched_priority = (int)(dposition + min);
586 } else
587 #endif
589 switch (policy) {
590 case SCHED_FIFO:
591 case SCHED_RR:
592 param.sched_priority = 50;
593 break;
594 #ifdef SCHED_BATCH
595 case SCHED_BATCH:
596 #endif
597 case SCHED_OTHER:
598 param.sched_priority = 0;
599 break;
600 default:
601 g_warning ("%s: unknown policy %d", __func__, policy);
602 return;
606 res = pthread_setschedparam (tid, policy, &param);
607 if (res != 0) {
608 if (res == EPERM) {
609 g_warning ("%s: pthread_setschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
610 return;
612 g_error ("%s: pthread_setschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
614 #endif /* HOST_WIN32 */
617 static void
618 mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean threadlocal);
620 static gboolean
621 mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean force_domain)
623 MonoThreadInfo *info;
624 MonoInternalThread *internal;
625 MonoDomain *domain, *root_domain;
627 g_assert (thread);
629 info = mono_thread_info_current ();
631 internal = thread->internal_thread;
632 internal->handle = mono_threads_open_thread_handle (info->handle);
633 #ifdef HOST_WIN32
634 internal->native_handle = OpenThread (THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId ());
635 #endif
636 internal->tid = MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ());
637 internal->thread_info = info;
638 internal->small_id = info->small_id;
640 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, mono_native_thread_id_get (), internal));
642 SET_CURRENT_OBJECT (internal);
644 domain = mono_object_domain (thread);
646 mono_thread_push_appdomain_ref (domain);
647 if (!mono_domain_set (domain, force_domain)) {
648 mono_thread_pop_appdomain_ref ();
649 return FALSE;
652 mono_threads_lock ();
654 if (threads_starting_up)
655 mono_g_hash_table_remove (threads_starting_up, thread);
657 if (shutting_down && !force_attach) {
658 mono_threads_unlock ();
659 mono_thread_pop_appdomain_ref ();
660 return FALSE;
663 if (!threads) {
664 threads = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_THREADING, "threads table");
667 /* We don't need to duplicate thread->handle, because it is
668 * only closed when the thread object is finalized by the GC. */
669 mono_g_hash_table_insert (threads, (gpointer)(gsize)(internal->tid), internal);
671 /* We have to do this here because mono_thread_start_cb
672 * requires that root_domain_thread is set up. */
673 if (thread_static_info.offset || thread_static_info.idx > 0) {
674 /* get the current allocated size */
675 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (thread_static_info.idx, thread_static_info.offset, 0);
676 mono_alloc_static_data (&internal->static_data, offset, TRUE);
679 mono_threads_unlock ();
681 root_domain = mono_get_root_domain ();
683 g_assert (!internal->root_domain_thread);
684 if (domain != root_domain)
685 MONO_OBJECT_SETREF (internal, root_domain_thread, create_thread_object (root_domain, internal));
686 else
687 MONO_OBJECT_SETREF (internal, root_domain_thread, thread);
689 if (domain != root_domain)
690 set_current_thread_for_domain (root_domain, internal, internal->root_domain_thread);
692 set_current_thread_for_domain (domain, internal, thread);
694 THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, internal->tid, internal->handle));
696 return TRUE;
699 typedef struct {
700 gint32 ref;
701 MonoThread *thread;
702 MonoObject *start_delegate;
703 MonoObject *start_delegate_arg;
704 MonoThreadStart start_func;
705 gpointer start_func_arg;
706 gboolean force_attach;
707 gboolean failed;
708 MonoCoopSem registered;
709 } StartInfo;
711 static guint32 WINAPI start_wrapper_internal(StartInfo *start_info, gsize *stack_ptr)
713 MonoError error;
714 MonoThreadStart start_func;
715 void *start_func_arg;
716 gsize tid;
718 * We don't create a local to hold start_info->thread, so hopefully it won't get pinned during a
719 * GC stack walk.
721 MonoThread *thread;
722 MonoInternalThread *internal;
723 MonoObject *start_delegate;
724 MonoObject *start_delegate_arg;
725 MonoDomain *domain;
727 thread = start_info->thread;
728 internal = thread->internal_thread;
729 domain = mono_object_domain (start_info->thread);
731 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper", __func__, mono_native_thread_id_get ()));
733 if (!mono_thread_attach_internal (thread, start_info->force_attach, FALSE)) {
734 start_info->failed = TRUE;
736 mono_coop_sem_post (&start_info->registered);
738 if (InterlockedDecrement (&start_info->ref) == 0) {
739 mono_coop_sem_destroy (&start_info->registered);
740 g_free (start_info);
743 return 0;
746 mono_thread_internal_set_priority (internal, internal->priority);
748 tid = internal->tid;
750 start_delegate = start_info->start_delegate;
751 start_delegate_arg = start_info->start_delegate_arg;
752 start_func = start_info->start_func;
753 start_func_arg = start_info->start_func_arg;
755 /* This MUST be called before any managed code can be
756 * executed, as it calls the callback function that (for the
757 * jit) sets the lmf marker.
760 if (mono_thread_start_cb)
761 mono_thread_start_cb (tid, stack_ptr, start_func);
763 /* On 2.0 profile (and higher), set explicitly since state might have been
764 Unknown */
765 if (internal->apartment_state == ThreadApartmentState_Unknown)
766 internal->apartment_state = ThreadApartmentState_MTA;
768 mono_thread_init_apartment_state ();
770 /* Let the thread that called Start() know we're ready */
771 mono_coop_sem_post (&start_info->registered);
773 if (InterlockedDecrement (&start_info->ref) == 0) {
774 mono_coop_sem_destroy (&start_info->registered);
775 g_free (start_info);
778 /* start_info is not valid anymore */
779 start_info = NULL;
782 * Call this after calling start_notify, since the profiler callback might want
783 * to lock the thread, and the lock is held by thread_start () which waits for
784 * start_notify.
786 mono_profiler_thread_start (tid);
788 /* if the name was set before starting, we didn't invoke the profiler callback */
789 if (internal->name) {
790 char *tname = g_utf16_to_utf8 (internal->name, internal->name_len, NULL, NULL, NULL);
791 mono_profiler_thread_name (internal->tid, tname);
792 mono_native_thread_set_name (MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid), tname);
793 g_free (tname);
796 /* start_func is set only for unmanaged start functions */
797 if (start_func) {
798 start_func (start_func_arg);
799 } else {
800 void *args [1];
802 g_assert (start_delegate != NULL);
804 /* we may want to handle the exception here. See comment below on unhandled exceptions */
805 args [0] = (gpointer) start_delegate_arg;
806 mono_runtime_delegate_invoke_checked (start_delegate, args, &error);
808 if (!mono_error_ok (&error)) {
809 MonoException *ex = mono_error_convert_to_exception (&error);
811 g_assert (ex != NULL);
812 MonoClass *klass = mono_object_get_class (&ex->object);
813 if ((mono_runtime_unhandled_exception_policy_get () != MONO_UNHANDLED_POLICY_LEGACY) &&
814 !is_threadabort_exception (klass)) {
815 mono_unhandled_exception (&ex->object);
816 mono_invoke_unhandled_exception_hook (&ex->object);
817 g_assert_not_reached ();
819 } else {
820 mono_error_cleanup (&error);
824 /* If the thread calls ExitThread at all, this remaining code
825 * will not be executed, but the main thread will eventually
826 * call mono_thread_detach_internal() on this thread's behalf.
829 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper terminating", __func__, mono_native_thread_id_get ()));
831 /* Do any cleanup needed for apartment state. This
832 * cannot be done in mono_thread_detach_internal since
833 * mono_thread_detach_internal could be called for a
834 * thread other than the current thread.
835 * mono_thread_cleanup_apartment_state cleans up apartment
836 * for the current thead */
837 mono_thread_cleanup_apartment_state ();
839 mono_thread_detach_internal (internal);
841 internal->tid = 0;
843 return(0);
846 static gsize WINAPI
847 start_wrapper (gpointer data)
849 StartInfo *start_info;
850 MonoThreadInfo *info;
851 gsize res;
853 start_info = (StartInfo*) data;
854 g_assert (start_info);
856 info = mono_thread_info_attach (&res);
857 info->runtime_thread = TRUE;
859 /* Run the actual main function of the thread */
860 res = start_wrapper_internal (start_info, &res);
862 mono_thread_info_exit (res);
864 g_assert_not_reached ();
868 * create_thread:
870 * Common thread creation code.
871 * LOCKING: Acquires the threads lock.
873 static gboolean
874 create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *start_delegate, MonoThreadStart start_func, gpointer start_func_arg,
875 MonoThreadCreateFlags flags, MonoError *error)
877 StartInfo *start_info = NULL;
878 MonoNativeThreadId tid;
879 gboolean ret;
880 gsize stack_set_size;
882 if (start_delegate)
883 g_assert (!start_func && !start_func_arg);
884 if (start_func)
885 g_assert (!start_delegate);
887 if (flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL) {
888 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER));
889 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE));
891 if (flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER) {
892 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL));
893 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE));
897 * Join joinable threads to prevent running out of threads since the finalizer
898 * thread might be blocked/backlogged.
900 mono_threads_join_threads ();
902 error_init (error);
904 mono_threads_lock ();
905 if (shutting_down && !(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE)) {
906 mono_threads_unlock ();
907 return FALSE;
909 if (threads_starting_up == NULL) {
910 threads_starting_up = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_VALUE_GC, MONO_ROOT_SOURCE_THREADING, "starting threads table");
912 mono_g_hash_table_insert (threads_starting_up, thread, thread);
913 mono_threads_unlock ();
915 internal->threadpool_thread = flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL;
916 if (internal->threadpool_thread)
917 mono_thread_set_state (internal, ThreadState_Background);
919 internal->debugger_thread = flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER;
921 start_info = g_new0 (StartInfo, 1);
922 start_info->ref = 2;
923 start_info->thread = thread;
924 start_info->start_delegate = start_delegate;
925 start_info->start_delegate_arg = thread->start_obj;
926 start_info->start_func = start_func;
927 start_info->start_func_arg = start_func_arg;
928 start_info->force_attach = flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE;
929 start_info->failed = FALSE;
930 mono_coop_sem_init (&start_info->registered, 0);
932 if (flags != MONO_THREAD_CREATE_FLAGS_SMALL_STACK)
933 stack_set_size = default_stacksize_for_thread (internal);
934 else
935 stack_set_size = 0;
937 if (!mono_thread_platform_create_thread (start_wrapper, start_info, &stack_set_size, &tid)) {
938 /* The thread couldn't be created, so set an exception */
939 mono_threads_lock ();
940 mono_g_hash_table_remove (threads_starting_up, thread);
941 mono_threads_unlock ();
942 mono_error_set_execution_engine (error, "Couldn't create thread. Error 0x%x", mono_w32error_get_last());
943 /* ref is not going to be decremented in start_wrapper_internal */
944 InterlockedDecrement (&start_info->ref);
945 ret = FALSE;
946 goto done;
949 internal->stack_size = (int) stack_set_size;
951 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Launching thread %p (%"G_GSIZE_FORMAT")", __func__, mono_native_thread_id_get (), internal, (gsize)internal->tid));
954 * Wait for the thread to set up its TLS data etc, so
955 * theres no potential race condition if someone tries
956 * to look up the data believing the thread has
957 * started
960 mono_coop_sem_wait (&start_info->registered, MONO_SEM_FLAGS_NONE);
962 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));
964 ret = !start_info->failed;
966 done:
967 if (InterlockedDecrement (&start_info->ref) == 0) {
968 mono_coop_sem_destroy (&start_info->registered);
969 g_free (start_info);
972 return ret;
975 void mono_thread_new_init (intptr_t tid, gpointer stack_start, gpointer func)
977 if (mono_thread_start_cb) {
978 mono_thread_start_cb (tid, stack_start, func);
982 void mono_threads_set_default_stacksize (guint32 stacksize)
984 default_stacksize = stacksize;
987 guint32 mono_threads_get_default_stacksize (void)
989 return default_stacksize;
993 * mono_thread_create_internal:
995 * ARG should not be a GC reference.
997 MonoInternalThread*
998 mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, MonoThreadCreateFlags flags, MonoError *error)
1000 MonoThread *thread;
1001 MonoInternalThread *internal;
1002 gboolean res;
1004 error_init (error);
1006 internal = create_internal_thread_object ();
1008 thread = create_thread_object (domain, internal);
1010 LOCK_THREAD (internal);
1012 res = create_thread (thread, internal, NULL, (MonoThreadStart) func, arg, flags, error);
1014 UNLOCK_THREAD (internal);
1016 return_val_if_nok (error, NULL);
1017 return internal;
1020 void
1021 mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
1023 MonoError error;
1024 if (!mono_thread_create_checked (domain, func, arg, &error))
1025 mono_error_cleanup (&error);
1028 gboolean
1029 mono_thread_create_checked (MonoDomain *domain, gpointer func, gpointer arg, MonoError *error)
1031 return (NULL != mono_thread_create_internal (domain, func, arg, MONO_THREAD_CREATE_FLAGS_NONE, error));
1034 MonoThread *
1035 mono_thread_attach (MonoDomain *domain)
1037 MonoThread *thread = mono_thread_attach_full (domain, FALSE);
1039 return thread;
1042 MonoThread *
1043 mono_thread_attach_full (MonoDomain *domain, gboolean force_attach)
1045 MonoInternalThread *internal;
1046 MonoThread *thread;
1047 MonoThreadInfo *info;
1048 MonoNativeThreadId tid;
1049 gsize stack_ptr;
1051 if (mono_thread_internal_current_is_attached ()) {
1052 if (domain != mono_domain_get ())
1053 mono_domain_set (domain, TRUE);
1054 /* Already attached */
1055 return mono_thread_current ();
1058 info = mono_thread_info_attach (&stack_ptr);
1059 g_assert (info);
1061 tid=mono_native_thread_id_get ();
1063 internal = create_internal_thread_object ();
1065 thread = create_thread_object (domain, internal);
1067 if (!mono_thread_attach_internal (thread, force_attach, TRUE)) {
1068 /* Mono is shutting down, so just wait for the end */
1069 for (;;)
1070 mono_thread_info_sleep (10000, NULL);
1073 THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, internal->handle));
1075 if (mono_thread_attach_cb) {
1076 guint8 *staddr;
1077 size_t stsize;
1079 mono_thread_info_get_stack_bounds (&staddr, &stsize);
1081 if (staddr == NULL)
1082 mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), &stack_ptr);
1083 else
1084 mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), staddr + stsize);
1087 /* Can happen when we attach the profiler helper thread in order to heapshot. */
1088 if (!mono_thread_info_current ()->tools_thread)
1089 // FIXME: Need a separate callback
1090 mono_profiler_thread_start (MONO_NATIVE_THREAD_ID_TO_UINT (tid));
1092 return thread;
1095 void
1096 mono_thread_detach_internal (MonoInternalThread *thread)
1098 gboolean removed;
1100 g_assert (thread != NULL);
1102 THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
1104 #ifndef HOST_WIN32
1105 mono_w32mutex_abandon ();
1106 #endif
1108 if (thread->abort_state_handle) {
1109 mono_gchandle_free (thread->abort_state_handle);
1110 thread->abort_state_handle = 0;
1113 thread->abort_exc = NULL;
1114 thread->current_appcontext = NULL;
1117 * thread->synch_cs can be NULL if this was called after
1118 * ves_icall_System_Threading_InternalThread_Thread_free_internal.
1119 * This can happen only during shutdown.
1120 * The shutting_down flag is not always set, so we can't assert on it.
1122 if (thread->synch_cs)
1123 LOCK_THREAD (thread);
1125 thread->state |= ThreadState_Stopped;
1126 thread->state &= ~ThreadState_Background;
1128 if (thread->synch_cs)
1129 UNLOCK_THREAD (thread);
1132 An interruption request has leaked to cleanup. Adjust the global counter.
1134 This can happen is the abort source thread finds the abortee (this) thread
1135 in unmanaged code. If this thread never trips back to managed code or check
1136 the local flag it will be left set and positively unbalance the global counter.
1138 Leaving the counter unbalanced will cause a performance degradation since all threads
1139 will now keep checking their local flags all the time.
1141 if (mono_thread_clear_interruption_requested (thread))
1142 InterlockedDecrement (&thread_interruption_requested);
1144 mono_threads_lock ();
1146 if (!threads) {
1147 removed = FALSE;
1148 } else if (mono_g_hash_table_lookup (threads, (gpointer)thread->tid) != thread) {
1149 /* We have to check whether the thread object for the
1150 * tid is still the same in the table because the
1151 * thread might have been destroyed and the tid reused
1152 * in the meantime, in which case the tid would be in
1153 * the table, but with another thread object.
1155 removed = FALSE;
1156 } else {
1157 mono_g_hash_table_remove (threads, (gpointer)thread->tid);
1158 removed = TRUE;
1161 mono_threads_unlock ();
1163 /* Don't close the handle here, wait for the object finalizer
1164 * to do it. Otherwise, the following race condition applies:
1166 * 1) Thread exits (and mono_thread_detach_internal() closes the handle)
1168 * 2) Some other handle is reassigned the same slot
1170 * 3) Another thread tries to join the first thread, and
1171 * blocks waiting for the reassigned handle to be signalled
1172 * (which might never happen). This is possible, because the
1173 * thread calling Join() still has a reference to the first
1174 * thread's object.
1177 /* if the thread is not in the hash it has been removed already */
1178 if (!removed) {
1179 mono_domain_unset ();
1180 mono_memory_barrier ();
1182 if (mono_thread_cleanup_fn)
1183 mono_thread_cleanup_fn (thread_get_tid (thread));
1185 goto done;
1188 mono_release_type_locks (thread);
1190 /* Can happen when we attach the profiler helper thread in order to heapshot. */
1191 if (!mono_thread_info_lookup (MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid))->tools_thread)
1192 mono_profiler_thread_end (thread->tid);
1194 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
1197 * This will signal async signal handlers that the thread has exited.
1198 * The profiler callback needs this to be set, so it cannot be done earlier.
1200 mono_domain_unset ();
1201 mono_memory_barrier ();
1203 if (thread == mono_thread_internal_current ())
1204 mono_thread_pop_appdomain_ref ();
1206 mono_free_static_data (thread->static_data);
1207 thread->static_data = NULL;
1208 ref_stack_destroy (thread->appdomain_refs);
1209 thread->appdomain_refs = NULL;
1211 g_assert (thread->suspended);
1212 mono_os_event_destroy (thread->suspended);
1213 g_free (thread->suspended);
1214 thread->suspended = NULL;
1216 if (mono_thread_cleanup_fn)
1217 mono_thread_cleanup_fn (thread_get_tid (thread));
1219 mono_memory_barrier ();
1221 if (mono_gc_is_moving ()) {
1222 MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref);
1223 thread->thread_pinning_ref = NULL;
1226 done:
1227 SET_CURRENT_OBJECT (NULL);
1228 mono_domain_unset ();
1230 /* Don't need to close the handle to this thread, even though we took a
1231 * reference in mono_thread_attach (), because the GC will do it
1232 * when the Thread object is finalised.
1236 void
1237 mono_thread_detach (MonoThread *thread)
1239 if (thread)
1240 mono_thread_detach_internal (thread->internal_thread);
1244 * mono_thread_detach_if_exiting:
1246 * Detach the current thread from the runtime if it is exiting, i.e. it is running pthread dtors.
1247 * This should be used at the end of embedding code which calls into managed code, and which
1248 * can be called from pthread dtors, like dealloc: implementations in objective-c.
1250 mono_bool
1251 mono_thread_detach_if_exiting (void)
1253 if (mono_thread_info_is_exiting ()) {
1254 MonoInternalThread *thread;
1256 thread = mono_thread_internal_current ();
1257 if (thread) {
1258 mono_thread_detach_internal (thread);
1259 mono_thread_info_detach ();
1260 return TRUE;
1263 return FALSE;
1266 gboolean
1267 mono_thread_internal_current_is_attached (void)
1269 MonoInternalThread *internal;
1271 internal = GET_CURRENT_OBJECT ();
1272 if (!internal)
1273 return FALSE;
1275 return TRUE;
1278 void
1279 mono_thread_exit (void)
1281 MonoInternalThread *thread = mono_thread_internal_current ();
1283 THREAD_DEBUG (g_message ("%s: mono_thread_exit for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
1285 mono_thread_detach_internal (thread);
1287 /* we could add a callback here for embedders to use. */
1288 if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread))
1289 exit (mono_environment_exitcode_get ());
1291 mono_thread_info_exit (0);
1294 void
1295 ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this_obj)
1297 MonoInternalThread *internal;
1299 internal = create_internal_thread_object ();
1301 internal->state = ThreadState_Unstarted;
1303 InterlockedCompareExchangePointer ((volatile gpointer *)&this_obj->internal_thread, internal, NULL);
1306 MonoThread *
1307 ves_icall_System_Threading_Thread_GetCurrentThread (void)
1309 return mono_thread_current ();
1312 HANDLE
1313 ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this_obj,
1314 MonoObject *start)
1316 MonoError error;
1317 MonoInternalThread *internal;
1318 gboolean res;
1320 THREAD_DEBUG (g_message("%s: Trying to start a new thread: this (%p) start (%p)", __func__, this_obj, start));
1322 if (!this_obj->internal_thread)
1323 ves_icall_System_Threading_Thread_ConstructInternalThread (this_obj);
1324 internal = this_obj->internal_thread;
1326 LOCK_THREAD (internal);
1328 if ((internal->state & ThreadState_Unstarted) == 0) {
1329 UNLOCK_THREAD (internal);
1330 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has already been started."));
1331 return NULL;
1334 if ((internal->state & ThreadState_Aborted) != 0) {
1335 UNLOCK_THREAD (internal);
1336 return this_obj;
1339 res = create_thread (this_obj, internal, start, NULL, NULL, MONO_THREAD_CREATE_FLAGS_NONE, &error);
1340 if (!res) {
1341 mono_error_cleanup (&error);
1342 UNLOCK_THREAD (internal);
1343 return NULL;
1346 internal->state &= ~ThreadState_Unstarted;
1348 THREAD_DEBUG (g_message ("%s: Started thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread));
1350 UNLOCK_THREAD (internal);
1351 return internal->handle;
1355 * This is called from the finalizer of the internal thread object.
1357 void
1358 ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThread *this_obj)
1360 THREAD_DEBUG (g_message ("%s: Closing thread %p, handle %p", __func__, this, this_obj->handle));
1363 * Since threads keep a reference to their thread object while running, by
1364 * the time this function is called, the thread has already exited/detached,
1365 * i.e. mono_thread_detach_internal () has ran. The exception is during
1366 * shutdown, when mono_thread_detach_internal () can be called after this.
1368 if (this_obj->handle) {
1369 mono_threads_close_thread_handle (this_obj->handle);
1370 this_obj->handle = NULL;
1373 #if HOST_WIN32
1374 CloseHandle (this_obj->native_handle);
1375 #endif
1377 if (this_obj->synch_cs) {
1378 MonoCoopMutex *synch_cs = this_obj->synch_cs;
1379 this_obj->synch_cs = NULL;
1380 mono_coop_mutex_destroy (synch_cs);
1381 g_free (synch_cs);
1384 if (this_obj->name) {
1385 void *name = this_obj->name;
1386 this_obj->name = NULL;
1387 g_free (name);
1391 void
1392 ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
1394 guint32 res;
1395 MonoInternalThread *thread = mono_thread_internal_current ();
1397 THREAD_DEBUG (g_message ("%s: Sleeping for %d ms", __func__, ms));
1399 if (mono_thread_current_check_pending_interrupt ())
1400 return;
1402 while (TRUE) {
1403 gboolean alerted = FALSE;
1405 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1407 res = mono_thread_info_sleep (ms, &alerted);
1409 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1411 if (alerted) {
1412 MonoException* exc = mono_thread_execute_interruption ();
1413 if (exc) {
1414 mono_raise_exception (exc);
1415 } else {
1416 // FIXME: !MONO_INFINITE_WAIT
1417 if (ms != MONO_INFINITE_WAIT)
1418 break;
1420 } else {
1421 break;
1426 void ves_icall_System_Threading_Thread_SpinWait_nop (void)
1430 gint32
1431 ves_icall_System_Threading_Thread_GetDomainID (void)
1433 return mono_domain_get()->domain_id;
1436 gboolean
1437 ves_icall_System_Threading_Thread_Yield (void)
1439 return mono_thread_info_yield ();
1443 * mono_thread_get_name:
1445 * Return the name of the thread. NAME_LEN is set to the length of the name.
1446 * Return NULL if the thread has no name. The returned memory is owned by the
1447 * caller.
1449 gunichar2*
1450 mono_thread_get_name (MonoInternalThread *this_obj, guint32 *name_len)
1452 gunichar2 *res;
1454 LOCK_THREAD (this_obj);
1456 if (!this_obj->name) {
1457 *name_len = 0;
1458 res = NULL;
1459 } else {
1460 *name_len = this_obj->name_len;
1461 res = g_new (gunichar2, this_obj->name_len);
1462 memcpy (res, this_obj->name, sizeof (gunichar2) * this_obj->name_len);
1465 UNLOCK_THREAD (this_obj);
1467 return res;
1471 * mono_thread_get_name_utf8:
1473 * Return the name of the thread in UTF-8.
1474 * Return NULL if the thread has no name.
1475 * The returned memory is owned by the caller.
1477 char *
1478 mono_thread_get_name_utf8 (MonoThread *thread)
1480 if (thread == NULL)
1481 return NULL;
1483 MonoInternalThread *internal = thread->internal_thread;
1484 if (internal == NULL)
1485 return NULL;
1487 LOCK_THREAD (internal);
1489 char *tname = g_utf16_to_utf8 (internal->name, internal->name_len, NULL, NULL, NULL);
1491 UNLOCK_THREAD (internal);
1493 return tname;
1497 * mono_thread_get_managed_id:
1499 * Return the Thread.ManagedThreadId value of `thread`.
1500 * Returns -1 if `thread` is NULL.
1502 int32_t
1503 mono_thread_get_managed_id (MonoThread *thread)
1505 if (thread == NULL)
1506 return -1;
1508 MonoInternalThread *internal = thread->internal_thread;
1509 if (internal == NULL)
1510 return -1;
1512 int32_t id = internal->managed_id;
1514 return id;
1517 MonoString*
1518 ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThread *this_obj)
1520 MonoError error;
1521 MonoString* str;
1523 error_init (&error);
1525 LOCK_THREAD (this_obj);
1527 if (!this_obj->name)
1528 str = NULL;
1529 else
1530 str = mono_string_new_utf16_checked (mono_domain_get (), this_obj->name, this_obj->name_len, &error);
1532 UNLOCK_THREAD (this_obj);
1534 if (mono_error_set_pending_exception (&error))
1535 return NULL;
1537 return str;
1540 void
1541 mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, gboolean reset, MonoError *error)
1543 LOCK_THREAD (this_obj);
1545 error_init (error);
1547 if (reset) {
1548 this_obj->flags &= ~MONO_THREAD_FLAG_NAME_SET;
1549 } else if (this_obj->flags & MONO_THREAD_FLAG_NAME_SET) {
1550 UNLOCK_THREAD (this_obj);
1552 mono_error_set_invalid_operation (error, "Thread.Name can only be set once.");
1553 return;
1555 if (this_obj->name) {
1556 g_free (this_obj->name);
1557 this_obj->name_len = 0;
1559 if (name) {
1560 this_obj->name = g_memdup (mono_string_chars (name), mono_string_length (name) * sizeof (gunichar2));
1561 this_obj->name_len = mono_string_length (name);
1563 if (permanent)
1564 this_obj->flags |= MONO_THREAD_FLAG_NAME_SET;
1566 else
1567 this_obj->name = NULL;
1570 UNLOCK_THREAD (this_obj);
1572 if (this_obj->name && this_obj->tid) {
1573 char *tname = mono_string_to_utf8_checked (name, error);
1574 return_if_nok (error);
1575 mono_profiler_thread_name (this_obj->tid, tname);
1576 mono_native_thread_set_name (thread_get_tid (this_obj), tname);
1577 mono_free (tname);
1581 void
1582 ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj, MonoString *name)
1584 MonoError error;
1585 mono_thread_set_name_internal (this_obj, name, TRUE, FALSE, &error);
1586 mono_error_set_pending_exception (&error);
1590 * ves_icall_System_Threading_Thread_GetPriority_internal:
1591 * @param this_obj: The MonoInternalThread on which to operate.
1593 * Gets the priority of the given thread.
1594 * @return: The priority of the given thread.
1597 ves_icall_System_Threading_Thread_GetPriority (MonoThread *this_obj)
1599 gint32 priority;
1600 MonoInternalThread *internal = this_obj->internal_thread;
1602 LOCK_THREAD (internal);
1603 priority = internal->priority;
1604 UNLOCK_THREAD (internal);
1606 return priority;
1610 * ves_icall_System_Threading_Thread_SetPriority_internal:
1611 * @param this_obj: The MonoInternalThread on which to operate.
1612 * @param priority: The priority to set.
1614 * Sets the priority of the given thread.
1616 void
1617 ves_icall_System_Threading_Thread_SetPriority (MonoThread *this_obj, int priority)
1619 MonoInternalThread *internal = this_obj->internal_thread;
1621 LOCK_THREAD (internal);
1622 internal->priority = priority;
1623 if (internal->thread_info != NULL)
1624 mono_thread_internal_set_priority (internal, priority);
1625 UNLOCK_THREAD (internal);
1628 /* If the array is already in the requested domain, we just return it,
1629 otherwise we return a copy in that domain. */
1630 static MonoArray*
1631 byte_array_to_domain (MonoArray *arr, MonoDomain *domain, MonoError *error)
1633 MonoArray *copy;
1635 error_init (error);
1636 if (!arr)
1637 return NULL;
1639 if (mono_object_domain (arr) == domain)
1640 return arr;
1642 copy = mono_array_new_checked (domain, mono_defaults.byte_class, arr->max_length, error);
1643 memmove (mono_array_addr (copy, guint8, 0), mono_array_addr (arr, guint8, 0), arr->max_length);
1644 return copy;
1647 MonoArray*
1648 ves_icall_System_Threading_Thread_ByteArrayToRootDomain (MonoArray *arr)
1650 MonoError error;
1651 MonoArray *result = byte_array_to_domain (arr, mono_get_root_domain (), &error);
1652 mono_error_set_pending_exception (&error);
1653 return result;
1656 MonoArray*
1657 ves_icall_System_Threading_Thread_ByteArrayToCurrentDomain (MonoArray *arr)
1659 MonoError error;
1660 MonoArray *result = byte_array_to_domain (arr, mono_domain_get (), &error);
1661 mono_error_set_pending_exception (&error);
1662 return result;
1665 MonoThread *
1666 mono_thread_current (void)
1668 MonoDomain *domain = mono_domain_get ();
1669 MonoInternalThread *internal = mono_thread_internal_current ();
1670 MonoThread **current_thread_ptr;
1672 g_assert (internal);
1673 current_thread_ptr = get_current_thread_ptr_for_domain (domain, internal);
1675 if (!*current_thread_ptr) {
1676 g_assert (domain != mono_get_root_domain ());
1677 *current_thread_ptr = create_thread_object (domain, internal);
1679 return *current_thread_ptr;
1682 /* Return the thread object belonging to INTERNAL in the current domain */
1683 static MonoThread *
1684 mono_thread_current_for_thread (MonoInternalThread *internal)
1686 MonoDomain *domain = mono_domain_get ();
1687 MonoThread **current_thread_ptr;
1689 g_assert (internal);
1690 current_thread_ptr = get_current_thread_ptr_for_domain (domain, internal);
1692 if (!*current_thread_ptr) {
1693 g_assert (domain != mono_get_root_domain ());
1694 *current_thread_ptr = create_thread_object (domain, internal);
1696 return *current_thread_ptr;
1699 MonoInternalThread*
1700 mono_thread_internal_current (void)
1702 MonoInternalThread *res = GET_CURRENT_OBJECT ();
1703 THREAD_DEBUG (g_message ("%s: returning %p", __func__, res));
1704 return res;
1707 static MonoThreadInfoWaitRet
1708 mono_join_uninterrupted (MonoThreadHandle* thread_to_join, gint32 ms, MonoError *error)
1710 MonoException *exc;
1711 MonoThreadInfoWaitRet ret;
1712 gint64 start;
1713 gint32 diff_ms;
1714 gint32 wait = ms;
1716 error_init (error);
1718 start = (ms == -1) ? 0 : mono_msec_ticks ();
1719 for (;;) {
1720 MONO_ENTER_GC_SAFE;
1721 ret = mono_thread_info_wait_one_handle (thread_to_join, ms, TRUE);
1722 MONO_EXIT_GC_SAFE;
1724 if (ret != MONO_THREAD_INFO_WAIT_RET_ALERTED)
1725 return ret;
1727 exc = mono_thread_execute_interruption ();
1728 if (exc) {
1729 mono_error_set_exception_instance (error, exc);
1730 return ret;
1733 if (ms == -1)
1734 continue;
1736 /* Re-calculate ms according to the time passed */
1737 diff_ms = (gint32)(mono_msec_ticks () - start);
1738 if (diff_ms >= ms) {
1739 ret = MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1740 return ret;
1742 wait = ms - diff_ms;
1745 return ret;
1748 gboolean
1749 ves_icall_System_Threading_Thread_Join_internal(MonoThread *this_obj, int ms)
1751 MonoInternalThread *thread = this_obj->internal_thread;
1752 MonoThreadHandle *handle = thread->handle;
1753 MonoInternalThread *cur_thread = mono_thread_internal_current ();
1754 gboolean ret;
1755 MonoError error;
1757 if (mono_thread_current_check_pending_interrupt ())
1758 return FALSE;
1760 LOCK_THREAD (thread);
1762 if ((thread->state & ThreadState_Unstarted) != 0) {
1763 UNLOCK_THREAD (thread);
1765 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started."));
1766 return FALSE;
1769 UNLOCK_THREAD (thread);
1771 if(ms== -1) {
1772 ms=MONO_INFINITE_WAIT;
1774 THREAD_DEBUG (g_message ("%s: joining thread handle %p, %d ms", __func__, handle, ms));
1776 mono_thread_set_state (cur_thread, ThreadState_WaitSleepJoin);
1778 ret=mono_join_uninterrupted (handle, ms, &error);
1780 mono_thread_clr_state (cur_thread, ThreadState_WaitSleepJoin);
1782 mono_error_set_pending_exception (&error);
1784 if(ret==MONO_THREAD_INFO_WAIT_RET_SUCCESS_0) {
1785 THREAD_DEBUG (g_message ("%s: join successful", __func__));
1787 return(TRUE);
1790 THREAD_DEBUG (g_message ("%s: join failed", __func__));
1792 return(FALSE);
1795 #define MANAGED_WAIT_FAILED 0x7fffffff
1797 static gint32
1798 map_native_wait_result_to_managed (MonoW32HandleWaitRet val, gsize numobjects)
1800 if (val >= MONO_W32HANDLE_WAIT_RET_SUCCESS_0 && val < MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + numobjects) {
1801 return WAIT_OBJECT_0 + (val - MONO_W32HANDLE_WAIT_RET_SUCCESS_0);
1802 } else if (val >= MONO_W32HANDLE_WAIT_RET_ABANDONED_0 && val < MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + numobjects) {
1803 return WAIT_ABANDONED_0 + (val - MONO_W32HANDLE_WAIT_RET_ABANDONED_0);
1804 } else if (val == MONO_W32HANDLE_WAIT_RET_ALERTED) {
1805 return WAIT_IO_COMPLETION;
1806 } else if (val == MONO_W32HANDLE_WAIT_RET_TIMEOUT) {
1807 return WAIT_TIMEOUT;
1808 } else if (val == MONO_W32HANDLE_WAIT_RET_FAILED) {
1809 /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
1810 return MANAGED_WAIT_FAILED;
1811 } else {
1812 g_error ("%s: unknown val value %d", __func__, val);
1816 static MonoW32HandleWaitRet
1817 mono_wait_uninterrupted (MonoInternalThread *thread, guint32 numhandles, gpointer *handles, gboolean waitall, gint32 ms, MonoError *error)
1819 MonoException *exc;
1820 MonoW32HandleWaitRet ret;
1821 gint64 start;
1822 gint32 diff_ms;
1823 gint32 wait = ms;
1825 error_init (error);
1827 start = (ms == -1) ? 0 : mono_100ns_ticks ();
1828 do {
1829 MONO_ENTER_GC_SAFE;
1830 #ifdef HOST_WIN32
1831 if (numhandles != 1)
1832 ret = mono_w32handle_convert_wait_ret (WaitForMultipleObjectsEx (numhandles, handles, waitall, wait, TRUE), numhandles);
1833 else
1834 ret = mono_w32handle_convert_wait_ret (WaitForSingleObjectEx (handles [0], ms, TRUE), 1);
1835 #else
1836 /* mono_w32handle_wait_multiple optimizes the case for numhandles == 1 */
1837 ret = mono_w32handle_wait_multiple (handles, numhandles, waitall, wait, TRUE);
1838 #endif /* HOST_WIN32 */
1839 MONO_EXIT_GC_SAFE;
1841 if (ret != MONO_W32HANDLE_WAIT_RET_ALERTED)
1842 break;
1844 exc = mono_thread_execute_interruption ();
1845 if (exc) {
1846 mono_error_set_exception_instance (error, exc);
1847 break;
1850 if (ms == -1)
1851 continue;
1853 /* Re-calculate ms according to the time passed */
1854 diff_ms = (gint32)((mono_100ns_ticks () - start) / 10000);
1855 if (diff_ms >= ms) {
1856 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1857 break;
1859 wait = ms - diff_ms;
1860 } while (TRUE);
1862 return ret;
1865 gint32 ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms)
1867 MonoError error;
1868 HANDLE *handles;
1869 guint32 numhandles;
1870 MonoW32HandleWaitRet ret;
1871 guint32 i;
1872 MonoObject *waitHandle;
1873 MonoInternalThread *thread = mono_thread_internal_current ();
1875 /* Do this WaitSleepJoin check before creating objects */
1876 if (mono_thread_current_check_pending_interrupt ())
1877 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
1879 /* We fail in managed if the array has more than 64 elements */
1880 numhandles = (guint32)mono_array_length(mono_handles);
1881 handles = g_new0(HANDLE, numhandles);
1883 for(i = 0; i < numhandles; i++) {
1884 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
1885 handles [i] = mono_wait_handle_get_handle ((MonoWaitHandle *) waitHandle);
1888 if(ms== -1) {
1889 ms=MONO_INFINITE_WAIT;
1892 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1894 ret = mono_wait_uninterrupted (thread, numhandles, handles, TRUE, ms, &error);
1896 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1898 g_free(handles);
1900 mono_error_set_pending_exception (&error);
1902 return map_native_wait_result_to_managed (ret, numhandles);
1905 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms)
1907 MonoError error;
1908 HANDLE handles [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
1909 uintptr_t numhandles;
1910 MonoW32HandleWaitRet ret;
1911 guint32 i;
1912 MonoObject *waitHandle;
1913 MonoInternalThread *thread = mono_thread_internal_current ();
1915 /* Do this WaitSleepJoin check before creating objects */
1916 if (mono_thread_current_check_pending_interrupt ())
1917 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
1919 numhandles = mono_array_length(mono_handles);
1920 if (numhandles > MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS)
1921 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
1923 for(i = 0; i < numhandles; i++) {
1924 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
1925 handles [i] = mono_wait_handle_get_handle ((MonoWaitHandle *) waitHandle);
1928 if(ms== -1) {
1929 ms=MONO_INFINITE_WAIT;
1932 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1934 ret = mono_wait_uninterrupted (thread, numhandles, handles, FALSE, ms, &error);
1936 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1938 THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") returning %d", __func__, mono_native_thread_id_get (), ret));
1940 mono_error_set_pending_exception (&error);
1942 return map_native_wait_result_to_managed (ret, numhandles);
1945 gint32 ves_icall_System_Threading_WaitHandle_WaitOne_internal(HANDLE handle, gint32 ms)
1947 MonoError error;
1948 MonoW32HandleWaitRet ret;
1949 MonoInternalThread *thread = mono_thread_internal_current ();
1951 THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") waiting for %p, %d ms", __func__, mono_native_thread_id_get (), handle, ms));
1953 if(ms== -1) {
1954 ms=MONO_INFINITE_WAIT;
1957 if (mono_thread_current_check_pending_interrupt ())
1958 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
1960 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1962 ret = mono_wait_uninterrupted (thread, 1, &handle, FALSE, ms, &error);
1964 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1966 mono_error_set_pending_exception (&error);
1967 return map_native_wait_result_to_managed (ret, 1);
1970 gint32
1971 ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (HANDLE toSignal, HANDLE toWait, gint32 ms)
1973 MonoW32HandleWaitRet ret;
1974 MonoInternalThread *thread = mono_thread_internal_current ();
1976 if (ms == -1)
1977 ms = MONO_INFINITE_WAIT;
1979 if (mono_thread_current_check_pending_interrupt ())
1980 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
1982 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1984 MONO_ENTER_GC_SAFE;
1985 #ifdef HOST_WIN32
1986 ret = mono_w32handle_convert_wait_ret (SignalObjectAndWait (toSignal, toWait, ms, TRUE), 1);
1987 #else
1988 ret = mono_w32handle_signal_and_wait (toSignal, toWait, ms, TRUE);
1989 #endif
1990 MONO_EXIT_GC_SAFE;
1992 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1994 return map_native_wait_result_to_managed (ret, 1);
1997 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
1999 return InterlockedIncrement (location);
2002 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
2004 #if SIZEOF_VOID_P == 4
2005 if (G_UNLIKELY ((size_t)location & 0x7)) {
2006 gint64 ret;
2007 mono_interlocked_lock ();
2008 (*location)++;
2009 ret = *location;
2010 mono_interlocked_unlock ();
2011 return ret;
2013 #endif
2014 return InterlockedIncrement64 (location);
2017 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
2019 return InterlockedDecrement(location);
2022 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
2024 #if SIZEOF_VOID_P == 4
2025 if (G_UNLIKELY ((size_t)location & 0x7)) {
2026 gint64 ret;
2027 mono_interlocked_lock ();
2028 (*location)--;
2029 ret = *location;
2030 mono_interlocked_unlock ();
2031 return ret;
2033 #endif
2034 return InterlockedDecrement64 (location);
2037 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location, gint32 value)
2039 return InterlockedExchange(location, value);
2042 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location, MonoObject *value)
2044 MonoObject *res;
2045 res = (MonoObject *) InterlockedExchangePointer((gpointer *) location, value);
2046 mono_gc_wbarrier_generic_nostore (location);
2047 return res;
2050 gpointer ves_icall_System_Threading_Interlocked_Exchange_IntPtr (gpointer *location, gpointer value)
2052 return InterlockedExchangePointer(location, value);
2055 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location, gfloat value)
2057 IntFloatUnion val, ret;
2059 val.fval = value;
2060 ret.ival = InterlockedExchange((gint32 *) location, val.ival);
2062 return ret.fval;
2065 gint64
2066 ves_icall_System_Threading_Interlocked_Exchange_Long (gint64 *location, gint64 value)
2068 #if SIZEOF_VOID_P == 4
2069 if (G_UNLIKELY ((size_t)location & 0x7)) {
2070 gint64 ret;
2071 mono_interlocked_lock ();
2072 ret = *location;
2073 *location = value;
2074 mono_interlocked_unlock ();
2075 return ret;
2077 #endif
2078 return InterlockedExchange64 (location, value);
2081 gdouble
2082 ves_icall_System_Threading_Interlocked_Exchange_Double (gdouble *location, gdouble value)
2084 LongDoubleUnion val, ret;
2086 val.fval = value;
2087 ret.ival = (gint64)InterlockedExchange64((gint64 *) location, val.ival);
2089 return ret.fval;
2092 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location, gint32 value, gint32 comparand)
2094 return InterlockedCompareExchange(location, value, comparand);
2097 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int_Success(gint32 *location, gint32 value, gint32 comparand, MonoBoolean *success)
2099 gint32 r = InterlockedCompareExchange(location, value, comparand);
2100 *success = r == comparand;
2101 return r;
2104 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location, MonoObject *value, MonoObject *comparand)
2106 MonoObject *res;
2107 res = (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location, value, comparand);
2108 mono_gc_wbarrier_generic_nostore (location);
2109 return res;
2112 gpointer ves_icall_System_Threading_Interlocked_CompareExchange_IntPtr(gpointer *location, gpointer value, gpointer comparand)
2114 return InterlockedCompareExchangePointer(location, value, comparand);
2117 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location, gfloat value, gfloat comparand)
2119 IntFloatUnion val, ret, cmp;
2121 val.fval = value;
2122 cmp.fval = comparand;
2123 ret.ival = InterlockedCompareExchange((gint32 *) location, val.ival, cmp.ival);
2125 return ret.fval;
2128 gdouble
2129 ves_icall_System_Threading_Interlocked_CompareExchange_Double (gdouble *location, gdouble value, gdouble comparand)
2131 #if SIZEOF_VOID_P == 8
2132 LongDoubleUnion val, comp, ret;
2134 val.fval = value;
2135 comp.fval = comparand;
2136 ret.ival = (gint64)InterlockedCompareExchangePointer((gpointer *) location, (gpointer)val.ival, (gpointer)comp.ival);
2138 return ret.fval;
2139 #else
2140 gdouble old;
2142 mono_interlocked_lock ();
2143 old = *location;
2144 if (old == comparand)
2145 *location = value;
2146 mono_interlocked_unlock ();
2148 return old;
2149 #endif
2152 gint64
2153 ves_icall_System_Threading_Interlocked_CompareExchange_Long (gint64 *location, gint64 value, gint64 comparand)
2155 #if SIZEOF_VOID_P == 4
2156 if (G_UNLIKELY ((size_t)location & 0x7)) {
2157 gint64 old;
2158 mono_interlocked_lock ();
2159 old = *location;
2160 if (old == comparand)
2161 *location = value;
2162 mono_interlocked_unlock ();
2163 return old;
2165 #endif
2166 return InterlockedCompareExchange64 (location, value, comparand);
2169 MonoObject*
2170 ves_icall_System_Threading_Interlocked_CompareExchange_T (MonoObject **location, MonoObject *value, MonoObject *comparand)
2172 MonoObject *res;
2173 res = (MonoObject *)InterlockedCompareExchangePointer ((volatile gpointer *)location, value, comparand);
2174 mono_gc_wbarrier_generic_nostore (location);
2175 return res;
2178 MonoObject*
2179 ves_icall_System_Threading_Interlocked_Exchange_T (MonoObject **location, MonoObject *value)
2181 MonoObject *res;
2182 MONO_CHECK_NULL (location, NULL);
2183 res = (MonoObject *)InterlockedExchangePointer ((volatile gpointer *)location, value);
2184 mono_gc_wbarrier_generic_nostore (location);
2185 return res;
2188 gint32
2189 ves_icall_System_Threading_Interlocked_Add_Int (gint32 *location, gint32 value)
2191 return InterlockedAdd (location, value);
2194 gint64
2195 ves_icall_System_Threading_Interlocked_Add_Long (gint64 *location, gint64 value)
2197 #if SIZEOF_VOID_P == 4
2198 if (G_UNLIKELY ((size_t)location & 0x7)) {
2199 gint64 ret;
2200 mono_interlocked_lock ();
2201 *location += value;
2202 ret = *location;
2203 mono_interlocked_unlock ();
2204 return ret;
2206 #endif
2207 return InterlockedAdd64 (location, value);
2210 gint64
2211 ves_icall_System_Threading_Interlocked_Read_Long (gint64 *location)
2213 #if SIZEOF_VOID_P == 4
2214 if (G_UNLIKELY ((size_t)location & 0x7)) {
2215 gint64 ret;
2216 mono_interlocked_lock ();
2217 ret = *location;
2218 mono_interlocked_unlock ();
2219 return ret;
2221 #endif
2222 return InterlockedRead64 (location);
2225 void
2226 ves_icall_System_Threading_Thread_MemoryBarrier (void)
2228 mono_memory_barrier ();
2231 void
2232 ves_icall_System_Threading_Thread_ClrState (MonoInternalThread* this_obj, guint32 state)
2234 mono_thread_clr_state (this_obj, (MonoThreadState)state);
2236 if (state & ThreadState_Background) {
2237 /* If the thread changes the background mode, the main thread has to
2238 * be notified, since it has to rebuild the list of threads to
2239 * wait for.
2241 mono_os_event_set (&background_change_event);
2245 void
2246 ves_icall_System_Threading_Thread_SetState (MonoInternalThread* this_obj, guint32 state)
2248 mono_thread_set_state (this_obj, (MonoThreadState)state);
2250 if (state & ThreadState_Background) {
2251 /* If the thread changes the background mode, the main thread has to
2252 * be notified, since it has to rebuild the list of threads to
2253 * wait for.
2255 mono_os_event_set (&background_change_event);
2259 guint32
2260 ves_icall_System_Threading_Thread_GetState (MonoInternalThread* this_obj)
2262 guint32 state;
2264 LOCK_THREAD (this_obj);
2266 state = this_obj->state;
2268 UNLOCK_THREAD (this_obj);
2270 return state;
2273 void ves_icall_System_Threading_Thread_Interrupt_internal (MonoThread *this_obj)
2275 MonoInternalThread *current;
2276 gboolean throw_;
2277 MonoInternalThread *thread = this_obj->internal_thread;
2279 LOCK_THREAD (thread);
2281 current = mono_thread_internal_current ();
2283 thread->thread_interrupt_requested = TRUE;
2284 throw_ = current != thread && (thread->state & ThreadState_WaitSleepJoin);
2286 UNLOCK_THREAD (thread);
2288 if (throw_) {
2289 async_abort_internal (thread, FALSE);
2294 * mono_thread_current_check_pending_interrupt:
2296 * Checks if there's a interruption request and set the pending exception if so.
2298 * @returns true if a pending exception was set
2300 gboolean
2301 mono_thread_current_check_pending_interrupt (void)
2303 MonoInternalThread *thread = mono_thread_internal_current ();
2304 gboolean throw_ = FALSE;
2306 LOCK_THREAD (thread);
2308 if (thread->thread_interrupt_requested) {
2309 throw_ = TRUE;
2310 thread->thread_interrupt_requested = FALSE;
2313 UNLOCK_THREAD (thread);
2315 if (throw_)
2316 mono_set_pending_exception (mono_get_exception_thread_interrupted ());
2317 return throw_;
2320 static gboolean
2321 request_thread_abort (MonoInternalThread *thread, MonoObject *state)
2323 LOCK_THREAD (thread);
2325 if (thread->state & (ThreadState_AbortRequested | ThreadState_Stopped))
2327 UNLOCK_THREAD (thread);
2328 return FALSE;
2331 if ((thread->state & ThreadState_Unstarted) != 0) {
2332 thread->state |= ThreadState_Aborted;
2333 UNLOCK_THREAD (thread);
2334 return FALSE;
2337 thread->state |= ThreadState_AbortRequested;
2338 if (thread->abort_state_handle)
2339 mono_gchandle_free (thread->abort_state_handle);
2340 if (state) {
2341 thread->abort_state_handle = mono_gchandle_new (state, FALSE);
2342 g_assert (thread->abort_state_handle);
2343 } else {
2344 thread->abort_state_handle = 0;
2346 thread->abort_exc = NULL;
2348 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));
2350 /* During shutdown, we can't wait for other threads */
2351 if (!shutting_down)
2352 /* Make sure the thread is awake */
2353 mono_thread_resume (thread);
2355 UNLOCK_THREAD (thread);
2356 return TRUE;
2359 void
2360 ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject *state)
2362 if (!request_thread_abort (thread, state))
2363 return;
2365 if (thread == mono_thread_internal_current ()) {
2366 MonoError error;
2367 self_abort_internal (&error);
2368 mono_error_set_pending_exception (&error);
2369 } else {
2370 async_abort_internal (thread, TRUE);
2375 * mono_thread_internal_abort:
2377 * Request thread @thread to be aborted.
2379 * @thread MUST NOT be the current thread.
2381 void
2382 mono_thread_internal_abort (MonoInternalThread *thread)
2384 g_assert (thread != mono_thread_internal_current ());
2386 if (!request_thread_abort (thread, NULL))
2387 return;
2388 async_abort_internal (thread, TRUE);
2391 void
2392 ves_icall_System_Threading_Thread_ResetAbort (MonoThread *this_obj)
2394 MonoInternalThread *thread = mono_thread_internal_current ();
2395 gboolean was_aborting;
2397 LOCK_THREAD (thread);
2398 was_aborting = thread->state & ThreadState_AbortRequested;
2399 thread->state &= ~ThreadState_AbortRequested;
2400 UNLOCK_THREAD (thread);
2402 if (!was_aborting) {
2403 const char *msg = "Unable to reset abort because no abort was requested";
2404 mono_set_pending_exception (mono_get_exception_thread_state (msg));
2405 return;
2408 mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
2409 thread->abort_exc = NULL;
2410 if (thread->abort_state_handle) {
2411 mono_gchandle_free (thread->abort_state_handle);
2412 /* This is actually not necessary - the handle
2413 only counts if the exception is set */
2414 thread->abort_state_handle = 0;
2418 void
2419 mono_thread_internal_reset_abort (MonoInternalThread *thread)
2421 LOCK_THREAD (thread);
2423 thread->state &= ~ThreadState_AbortRequested;
2425 if (thread->abort_exc) {
2426 mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
2427 thread->abort_exc = NULL;
2428 if (thread->abort_state_handle) {
2429 mono_gchandle_free (thread->abort_state_handle);
2430 /* This is actually not necessary - the handle
2431 only counts if the exception is set */
2432 thread->abort_state_handle = 0;
2436 UNLOCK_THREAD (thread);
2439 MonoObject*
2440 ves_icall_System_Threading_Thread_GetAbortExceptionState (MonoThread *this_obj)
2442 MonoError error;
2443 MonoInternalThread *thread = this_obj->internal_thread;
2444 MonoObject *state, *deserialized = NULL;
2445 MonoDomain *domain;
2447 if (!thread->abort_state_handle)
2448 return NULL;
2450 state = mono_gchandle_get_target (thread->abort_state_handle);
2451 g_assert (state);
2453 domain = mono_domain_get ();
2454 if (mono_object_domain (state) == domain)
2455 return state;
2457 deserialized = mono_object_xdomain_representation (state, domain, &error);
2459 if (!deserialized) {
2460 MonoException *invalid_op_exc = mono_get_exception_invalid_operation ("Thread.ExceptionState cannot access an ExceptionState from a different AppDomain");
2461 if (!is_ok (&error)) {
2462 MonoObject *exc = (MonoObject*)mono_error_convert_to_exception (&error);
2463 MONO_OBJECT_SETREF (invalid_op_exc, inner_ex, exc);
2465 mono_set_pending_exception (invalid_op_exc);
2466 return NULL;
2469 return deserialized;
2472 static gboolean
2473 mono_thread_suspend (MonoInternalThread *thread)
2475 LOCK_THREAD (thread);
2477 if (thread->state & (ThreadState_Unstarted | ThreadState_Aborted | ThreadState_Stopped))
2479 UNLOCK_THREAD (thread);
2480 return FALSE;
2483 if (thread->state & (ThreadState_Suspended | ThreadState_SuspendRequested | ThreadState_AbortRequested))
2485 UNLOCK_THREAD (thread);
2486 return TRUE;
2489 thread->state |= ThreadState_SuspendRequested;
2490 mono_os_event_reset (thread->suspended);
2492 if (thread == mono_thread_internal_current ()) {
2493 /* calls UNLOCK_THREAD (thread) */
2494 self_suspend_internal ();
2495 } else {
2496 /* calls UNLOCK_THREAD (thread) */
2497 async_suspend_internal (thread, FALSE);
2500 return TRUE;
2503 void
2504 ves_icall_System_Threading_Thread_Suspend (MonoThread *this_obj)
2506 if (!mono_thread_suspend (this_obj->internal_thread)) {
2507 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
2508 return;
2512 /* LOCKING: LOCK_THREAD(thread) must be held */
2513 static gboolean
2514 mono_thread_resume (MonoInternalThread *thread)
2516 if ((thread->state & ThreadState_SuspendRequested) != 0) {
2517 // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (1) thread %p\n", thread_get_tid (thread));
2518 thread->state &= ~ThreadState_SuspendRequested;
2519 mono_os_event_set (thread->suspended);
2520 return TRUE;
2523 if ((thread->state & ThreadState_Suspended) == 0 ||
2524 (thread->state & ThreadState_Unstarted) != 0 ||
2525 (thread->state & ThreadState_Aborted) != 0 ||
2526 (thread->state & ThreadState_Stopped) != 0)
2528 // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (2) thread %p\n", thread_get_tid (thread));
2529 return FALSE;
2532 // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (3) thread %p\n", thread_get_tid (thread));
2534 mono_os_event_set (thread->suspended);
2536 if (!thread->self_suspended) {
2537 UNLOCK_THREAD (thread);
2539 /* Awake the thread */
2540 if (!mono_thread_info_resume (thread_get_tid (thread)))
2541 return FALSE;
2543 LOCK_THREAD (thread);
2546 thread->state &= ~ThreadState_Suspended;
2548 return TRUE;
2551 void
2552 ves_icall_System_Threading_Thread_Resume (MonoThread *thread)
2554 if (!thread->internal_thread) {
2555 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
2556 } else {
2557 LOCK_THREAD (thread->internal_thread);
2558 if (!mono_thread_resume (thread->internal_thread))
2559 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
2560 UNLOCK_THREAD (thread->internal_thread);
2564 static gboolean
2565 mono_threads_is_critical_method (MonoMethod *method)
2567 switch (method->wrapper_type) {
2568 case MONO_WRAPPER_RUNTIME_INVOKE:
2569 case MONO_WRAPPER_XDOMAIN_INVOKE:
2570 case MONO_WRAPPER_XDOMAIN_DISPATCH:
2571 return TRUE;
2573 return FALSE;
2576 static gboolean
2577 find_wrapper (MonoMethod *m, gint no, gint ilo, gboolean managed, gpointer data)
2579 if (managed)
2580 return TRUE;
2582 if (mono_threads_is_critical_method (m)) {
2583 *((gboolean*)data) = TRUE;
2584 return TRUE;
2586 return FALSE;
2589 static gboolean
2590 is_running_protected_wrapper (void)
2592 gboolean found = FALSE;
2593 mono_stack_walk (find_wrapper, &found);
2594 return found;
2597 void
2598 mono_thread_stop (MonoThread *thread)
2600 MonoInternalThread *internal = thread->internal_thread;
2602 if (!request_thread_abort (internal, NULL))
2603 return;
2605 if (internal == mono_thread_internal_current ()) {
2606 MonoError error;
2607 self_abort_internal (&error);
2609 This function is part of the embeding API and has no way to return the exception
2610 to be thrown. So what we do is keep the old behavior and raise the exception.
2612 mono_error_raise_exception (&error); /* OK to throw, see note */
2613 } else {
2614 async_abort_internal (internal, TRUE);
2618 gint8
2619 ves_icall_System_Threading_Thread_VolatileRead1 (void *ptr)
2621 gint8 tmp = *(volatile gint8 *)ptr;
2622 mono_memory_barrier ();
2623 return tmp;
2626 gint16
2627 ves_icall_System_Threading_Thread_VolatileRead2 (void *ptr)
2629 gint16 tmp = *(volatile gint16 *)ptr;
2630 mono_memory_barrier ();
2631 return tmp;
2634 gint32
2635 ves_icall_System_Threading_Thread_VolatileRead4 (void *ptr)
2637 gint32 tmp = *(volatile gint32 *)ptr;
2638 mono_memory_barrier ();
2639 return tmp;
2642 gint64
2643 ves_icall_System_Threading_Thread_VolatileRead8 (void *ptr)
2645 gint64 tmp = *(volatile gint64 *)ptr;
2646 mono_memory_barrier ();
2647 return tmp;
2650 void *
2651 ves_icall_System_Threading_Thread_VolatileReadIntPtr (void *ptr)
2653 volatile void *tmp = *(volatile void **)ptr;
2654 mono_memory_barrier ();
2655 return (void *) tmp;
2658 void *
2659 ves_icall_System_Threading_Thread_VolatileReadObject (void *ptr)
2661 volatile MonoObject *tmp = *(volatile MonoObject **)ptr;
2662 mono_memory_barrier ();
2663 return (MonoObject *) tmp;
2666 double
2667 ves_icall_System_Threading_Thread_VolatileReadDouble (void *ptr)
2669 double tmp = *(volatile double *)ptr;
2670 mono_memory_barrier ();
2671 return tmp;
2674 float
2675 ves_icall_System_Threading_Thread_VolatileReadFloat (void *ptr)
2677 float tmp = *(volatile float *)ptr;
2678 mono_memory_barrier ();
2679 return tmp;
2682 gint8
2683 ves_icall_System_Threading_Volatile_Read1 (void *ptr)
2685 return InterlockedRead8 ((volatile gint8 *)ptr);
2688 gint16
2689 ves_icall_System_Threading_Volatile_Read2 (void *ptr)
2691 return InterlockedRead16 ((volatile gint16 *)ptr);
2694 gint32
2695 ves_icall_System_Threading_Volatile_Read4 (void *ptr)
2697 return InterlockedRead ((volatile gint32 *)ptr);
2700 gint64
2701 ves_icall_System_Threading_Volatile_Read8 (void *ptr)
2703 #if SIZEOF_VOID_P == 4
2704 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2705 gint64 val;
2706 mono_interlocked_lock ();
2707 val = *(gint64*)ptr;
2708 mono_interlocked_unlock ();
2709 return val;
2711 #endif
2712 return InterlockedRead64 ((volatile gint64 *)ptr);
2715 void *
2716 ves_icall_System_Threading_Volatile_ReadIntPtr (void *ptr)
2718 return InterlockedReadPointer ((volatile gpointer *)ptr);
2721 double
2722 ves_icall_System_Threading_Volatile_ReadDouble (void *ptr)
2724 LongDoubleUnion u;
2726 #if SIZEOF_VOID_P == 4
2727 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2728 double val;
2729 mono_interlocked_lock ();
2730 val = *(double*)ptr;
2731 mono_interlocked_unlock ();
2732 return val;
2734 #endif
2736 u.ival = InterlockedRead64 ((volatile gint64 *)ptr);
2738 return u.fval;
2741 float
2742 ves_icall_System_Threading_Volatile_ReadFloat (void *ptr)
2744 IntFloatUnion u;
2746 u.ival = InterlockedRead ((volatile gint32 *)ptr);
2748 return u.fval;
2751 MonoObject*
2752 ves_icall_System_Threading_Volatile_Read_T (void *ptr)
2754 return (MonoObject *)InterlockedReadPointer ((volatile gpointer *)ptr);
2757 void
2758 ves_icall_System_Threading_Thread_VolatileWrite1 (void *ptr, gint8 value)
2760 mono_memory_barrier ();
2761 *(volatile gint8 *)ptr = value;
2764 void
2765 ves_icall_System_Threading_Thread_VolatileWrite2 (void *ptr, gint16 value)
2767 mono_memory_barrier ();
2768 *(volatile gint16 *)ptr = value;
2771 void
2772 ves_icall_System_Threading_Thread_VolatileWrite4 (void *ptr, gint32 value)
2774 mono_memory_barrier ();
2775 *(volatile gint32 *)ptr = value;
2778 void
2779 ves_icall_System_Threading_Thread_VolatileWrite8 (void *ptr, gint64 value)
2781 mono_memory_barrier ();
2782 *(volatile gint64 *)ptr = value;
2785 void
2786 ves_icall_System_Threading_Thread_VolatileWriteIntPtr (void *ptr, void *value)
2788 mono_memory_barrier ();
2789 *(volatile void **)ptr = value;
2792 void
2793 ves_icall_System_Threading_Thread_VolatileWriteObject (void *ptr, MonoObject *value)
2795 mono_memory_barrier ();
2796 mono_gc_wbarrier_generic_store (ptr, value);
2799 void
2800 ves_icall_System_Threading_Thread_VolatileWriteDouble (void *ptr, double value)
2802 mono_memory_barrier ();
2803 *(volatile double *)ptr = value;
2806 void
2807 ves_icall_System_Threading_Thread_VolatileWriteFloat (void *ptr, float value)
2809 mono_memory_barrier ();
2810 *(volatile float *)ptr = value;
2813 void
2814 ves_icall_System_Threading_Volatile_Write1 (void *ptr, gint8 value)
2816 InterlockedWrite8 ((volatile gint8 *)ptr, value);
2819 void
2820 ves_icall_System_Threading_Volatile_Write2 (void *ptr, gint16 value)
2822 InterlockedWrite16 ((volatile gint16 *)ptr, value);
2825 void
2826 ves_icall_System_Threading_Volatile_Write4 (void *ptr, gint32 value)
2828 InterlockedWrite ((volatile gint32 *)ptr, value);
2831 void
2832 ves_icall_System_Threading_Volatile_Write8 (void *ptr, gint64 value)
2834 #if SIZEOF_VOID_P == 4
2835 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2836 mono_interlocked_lock ();
2837 *(gint64*)ptr = value;
2838 mono_interlocked_unlock ();
2839 return;
2841 #endif
2843 InterlockedWrite64 ((volatile gint64 *)ptr, value);
2846 void
2847 ves_icall_System_Threading_Volatile_WriteIntPtr (void *ptr, void *value)
2849 InterlockedWritePointer ((volatile gpointer *)ptr, value);
2852 void
2853 ves_icall_System_Threading_Volatile_WriteDouble (void *ptr, double value)
2855 LongDoubleUnion u;
2857 #if SIZEOF_VOID_P == 4
2858 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2859 mono_interlocked_lock ();
2860 *(double*)ptr = value;
2861 mono_interlocked_unlock ();
2862 return;
2864 #endif
2866 u.fval = value;
2868 InterlockedWrite64 ((volatile gint64 *)ptr, u.ival);
2871 void
2872 ves_icall_System_Threading_Volatile_WriteFloat (void *ptr, float value)
2874 IntFloatUnion u;
2876 u.fval = value;
2878 InterlockedWrite ((volatile gint32 *)ptr, u.ival);
2881 void
2882 ves_icall_System_Threading_Volatile_Write_T (void *ptr, MonoObject *value)
2884 mono_gc_wbarrier_generic_store_atomic (ptr, value);
2887 static void
2888 free_context (void *user_data)
2890 ContextStaticData *data = user_data;
2892 mono_threads_lock ();
2895 * There is no guarantee that, by the point this reference queue callback
2896 * has been invoked, the GC handle associated with the object will fail to
2897 * resolve as one might expect. So if we don't free and remove the GC
2898 * handle here, free_context_static_data_helper () could end up resolving
2899 * a GC handle to an actually-dead context which would contain a pointer
2900 * to an already-freed static data segment, resulting in a crash when
2901 * accessing it.
2903 g_hash_table_remove (contexts, GUINT_TO_POINTER (data->gc_handle));
2905 mono_threads_unlock ();
2907 mono_gchandle_free (data->gc_handle);
2908 mono_free_static_data (data->static_data);
2909 g_free (data);
2912 void
2913 ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppContext *ctx)
2915 mono_threads_lock ();
2917 //g_print ("Registering context %d in domain %d\n", ctx->context_id, ctx->domain_id);
2919 if (!contexts)
2920 contexts = g_hash_table_new (NULL, NULL);
2922 if (!context_queue)
2923 context_queue = mono_gc_reference_queue_new (free_context);
2925 gpointer gch = GUINT_TO_POINTER (mono_gchandle_new_weakref (&ctx->obj, FALSE));
2926 g_hash_table_insert (contexts, gch, gch);
2929 * We use this intermediate structure to contain a duplicate pointer to
2930 * the static data because we can't rely on being able to resolve the GC
2931 * handle in the reference queue callback.
2933 ContextStaticData *data = g_new0 (ContextStaticData, 1);
2934 data->gc_handle = GPOINTER_TO_UINT (gch);
2935 ctx->data = data;
2937 context_adjust_static_data (ctx);
2938 mono_gc_reference_queue_add (context_queue, &ctx->obj, data);
2940 mono_threads_unlock ();
2942 mono_profiler_context_loaded (ctx);
2945 void
2946 ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContext *ctx)
2949 * NOTE: Since finalizers are unreliable for the purposes of ensuring
2950 * cleanup in exceptional circumstances, we don't actually do any
2951 * cleanup work here. We instead do this via a reference queue.
2954 //g_print ("Releasing context %d in domain %d\n", ctx->context_id, ctx->domain_id);
2956 mono_profiler_context_unloaded (ctx);
2959 void mono_thread_init (MonoThreadStartCB start_cb,
2960 MonoThreadAttachCB attach_cb)
2962 mono_coop_mutex_init_recursive (&threads_mutex);
2964 mono_os_mutex_init_recursive(&interlocked_mutex);
2965 mono_os_mutex_init_recursive(&joinable_threads_mutex);
2967 mono_os_event_init (&background_change_event, FALSE);
2969 mono_init_static_data_info (&thread_static_info);
2970 mono_init_static_data_info (&context_static_info);
2972 mono_thread_start_cb = start_cb;
2973 mono_thread_attach_cb = attach_cb;
2976 void mono_thread_cleanup (void)
2978 #if !defined(RUN_IN_SUBTHREAD) && !defined(HOST_WIN32)
2979 /* The main thread must abandon any held mutexes (particularly
2980 * important for named mutexes as they are shared across
2981 * processes, see bug 74680.) This will happen when the
2982 * thread exits, but if it's not running in a subthread it
2983 * won't exit in time.
2985 mono_w32mutex_abandon ();
2986 #endif
2988 #if 0
2989 /* This stuff needs more testing, it seems one of these
2990 * critical sections can be locked when mono_thread_cleanup is
2991 * called.
2993 mono_coop_mutex_destroy (&threads_mutex);
2994 mono_os_mutex_destroy (&interlocked_mutex);
2995 mono_os_mutex_destroy (&delayed_free_table_mutex);
2996 mono_os_mutex_destroy (&small_id_mutex);
2997 mono_os_event_destroy (&background_change_event);
2998 #endif
3001 void
3002 mono_threads_install_cleanup (MonoThreadCleanupFunc func)
3004 mono_thread_cleanup_fn = func;
3007 void
3008 mono_thread_set_manage_callback (MonoThread *thread, MonoThreadManageCallback func)
3010 thread->internal_thread->manage_callback = func;
3013 G_GNUC_UNUSED
3014 static void print_tids (gpointer key, gpointer value, gpointer user)
3016 /* GPOINTER_TO_UINT breaks horribly if sizeof(void *) >
3017 * sizeof(uint) and a cast to uint would overflow
3019 /* Older versions of glib don't have G_GSIZE_FORMAT, so just
3020 * print this as a pointer.
3022 g_message ("Waiting for: %p", key);
3025 struct wait_data
3027 MonoThreadHandle *handles[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
3028 MonoInternalThread *threads[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
3029 guint32 num;
3032 static void
3033 wait_for_tids (struct wait_data *wait, guint32 timeout, gboolean check_state_change)
3035 guint32 i;
3036 MonoThreadInfoWaitRet ret;
3038 THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num));
3040 /* Add the thread state change event, so it wakes
3041 * up if a thread changes to background mode. */
3043 MONO_ENTER_GC_SAFE;
3044 if (check_state_change)
3045 ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, &background_change_event, FALSE, timeout, TRUE);
3046 else
3047 ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, NULL, TRUE, timeout, TRUE);
3048 MONO_EXIT_GC_SAFE;
3050 if (ret == MONO_THREAD_INFO_WAIT_RET_FAILED) {
3051 /* See the comment in build_wait_tids() */
3052 THREAD_DEBUG (g_message ("%s: Wait failed", __func__));
3053 return;
3056 for( i = 0; i < wait->num; i++)
3057 mono_threads_close_thread_handle (wait->handles [i]);
3059 if (ret == MONO_THREAD_INFO_WAIT_RET_TIMEOUT)
3060 return;
3062 if (ret < wait->num) {
3063 MonoInternalThread *internal;
3065 internal = wait->threads [ret];
3067 mono_threads_lock ();
3068 if (mono_g_hash_table_lookup (threads, (gpointer) internal->tid) == internal)
3069 g_error ("%s: failed to call mono_thread_detach_internal on thread %p, InternalThread: %p", __func__, internal->tid, internal);
3070 mono_threads_unlock ();
3074 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
3076 struct wait_data *wait=(struct wait_data *)user;
3078 if(wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS - 1) {
3079 MonoInternalThread *thread=(MonoInternalThread *)value;
3081 /* Ignore background threads, we abort them later */
3082 /* Do not lock here since it is not needed and the caller holds threads_lock */
3083 if (thread->state & ThreadState_Background) {
3084 THREAD_DEBUG (g_message ("%s: ignoring background thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3085 return; /* just leave, ignore */
3088 if (mono_gc_is_finalizer_internal_thread (thread)) {
3089 THREAD_DEBUG (g_message ("%s: ignoring finalizer thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3090 return;
3093 if (thread == mono_thread_internal_current ()) {
3094 THREAD_DEBUG (g_message ("%s: ignoring current thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3095 return;
3098 if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread)) {
3099 THREAD_DEBUG (g_message ("%s: ignoring main thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3100 return;
3103 if (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE) {
3104 THREAD_DEBUG (g_message ("%s: ignoring thread %" G_GSIZE_FORMAT "with DONT_MANAGE flag set.", __func__, (gsize)thread->tid));
3105 return;
3108 THREAD_DEBUG (g_message ("%s: Invoking mono_thread_manage callback on thread %p", __func__, thread));
3109 if ((thread->manage_callback == NULL) || (thread->manage_callback (thread->root_domain_thread) == TRUE)) {
3110 wait->handles[wait->num]=mono_threads_open_thread_handle (thread->handle);
3111 wait->threads[wait->num]=thread;
3112 wait->num++;
3114 THREAD_DEBUG (g_message ("%s: adding thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3115 } else {
3116 THREAD_DEBUG (g_message ("%s: ignoring (because of callback) thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3120 } else {
3121 /* Just ignore the rest, we can't do anything with
3122 * them yet
3127 static gboolean
3128 remove_and_abort_threads (gpointer key, gpointer value, gpointer user)
3130 struct wait_data *wait=(struct wait_data *)user;
3131 MonoNativeThreadId self = mono_native_thread_id_get ();
3132 MonoInternalThread *thread = (MonoInternalThread *)value;
3134 if (wait->num >= MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS)
3135 return FALSE;
3137 if (mono_native_thread_id_equals (thread_get_tid (thread), self))
3138 return FALSE;
3139 if (mono_gc_is_finalizer_internal_thread (thread))
3140 return FALSE;
3142 if ((thread->state & ThreadState_Background) && !(thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)) {
3143 wait->handles[wait->num] = mono_threads_open_thread_handle (thread->handle);
3144 wait->threads[wait->num] = thread;
3145 wait->num++;
3147 THREAD_DEBUG (g_print ("%s: Aborting id: %"G_GSIZE_FORMAT"\n", __func__, (gsize)thread->tid));
3148 mono_thread_internal_abort (thread);
3151 return TRUE;
3154 /**
3155 * mono_threads_set_shutting_down:
3157 * Is called by a thread that wants to shut down Mono. If the runtime is already
3158 * shutting down, the calling thread is suspended/stopped, and this function never
3159 * returns.
3161 void
3162 mono_threads_set_shutting_down (void)
3164 MonoInternalThread *current_thread = mono_thread_internal_current ();
3166 mono_threads_lock ();
3168 if (shutting_down) {
3169 mono_threads_unlock ();
3171 /* Make sure we're properly suspended/stopped */
3173 LOCK_THREAD (current_thread);
3175 if (current_thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
3176 UNLOCK_THREAD (current_thread);
3177 mono_thread_execute_interruption ();
3178 } else {
3179 UNLOCK_THREAD (current_thread);
3182 /*since we're killing the thread, detach it.*/
3183 mono_thread_detach_internal (current_thread);
3185 /* Wake up other threads potentially waiting for us */
3186 mono_thread_info_exit (0);
3187 } else {
3188 shutting_down = TRUE;
3190 /* Not really a background state change, but this will
3191 * interrupt the main thread if it is waiting for all
3192 * the other threads.
3194 mono_os_event_set (&background_change_event);
3196 mono_threads_unlock ();
3200 void mono_thread_manage (void)
3202 struct wait_data wait_data;
3203 struct wait_data *wait = &wait_data;
3205 memset (wait, 0, sizeof (struct wait_data));
3206 /* join each thread that's still running */
3207 THREAD_DEBUG (g_message ("%s: Joining each running thread...", __func__));
3209 mono_threads_lock ();
3210 if(threads==NULL) {
3211 THREAD_DEBUG (g_message("%s: No threads", __func__));
3212 mono_threads_unlock ();
3213 return;
3215 mono_threads_unlock ();
3217 do {
3218 mono_threads_lock ();
3219 if (shutting_down) {
3220 /* somebody else is shutting down */
3221 mono_threads_unlock ();
3222 break;
3224 THREAD_DEBUG (g_message ("%s: There are %d threads to join", __func__, mono_g_hash_table_size (threads));
3225 mono_g_hash_table_foreach (threads, print_tids, NULL));
3227 mono_os_event_reset (&background_change_event);
3228 wait->num=0;
3229 /* We must zero all InternalThread pointers to avoid making the GC unhappy. */
3230 memset (wait->threads, 0, MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
3231 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
3232 mono_threads_unlock ();
3233 if (wait->num > 0)
3234 /* Something to wait for */
3235 wait_for_tids (wait, MONO_INFINITE_WAIT, TRUE);
3236 THREAD_DEBUG (g_message ("%s: I have %d threads after waiting.", __func__, wait->num));
3237 } while(wait->num>0);
3239 /* Mono is shutting down, so just wait for the end */
3240 if (!mono_runtime_try_shutdown ()) {
3241 /*FIXME mono_thread_suspend probably should call mono_thread_execute_interruption when self interrupting. */
3242 mono_thread_suspend (mono_thread_internal_current ());
3243 mono_thread_execute_interruption ();
3247 * Remove everything but the finalizer thread and self.
3248 * Also abort all the background threads
3249 * */
3250 do {
3251 mono_threads_lock ();
3253 wait->num = 0;
3254 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
3255 memset (wait->threads, 0, MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
3256 mono_g_hash_table_foreach_remove (threads, remove_and_abort_threads, wait);
3258 mono_threads_unlock ();
3260 THREAD_DEBUG (g_message ("%s: wait->num is now %d", __func__, wait->num));
3261 if (wait->num > 0) {
3262 /* Something to wait for */
3263 wait_for_tids (wait, MONO_INFINITE_WAIT, FALSE);
3265 } while (wait->num > 0);
3268 * give the subthreads a chance to really quit (this is mainly needed
3269 * to get correct user and system times from getrusage/wait/time(1)).
3270 * This could be removed if we avoid pthread_detach() and use pthread_join().
3272 mono_thread_info_yield ();
3275 static void
3276 collect_threads_for_suspend (gpointer key, gpointer value, gpointer user_data)
3278 MonoInternalThread *thread = (MonoInternalThread*)value;
3279 struct wait_data *wait = (struct wait_data*)user_data;
3282 * We try to exclude threads early, to avoid running into the MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
3283 * limitation.
3284 * This needs no locking.
3286 if ((thread->state & ThreadState_Suspended) != 0 ||
3287 (thread->state & ThreadState_Stopped) != 0)
3288 return;
3290 if (wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
3291 wait->handles [wait->num] = mono_threads_open_thread_handle (thread->handle);
3292 wait->threads [wait->num] = thread;
3293 wait->num++;
3298 * mono_thread_suspend_all_other_threads:
3300 * Suspend all managed threads except the finalizer thread and this thread. It is
3301 * not possible to resume them later.
3303 void mono_thread_suspend_all_other_threads (void)
3305 struct wait_data wait_data;
3306 struct wait_data *wait = &wait_data;
3307 int i;
3308 MonoNativeThreadId self = mono_native_thread_id_get ();
3309 guint32 eventidx = 0;
3310 gboolean starting, finished;
3312 memset (wait, 0, sizeof (struct wait_data));
3314 * The other threads could be in an arbitrary state at this point, i.e.
3315 * they could be starting up, shutting down etc. This means that there could be
3316 * threads which are not even in the threads hash table yet.
3320 * First we set a barrier which will be checked by all threads before they
3321 * are added to the threads hash table, and they will exit if the flag is set.
3322 * This ensures that no threads could be added to the hash later.
3323 * We will use shutting_down as the barrier for now.
3325 g_assert (shutting_down);
3328 * We make multiple calls to WaitForMultipleObjects since:
3329 * - we can only wait for MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS threads
3330 * - some threads could exit without becoming suspended
3332 finished = FALSE;
3333 while (!finished) {
3335 * Make a copy of the hashtable since we can't do anything with
3336 * threads while threads_mutex is held.
3338 wait->num = 0;
3339 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
3340 memset (wait->threads, 0, MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
3341 mono_threads_lock ();
3342 mono_g_hash_table_foreach (threads, collect_threads_for_suspend, wait);
3343 mono_threads_unlock ();
3345 eventidx = 0;
3346 /* Get the suspended events that we'll be waiting for */
3347 for (i = 0; i < wait->num; ++i) {
3348 MonoInternalThread *thread = wait->threads [i];
3350 if (mono_native_thread_id_equals (thread_get_tid (thread), self)
3351 || mono_gc_is_finalizer_internal_thread (thread)
3352 || (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)
3354 mono_threads_close_thread_handle (wait->handles [i]);
3355 wait->threads [i] = NULL;
3356 continue;
3359 LOCK_THREAD (thread);
3361 if (thread->state & (ThreadState_Suspended | ThreadState_Stopped)) {
3362 UNLOCK_THREAD (thread);
3363 mono_threads_close_thread_handle (wait->handles [i]);
3364 wait->threads [i] = NULL;
3365 continue;
3368 ++eventidx;
3370 /* Convert abort requests into suspend requests */
3371 if ((thread->state & ThreadState_AbortRequested) != 0)
3372 thread->state &= ~ThreadState_AbortRequested;
3374 thread->state |= ThreadState_SuspendRequested;
3375 mono_os_event_reset (thread->suspended);
3377 /* Signal the thread to suspend + calls UNLOCK_THREAD (thread) */
3378 async_suspend_internal (thread, TRUE);
3380 mono_threads_close_thread_handle (wait->handles [i]);
3381 wait->threads [i] = NULL;
3383 if (eventidx <= 0) {
3385 * If there are threads which are starting up, we wait until they
3386 * are suspended when they try to register in the threads hash.
3387 * This is guaranteed to finish, since the threads which can create new
3388 * threads get suspended after a while.
3389 * FIXME: The finalizer thread can still create new threads.
3391 mono_threads_lock ();
3392 if (threads_starting_up)
3393 starting = mono_g_hash_table_size (threads_starting_up) > 0;
3394 else
3395 starting = FALSE;
3396 mono_threads_unlock ();
3397 if (starting)
3398 mono_thread_info_sleep (100, NULL);
3399 else
3400 finished = TRUE;
3405 typedef struct {
3406 MonoInternalThread *thread;
3407 MonoStackFrameInfo *frames;
3408 int nframes, max_frames;
3409 int nthreads, max_threads;
3410 MonoInternalThread **threads;
3411 } ThreadDumpUserData;
3413 static gboolean thread_dump_requested;
3415 /* This needs to be async safe */
3416 static gboolean
3417 collect_frame (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
3419 ThreadDumpUserData *ud = (ThreadDumpUserData *)data;
3421 if (ud->nframes < ud->max_frames) {
3422 memcpy (&ud->frames [ud->nframes], frame, sizeof (MonoStackFrameInfo));
3423 ud->nframes ++;
3426 return FALSE;
3429 /* This needs to be async safe */
3430 static SuspendThreadResult
3431 get_thread_dump (MonoThreadInfo *info, gpointer ud)
3433 ThreadDumpUserData *user_data = (ThreadDumpUserData *)ud;
3434 MonoInternalThread *thread = user_data->thread;
3436 #if 0
3437 /* This no longer works with remote unwinding */
3438 g_string_append_printf (text, " tid=0x%p this=0x%p ", (gpointer)(gsize)thread->tid, thread);
3439 mono_thread_internal_describe (thread, text);
3440 g_string_append (text, "\n");
3441 #endif
3443 if (thread == mono_thread_internal_current ())
3444 mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (collect_frame, NULL, MONO_UNWIND_SIGNAL_SAFE, ud);
3445 else
3446 mono_get_eh_callbacks ()->mono_walk_stack_with_state (collect_frame, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, ud);
3448 return MonoResumeThread;
3451 typedef struct {
3452 int nthreads, max_threads;
3453 MonoInternalThread **threads;
3454 } CollectThreadsUserData;
3456 static void
3457 collect_thread (gpointer key, gpointer value, gpointer user)
3459 CollectThreadsUserData *ud = (CollectThreadsUserData *)user;
3460 MonoInternalThread *thread = (MonoInternalThread *)value;
3462 if (ud->nthreads < ud->max_threads)
3463 ud->threads [ud->nthreads ++] = thread;
3467 * Collect running threads into the THREADS array.
3468 * THREADS should be an array allocated on the stack.
3470 static int
3471 collect_threads (MonoInternalThread **thread_array, int max_threads)
3473 CollectThreadsUserData ud;
3475 memset (&ud, 0, sizeof (ud));
3476 /* This array contains refs, but its on the stack, so its ok */
3477 ud.threads = thread_array;
3478 ud.max_threads = max_threads;
3480 mono_threads_lock ();
3481 mono_g_hash_table_foreach (threads, collect_thread, &ud);
3482 mono_threads_unlock ();
3484 return ud.nthreads;
3487 static void
3488 dump_thread (MonoInternalThread *thread, ThreadDumpUserData *ud)
3490 GString* text = g_string_new (0);
3491 char *name;
3492 GError *error = NULL;
3493 int i;
3495 ud->thread = thread;
3496 ud->nframes = 0;
3498 /* Collect frames for the thread */
3499 if (thread == mono_thread_internal_current ()) {
3500 get_thread_dump (mono_thread_info_current (), ud);
3501 } else {
3502 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, get_thread_dump, ud);
3506 * Do all the non async-safe work outside of get_thread_dump.
3508 if (thread->name) {
3509 name = g_utf16_to_utf8 (thread->name, thread->name_len, NULL, NULL, &error);
3510 g_assert (!error);
3511 g_string_append_printf (text, "\n\"%s\"", name);
3512 g_free (name);
3514 else if (thread->threadpool_thread) {
3515 g_string_append (text, "\n\"<threadpool thread>\"");
3516 } else {
3517 g_string_append (text, "\n\"<unnamed thread>\"");
3520 for (i = 0; i < ud->nframes; ++i) {
3521 MonoStackFrameInfo *frame = &ud->frames [i];
3522 MonoMethod *method = NULL;
3524 if (frame->type == FRAME_TYPE_MANAGED)
3525 method = mono_jit_info_get_method (frame->ji);
3527 if (method) {
3528 gchar *location = mono_debug_print_stack_frame (method, frame->native_offset, frame->domain);
3529 g_string_append_printf (text, " %s\n", location);
3530 g_free (location);
3531 } else {
3532 g_string_append_printf (text, " at <unknown> <0x%05x>\n", frame->native_offset);
3536 fprintf (stdout, "%s", text->str);
3538 #if PLATFORM_WIN32 && TARGET_WIN32 && _DEBUG
3539 OutputDebugStringA(text->str);
3540 #endif
3542 g_string_free (text, TRUE);
3543 fflush (stdout);
3546 void
3547 mono_threads_perform_thread_dump (void)
3549 ThreadDumpUserData ud;
3550 MonoInternalThread *thread_array [128];
3551 int tindex, nthreads;
3553 if (!thread_dump_requested)
3554 return;
3556 printf ("Full thread dump:\n");
3558 /* Make a copy of the threads hash to avoid doing work inside threads_lock () */
3559 nthreads = collect_threads (thread_array, 128);
3561 memset (&ud, 0, sizeof (ud));
3562 ud.frames = g_new0 (MonoStackFrameInfo, 256);
3563 ud.max_frames = 256;
3565 for (tindex = 0; tindex < nthreads; ++tindex)
3566 dump_thread (thread_array [tindex], &ud);
3568 g_free (ud.frames);
3570 thread_dump_requested = FALSE;
3573 /* Obtain the thread dump of all threads */
3574 static gboolean
3575 mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_frames, MonoError *error)
3578 ThreadDumpUserData ud;
3579 MonoInternalThread *thread_array [128];
3580 MonoDomain *domain = mono_domain_get ();
3581 MonoDebugSourceLocation *location;
3582 int tindex, nthreads;
3584 error_init (error);
3586 *out_threads = NULL;
3587 *out_stack_frames = NULL;
3589 /* Make a copy of the threads hash to avoid doing work inside threads_lock () */
3590 nthreads = collect_threads (thread_array, 128);
3592 memset (&ud, 0, sizeof (ud));
3593 ud.frames = g_new0 (MonoStackFrameInfo, 256);
3594 ud.max_frames = 256;
3596 *out_threads = mono_array_new_checked (domain, mono_defaults.thread_class, nthreads, error);
3597 if (!is_ok (error))
3598 goto leave;
3599 *out_stack_frames = mono_array_new_checked (domain, mono_defaults.array_class, nthreads, error);
3600 if (!is_ok (error))
3601 goto leave;
3603 for (tindex = 0; tindex < nthreads; ++tindex) {
3604 MonoInternalThread *thread = thread_array [tindex];
3605 MonoArray *thread_frames;
3606 int i;
3608 ud.thread = thread;
3609 ud.nframes = 0;
3611 /* Collect frames for the thread */
3612 if (thread == mono_thread_internal_current ()) {
3613 get_thread_dump (mono_thread_info_current (), &ud);
3614 } else {
3615 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, get_thread_dump, &ud);
3618 mono_array_setref_fast (*out_threads, tindex, mono_thread_current_for_thread (thread));
3620 thread_frames = mono_array_new_checked (domain, mono_defaults.stack_frame_class, ud.nframes, error);
3621 if (!is_ok (error))
3622 goto leave;
3623 mono_array_setref_fast (*out_stack_frames, tindex, thread_frames);
3625 for (i = 0; i < ud.nframes; ++i) {
3626 MonoStackFrameInfo *frame = &ud.frames [i];
3627 MonoMethod *method = NULL;
3628 MonoStackFrame *sf = (MonoStackFrame *)mono_object_new_checked (domain, mono_defaults.stack_frame_class, error);
3629 if (!is_ok (error))
3630 goto leave;
3632 sf->native_offset = frame->native_offset;
3634 if (frame->type == FRAME_TYPE_MANAGED)
3635 method = mono_jit_info_get_method (frame->ji);
3637 if (method) {
3638 sf->method_address = (gsize) frame->ji->code_start;
3640 MonoReflectionMethod *rm = mono_method_get_object_checked (domain, method, NULL, error);
3641 if (!is_ok (error))
3642 goto leave;
3643 MONO_OBJECT_SETREF (sf, method, rm);
3645 location = mono_debug_lookup_source_location (method, frame->native_offset, domain);
3646 if (location) {
3647 sf->il_offset = location->il_offset;
3649 if (location && location->source_file) {
3650 MONO_OBJECT_SETREF (sf, filename, mono_string_new (domain, location->source_file));
3651 sf->line = location->row;
3652 sf->column = location->column;
3654 mono_debug_free_source_location (location);
3655 } else {
3656 sf->il_offset = -1;
3659 mono_array_setref (thread_frames, i, sf);
3663 leave:
3664 g_free (ud.frames);
3665 return is_ok (error);
3669 * mono_threads_request_thread_dump:
3671 * Ask all threads except the current to print their stacktrace to stdout.
3673 void
3674 mono_threads_request_thread_dump (void)
3676 /*The new thread dump code runs out of the finalizer thread. */
3677 thread_dump_requested = TRUE;
3678 mono_gc_finalize_notify ();
3681 struct ref_stack {
3682 gpointer *refs;
3683 gint allocated; /* +1 so that refs [allocated] == NULL */
3684 gint bottom;
3687 typedef struct ref_stack RefStack;
3689 static RefStack *
3690 ref_stack_new (gint initial_size)
3692 RefStack *rs;
3694 initial_size = MAX (initial_size, 16) + 1;
3695 rs = g_new0 (RefStack, 1);
3696 rs->refs = g_new0 (gpointer, initial_size);
3697 rs->allocated = initial_size;
3698 return rs;
3701 static void
3702 ref_stack_destroy (gpointer ptr)
3704 RefStack *rs = (RefStack *)ptr;
3706 if (rs != NULL) {
3707 g_free (rs->refs);
3708 g_free (rs);
3712 static void
3713 ref_stack_push (RefStack *rs, gpointer ptr)
3715 g_assert (rs != NULL);
3717 if (rs->bottom >= rs->allocated) {
3718 rs->refs = (void **)g_realloc (rs->refs, rs->allocated * 2 * sizeof (gpointer) + 1);
3719 rs->allocated <<= 1;
3720 rs->refs [rs->allocated] = NULL;
3722 rs->refs [rs->bottom++] = ptr;
3725 static void
3726 ref_stack_pop (RefStack *rs)
3728 if (rs == NULL || rs->bottom == 0)
3729 return;
3731 rs->bottom--;
3732 rs->refs [rs->bottom] = NULL;
3735 static gboolean
3736 ref_stack_find (RefStack *rs, gpointer ptr)
3738 gpointer *refs;
3740 if (rs == NULL)
3741 return FALSE;
3743 for (refs = rs->refs; refs && *refs; refs++) {
3744 if (*refs == ptr)
3745 return TRUE;
3747 return FALSE;
3751 * mono_thread_push_appdomain_ref:
3753 * Register that the current thread may have references to objects in domain
3754 * @domain on its stack. Each call to this function should be paired with a
3755 * call to pop_appdomain_ref.
3757 void
3758 mono_thread_push_appdomain_ref (MonoDomain *domain)
3760 MonoInternalThread *thread = mono_thread_internal_current ();
3762 if (thread) {
3763 /* printf ("PUSH REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, domain->friendly_name); */
3764 SPIN_LOCK (thread->lock_thread_id);
3765 if (thread->appdomain_refs == NULL)
3766 thread->appdomain_refs = ref_stack_new (16);
3767 ref_stack_push ((RefStack *)thread->appdomain_refs, domain);
3768 SPIN_UNLOCK (thread->lock_thread_id);
3772 void
3773 mono_thread_pop_appdomain_ref (void)
3775 MonoInternalThread *thread = mono_thread_internal_current ();
3777 if (thread) {
3778 /* printf ("POP REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, ((MonoDomain*)(thread->appdomain_refs->data))->friendly_name); */
3779 SPIN_LOCK (thread->lock_thread_id);
3780 ref_stack_pop ((RefStack *)thread->appdomain_refs);
3781 SPIN_UNLOCK (thread->lock_thread_id);
3785 gboolean
3786 mono_thread_internal_has_appdomain_ref (MonoInternalThread *thread, MonoDomain *domain)
3788 gboolean res;
3789 SPIN_LOCK (thread->lock_thread_id);
3790 res = ref_stack_find ((RefStack *)thread->appdomain_refs, domain);
3791 SPIN_UNLOCK (thread->lock_thread_id);
3792 return res;
3795 gboolean
3796 mono_thread_has_appdomain_ref (MonoThread *thread, MonoDomain *domain)
3798 return mono_thread_internal_has_appdomain_ref (thread->internal_thread, domain);
3801 typedef struct abort_appdomain_data {
3802 struct wait_data wait;
3803 MonoDomain *domain;
3804 } abort_appdomain_data;
3806 static void
3807 collect_appdomain_thread (gpointer key, gpointer value, gpointer user_data)
3809 MonoInternalThread *thread = (MonoInternalThread*)value;
3810 abort_appdomain_data *data = (abort_appdomain_data*)user_data;
3811 MonoDomain *domain = data->domain;
3813 if (mono_thread_internal_has_appdomain_ref (thread, domain)) {
3814 /* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread->tid, domain->friendly_name); */
3816 if(data->wait.num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
3817 data->wait.handles [data->wait.num] = mono_threads_open_thread_handle (thread->handle);
3818 data->wait.threads [data->wait.num] = thread;
3819 data->wait.num++;
3820 } else {
3821 /* Just ignore the rest, we can't do anything with
3822 * them yet
3829 * mono_threads_abort_appdomain_threads:
3831 * Abort threads which has references to the given appdomain.
3833 gboolean
3834 mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
3836 #ifdef __native_client__
3837 return FALSE;
3838 #endif
3840 abort_appdomain_data user_data;
3841 gint64 start_time;
3842 int orig_timeout = timeout;
3843 int i;
3845 THREAD_DEBUG (g_message ("%s: starting abort", __func__));
3847 start_time = mono_msec_ticks ();
3848 do {
3849 mono_threads_lock ();
3851 user_data.domain = domain;
3852 user_data.wait.num = 0;
3853 /* This shouldn't take any locks */
3854 mono_g_hash_table_foreach (threads, collect_appdomain_thread, &user_data);
3855 mono_threads_unlock ();
3857 if (user_data.wait.num > 0) {
3858 /* Abort the threads outside the threads lock */
3859 for (i = 0; i < user_data.wait.num; ++i)
3860 mono_thread_internal_abort (user_data.wait.threads [i]);
3863 * We should wait for the threads either to abort, or to leave the
3864 * domain. We can't do the latter, so we wait with a timeout.
3866 wait_for_tids (&user_data.wait, 100, FALSE);
3869 /* Update remaining time */
3870 timeout -= mono_msec_ticks () - start_time;
3871 start_time = mono_msec_ticks ();
3873 if (orig_timeout != -1 && timeout < 0)
3874 return FALSE;
3876 while (user_data.wait.num > 0);
3878 THREAD_DEBUG (g_message ("%s: abort done", __func__));
3880 return TRUE;
3884 * mono_thread_get_undeniable_exception:
3886 * Return an exception which needs to be raised when leaving a catch clause.
3887 * This is used for undeniable exception propagation.
3889 MonoException*
3890 mono_thread_get_undeniable_exception (void)
3892 MonoInternalThread *thread = mono_thread_internal_current ();
3894 if (!(thread && thread->abort_exc && !is_running_protected_wrapper ()))
3895 return NULL;
3897 // We don't want to have our exception effect calls made by
3898 // the catching block
3900 if (!mono_get_eh_callbacks ()->mono_above_abort_threshold ())
3901 return NULL;
3904 * FIXME: Clear the abort exception and return an AppDomainUnloaded
3905 * exception if the thread no longer references a dying appdomain.
3907 thread->abort_exc->trace_ips = NULL;
3908 thread->abort_exc->stack_trace = NULL;
3909 return thread->abort_exc;
3912 #if MONO_SMALL_CONFIG
3913 #define NUM_STATIC_DATA_IDX 4
3914 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
3915 64, 256, 1024, 4096
3917 #else
3918 #define NUM_STATIC_DATA_IDX 8
3919 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
3920 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
3922 #endif
3924 static MonoBitSet *thread_reference_bitmaps [NUM_STATIC_DATA_IDX];
3925 static MonoBitSet *context_reference_bitmaps [NUM_STATIC_DATA_IDX];
3927 static void
3928 mark_slots (void *addr, MonoBitSet **bitmaps, MonoGCMarkFunc mark_func, void *gc_data)
3930 gpointer *static_data = (gpointer *)addr;
3932 for (int i = 0; i < NUM_STATIC_DATA_IDX; ++i) {
3933 void **ptr = (void **)static_data [i];
3935 if (!ptr)
3936 continue;
3938 MONO_BITSET_FOREACH (bitmaps [i], idx, {
3939 void **p = ptr + idx;
3941 if (*p)
3942 mark_func ((MonoObject**)p, gc_data);
3947 static void
3948 mark_tls_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
3950 mark_slots (addr, thread_reference_bitmaps, mark_func, gc_data);
3953 static void
3954 mark_ctx_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
3956 mark_slots (addr, context_reference_bitmaps, mark_func, gc_data);
3960 * mono_alloc_static_data
3962 * Allocate memory blocks for storing threads or context static data
3964 static void
3965 mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean threadlocal)
3967 guint idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
3968 int i;
3970 gpointer* static_data = *static_data_ptr;
3971 if (!static_data) {
3972 static MonoGCDescriptor tls_desc = MONO_GC_DESCRIPTOR_NULL;
3973 static MonoGCDescriptor ctx_desc = MONO_GC_DESCRIPTOR_NULL;
3975 if (mono_gc_user_markers_supported ()) {
3976 if (tls_desc == MONO_GC_DESCRIPTOR_NULL)
3977 tls_desc = mono_gc_make_root_descr_user (mark_tls_slots);
3979 if (ctx_desc == MONO_GC_DESCRIPTOR_NULL)
3980 ctx_desc = mono_gc_make_root_descr_user (mark_ctx_slots);
3983 static_data = (void **)mono_gc_alloc_fixed (static_data_size [0], threadlocal ? tls_desc : ctx_desc,
3984 threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC,
3985 threadlocal ? "managed thread-static variables" : "managed context-static variables");
3986 *static_data_ptr = static_data;
3987 static_data [0] = static_data;
3990 for (i = 1; i <= idx; ++i) {
3991 if (static_data [i])
3992 continue;
3994 if (mono_gc_user_markers_supported ())
3995 static_data [i] = g_malloc0 (static_data_size [i]);
3996 else
3997 static_data [i] = mono_gc_alloc_fixed (static_data_size [i], MONO_GC_DESCRIPTOR_NULL,
3998 threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC,
3999 threadlocal ? "managed thread-static variables" : "managed context-static variables");
4003 static void
4004 mono_free_static_data (gpointer* static_data)
4006 int i;
4007 for (i = 1; i < NUM_STATIC_DATA_IDX; ++i) {
4008 gpointer p = static_data [i];
4009 if (!p)
4010 continue;
4012 * At this point, the static data pointer array is still registered with the
4013 * GC, so must ensure that mark_tls_slots() will not encounter any invalid
4014 * data. Freeing the individual arrays without first nulling their slots
4015 * would make it possible for mark_tls/ctx_slots() to encounter a pointer to
4016 * such an already freed array. See bug #13813.
4018 static_data [i] = NULL;
4019 mono_memory_write_barrier ();
4020 if (mono_gc_user_markers_supported ())
4021 g_free (p);
4022 else
4023 mono_gc_free_fixed (p);
4025 mono_gc_free_fixed (static_data);
4029 * mono_init_static_data_info
4031 * Initializes static data counters
4033 static void mono_init_static_data_info (StaticDataInfo *static_data)
4035 static_data->idx = 0;
4036 static_data->offset = 0;
4037 static_data->freelist = NULL;
4041 * mono_alloc_static_data_slot
4043 * Generates an offset for static data. static_data contains the counters
4044 * used to generate it.
4046 static guint32
4047 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align)
4049 if (!static_data->idx && !static_data->offset) {
4051 * we use the first chunk of the first allocation also as
4052 * an array for the rest of the data
4054 static_data->offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX;
4056 static_data->offset += align - 1;
4057 static_data->offset &= ~(align - 1);
4058 if (static_data->offset + size >= static_data_size [static_data->idx]) {
4059 static_data->idx ++;
4060 g_assert (size <= static_data_size [static_data->idx]);
4061 g_assert (static_data->idx < NUM_STATIC_DATA_IDX);
4062 static_data->offset = 0;
4064 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (static_data->idx, static_data->offset, 0);
4065 static_data->offset += size;
4066 return offset;
4070 * LOCKING: requires that threads_mutex is held
4072 static void
4073 context_adjust_static_data (MonoAppContext *ctx)
4075 if (context_static_info.offset || context_static_info.idx > 0) {
4076 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (context_static_info.idx, context_static_info.offset, 0);
4077 mono_alloc_static_data (&ctx->static_data, offset, FALSE);
4078 ctx->data->static_data = ctx->static_data;
4083 * LOCKING: requires that threads_mutex is held
4085 static void
4086 alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
4088 MonoInternalThread *thread = (MonoInternalThread *)value;
4089 guint32 offset = GPOINTER_TO_UINT (user);
4091 mono_alloc_static_data (&(thread->static_data), offset, TRUE);
4095 * LOCKING: requires that threads_mutex is held
4097 static void
4098 alloc_context_static_data_helper (gpointer key, gpointer value, gpointer user)
4100 MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (GPOINTER_TO_INT (key));
4102 if (!ctx)
4103 return;
4105 guint32 offset = GPOINTER_TO_UINT (user);
4106 mono_alloc_static_data (&ctx->static_data, offset, FALSE);
4107 ctx->data->static_data = ctx->static_data;
4110 static StaticDataFreeList*
4111 search_slot_in_freelist (StaticDataInfo *static_data, guint32 size, guint32 align)
4113 StaticDataFreeList* prev = NULL;
4114 StaticDataFreeList* tmp = static_data->freelist;
4115 while (tmp) {
4116 if (tmp->size == size) {
4117 if (prev)
4118 prev->next = tmp->next;
4119 else
4120 static_data->freelist = tmp->next;
4121 return tmp;
4123 prev = tmp;
4124 tmp = tmp->next;
4126 return NULL;
4129 #if SIZEOF_VOID_P == 4
4130 #define ONE_P 1
4131 #else
4132 #define ONE_P 1ll
4133 #endif
4135 static void
4136 update_reference_bitmap (MonoBitSet **sets, guint32 offset, uintptr_t *bitmap, int numbits)
4138 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4139 if (!sets [idx])
4140 sets [idx] = mono_bitset_new (static_data_size [idx] / sizeof (uintptr_t), 0);
4141 MonoBitSet *rb = sets [idx];
4142 offset = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
4143 offset /= sizeof (uintptr_t);
4144 /* offset is now the bitmap offset */
4145 for (int i = 0; i < numbits; ++i) {
4146 if (bitmap [i / sizeof (uintptr_t)] & (ONE_P << (i & (sizeof (uintptr_t) * 8 -1))))
4147 mono_bitset_set_fast (rb, offset + i);
4151 static void
4152 clear_reference_bitmap (MonoBitSet **sets, guint32 offset, guint32 size)
4154 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4155 MonoBitSet *rb = sets [idx];
4156 offset = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
4157 offset /= sizeof (uintptr_t);
4158 /* offset is now the bitmap offset */
4159 for (int i = 0; i < size / sizeof (uintptr_t); i++)
4160 mono_bitset_clear_fast (rb, offset + i);
4163 guint32
4164 mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align, uintptr_t *bitmap, int numbits)
4166 g_assert (static_type == SPECIAL_STATIC_THREAD || static_type == SPECIAL_STATIC_CONTEXT);
4168 StaticDataInfo *info;
4169 MonoBitSet **sets;
4171 if (static_type == SPECIAL_STATIC_THREAD) {
4172 info = &thread_static_info;
4173 sets = thread_reference_bitmaps;
4174 } else {
4175 info = &context_static_info;
4176 sets = context_reference_bitmaps;
4179 mono_threads_lock ();
4181 StaticDataFreeList *item = search_slot_in_freelist (info, size, align);
4182 guint32 offset;
4184 if (item) {
4185 offset = item->offset;
4186 g_free (item);
4187 } else {
4188 offset = mono_alloc_static_data_slot (info, size, align);
4191 update_reference_bitmap (sets, offset, bitmap, numbits);
4193 if (static_type == SPECIAL_STATIC_THREAD) {
4194 /* This can be called during startup */
4195 if (threads != NULL)
4196 mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
4197 } else {
4198 if (contexts != NULL)
4199 g_hash_table_foreach (contexts, alloc_context_static_data_helper, GUINT_TO_POINTER (offset));
4201 ACCESS_SPECIAL_STATIC_OFFSET (offset, type) = SPECIAL_STATIC_OFFSET_TYPE_CONTEXT;
4204 mono_threads_unlock ();
4206 return offset;
4209 gpointer
4210 mono_get_special_static_data_for_thread (MonoInternalThread *thread, guint32 offset)
4212 guint32 static_type = ACCESS_SPECIAL_STATIC_OFFSET (offset, type);
4214 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4215 return get_thread_static_data (thread, offset);
4216 } else {
4217 return get_context_static_data (thread->current_appcontext, offset);
4221 gpointer
4222 mono_get_special_static_data (guint32 offset)
4224 return mono_get_special_static_data_for_thread (mono_thread_internal_current (), offset);
4227 typedef struct {
4228 guint32 offset;
4229 guint32 size;
4230 } OffsetSize;
4233 * LOCKING: requires that threads_mutex is held
4235 static void
4236 free_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
4238 MonoInternalThread *thread = (MonoInternalThread *)value;
4239 OffsetSize *data = (OffsetSize *)user;
4240 int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index);
4241 int off = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, offset);
4242 char *ptr;
4244 if (!thread->static_data || !thread->static_data [idx])
4245 return;
4246 ptr = ((char*) thread->static_data [idx]) + off;
4247 mono_gc_bzero_atomic (ptr, data->size);
4251 * LOCKING: requires that threads_mutex is held
4253 static void
4254 free_context_static_data_helper (gpointer key, gpointer value, gpointer user)
4256 MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (GPOINTER_TO_INT (key));
4258 if (!ctx)
4259 return;
4261 OffsetSize *data = (OffsetSize *)user;
4262 int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index);
4263 int off = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, offset);
4264 char *ptr;
4266 if (!ctx->static_data || !ctx->static_data [idx])
4267 return;
4269 ptr = ((char*) ctx->static_data [idx]) + off;
4270 mono_gc_bzero_atomic (ptr, data->size);
4273 static void
4274 do_free_special_slot (guint32 offset, guint32 size)
4276 guint32 static_type = ACCESS_SPECIAL_STATIC_OFFSET (offset, type);
4277 MonoBitSet **sets;
4278 StaticDataInfo *info;
4280 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4281 info = &thread_static_info;
4282 sets = thread_reference_bitmaps;
4283 } else {
4284 info = &context_static_info;
4285 sets = context_reference_bitmaps;
4288 guint32 data_offset = offset;
4289 ACCESS_SPECIAL_STATIC_OFFSET (data_offset, type) = 0;
4290 OffsetSize data = { data_offset, size };
4292 clear_reference_bitmap (sets, data.offset, data.size);
4294 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4295 if (threads != NULL)
4296 mono_g_hash_table_foreach (threads, free_thread_static_data_helper, &data);
4297 } else {
4298 if (contexts != NULL)
4299 g_hash_table_foreach (contexts, free_context_static_data_helper, &data);
4302 if (!mono_runtime_is_shutting_down ()) {
4303 StaticDataFreeList *item = g_new0 (StaticDataFreeList, 1);
4305 item->offset = offset;
4306 item->size = size;
4308 item->next = info->freelist;
4309 info->freelist = item;
4313 static void
4314 do_free_special (gpointer key, gpointer value, gpointer data)
4316 MonoClassField *field = (MonoClassField *)key;
4317 guint32 offset = GPOINTER_TO_UINT (value);
4318 gint32 align;
4319 guint32 size;
4320 size = mono_type_size (field->type, &align);
4321 do_free_special_slot (offset, size);
4324 void
4325 mono_alloc_special_static_data_free (GHashTable *special_static_fields)
4327 mono_threads_lock ();
4329 g_hash_table_foreach (special_static_fields, do_free_special, NULL);
4331 mono_threads_unlock ();
4334 #ifdef HOST_WIN32
4335 static void CALLBACK dummy_apc (ULONG_PTR param)
4338 #endif
4341 * mono_thread_execute_interruption
4343 * Performs the operation that the requested thread state requires (abort,
4344 * suspend or stop)
4346 static MonoException*
4347 mono_thread_execute_interruption (void)
4349 MonoInternalThread *thread = mono_thread_internal_current ();
4350 MonoThread *sys_thread = mono_thread_current ();
4352 LOCK_THREAD (thread);
4354 /* MonoThread::interruption_requested can only be changed with atomics */
4355 if (mono_thread_clear_interruption_requested (thread)) {
4356 /* this will consume pending APC calls */
4357 #ifdef HOST_WIN32
4358 WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
4359 #endif
4360 InterlockedDecrement (&thread_interruption_requested);
4362 /* Clear the interrupted flag of the thread so it can wait again */
4363 mono_thread_info_clear_self_interrupt ();
4366 /* If there's a pending exception and an AbortRequested, the pending exception takes precedence */
4367 if (sys_thread->pending_exception) {
4368 MonoException *exc;
4370 exc = sys_thread->pending_exception;
4371 sys_thread->pending_exception = NULL;
4373 UNLOCK_THREAD (thread);
4374 return exc;
4375 } else if (thread->state & (ThreadState_AbortRequested)) {
4376 UNLOCK_THREAD (thread);
4377 g_assert (sys_thread->pending_exception == NULL);
4378 if (thread->abort_exc == NULL) {
4380 * This might be racy, but it has to be called outside the lock
4381 * since it calls managed code.
4383 MONO_OBJECT_SETREF (thread, abort_exc, mono_get_exception_thread_abort ());
4385 return thread->abort_exc;
4386 } else if (thread->state & (ThreadState_SuspendRequested)) {
4387 /* calls UNLOCK_THREAD (thread) */
4388 self_suspend_internal ();
4389 return NULL;
4390 } else if (thread->thread_interrupt_requested) {
4392 thread->thread_interrupt_requested = FALSE;
4393 UNLOCK_THREAD (thread);
4395 return(mono_get_exception_thread_interrupted ());
4398 UNLOCK_THREAD (thread);
4400 return NULL;
4404 * mono_thread_request_interruption
4406 * A signal handler can call this method to request the interruption of a
4407 * thread. The result of the interruption will depend on the current state of
4408 * the thread. If the result is an exception that needs to be throw, it is
4409 * provided as return value.
4411 MonoException*
4412 mono_thread_request_interruption (gboolean running_managed)
4414 MonoInternalThread *thread = mono_thread_internal_current ();
4416 /* The thread may already be stopping */
4417 if (thread == NULL)
4418 return NULL;
4420 if (!mono_thread_set_interruption_requested (thread))
4421 return NULL;
4422 InterlockedIncrement (&thread_interruption_requested);
4424 if (!running_managed || is_running_protected_wrapper ()) {
4425 /* Can't stop while in unmanaged code. Increase the global interruption
4426 request count. When exiting the unmanaged method the count will be
4427 checked and the thread will be interrupted. */
4429 /* this will awake the thread if it is in WaitForSingleObject
4430 or similar */
4431 /* Our implementation of this function ignores the func argument */
4432 #ifdef HOST_WIN32
4433 QueueUserAPC ((PAPCFUNC)dummy_apc, thread->native_handle, (ULONG_PTR)NULL);
4434 #else
4435 mono_thread_info_self_interrupt ();
4436 #endif
4437 return NULL;
4439 else {
4440 return mono_thread_execute_interruption ();
4444 /*This function should be called by a thread after it has exited all of
4445 * its handle blocks at interruption time.*/
4446 MonoException*
4447 mono_thread_resume_interruption (void)
4449 MonoInternalThread *thread = mono_thread_internal_current ();
4450 gboolean still_aborting;
4452 /* The thread may already be stopping */
4453 if (thread == NULL)
4454 return NULL;
4456 LOCK_THREAD (thread);
4457 still_aborting = (thread->state & (ThreadState_AbortRequested)) != 0;
4458 UNLOCK_THREAD (thread);
4460 /*This can happen if the protected block called Thread::ResetAbort*/
4461 if (!still_aborting)
4462 return FALSE;
4464 if (!mono_thread_set_interruption_requested (thread))
4465 return NULL;
4466 InterlockedIncrement (&thread_interruption_requested);
4468 mono_thread_info_self_interrupt ();
4470 return mono_thread_execute_interruption ();
4473 gboolean mono_thread_interruption_requested ()
4475 if (thread_interruption_requested) {
4476 MonoInternalThread *thread = mono_thread_internal_current ();
4477 /* The thread may already be stopping */
4478 if (thread != NULL)
4479 return mono_thread_get_interruption_requested (thread);
4481 return FALSE;
4484 static MonoException*
4485 mono_thread_interruption_checkpoint_request (gboolean bypass_abort_protection)
4487 MonoInternalThread *thread = mono_thread_internal_current ();
4489 /* The thread may already be stopping */
4490 if (!thread)
4491 return NULL;
4492 if (!mono_thread_get_interruption_requested (thread))
4493 return NULL;
4494 if (!bypass_abort_protection && is_running_protected_wrapper ())
4495 return NULL;
4497 return mono_thread_execute_interruption ();
4501 * Performs the interruption of the current thread, if one has been requested,
4502 * and the thread is not running a protected wrapper.
4503 * Return the exception which needs to be thrown, if any.
4505 MonoException*
4506 mono_thread_interruption_checkpoint (void)
4508 return mono_thread_interruption_checkpoint_request (FALSE);
4512 * Performs the interruption of the current thread, if one has been requested.
4513 * Return the exception which needs to be thrown, if any.
4515 MonoException*
4516 mono_thread_force_interruption_checkpoint_noraise (void)
4518 return mono_thread_interruption_checkpoint_request (TRUE);
4522 * mono_set_pending_exception:
4524 * Set the pending exception of the current thread to EXC.
4525 * The exception will be thrown when execution returns to managed code.
4527 void
4528 mono_set_pending_exception (MonoException *exc)
4530 MonoThread *thread = mono_thread_current ();
4532 /* The thread may already be stopping */
4533 if (thread == NULL)
4534 return;
4536 MONO_OBJECT_SETREF (thread, pending_exception, exc);
4538 mono_thread_request_interruption (FALSE);
4542 * mono_thread_interruption_request_flag:
4544 * Returns the address of a flag that will be non-zero if an interruption has
4545 * been requested for a thread. The thread to interrupt may not be the current
4546 * thread, so an additional call to mono_thread_interruption_requested() or
4547 * mono_thread_interruption_checkpoint() is allways needed if the flag is not
4548 * zero.
4550 gint32* mono_thread_interruption_request_flag ()
4552 return &thread_interruption_requested;
4555 void
4556 mono_thread_init_apartment_state (void)
4558 #ifdef HOST_WIN32
4559 MonoInternalThread* thread = mono_thread_internal_current ();
4561 /* Positive return value indicates success, either
4562 * S_OK if this is first CoInitialize call, or
4563 * S_FALSE if CoInitialize already called, but with same
4564 * threading model. A negative value indicates failure,
4565 * probably due to trying to change the threading model.
4567 if (CoInitializeEx(NULL, (thread->apartment_state == ThreadApartmentState_STA)
4568 ? COINIT_APARTMENTTHREADED
4569 : COINIT_MULTITHREADED) < 0) {
4570 thread->apartment_state = ThreadApartmentState_Unknown;
4572 #endif
4575 void
4576 mono_thread_cleanup_apartment_state (void)
4578 #ifdef HOST_WIN32
4579 MonoInternalThread* thread = mono_thread_internal_current ();
4581 if (thread && thread->apartment_state != ThreadApartmentState_Unknown) {
4582 CoUninitialize ();
4584 #endif
4587 void
4588 mono_thread_set_state (MonoInternalThread *thread, MonoThreadState state)
4590 LOCK_THREAD (thread);
4591 thread->state |= state;
4592 UNLOCK_THREAD (thread);
4596 * mono_thread_test_and_set_state:
4598 * Test if current state of @thread include @test. If it does not, OR @set into the state.
4600 * Returns TRUE is @set was OR'd in.
4602 gboolean
4603 mono_thread_test_and_set_state (MonoInternalThread *thread, MonoThreadState test, MonoThreadState set)
4605 LOCK_THREAD (thread);
4607 if ((thread->state & test) != 0) {
4608 UNLOCK_THREAD (thread);
4609 return FALSE;
4612 thread->state |= set;
4613 UNLOCK_THREAD (thread);
4615 return TRUE;
4618 void
4619 mono_thread_clr_state (MonoInternalThread *thread, MonoThreadState state)
4621 LOCK_THREAD (thread);
4622 thread->state &= ~state;
4623 UNLOCK_THREAD (thread);
4626 gboolean
4627 mono_thread_test_state (MonoInternalThread *thread, MonoThreadState test)
4629 gboolean ret = FALSE;
4631 LOCK_THREAD (thread);
4633 if ((thread->state & test) != 0) {
4634 ret = TRUE;
4637 UNLOCK_THREAD (thread);
4639 return ret;
4642 static void
4643 self_interrupt_thread (void *_unused)
4645 MonoException *exc;
4646 MonoThreadInfo *info;
4648 exc = mono_thread_execute_interruption ();
4649 if (!exc) {
4650 if (mono_threads_is_coop_enabled ()) {
4651 /* We can return from an async call in coop, as
4652 * it's simply called when exiting the safepoint */
4653 return;
4656 g_error ("%s: we can't resume from an async call", __func__);
4659 info = mono_thread_info_current ();
4661 /* We must use _with_context since we didn't trampoline into the runtime */
4662 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. */
4665 static gboolean
4666 mono_jit_info_match (MonoJitInfo *ji, gpointer ip)
4668 if (!ji)
4669 return FALSE;
4670 return ji->code_start <= ip && (char*)ip < (char*)ji->code_start + ji->code_size;
4673 static gboolean
4674 last_managed (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
4676 MonoJitInfo **dest = (MonoJitInfo **)data;
4677 *dest = frame->ji;
4678 return TRUE;
4681 static MonoJitInfo*
4682 mono_thread_info_get_last_managed (MonoThreadInfo *info)
4684 MonoJitInfo *ji = NULL;
4685 if (!info)
4686 return NULL;
4689 * The suspended thread might be holding runtime locks. Make sure we don't try taking
4690 * any runtime locks while unwinding. In coop case we shouldn't safepoint in regions
4691 * where we hold runtime locks.
4693 if (!mono_threads_is_coop_enabled ())
4694 mono_thread_info_set_is_async_context (TRUE);
4695 mono_get_eh_callbacks ()->mono_walk_stack_with_state (last_managed, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, &ji);
4696 if (!mono_threads_is_coop_enabled ())
4697 mono_thread_info_set_is_async_context (FALSE);
4698 return ji;
4701 typedef struct {
4702 MonoInternalThread *thread;
4703 gboolean install_async_abort;
4704 MonoThreadInfoInterruptToken *interrupt_token;
4705 } AbortThreadData;
4707 static SuspendThreadResult
4708 async_abort_critical (MonoThreadInfo *info, gpointer ud)
4710 AbortThreadData *data = (AbortThreadData *)ud;
4711 MonoInternalThread *thread = data->thread;
4712 MonoJitInfo *ji = NULL;
4713 gboolean protected_wrapper;
4714 gboolean running_managed;
4716 if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (mono_thread_info_get_suspend_state (info)))
4717 return MonoResumeThread;
4719 /*someone is already interrupting it*/
4720 if (!mono_thread_set_interruption_requested (thread))
4721 return MonoResumeThread;
4723 InterlockedIncrement (&thread_interruption_requested);
4725 ji = mono_thread_info_get_last_managed (info);
4726 protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
4727 running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
4729 if (!protected_wrapper && running_managed) {
4730 /*We are in managed code*/
4731 /*Set the thread to call */
4732 if (data->install_async_abort)
4733 mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
4734 return MonoResumeThread;
4735 } else {
4737 * This will cause waits to be broken.
4738 * It will also prevent the thread from entering a wait, so if the thread returns
4739 * from the wait before it receives the abort signal, it will just spin in the wait
4740 * functions in the io-layer until the signal handler calls QueueUserAPC which will
4741 * make it return.
4743 data->interrupt_token = mono_thread_info_prepare_interrupt (info);
4745 return MonoResumeThread;
4749 static void
4750 async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort)
4752 AbortThreadData data;
4754 g_assert (thread != mono_thread_internal_current ());
4756 data.thread = thread;
4757 data.install_async_abort = install_async_abort;
4758 data.interrupt_token = NULL;
4760 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), TRUE, async_abort_critical, &data);
4761 if (data.interrupt_token)
4762 mono_thread_info_finish_interrupt (data.interrupt_token);
4763 /*FIXME we need to wait for interruption to complete -- figure out how much into interruption we should wait for here*/
4766 static void
4767 self_abort_internal (MonoError *error)
4769 MonoException *exc;
4771 error_init (error);
4773 /* FIXME this is insanely broken, it doesn't cause interruption to happen synchronously
4774 * since passing FALSE to mono_thread_request_interruption makes sure it returns NULL */
4777 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.
4779 exc = mono_thread_request_interruption (TRUE);
4780 if (exc)
4781 mono_error_set_exception_instance (error, exc);
4782 else
4783 mono_thread_info_self_interrupt ();
4786 typedef struct {
4787 MonoInternalThread *thread;
4788 gboolean interrupt;
4789 MonoThreadInfoInterruptToken *interrupt_token;
4790 } SuspendThreadData;
4792 static SuspendThreadResult
4793 async_suspend_critical (MonoThreadInfo *info, gpointer ud)
4795 SuspendThreadData *data = (SuspendThreadData *)ud;
4796 MonoInternalThread *thread = data->thread;
4797 MonoJitInfo *ji = NULL;
4798 gboolean protected_wrapper;
4799 gboolean running_managed;
4801 ji = mono_thread_info_get_last_managed (info);
4802 protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
4803 running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
4805 if (running_managed && !protected_wrapper) {
4806 if (mono_threads_is_coop_enabled ()) {
4807 mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
4808 return MonoResumeThread;
4809 } else {
4810 thread->state &= ~ThreadState_SuspendRequested;
4811 thread->state |= ThreadState_Suspended;
4812 return KeepSuspended;
4814 } else {
4815 if (mono_thread_set_interruption_requested (thread))
4816 InterlockedIncrement (&thread_interruption_requested);
4817 if (data->interrupt)
4818 data->interrupt_token = mono_thread_info_prepare_interrupt ((MonoThreadInfo *)thread->thread_info);
4820 return MonoResumeThread;
4824 /* LOCKING: called with @thread synch_cs held, and releases it */
4825 static void
4826 async_suspend_internal (MonoInternalThread *thread, gboolean interrupt)
4828 SuspendThreadData data;
4830 g_assert (thread != mono_thread_internal_current ());
4832 // MOSTLY_ASYNC_SAFE_PRINTF ("ASYNC SUSPEND thread %p\n", thread_get_tid (thread));
4834 thread->self_suspended = FALSE;
4836 data.thread = thread;
4837 data.interrupt = interrupt;
4838 data.interrupt_token = NULL;
4840 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), interrupt, async_suspend_critical, &data);
4841 if (data.interrupt_token)
4842 mono_thread_info_finish_interrupt (data.interrupt_token);
4844 UNLOCK_THREAD (thread);
4847 /* LOCKING: called with @thread synch_cs held, and releases it */
4848 static void
4849 self_suspend_internal (void)
4851 MonoInternalThread *thread;
4852 MonoOSEvent *event;
4853 MonoOSEventWaitRet res;
4855 thread = mono_thread_internal_current ();
4857 // MOSTLY_ASYNC_SAFE_PRINTF ("SELF SUSPEND thread %p\n", thread_get_tid (thread));
4859 thread->self_suspended = TRUE;
4861 thread->state &= ~ThreadState_SuspendRequested;
4862 thread->state |= ThreadState_Suspended;
4864 UNLOCK_THREAD (thread);
4866 event = thread->suspended;
4868 MONO_ENTER_GC_SAFE;
4869 res = mono_os_event_wait_one (event, MONO_INFINITE_WAIT, TRUE);
4870 g_assert (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0 || res == MONO_OS_EVENT_WAIT_RET_ALERTED);
4871 MONO_EXIT_GC_SAFE;
4874 static void
4875 suspend_for_shutdown_async_call (gpointer unused)
4877 for (;;)
4878 mono_thread_info_yield ();
4881 static SuspendThreadResult
4882 suspend_for_shutdown_critical (MonoThreadInfo *info, gpointer unused)
4884 mono_thread_info_setup_async_call (info, suspend_for_shutdown_async_call, NULL);
4885 return MonoResumeThread;
4888 void
4889 mono_thread_internal_suspend_for_shutdown (MonoInternalThread *thread)
4891 g_assert (thread != mono_thread_internal_current ());
4893 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, suspend_for_shutdown_critical, NULL);
4897 * mono_thread_is_foreign:
4898 * @thread: the thread to query
4900 * This function allows one to determine if a thread was created by the mono runtime and has
4901 * a well defined lifecycle or it's a foreigh one, created by the native environment.
4903 * Returns: TRUE if @thread was not created by the runtime.
4905 mono_bool
4906 mono_thread_is_foreign (MonoThread *thread)
4908 MonoThreadInfo *info = (MonoThreadInfo *)thread->internal_thread->thread_info;
4909 return info->runtime_thread == FALSE;
4913 * mono_add_joinable_thread:
4915 * Add TID to the list of joinable threads.
4916 * LOCKING: Acquires the threads lock.
4918 void
4919 mono_threads_add_joinable_thread (gpointer tid)
4921 #ifndef HOST_WIN32
4923 * We cannot detach from threads because it causes problems like
4924 * 2fd16f60/r114307. So we collect them and join them when
4925 * we have time (in he finalizer thread).
4927 joinable_threads_lock ();
4928 if (!joinable_threads)
4929 joinable_threads = g_hash_table_new (NULL, NULL);
4930 g_hash_table_insert (joinable_threads, tid, tid);
4931 joinable_thread_count ++;
4932 joinable_threads_unlock ();
4934 mono_gc_finalize_notify ();
4935 #endif
4939 * mono_threads_join_threads:
4941 * Join all joinable threads. This is called from the finalizer thread.
4942 * LOCKING: Acquires the threads lock.
4944 void
4945 mono_threads_join_threads (void)
4947 #ifndef HOST_WIN32
4948 GHashTableIter iter;
4949 gpointer key;
4950 gpointer tid;
4951 pthread_t thread;
4952 gboolean found;
4954 /* Fastpath */
4955 if (!joinable_thread_count)
4956 return;
4958 while (TRUE) {
4959 joinable_threads_lock ();
4960 found = FALSE;
4961 if (g_hash_table_size (joinable_threads)) {
4962 g_hash_table_iter_init (&iter, joinable_threads);
4963 g_hash_table_iter_next (&iter, &key, (void**)&tid);
4964 thread = (pthread_t)tid;
4965 g_hash_table_remove (joinable_threads, key);
4966 joinable_thread_count --;
4967 found = TRUE;
4969 joinable_threads_unlock ();
4970 if (found) {
4971 if (thread != pthread_self ()) {
4972 MONO_ENTER_GC_SAFE;
4973 /* This shouldn't block */
4974 mono_threads_join_lock ();
4975 mono_native_thread_join (thread);
4976 mono_threads_join_unlock ();
4977 MONO_EXIT_GC_SAFE;
4979 } else {
4980 break;
4983 #endif
4987 * mono_thread_join:
4989 * Wait for thread TID to exit.
4990 * LOCKING: Acquires the threads lock.
4992 void
4993 mono_thread_join (gpointer tid)
4995 #ifndef HOST_WIN32
4996 pthread_t thread;
4997 gboolean found = FALSE;
4999 joinable_threads_lock ();
5000 if (!joinable_threads)
5001 joinable_threads = g_hash_table_new (NULL, NULL);
5002 if (g_hash_table_lookup (joinable_threads, tid)) {
5003 g_hash_table_remove (joinable_threads, tid);
5004 joinable_thread_count --;
5005 found = TRUE;
5007 joinable_threads_unlock ();
5008 if (!found)
5009 return;
5010 thread = (pthread_t)tid;
5011 MONO_ENTER_GC_SAFE;
5012 mono_native_thread_join (thread);
5013 MONO_EXIT_GC_SAFE;
5014 #endif
5017 void
5018 mono_thread_internal_unhandled_exception (MonoObject* exc)
5020 MonoClass *klass = exc->vtable->klass;
5021 if (is_threadabort_exception (klass)) {
5022 mono_thread_internal_reset_abort (mono_thread_internal_current ());
5023 } else if (!is_appdomainunloaded_exception (klass)
5024 && mono_runtime_unhandled_exception_policy_get () == MONO_UNHANDLED_POLICY_CURRENT) {
5025 mono_unhandled_exception (exc);
5026 if (mono_environment_exitcode_get () == 1) {
5027 mono_environment_exitcode_set (255);
5028 mono_invoke_unhandled_exception_hook (exc);
5029 g_assert_not_reached ();
5034 void
5035 ves_icall_System_Threading_Thread_GetStackTraces (MonoArray **out_threads, MonoArray **out_stack_traces)
5037 MonoError error;
5038 mono_threads_get_thread_dump (out_threads, out_stack_traces, &error);
5039 mono_error_set_pending_exception (&error);
5043 * mono_threads_attach_coop: called by native->managed wrappers
5045 * In non-coop mode:
5046 * - @dummy: is NULL
5047 * - @return: the original domain which needs to be restored, or NULL.
5049 * In coop mode:
5050 * - @dummy: contains the original domain
5051 * - @return: a cookie containing current MonoThreadInfo*.
5053 gpointer
5054 mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
5056 MonoDomain *orig;
5057 gboolean fresh_thread = FALSE;
5059 if (!domain) {
5060 /* Happens when called from AOTed code which is only used in the root domain. */
5061 domain = mono_get_root_domain ();
5064 g_assert (domain);
5066 /* On coop, when we detached, we moved the thread from RUNNING->BLOCKING.
5067 * If we try to reattach we do a BLOCKING->RUNNING transition. If the thread
5068 * is fresh, mono_thread_attach() will do a STARTING->RUNNING transition so
5069 * we're only responsible for making the cookie. */
5070 if (mono_threads_is_coop_enabled ()) {
5071 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
5072 fresh_thread = !info || !mono_thread_info_is_live (info);
5075 if (!mono_thread_internal_current ()) {
5076 mono_thread_attach_full (domain, FALSE);
5078 // #678164
5079 mono_thread_set_state (mono_thread_internal_current (), ThreadState_Background);
5082 orig = mono_domain_get ();
5083 if (orig != domain)
5084 mono_domain_set (domain, TRUE);
5086 if (!mono_threads_is_coop_enabled ())
5087 return orig != domain ? orig : NULL;
5089 if (fresh_thread) {
5090 *dummy = NULL;
5091 /* mono_thread_attach put the thread in RUNNING mode from STARTING, but we need to
5092 * return the right cookie. */
5093 return mono_threads_enter_gc_unsafe_region_cookie ();
5094 } else {
5095 *dummy = orig;
5096 /* thread state (BLOCKING|RUNNING) -> RUNNING */
5097 return mono_threads_enter_gc_unsafe_region (dummy);
5102 * mono_threads_detach_coop: called by native->managed wrappers
5104 * In non-coop mode:
5105 * - @cookie: the original domain which needs to be restored, or NULL.
5106 * - @dummy: is NULL
5108 * In coop mode:
5109 * - @cookie: contains current MonoThreadInfo* if it was in BLOCKING mode, NULL otherwise
5110 * - @dummy: contains the original domain
5112 void
5113 mono_threads_detach_coop (gpointer cookie, gpointer *dummy)
5115 MonoDomain *domain, *orig;
5117 if (!mono_threads_is_coop_enabled ()) {
5118 orig = (MonoDomain*) cookie;
5119 if (orig)
5120 mono_domain_set (orig, TRUE);
5121 } else {
5122 orig = (MonoDomain*) *dummy;
5124 domain = mono_domain_get ();
5125 g_assert (domain);
5127 /* it won't do anything if cookie is NULL
5128 * thread state RUNNING -> (RUNNING|BLOCKING) */
5129 mono_threads_exit_gc_unsafe_region (cookie, dummy);
5131 if (orig != domain) {
5132 if (!orig)
5133 mono_domain_unset ();
5134 else
5135 mono_domain_set (orig, TRUE);
5140 MonoException*
5141 mono_thread_try_resume_interruption (void)
5143 MonoInternalThread *thread;
5145 thread = mono_thread_internal_current ();
5146 if (!mono_get_eh_callbacks ()->mono_above_abort_threshold ())
5147 return NULL;
5148 if (mono_thread_get_abort_prot_block_count (thread) > 0 || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ())
5149 return NULL;
5151 return mono_thread_resume_interruption ();
5154 #if 0
5155 /* Returns TRUE if the current thread is ready to be interrupted. */
5156 gboolean
5157 mono_threads_is_ready_to_be_interrupted (void)
5159 MonoInternalThread *thread;
5161 thread = mono_thread_internal_current ();
5162 LOCK_THREAD (thread);
5163 if (thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
5164 UNLOCK_THREAD (thread);
5165 return FALSE;
5168 if (mono_thread_get_abort_prot_block_count (thread) || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ()) {
5169 UNLOCK_THREAD (thread);
5170 return FALSE;
5173 UNLOCK_THREAD (thread);
5174 return TRUE;
5176 #endif
5178 void
5179 mono_thread_internal_describe (MonoInternalThread *internal, GString *text)
5181 g_string_append_printf (text, ", thread handle : %p", internal->handle);
5183 if (internal->thread_info) {
5184 g_string_append (text, ", state : ");
5185 mono_thread_info_describe_interrupt_token ((MonoThreadInfo*) internal->thread_info, text);
5188 if (internal->owned_mutexes) {
5189 int i;
5191 g_string_append (text, ", owns : [");
5192 for (i = 0; i < internal->owned_mutexes->len; i++)
5193 g_string_append_printf (text, i == 0 ? "%p" : ", %p", g_ptr_array_index (internal->owned_mutexes, i));
5194 g_string_append (text, "]");
5198 gboolean
5199 mono_thread_internal_is_current (MonoInternalThread *internal)
5201 g_assert (internal);
5202 return mono_native_thread_id_equals (mono_native_thread_id_get (), MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid));