Remove suspicious mono_gchandle_free
[mono-project.git] / mono / metadata / threads.c
blobe5ab853cd3c68b19321c1f59cc7c08e00e1bcd9b
1 /**
2 * \file
3 * Thread support internal calls
5 * Author:
6 * Dick Porter (dick@ximian.com)
7 * Paolo Molaro (lupus@ximian.com)
8 * Patrik Torstensson (patrik.torstensson@labs2.com)
10 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
11 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
12 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
13 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
16 #include <config.h>
18 #include <glib.h>
19 #include <string.h>
21 #include <mono/metadata/object.h>
22 #include <mono/metadata/domain-internals.h>
23 #include <mono/metadata/profiler-private.h>
24 #include <mono/metadata/threads.h>
25 #include <mono/metadata/threads-types.h>
26 #include <mono/metadata/exception.h>
27 #include <mono/metadata/environment.h>
28 #include <mono/metadata/monitor.h>
29 #include <mono/metadata/gc-internals.h>
30 #include <mono/metadata/marshal.h>
31 #include <mono/metadata/runtime.h>
32 #include <mono/metadata/object-internals.h>
33 #include <mono/metadata/debug-internals.h>
34 #include <mono/utils/monobitset.h>
35 #include <mono/utils/mono-compiler.h>
36 #include <mono/utils/mono-mmap.h>
37 #include <mono/utils/mono-membar.h>
38 #include <mono/utils/mono-time.h>
39 #include <mono/utils/mono-threads.h>
40 #include <mono/utils/mono-threads-coop.h>
41 #include <mono/utils/mono-tls.h>
42 #include <mono/utils/atomic.h>
43 #include <mono/utils/mono-memory-model.h>
44 #include <mono/utils/mono-error-internals.h>
45 #include <mono/utils/os-event.h>
46 #include <mono/utils/mono-threads-debug.h>
47 #include <mono/utils/unlocked.h>
48 #include <mono/metadata/w32handle.h>
49 #include <mono/metadata/w32event.h>
50 #include <mono/metadata/w32mutex.h>
52 #include <mono/metadata/reflection-internals.h>
53 #include <mono/metadata/abi-details.h>
54 #include <mono/metadata/w32error.h>
55 #include <mono/utils/w32api.h>
56 #include <mono/utils/mono-os-wait.h>
58 #ifdef HAVE_SIGNAL_H
59 #include <signal.h>
60 #endif
62 #if defined(HOST_WIN32)
63 #include <objbase.h>
65 extern gboolean
66 mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle);
67 #endif
69 #if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
70 #define USE_TKILL_ON_ANDROID 1
71 #endif
73 #ifdef HOST_ANDROID
74 #include <errno.h>
76 #ifdef USE_TKILL_ON_ANDROID
77 extern int tkill (pid_t tid, int signal);
78 #endif
79 #endif
81 /*#define THREAD_DEBUG(a) do { a; } while (0)*/
82 #define THREAD_DEBUG(a)
83 /*#define THREAD_WAIT_DEBUG(a) do { a; } while (0)*/
84 #define THREAD_WAIT_DEBUG(a)
85 /*#define LIBGC_DEBUG(a) do { a; } while (0)*/
86 #define LIBGC_DEBUG(a)
88 #define SPIN_TRYLOCK(i) (mono_atomic_cas_i32 (&(i), 1, 0) == 0)
89 #define SPIN_LOCK(i) do { \
90 if (SPIN_TRYLOCK (i)) \
91 break; \
92 } while (1)
94 #define SPIN_UNLOCK(i) i = 0
96 #define LOCK_THREAD(thread) lock_thread((thread))
97 #define UNLOCK_THREAD(thread) unlock_thread((thread))
99 typedef union {
100 gint32 ival;
101 gfloat fval;
102 } IntFloatUnion;
104 typedef union {
105 gint64 ival;
106 gdouble fval;
107 } LongDoubleUnion;
109 typedef struct _StaticDataFreeList StaticDataFreeList;
110 struct _StaticDataFreeList {
111 StaticDataFreeList *next;
112 guint32 offset;
113 guint32 size;
116 typedef struct {
117 int idx;
118 int offset;
119 StaticDataFreeList *freelist;
120 } StaticDataInfo;
122 /* Controls access to the 'threads' hash table */
123 static void mono_threads_lock (void);
124 static void mono_threads_unlock (void);
125 static MonoCoopMutex threads_mutex;
127 /* Controls access to the 'joinable_threads' hash table */
128 #define joinable_threads_lock() mono_os_mutex_lock (&joinable_threads_mutex)
129 #define joinable_threads_unlock() mono_os_mutex_unlock (&joinable_threads_mutex)
130 static mono_mutex_t joinable_threads_mutex;
132 /* Holds current status of static data heap */
133 static StaticDataInfo thread_static_info;
134 static StaticDataInfo context_static_info;
136 /* The hash of existing threads (key is thread ID, value is
137 * MonoInternalThread*) that need joining before exit
139 static MonoGHashTable *threads=NULL;
141 /* List of app context GC handles.
142 * Added to from mono_threads_register_app_context ().
144 static GHashTable *contexts = NULL;
146 /* Cleanup queue for contexts. */
147 static MonoReferenceQueue *context_queue;
150 * Threads which are starting up and they are not in the 'threads' hash yet.
151 * When mono_thread_attach_internal is called for a thread, it will be removed from this hash table.
152 * Protected by mono_threads_lock ().
154 static MonoGHashTable *threads_starting_up = NULL;
156 /* Contains tids */
157 /* Protected by the threads lock */
158 static GHashTable *joinable_threads;
159 static gint32 joinable_thread_count;
161 #define SET_CURRENT_OBJECT(x) mono_tls_set_thread (x)
162 #define GET_CURRENT_OBJECT() (MonoInternalThread*) mono_tls_get_thread ()
164 /* function called at thread start */
165 static MonoThreadStartCB mono_thread_start_cb = NULL;
167 /* function called at thread attach */
168 static MonoThreadAttachCB mono_thread_attach_cb = NULL;
170 /* function called at thread cleanup */
171 static MonoThreadCleanupFunc mono_thread_cleanup_fn = NULL;
173 /* The default stack size for each thread */
174 static guint32 default_stacksize = 0;
175 #define default_stacksize_for_thread(thread) ((thread)->stack_size? (thread)->stack_size: default_stacksize)
177 static void context_adjust_static_data (MonoAppContext *ctx);
178 static void mono_free_static_data (gpointer* static_data);
179 static void mono_init_static_data_info (StaticDataInfo *static_data);
180 static guint32 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align);
181 static gboolean mono_thread_resume (MonoInternalThread* thread);
182 static void async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort);
183 static void self_abort_internal (MonoError *error);
184 static void async_suspend_internal (MonoInternalThread *thread, gboolean interrupt);
185 static void self_suspend_internal (void);
187 static MonoException* mono_thread_execute_interruption (void);
188 static void ref_stack_destroy (gpointer rs);
190 /* Spin lock for InterlockedXXX 64 bit functions */
191 #define mono_interlocked_lock() mono_os_mutex_lock (&interlocked_mutex)
192 #define mono_interlocked_unlock() mono_os_mutex_unlock (&interlocked_mutex)
193 static mono_mutex_t interlocked_mutex;
195 /* global count of thread interruptions requested */
196 static gint32 thread_interruption_requested = 0;
198 /* Event signaled when a thread changes its background mode */
199 static MonoOSEvent background_change_event;
201 static gboolean shutting_down = FALSE;
203 static gint32 managed_thread_id_counter = 0;
205 static void
206 mono_threads_lock (void)
208 mono_locks_coop_acquire (&threads_mutex, ThreadsLock);
211 static void
212 mono_threads_unlock (void)
214 mono_locks_coop_release (&threads_mutex, ThreadsLock);
218 static guint32
219 get_next_managed_thread_id (void)
221 return mono_atomic_inc_i32 (&managed_thread_id_counter);
225 * We separate interruptions/exceptions into either sync (they can be processed anytime,
226 * normally as soon as they are set, and are set by the same thread) and async (they can't
227 * be processed inside abort protected blocks and are normally set by other threads). We
228 * can have both a pending sync and async interruption. In this case, the sync exception is
229 * processed first. Since we clean sync flag first, mono_thread_execute_interruption must
230 * also handle all sync type exceptions before the async type exceptions.
232 enum {
233 INTERRUPT_SYNC_REQUESTED_BIT = 0x1,
234 INTERRUPT_ASYNC_REQUESTED_BIT = 0x2,
235 INTERRUPT_REQUESTED_MASK = 0x3,
236 ABORT_PROT_BLOCK_SHIFT = 2,
237 ABORT_PROT_BLOCK_BITS = 8,
238 ABORT_PROT_BLOCK_MASK = (((1 << ABORT_PROT_BLOCK_BITS) - 1) << ABORT_PROT_BLOCK_SHIFT)
241 static int
242 mono_thread_get_abort_prot_block_count (MonoInternalThread *thread)
244 gsize state = thread->thread_state;
245 return (state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT;
248 void
249 mono_threads_begin_abort_protected_block (void)
251 MonoInternalThread *thread = mono_thread_internal_current ();
252 gsize old_state, new_state;
253 int new_val;
254 do {
255 old_state = thread->thread_state;
257 new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) + 1;
258 //bounds check abort_prot_count
259 g_assert (new_val > 0);
260 g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
262 new_state = old_state + (1 << ABORT_PROT_BLOCK_SHIFT);
263 } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
265 /* Defer async request since we won't be able to process until exiting the block */
266 if (new_val == 1 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT)) {
267 mono_atomic_dec_i32 (&thread_interruption_requested);
268 THREADS_INTERRUPT_DEBUG ("[%d] begin abort protected block old_state %ld new_state %ld, defer tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
269 if (thread_interruption_requested < 0)
270 g_warning ("bad thread_interruption_requested state");
271 } else {
272 THREADS_INTERRUPT_DEBUG ("[%d] begin abort protected block old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
276 static gboolean
277 mono_thread_state_has_interruption (gsize state)
279 /* pending exception, self abort */
280 if (state & INTERRUPT_SYNC_REQUESTED_BIT)
281 return TRUE;
283 /* abort, interruption, suspend */
284 if ((state & INTERRUPT_ASYNC_REQUESTED_BIT) && !(state & ABORT_PROT_BLOCK_MASK))
285 return TRUE;
287 return FALSE;
290 gboolean
291 mono_threads_end_abort_protected_block (void)
293 MonoInternalThread *thread = mono_thread_internal_current ();
294 gsize old_state, new_state;
295 int new_val;
296 do {
297 old_state = thread->thread_state;
299 //bounds check abort_prot_count
300 new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) - 1;
301 g_assert (new_val >= 0);
302 g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
304 new_state = old_state - (1 << ABORT_PROT_BLOCK_SHIFT);
305 } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
307 if (new_val == 0 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT)) {
308 mono_atomic_inc_i32 (&thread_interruption_requested);
309 THREADS_INTERRUPT_DEBUG ("[%d] end abort protected block old_state %ld new_state %ld, restore tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
310 } else {
311 THREADS_INTERRUPT_DEBUG ("[%d] end abort protected block old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
314 return mono_thread_state_has_interruption (new_state);
317 static gboolean
318 mono_thread_get_interruption_requested (MonoInternalThread *thread)
320 gsize state = thread->thread_state;
322 return mono_thread_state_has_interruption (state);
326 * Returns TRUE is there was a state change
327 * We clear a single interruption request, sync has priority.
329 static gboolean
330 mono_thread_clear_interruption_requested (MonoInternalThread *thread)
332 gsize old_state, new_state;
333 do {
334 old_state = thread->thread_state;
336 // no interruption to process
337 if (!(old_state & INTERRUPT_SYNC_REQUESTED_BIT) &&
338 (!(old_state & INTERRUPT_ASYNC_REQUESTED_BIT) || (old_state & ABORT_PROT_BLOCK_MASK)))
339 return FALSE;
341 if (old_state & INTERRUPT_SYNC_REQUESTED_BIT)
342 new_state = old_state & ~INTERRUPT_SYNC_REQUESTED_BIT;
343 else
344 new_state = old_state & ~INTERRUPT_ASYNC_REQUESTED_BIT;
345 } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
347 mono_atomic_dec_i32 (&thread_interruption_requested);
348 THREADS_INTERRUPT_DEBUG ("[%d] clear interruption old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
349 if (thread_interruption_requested < 0)
350 g_warning ("bad thread_interruption_requested state");
351 return TRUE;
354 /* Returns TRUE is there was a state change and the interruption can be processed */
355 static gboolean
356 mono_thread_set_interruption_requested (MonoInternalThread *thread)
358 //always force when the current thread is doing it to itself.
359 gboolean sync = thread == mono_thread_internal_current ();
360 gsize old_state, new_state;
361 do {
362 old_state = thread->thread_state;
364 //Already set
365 if ((sync && (old_state & INTERRUPT_SYNC_REQUESTED_BIT)) ||
366 (!sync && (old_state & INTERRUPT_ASYNC_REQUESTED_BIT)))
367 return FALSE;
369 if (sync)
370 new_state = old_state | INTERRUPT_SYNC_REQUESTED_BIT;
371 else
372 new_state = old_state | INTERRUPT_ASYNC_REQUESTED_BIT;
373 } while (mono_atomic_cas_ptr ((volatile gpointer *)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
375 if (sync || !(new_state & ABORT_PROT_BLOCK_MASK)) {
376 mono_atomic_inc_i32 (&thread_interruption_requested);
377 THREADS_INTERRUPT_DEBUG ("[%d] set interruption on [%d] old_state %ld new_state %ld, tir %d\n", mono_thread_internal_current ()->small_id, thread->small_id, old_state, new_state, thread_interruption_requested);
378 } else {
379 THREADS_INTERRUPT_DEBUG ("[%d] set interruption on [%d] old_state %ld new_state %ld, tir deferred %d\n", mono_thread_internal_current ()->small_id, thread->small_id, old_state, new_state, thread_interruption_requested);
382 return sync || !(new_state & ABORT_PROT_BLOCK_MASK);
385 static inline MonoNativeThreadId
386 thread_get_tid (MonoInternalThread *thread)
388 /* We store the tid as a guint64 to keep the object layout constant between platforms */
389 return MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid);
392 static void ensure_synch_cs_set (MonoInternalThread *thread)
394 MonoCoopMutex *synch_cs;
396 if (thread->synch_cs != NULL) {
397 return;
400 synch_cs = g_new0 (MonoCoopMutex, 1);
401 mono_coop_mutex_init_recursive (synch_cs);
403 if (mono_atomic_cas_ptr ((gpointer *)&thread->synch_cs,
404 synch_cs, NULL) != NULL) {
405 /* Another thread must have installed this CS */
406 mono_coop_mutex_destroy (synch_cs);
407 g_free (synch_cs);
411 static inline void
412 lock_thread (MonoInternalThread *thread)
414 if (!thread->synch_cs)
415 ensure_synch_cs_set (thread);
417 g_assert (thread->synch_cs);
419 mono_coop_mutex_lock (thread->synch_cs);
422 static inline void
423 unlock_thread (MonoInternalThread *thread)
425 mono_coop_mutex_unlock (thread->synch_cs);
428 static inline gboolean
429 is_appdomainunloaded_exception (MonoClass *klass)
431 return klass == mono_class_get_appdomain_unloaded_exception_class ();
434 static inline gboolean
435 is_threadabort_exception (MonoClass *klass)
437 return klass == mono_defaults.threadabortexception_class;
441 * A special static data offset (guint32) consists of 3 parts:
443 * [0] 6-bit index into the array of chunks.
444 * [6] 25-bit offset into the array.
445 * [31] Bit indicating thread or context static.
448 typedef union {
449 struct {
450 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
451 guint32 type : 1;
452 guint32 offset : 25;
453 guint32 index : 6;
454 #else
455 guint32 index : 6;
456 guint32 offset : 25;
457 guint32 type : 1;
458 #endif
459 } fields;
460 guint32 raw;
461 } SpecialStaticOffset;
463 #define SPECIAL_STATIC_OFFSET_TYPE_THREAD 0
464 #define SPECIAL_STATIC_OFFSET_TYPE_CONTEXT 1
466 #define MAKE_SPECIAL_STATIC_OFFSET(idx, off, ty) \
467 ((SpecialStaticOffset) { .fields = { .index = (idx), .offset = (off), .type = (ty) } }.raw)
468 #define ACCESS_SPECIAL_STATIC_OFFSET(x,f) \
469 (((SpecialStaticOffset *) &(x))->fields.f)
471 static gpointer
472 get_thread_static_data (MonoInternalThread *thread, guint32 offset)
474 g_assert (ACCESS_SPECIAL_STATIC_OFFSET (offset, type) == SPECIAL_STATIC_OFFSET_TYPE_THREAD);
476 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
477 int off = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
479 return ((char *) thread->static_data [idx]) + off;
482 static gpointer
483 get_context_static_data (MonoAppContext *ctx, guint32 offset)
485 g_assert (ACCESS_SPECIAL_STATIC_OFFSET (offset, type) == SPECIAL_STATIC_OFFSET_TYPE_CONTEXT);
487 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
488 int off = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
490 return ((char *) ctx->static_data [idx]) + off;
493 static MonoThread**
494 get_current_thread_ptr_for_domain (MonoDomain *domain, MonoInternalThread *thread)
496 static MonoClassField *current_thread_field = NULL;
498 guint32 offset;
500 if (!current_thread_field) {
501 current_thread_field = mono_class_get_field_from_name (mono_defaults.thread_class, "current_thread");
502 g_assert (current_thread_field);
505 mono_class_vtable (domain, mono_defaults.thread_class);
506 mono_domain_lock (domain);
507 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, current_thread_field));
508 mono_domain_unlock (domain);
509 g_assert (offset);
511 return (MonoThread **)get_thread_static_data (thread, offset);
514 static void
515 set_current_thread_for_domain (MonoDomain *domain, MonoInternalThread *thread, MonoThread *current)
517 MonoThread **current_thread_ptr = get_current_thread_ptr_for_domain (domain, thread);
519 g_assert (current->obj.vtable->domain == domain);
521 g_assert (!*current_thread_ptr);
522 *current_thread_ptr = current;
525 static MonoThread*
526 create_thread_object (MonoDomain *domain, MonoInternalThread *internal)
528 MonoThread *thread;
529 MonoVTable *vtable;
530 MonoError error;
532 vtable = mono_class_vtable (domain, mono_defaults.thread_class);
533 g_assert (vtable);
535 thread = (MonoThread*)mono_object_new_mature (vtable, &error);
536 /* only possible failure mode is OOM, from which we don't expect to recover. */
537 mono_error_assert_ok (&error);
539 MONO_OBJECT_SETREF (thread, internal_thread, internal);
541 return thread;
544 static MonoInternalThread*
545 create_internal_thread_object (void)
547 MonoError error;
548 MonoInternalThread *thread;
549 MonoVTable *vt;
551 vt = mono_class_vtable (mono_get_root_domain (), mono_defaults.internal_thread_class);
552 thread = (MonoInternalThread*) mono_object_new_mature (vt, &error);
553 /* only possible failure mode is OOM, from which we don't exect to recover */
554 mono_error_assert_ok (&error);
556 thread->synch_cs = g_new0 (MonoCoopMutex, 1);
557 mono_coop_mutex_init_recursive (thread->synch_cs);
559 thread->apartment_state = ThreadApartmentState_Unknown;
560 thread->managed_id = get_next_managed_thread_id ();
561 if (mono_gc_is_moving ()) {
562 thread->thread_pinning_ref = thread;
563 MONO_GC_REGISTER_ROOT_PINNING (thread->thread_pinning_ref, MONO_ROOT_SOURCE_THREADING, "thread pinning reference");
566 thread->priority = MONO_THREAD_PRIORITY_NORMAL;
568 thread->suspended = g_new0 (MonoOSEvent, 1);
569 mono_os_event_init (thread->suspended, TRUE);
571 return thread;
574 static void
575 mono_thread_internal_set_priority (MonoInternalThread *internal, MonoThreadPriority priority)
577 g_assert (internal);
579 g_assert (priority >= MONO_THREAD_PRIORITY_LOWEST);
580 g_assert (priority <= MONO_THREAD_PRIORITY_HIGHEST);
581 g_assert (MONO_THREAD_PRIORITY_LOWEST < MONO_THREAD_PRIORITY_HIGHEST);
583 #ifdef HOST_WIN32
584 BOOL res;
586 g_assert (internal->native_handle);
588 res = SetThreadPriority (internal->native_handle, priority - 2);
589 if (!res)
590 g_error ("%s: SetThreadPriority failed, error %d", __func__, GetLastError ());
591 #else /* HOST_WIN32 */
592 pthread_t tid;
593 int policy;
594 struct sched_param param;
595 gint res;
597 tid = thread_get_tid (internal);
599 res = pthread_getschedparam (tid, &policy, &param);
600 if (res != 0)
601 g_error ("%s: pthread_getschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
603 #ifdef _POSIX_PRIORITY_SCHEDULING
604 int max, min;
606 /* Necessary to get valid priority range */
608 min = sched_get_priority_min (policy);
609 max = sched_get_priority_max (policy);
611 if (max > 0 && min >= 0 && max > min) {
612 double srange, drange, sposition, dposition;
613 srange = MONO_THREAD_PRIORITY_HIGHEST - MONO_THREAD_PRIORITY_LOWEST;
614 drange = max - min;
615 sposition = priority - MONO_THREAD_PRIORITY_LOWEST;
616 dposition = (sposition / srange) * drange;
617 param.sched_priority = (int)(dposition + min);
618 } else
619 #endif
621 switch (policy) {
622 case SCHED_FIFO:
623 case SCHED_RR:
624 param.sched_priority = 50;
625 break;
626 #ifdef SCHED_BATCH
627 case SCHED_BATCH:
628 #endif
629 case SCHED_OTHER:
630 param.sched_priority = 0;
631 break;
632 default:
633 g_warning ("%s: unknown policy %d", __func__, policy);
634 return;
638 res = pthread_setschedparam (tid, policy, &param);
639 if (res != 0) {
640 if (res == EPERM) {
641 g_warning ("%s: pthread_setschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
642 return;
644 g_error ("%s: pthread_setschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
646 #endif /* HOST_WIN32 */
649 static void
650 mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean threadlocal);
652 static gboolean
653 mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean force_domain)
655 MonoThreadInfo *info;
656 MonoInternalThread *internal;
657 MonoDomain *domain, *root_domain;
659 g_assert (thread);
661 info = mono_thread_info_current ();
662 g_assert (info);
664 internal = thread->internal_thread;
665 g_assert (internal);
667 /* It is needed to store the MonoInternalThread on the MonoThreadInfo, because of the following case:
668 * - the MonoInternalThread TLS key is destroyed: set it to NULL
669 * - the MonoThreadInfo TLS key is destroyed: calls mono_thread_info_detach
670 * - it calls MonoThreadInfoCallbacks.thread_detach
671 * - mono_thread_internal_current returns NULL -> fails to detach the MonoInternalThread. */
672 mono_thread_info_set_internal_thread_gchandle (info, mono_gchandle_new ((MonoObject*) internal, FALSE));
674 internal->handle = mono_threads_open_thread_handle (info->handle);
675 #ifdef HOST_WIN32
676 internal->native_handle = OpenThread (THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId ());
677 #endif
678 internal->tid = MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ());
679 internal->thread_info = info;
680 internal->small_id = info->small_id;
682 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, mono_native_thread_id_get (), internal));
684 SET_CURRENT_OBJECT (internal);
686 domain = mono_object_domain (thread);
688 mono_thread_push_appdomain_ref (domain);
689 if (!mono_domain_set (domain, force_domain)) {
690 mono_thread_pop_appdomain_ref ();
691 return FALSE;
694 mono_threads_lock ();
696 if (threads_starting_up)
697 mono_g_hash_table_remove (threads_starting_up, thread);
699 if (shutting_down && !force_attach) {
700 mono_threads_unlock ();
701 mono_thread_pop_appdomain_ref ();
702 return FALSE;
705 if (!threads) {
706 threads = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_THREADING, "threads table");
709 /* We don't need to duplicate thread->handle, because it is
710 * only closed when the thread object is finalized by the GC. */
711 mono_g_hash_table_insert (threads, (gpointer)(gsize)(internal->tid), internal);
713 /* We have to do this here because mono_thread_start_cb
714 * requires that root_domain_thread is set up. */
715 if (thread_static_info.offset || thread_static_info.idx > 0) {
716 /* get the current allocated size */
717 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (thread_static_info.idx, thread_static_info.offset, 0);
718 mono_alloc_static_data (&internal->static_data, offset, TRUE);
721 mono_threads_unlock ();
723 root_domain = mono_get_root_domain ();
725 g_assert (!internal->root_domain_thread);
726 if (domain != root_domain)
727 MONO_OBJECT_SETREF (internal, root_domain_thread, create_thread_object (root_domain, internal));
728 else
729 MONO_OBJECT_SETREF (internal, root_domain_thread, thread);
731 if (domain != root_domain)
732 set_current_thread_for_domain (root_domain, internal, internal->root_domain_thread);
734 set_current_thread_for_domain (domain, internal, thread);
736 THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, internal->tid, internal->handle));
738 return TRUE;
741 static void
742 mono_thread_detach_internal (MonoInternalThread *thread)
744 MonoThreadInfo *info;
745 gboolean removed;
746 guint32 gchandle;
748 g_assert (thread != NULL);
749 SET_CURRENT_OBJECT (thread);
751 info = (MonoThreadInfo*) thread->thread_info;
752 g_assert (info);
754 THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
756 MONO_PROFILER_RAISE (thread_stopping, (thread->tid));
758 #ifndef HOST_WIN32
759 mono_w32mutex_abandon ();
760 #endif
762 if (thread->abort_state_handle) {
763 mono_gchandle_free (thread->abort_state_handle);
764 thread->abort_state_handle = 0;
767 thread->abort_exc = NULL;
768 thread->current_appcontext = NULL;
771 * Prevent race condition between execution of this method and runtime shutdown.
772 * Adding runtime thread to the joinable threads list will make sure runtime shutdown
773 * won't complete until added runtime thread have exited. Owner of threads attached to the
774 * runtime but not identified as runtime threads needs to make sure thread detach calls won't
775 * race with runtime shutdown.
777 #ifdef HOST_WIN32
778 mono_threads_add_joinable_runtime_thread (info);
779 #endif
782 * thread->synch_cs can be NULL if this was called after
783 * ves_icall_System_Threading_InternalThread_Thread_free_internal.
784 * This can happen only during shutdown.
785 * The shutting_down flag is not always set, so we can't assert on it.
787 if (thread->synch_cs)
788 LOCK_THREAD (thread);
790 thread->state |= ThreadState_Stopped;
791 thread->state &= ~ThreadState_Background;
793 if (thread->synch_cs)
794 UNLOCK_THREAD (thread);
797 An interruption request has leaked to cleanup. Adjust the global counter.
799 This can happen is the abort source thread finds the abortee (this) thread
800 in unmanaged code. If this thread never trips back to managed code or check
801 the local flag it will be left set and positively unbalance the global counter.
803 Leaving the counter unbalanced will cause a performance degradation since all threads
804 will now keep checking their local flags all the time.
806 mono_thread_clear_interruption_requested (thread);
808 mono_threads_lock ();
810 if (!threads) {
811 removed = FALSE;
812 } else if (mono_g_hash_table_lookup (threads, (gpointer)thread->tid) != thread) {
813 /* We have to check whether the thread object for the
814 * tid is still the same in the table because the
815 * thread might have been destroyed and the tid reused
816 * in the meantime, in which case the tid would be in
817 * the table, but with another thread object.
819 removed = FALSE;
820 } else {
821 mono_g_hash_table_remove (threads, (gpointer)thread->tid);
822 removed = TRUE;
825 mono_threads_unlock ();
827 /* Don't close the handle here, wait for the object finalizer
828 * to do it. Otherwise, the following race condition applies:
830 * 1) Thread exits (and mono_thread_detach_internal() closes the handle)
832 * 2) Some other handle is reassigned the same slot
834 * 3) Another thread tries to join the first thread, and
835 * blocks waiting for the reassigned handle to be signalled
836 * (which might never happen). This is possible, because the
837 * thread calling Join() still has a reference to the first
838 * thread's object.
841 /* if the thread is not in the hash it has been removed already */
842 if (!removed) {
843 mono_domain_unset ();
844 mono_memory_barrier ();
846 if (mono_thread_cleanup_fn)
847 mono_thread_cleanup_fn (thread_get_tid (thread));
849 goto done;
852 mono_release_type_locks (thread);
854 MONO_PROFILER_RAISE (thread_stopped, (thread->tid));
857 * This will signal async signal handlers that the thread has exited.
858 * The profiler callback needs this to be set, so it cannot be done earlier.
860 mono_domain_unset ();
861 mono_memory_barrier ();
863 if (thread == mono_thread_internal_current ())
864 mono_thread_pop_appdomain_ref ();
866 mono_free_static_data (thread->static_data);
867 thread->static_data = NULL;
868 ref_stack_destroy (thread->appdomain_refs);
869 thread->appdomain_refs = NULL;
871 g_assert (thread->suspended);
872 mono_os_event_destroy (thread->suspended);
873 g_free (thread->suspended);
874 thread->suspended = NULL;
876 if (mono_thread_cleanup_fn)
877 mono_thread_cleanup_fn (thread_get_tid (thread));
879 mono_memory_barrier ();
881 if (mono_gc_is_moving ()) {
882 MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref);
883 thread->thread_pinning_ref = NULL;
886 done:
887 SET_CURRENT_OBJECT (NULL);
888 mono_domain_unset ();
890 if (!mono_thread_info_try_get_internal_thread_gchandle (info, &gchandle))
891 g_error ("%s: failed to get gchandle, info = %p", __func__, info);
893 mono_gchandle_free (gchandle);
895 mono_thread_info_unset_internal_thread_gchandle (info);
897 MONO_PROFILER_RAISE (thread_exited, (thread->tid));
899 /* Don't need to close the handle to this thread, even though we took a
900 * reference in mono_thread_attach (), because the GC will do it
901 * when the Thread object is finalised.
905 typedef struct {
906 gint32 ref;
907 MonoThread *thread;
908 MonoObject *start_delegate;
909 MonoObject *start_delegate_arg;
910 MonoThreadStart start_func;
911 gpointer start_func_arg;
912 gboolean force_attach;
913 gboolean failed;
914 MonoCoopSem registered;
915 } StartInfo;
917 static guint32 WINAPI start_wrapper_internal(StartInfo *start_info, gsize *stack_ptr)
919 MonoError error;
920 MonoThreadStart start_func;
921 void *start_func_arg;
922 gsize tid;
924 * We don't create a local to hold start_info->thread, so hopefully it won't get pinned during a
925 * GC stack walk.
927 MonoThread *thread;
928 MonoInternalThread *internal;
929 MonoObject *start_delegate;
930 MonoObject *start_delegate_arg;
931 MonoDomain *domain;
933 thread = start_info->thread;
934 internal = thread->internal_thread;
935 domain = mono_object_domain (start_info->thread);
937 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper", __func__, mono_native_thread_id_get ()));
939 if (!mono_thread_attach_internal (thread, start_info->force_attach, FALSE)) {
940 start_info->failed = TRUE;
942 mono_coop_sem_post (&start_info->registered);
944 if (mono_atomic_dec_i32 (&start_info->ref) == 0) {
945 mono_coop_sem_destroy (&start_info->registered);
946 g_free (start_info);
949 return 0;
952 mono_thread_internal_set_priority (internal, internal->priority);
954 tid = internal->tid;
956 start_delegate = start_info->start_delegate;
957 start_delegate_arg = start_info->start_delegate_arg;
958 start_func = start_info->start_func;
959 start_func_arg = start_info->start_func_arg;
961 /* This MUST be called before any managed code can be
962 * executed, as it calls the callback function that (for the
963 * jit) sets the lmf marker.
966 if (mono_thread_start_cb)
967 mono_thread_start_cb (tid, stack_ptr, start_func);
969 /* On 2.0 profile (and higher), set explicitly since state might have been
970 Unknown */
971 if (internal->apartment_state == ThreadApartmentState_Unknown)
972 internal->apartment_state = ThreadApartmentState_MTA;
974 mono_thread_init_apartment_state ();
976 /* Let the thread that called Start() know we're ready */
977 mono_coop_sem_post (&start_info->registered);
979 if (mono_atomic_dec_i32 (&start_info->ref) == 0) {
980 mono_coop_sem_destroy (&start_info->registered);
981 g_free (start_info);
984 /* start_info is not valid anymore */
985 start_info = NULL;
988 * Call this after calling start_notify, since the profiler callback might want
989 * to lock the thread, and the lock is held by thread_start () which waits for
990 * start_notify.
992 MONO_PROFILER_RAISE (thread_started, (tid));
994 /* if the name was set before starting, we didn't invoke the profiler callback */
995 if (internal->name) {
996 char *tname = g_utf16_to_utf8 (internal->name, internal->name_len, NULL, NULL, NULL);
997 MONO_PROFILER_RAISE (thread_name, (internal->tid, tname));
998 mono_native_thread_set_name (MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid), tname);
999 g_free (tname);
1002 /* start_func is set only for unmanaged start functions */
1003 if (start_func) {
1004 start_func (start_func_arg);
1005 } else {
1006 void *args [1];
1008 g_assert (start_delegate != NULL);
1010 /* we may want to handle the exception here. See comment below on unhandled exceptions */
1011 args [0] = (gpointer) start_delegate_arg;
1012 mono_runtime_delegate_invoke_checked (start_delegate, args, &error);
1014 if (!mono_error_ok (&error)) {
1015 MonoException *ex = mono_error_convert_to_exception (&error);
1017 g_assert (ex != NULL);
1018 MonoClass *klass = mono_object_get_class (&ex->object);
1019 if ((mono_runtime_unhandled_exception_policy_get () != MONO_UNHANDLED_POLICY_LEGACY) &&
1020 !is_threadabort_exception (klass)) {
1021 mono_unhandled_exception (&ex->object);
1022 mono_invoke_unhandled_exception_hook (&ex->object);
1023 g_assert_not_reached ();
1025 } else {
1026 mono_error_cleanup (&error);
1030 /* If the thread calls ExitThread at all, this remaining code
1031 * will not be executed, but the main thread will eventually
1032 * call mono_thread_detach_internal() on this thread's behalf.
1035 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper terminating", __func__, mono_native_thread_id_get ()));
1037 /* Do any cleanup needed for apartment state. This
1038 * cannot be done in mono_thread_detach_internal since
1039 * mono_thread_detach_internal could be called for a
1040 * thread other than the current thread.
1041 * mono_thread_cleanup_apartment_state cleans up apartment
1042 * for the current thead */
1043 mono_thread_cleanup_apartment_state ();
1045 mono_thread_detach_internal (internal);
1047 return 0;
1050 static gsize WINAPI
1051 start_wrapper (gpointer data)
1053 StartInfo *start_info;
1054 MonoThreadInfo *info;
1055 gsize res;
1057 start_info = (StartInfo*) data;
1058 g_assert (start_info);
1060 info = mono_thread_info_attach ();
1061 info->runtime_thread = TRUE;
1063 /* Run the actual main function of the thread */
1064 res = start_wrapper_internal (start_info, info->stack_end);
1066 mono_thread_info_exit (res);
1068 g_assert_not_reached ();
1072 * create_thread:
1074 * Common thread creation code.
1075 * LOCKING: Acquires the threads lock.
1077 static gboolean
1078 create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *start_delegate, MonoThreadStart start_func, gpointer start_func_arg,
1079 MonoThreadCreateFlags flags, MonoError *error)
1081 StartInfo *start_info = NULL;
1082 MonoNativeThreadId tid;
1083 gboolean ret;
1084 gsize stack_set_size;
1086 if (start_delegate)
1087 g_assert (!start_func && !start_func_arg);
1088 if (start_func)
1089 g_assert (!start_delegate);
1091 if (flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL) {
1092 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER));
1093 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE));
1095 if (flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER) {
1096 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL));
1097 g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE));
1101 * Join joinable threads to prevent running out of threads since the finalizer
1102 * thread might be blocked/backlogged.
1104 mono_threads_join_threads ();
1106 error_init (error);
1108 mono_threads_lock ();
1109 if (shutting_down && !(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE)) {
1110 mono_threads_unlock ();
1111 return FALSE;
1113 if (threads_starting_up == NULL) {
1114 threads_starting_up = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_VALUE_GC, MONO_ROOT_SOURCE_THREADING, "starting threads table");
1116 mono_g_hash_table_insert (threads_starting_up, thread, thread);
1117 mono_threads_unlock ();
1119 internal->threadpool_thread = flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL;
1120 if (internal->threadpool_thread)
1121 mono_thread_set_state (internal, ThreadState_Background);
1123 internal->debugger_thread = flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER;
1125 start_info = g_new0 (StartInfo, 1);
1126 start_info->ref = 2;
1127 start_info->thread = thread;
1128 start_info->start_delegate = start_delegate;
1129 start_info->start_delegate_arg = thread->start_obj;
1130 start_info->start_func = start_func;
1131 start_info->start_func_arg = start_func_arg;
1132 start_info->force_attach = flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE;
1133 start_info->failed = FALSE;
1134 mono_coop_sem_init (&start_info->registered, 0);
1136 if (flags != MONO_THREAD_CREATE_FLAGS_SMALL_STACK)
1137 stack_set_size = default_stacksize_for_thread (internal);
1138 else
1139 stack_set_size = 0;
1141 if (!mono_thread_platform_create_thread ((MonoThreadStart)start_wrapper, start_info, &stack_set_size, &tid)) {
1142 /* The thread couldn't be created, so set an exception */
1143 mono_threads_lock ();
1144 mono_g_hash_table_remove (threads_starting_up, thread);
1145 mono_threads_unlock ();
1146 mono_error_set_execution_engine (error, "Couldn't create thread. Error 0x%x", mono_w32error_get_last());
1147 /* ref is not going to be decremented in start_wrapper_internal */
1148 mono_atomic_dec_i32 (&start_info->ref);
1149 ret = FALSE;
1150 goto done;
1153 internal->stack_size = (int) stack_set_size;
1155 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Launching thread %p (%"G_GSIZE_FORMAT")", __func__, mono_native_thread_id_get (), internal, (gsize)internal->tid));
1158 * Wait for the thread to set up its TLS data etc, so
1159 * theres no potential race condition if someone tries
1160 * to look up the data believing the thread has
1161 * started
1164 mono_coop_sem_wait (&start_info->registered, MONO_SEM_FLAGS_NONE);
1166 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));
1168 ret = !start_info->failed;
1170 done:
1171 if (mono_atomic_dec_i32 (&start_info->ref) == 0) {
1172 mono_coop_sem_destroy (&start_info->registered);
1173 g_free (start_info);
1176 return ret;
1180 * mono_thread_new_init:
1182 void
1183 mono_thread_new_init (intptr_t tid, gpointer stack_start, gpointer func)
1185 if (mono_thread_start_cb) {
1186 mono_thread_start_cb (tid, stack_start, func);
1191 * mono_threads_set_default_stacksize:
1193 void
1194 mono_threads_set_default_stacksize (guint32 stacksize)
1196 default_stacksize = stacksize;
1200 * mono_threads_get_default_stacksize:
1202 guint32
1203 mono_threads_get_default_stacksize (void)
1205 return default_stacksize;
1209 * mono_thread_create_internal:
1211 * ARG should not be a GC reference.
1213 MonoInternalThread*
1214 mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, MonoThreadCreateFlags flags, MonoError *error)
1216 MonoThread *thread;
1217 MonoInternalThread *internal;
1218 gboolean res;
1220 error_init (error);
1222 internal = create_internal_thread_object ();
1224 thread = create_thread_object (domain, internal);
1226 LOCK_THREAD (internal);
1228 res = create_thread (thread, internal, NULL, (MonoThreadStart) func, arg, flags, error);
1230 UNLOCK_THREAD (internal);
1232 return_val_if_nok (error, NULL);
1233 return internal;
1237 * mono_thread_create:
1239 void
1240 mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
1242 MonoError error;
1243 if (!mono_thread_create_checked (domain, func, arg, &error))
1244 mono_error_cleanup (&error);
1247 gboolean
1248 mono_thread_create_checked (MonoDomain *domain, gpointer func, gpointer arg, MonoError *error)
1250 return (NULL != mono_thread_create_internal (domain, func, arg, MONO_THREAD_CREATE_FLAGS_NONE, error));
1253 static MonoThread *
1254 mono_thread_attach_full (MonoDomain *domain, gboolean force_attach)
1256 MonoInternalThread *internal;
1257 MonoThread *thread;
1258 MonoThreadInfo *info;
1259 MonoNativeThreadId tid;
1261 if (mono_thread_internal_current_is_attached ()) {
1262 if (domain != mono_domain_get ())
1263 mono_domain_set (domain, TRUE);
1264 /* Already attached */
1265 return mono_thread_current ();
1268 info = mono_thread_info_attach ();
1269 g_assert (info);
1271 tid=mono_native_thread_id_get ();
1273 internal = create_internal_thread_object ();
1275 thread = create_thread_object (domain, internal);
1277 if (!mono_thread_attach_internal (thread, force_attach, TRUE)) {
1278 /* Mono is shutting down, so just wait for the end */
1279 for (;;)
1280 mono_thread_info_sleep (10000, NULL);
1283 THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, internal->handle));
1285 if (mono_thread_attach_cb)
1286 mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), info->stack_end);
1288 MONO_PROFILER_RAISE (thread_started, (MONO_NATIVE_THREAD_ID_TO_UINT (tid)));
1290 return thread;
1294 * mono_thread_attach:
1296 MonoThread *
1297 mono_thread_attach (MonoDomain *domain)
1299 return mono_thread_attach_full (domain, FALSE);
1303 * mono_thread_detach:
1305 void
1306 mono_thread_detach (MonoThread *thread)
1308 if (thread)
1309 mono_thread_detach_internal (thread->internal_thread);
1313 * mono_thread_detach_if_exiting:
1315 * Detach the current thread from the runtime if it is exiting, i.e. it is running pthread dtors.
1316 * This should be used at the end of embedding code which calls into managed code, and which
1317 * can be called from pthread dtors, like <code>dealloc:</code> implementations in Objective-C.
1319 mono_bool
1320 mono_thread_detach_if_exiting (void)
1322 if (mono_thread_info_is_exiting ()) {
1323 MonoInternalThread *thread;
1325 thread = mono_thread_internal_current ();
1326 if (thread) {
1327 mono_thread_detach_internal (thread);
1328 mono_thread_info_detach ();
1329 return TRUE;
1332 return FALSE;
1335 gboolean
1336 mono_thread_internal_current_is_attached (void)
1338 MonoInternalThread *internal;
1340 internal = GET_CURRENT_OBJECT ();
1341 if (!internal)
1342 return FALSE;
1344 return TRUE;
1348 * mono_thread_exit:
1350 void
1351 mono_thread_exit (void)
1353 MonoInternalThread *thread = mono_thread_internal_current ();
1355 THREAD_DEBUG (g_message ("%s: mono_thread_exit for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
1357 mono_thread_detach_internal (thread);
1359 /* we could add a callback here for embedders to use. */
1360 if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread))
1361 exit (mono_environment_exitcode_get ());
1363 mono_thread_info_exit (0);
1366 void
1367 ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this_obj)
1369 MonoInternalThread *internal;
1371 internal = create_internal_thread_object ();
1373 internal->state = ThreadState_Unstarted;
1375 mono_atomic_cas_ptr ((volatile gpointer *)&this_obj->internal_thread, internal, NULL);
1378 MonoThread *
1379 ves_icall_System_Threading_Thread_GetCurrentThread (void)
1381 return mono_thread_current ();
1384 HANDLE
1385 ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this_obj,
1386 MonoObject *start)
1388 MonoError error;
1389 MonoInternalThread *internal;
1390 gboolean res;
1392 THREAD_DEBUG (g_message("%s: Trying to start a new thread: this (%p) start (%p)", __func__, this_obj, start));
1394 if (!this_obj->internal_thread)
1395 ves_icall_System_Threading_Thread_ConstructInternalThread (this_obj);
1396 internal = this_obj->internal_thread;
1398 LOCK_THREAD (internal);
1400 if ((internal->state & ThreadState_Unstarted) == 0) {
1401 UNLOCK_THREAD (internal);
1402 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has already been started."));
1403 return NULL;
1406 if ((internal->state & ThreadState_Aborted) != 0) {
1407 UNLOCK_THREAD (internal);
1408 return this_obj;
1411 res = create_thread (this_obj, internal, start, NULL, NULL, MONO_THREAD_CREATE_FLAGS_NONE, &error);
1412 if (!res) {
1413 mono_error_cleanup (&error);
1414 UNLOCK_THREAD (internal);
1415 return NULL;
1418 internal->state &= ~ThreadState_Unstarted;
1420 THREAD_DEBUG (g_message ("%s: Started thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread));
1422 UNLOCK_THREAD (internal);
1423 return internal->handle;
1427 * This is called from the finalizer of the internal thread object.
1429 void
1430 ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThread *this_obj)
1432 THREAD_DEBUG (g_message ("%s: Closing thread %p, handle %p", __func__, this, this_obj->handle));
1435 * Since threads keep a reference to their thread object while running, by
1436 * the time this function is called, the thread has already exited/detached,
1437 * i.e. mono_thread_detach_internal () has ran. The exception is during
1438 * shutdown, when mono_thread_detach_internal () can be called after this.
1440 if (this_obj->handle) {
1441 mono_threads_close_thread_handle (this_obj->handle);
1442 this_obj->handle = NULL;
1445 #if HOST_WIN32
1446 CloseHandle (this_obj->native_handle);
1447 #endif
1449 if (this_obj->synch_cs) {
1450 MonoCoopMutex *synch_cs = this_obj->synch_cs;
1451 this_obj->synch_cs = NULL;
1452 mono_coop_mutex_destroy (synch_cs);
1453 g_free (synch_cs);
1456 if (this_obj->name) {
1457 void *name = this_obj->name;
1458 this_obj->name = NULL;
1459 g_free (name);
1463 void
1464 ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
1466 guint32 res;
1467 MonoInternalThread *thread = mono_thread_internal_current ();
1469 THREAD_DEBUG (g_message ("%s: Sleeping for %d ms", __func__, ms));
1471 if (mono_thread_current_check_pending_interrupt ())
1472 return;
1474 while (TRUE) {
1475 gboolean alerted = FALSE;
1477 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1479 res = mono_thread_info_sleep (ms, &alerted);
1481 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1483 if (alerted) {
1484 MonoException* exc = mono_thread_execute_interruption ();
1485 if (exc) {
1486 mono_set_pending_exception (exc);
1487 return;
1488 } else {
1489 // FIXME: !MONO_INFINITE_WAIT
1490 if (ms != MONO_INFINITE_WAIT)
1491 break;
1493 } else {
1494 break;
1499 void ves_icall_System_Threading_Thread_SpinWait_nop (void)
1503 gint32
1504 ves_icall_System_Threading_Thread_GetDomainID (void)
1506 return mono_domain_get()->domain_id;
1509 gboolean
1510 ves_icall_System_Threading_Thread_Yield (void)
1512 return mono_thread_info_yield ();
1516 * mono_thread_get_name:
1518 * Return the name of the thread. NAME_LEN is set to the length of the name.
1519 * Return NULL if the thread has no name. The returned memory is owned by the
1520 * caller.
1522 gunichar2*
1523 mono_thread_get_name (MonoInternalThread *this_obj, guint32 *name_len)
1525 gunichar2 *res;
1527 LOCK_THREAD (this_obj);
1529 if (!this_obj->name) {
1530 *name_len = 0;
1531 res = NULL;
1532 } else {
1533 *name_len = this_obj->name_len;
1534 res = g_new (gunichar2, this_obj->name_len);
1535 memcpy (res, this_obj->name, sizeof (gunichar2) * this_obj->name_len);
1538 UNLOCK_THREAD (this_obj);
1540 return res;
1544 * mono_thread_get_name_utf8:
1545 * \returns the name of the thread in UTF-8.
1546 * Return NULL if the thread has no name.
1547 * The returned memory is owned by the caller.
1549 char *
1550 mono_thread_get_name_utf8 (MonoThread *thread)
1552 if (thread == NULL)
1553 return NULL;
1555 MonoInternalThread *internal = thread->internal_thread;
1556 if (internal == NULL)
1557 return NULL;
1559 LOCK_THREAD (internal);
1561 char *tname = g_utf16_to_utf8 (internal->name, internal->name_len, NULL, NULL, NULL);
1563 UNLOCK_THREAD (internal);
1565 return tname;
1569 * mono_thread_get_managed_id:
1570 * \returns the \c Thread.ManagedThreadId value of \p thread.
1571 * Returns \c -1 if \p thread is NULL.
1573 int32_t
1574 mono_thread_get_managed_id (MonoThread *thread)
1576 if (thread == NULL)
1577 return -1;
1579 MonoInternalThread *internal = thread->internal_thread;
1580 if (internal == NULL)
1581 return -1;
1583 int32_t id = internal->managed_id;
1585 return id;
1588 MonoString*
1589 ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThread *this_obj)
1591 MonoError error;
1592 MonoString* str;
1594 error_init (&error);
1596 LOCK_THREAD (this_obj);
1598 if (!this_obj->name)
1599 str = NULL;
1600 else
1601 str = mono_string_new_utf16_checked (mono_domain_get (), this_obj->name, this_obj->name_len, &error);
1603 UNLOCK_THREAD (this_obj);
1605 if (mono_error_set_pending_exception (&error))
1606 return NULL;
1608 return str;
1611 void
1612 mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, gboolean reset, MonoError *error)
1614 MonoNativeThreadId tid = 0;
1616 LOCK_THREAD (this_obj);
1618 error_init (error);
1620 if (reset) {
1621 this_obj->flags &= ~MONO_THREAD_FLAG_NAME_SET;
1622 } else if (this_obj->flags & MONO_THREAD_FLAG_NAME_SET) {
1623 UNLOCK_THREAD (this_obj);
1625 mono_error_set_invalid_operation (error, "Thread.Name can only be set once.");
1626 return;
1628 if (this_obj->name) {
1629 g_free (this_obj->name);
1630 this_obj->name_len = 0;
1632 if (name) {
1633 this_obj->name = g_memdup (mono_string_chars (name), mono_string_length (name) * sizeof (gunichar2));
1634 this_obj->name_len = mono_string_length (name);
1636 if (permanent)
1637 this_obj->flags |= MONO_THREAD_FLAG_NAME_SET;
1639 else
1640 this_obj->name = NULL;
1642 if (!(this_obj->state & ThreadState_Stopped))
1643 tid = thread_get_tid (this_obj);
1645 UNLOCK_THREAD (this_obj);
1647 if (this_obj->name && tid) {
1648 char *tname = mono_string_to_utf8_checked (name, error);
1649 return_if_nok (error);
1650 MONO_PROFILER_RAISE (thread_name, ((uintptr_t)tid, tname));
1651 mono_native_thread_set_name (tid, tname);
1652 mono_free (tname);
1656 void
1657 ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj, MonoString *name)
1659 MonoError error;
1660 mono_thread_set_name_internal (this_obj, name, TRUE, FALSE, &error);
1661 mono_error_set_pending_exception (&error);
1665 * ves_icall_System_Threading_Thread_GetPriority_internal:
1666 * @param this_obj: The MonoInternalThread on which to operate.
1668 * Gets the priority of the given thread.
1669 * @return: The priority of the given thread.
1672 ves_icall_System_Threading_Thread_GetPriority (MonoThread *this_obj)
1674 gint32 priority;
1675 MonoInternalThread *internal = this_obj->internal_thread;
1677 LOCK_THREAD (internal);
1678 priority = internal->priority;
1679 UNLOCK_THREAD (internal);
1681 return priority;
1685 * ves_icall_System_Threading_Thread_SetPriority_internal:
1686 * @param this_obj: The MonoInternalThread on which to operate.
1687 * @param priority: The priority to set.
1689 * Sets the priority of the given thread.
1691 void
1692 ves_icall_System_Threading_Thread_SetPriority (MonoThread *this_obj, int priority)
1694 MonoInternalThread *internal = this_obj->internal_thread;
1696 LOCK_THREAD (internal);
1697 internal->priority = priority;
1698 if (internal->thread_info != NULL)
1699 mono_thread_internal_set_priority (internal, priority);
1700 UNLOCK_THREAD (internal);
1703 /* If the array is already in the requested domain, we just return it,
1704 otherwise we return a copy in that domain. */
1705 static MonoArray*
1706 byte_array_to_domain (MonoArray *arr, MonoDomain *domain, MonoError *error)
1708 MonoArray *copy;
1710 error_init (error);
1711 if (!arr)
1712 return NULL;
1714 if (mono_object_domain (arr) == domain)
1715 return arr;
1717 copy = mono_array_new_checked (domain, mono_defaults.byte_class, arr->max_length, error);
1718 memmove (mono_array_addr (copy, guint8, 0), mono_array_addr (arr, guint8, 0), arr->max_length);
1719 return copy;
1722 MonoArray*
1723 ves_icall_System_Threading_Thread_ByteArrayToRootDomain (MonoArray *arr)
1725 MonoError error;
1726 MonoArray *result = byte_array_to_domain (arr, mono_get_root_domain (), &error);
1727 mono_error_set_pending_exception (&error);
1728 return result;
1731 MonoArray*
1732 ves_icall_System_Threading_Thread_ByteArrayToCurrentDomain (MonoArray *arr)
1734 MonoError error;
1735 MonoArray *result = byte_array_to_domain (arr, mono_domain_get (), &error);
1736 mono_error_set_pending_exception (&error);
1737 return result;
1741 * mono_thread_current:
1743 MonoThread *
1744 mono_thread_current (void)
1746 MonoDomain *domain = mono_domain_get ();
1747 MonoInternalThread *internal = mono_thread_internal_current ();
1748 MonoThread **current_thread_ptr;
1750 g_assert (internal);
1751 current_thread_ptr = get_current_thread_ptr_for_domain (domain, internal);
1753 if (!*current_thread_ptr) {
1754 g_assert (domain != mono_get_root_domain ());
1755 *current_thread_ptr = create_thread_object (domain, internal);
1757 return *current_thread_ptr;
1760 /* Return the thread object belonging to INTERNAL in the current domain */
1761 static MonoThread *
1762 mono_thread_current_for_thread (MonoInternalThread *internal)
1764 MonoDomain *domain = mono_domain_get ();
1765 MonoThread **current_thread_ptr;
1767 g_assert (internal);
1768 current_thread_ptr = get_current_thread_ptr_for_domain (domain, internal);
1770 if (!*current_thread_ptr) {
1771 g_assert (domain != mono_get_root_domain ());
1772 *current_thread_ptr = create_thread_object (domain, internal);
1774 return *current_thread_ptr;
1777 MonoInternalThread*
1778 mono_thread_internal_current (void)
1780 MonoInternalThread *res = GET_CURRENT_OBJECT ();
1781 THREAD_DEBUG (g_message ("%s: returning %p", __func__, res));
1782 return res;
1785 static MonoThreadInfoWaitRet
1786 mono_join_uninterrupted (MonoThreadHandle* thread_to_join, gint32 ms, MonoError *error)
1788 MonoException *exc;
1789 MonoThreadInfoWaitRet ret;
1790 gint64 start;
1791 gint32 diff_ms;
1792 gint32 wait = ms;
1794 error_init (error);
1796 start = (ms == -1) ? 0 : mono_msec_ticks ();
1797 for (;;) {
1798 MONO_ENTER_GC_SAFE;
1799 ret = mono_thread_info_wait_one_handle (thread_to_join, ms, TRUE);
1800 MONO_EXIT_GC_SAFE;
1802 if (ret != MONO_THREAD_INFO_WAIT_RET_ALERTED)
1803 return ret;
1805 exc = mono_thread_execute_interruption ();
1806 if (exc) {
1807 mono_error_set_exception_instance (error, exc);
1808 return ret;
1811 if (ms == -1)
1812 continue;
1814 /* Re-calculate ms according to the time passed */
1815 diff_ms = (gint32)(mono_msec_ticks () - start);
1816 if (diff_ms >= ms) {
1817 ret = MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1818 return ret;
1820 wait = ms - diff_ms;
1823 return ret;
1826 gboolean
1827 ves_icall_System_Threading_Thread_Join_internal (MonoThread *this_obj, int ms)
1829 MonoInternalThread *thread = this_obj->internal_thread;
1830 MonoThreadHandle *handle = thread->handle;
1831 MonoInternalThread *cur_thread = mono_thread_internal_current ();
1832 gboolean ret;
1833 MonoError error;
1835 if (mono_thread_current_check_pending_interrupt ())
1836 return FALSE;
1838 LOCK_THREAD (thread);
1840 if ((thread->state & ThreadState_Unstarted) != 0) {
1841 UNLOCK_THREAD (thread);
1843 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started."));
1844 return FALSE;
1847 UNLOCK_THREAD (thread);
1849 if (ms == -1)
1850 ms = MONO_INFINITE_WAIT;
1851 THREAD_DEBUG (g_message ("%s: joining thread handle %p, %d ms", __func__, handle, ms));
1853 mono_thread_set_state (cur_thread, ThreadState_WaitSleepJoin);
1855 ret = mono_join_uninterrupted (handle, ms, &error);
1857 mono_thread_clr_state (cur_thread, ThreadState_WaitSleepJoin);
1859 mono_error_set_pending_exception (&error);
1861 if (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0) {
1862 THREAD_DEBUG (g_message ("%s: join successful", __func__));
1864 /* Wait for the thread to really exit */
1865 MonoNativeThreadId tid = thread_get_tid (thread);
1866 mono_thread_join (tid);
1868 return TRUE;
1871 THREAD_DEBUG (g_message ("%s: join failed", __func__));
1873 return FALSE;
1876 #define MANAGED_WAIT_FAILED 0x7fffffff
1878 static gint32
1879 map_native_wait_result_to_managed (MonoW32HandleWaitRet val, gsize numobjects)
1881 if (val >= MONO_W32HANDLE_WAIT_RET_SUCCESS_0 && val < MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + numobjects) {
1882 return WAIT_OBJECT_0 + (val - MONO_W32HANDLE_WAIT_RET_SUCCESS_0);
1883 } else if (val >= MONO_W32HANDLE_WAIT_RET_ABANDONED_0 && val < MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + numobjects) {
1884 return WAIT_ABANDONED_0 + (val - MONO_W32HANDLE_WAIT_RET_ABANDONED_0);
1885 } else if (val == MONO_W32HANDLE_WAIT_RET_ALERTED) {
1886 return WAIT_IO_COMPLETION;
1887 } else if (val == MONO_W32HANDLE_WAIT_RET_TIMEOUT) {
1888 return WAIT_TIMEOUT;
1889 } else if (val == MONO_W32HANDLE_WAIT_RET_FAILED) {
1890 /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
1891 return MANAGED_WAIT_FAILED;
1892 } else {
1893 g_error ("%s: unknown val value %d", __func__, val);
1897 gint32
1898 ves_icall_System_Threading_WaitHandle_Wait_internal (gpointer *handles, gint32 numhandles, MonoBoolean waitall, gint32 timeout, MonoError *error)
1900 MonoW32HandleWaitRet ret;
1901 MonoInternalThread *thread;
1902 MonoException *exc;
1903 gint64 start;
1904 guint32 timeoutLeft;
1906 /* Do this WaitSleepJoin check before creating objects */
1907 if (mono_thread_current_check_pending_interrupt ())
1908 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
1910 thread = mono_thread_internal_current ();
1912 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1914 if (timeout == -1)
1915 timeout = MONO_INFINITE_WAIT;
1916 if (timeout != MONO_INFINITE_WAIT)
1917 start = mono_msec_ticks ();
1919 timeoutLeft = timeout;
1921 for (;;) {
1922 #ifdef HOST_WIN32
1923 MONO_ENTER_GC_SAFE;
1924 if (numhandles != 1)
1925 ret = mono_w32handle_convert_wait_ret (mono_win32_wait_for_multiple_objects_ex(numhandles, handles, waitall, timeoutLeft, TRUE), numhandles);
1926 else
1927 ret = mono_w32handle_convert_wait_ret (mono_win32_wait_for_single_object_ex (handles [0], timeoutLeft, TRUE), 1);
1928 MONO_EXIT_GC_SAFE;
1929 #else
1930 /* mono_w32handle_wait_multiple optimizes the case for numhandles == 1 */
1931 ret = mono_w32handle_wait_multiple (handles, numhandles, waitall, timeoutLeft, TRUE);
1932 #endif /* HOST_WIN32 */
1934 if (ret != MONO_W32HANDLE_WAIT_RET_ALERTED)
1935 break;
1937 exc = mono_thread_execute_interruption ();
1938 if (exc) {
1939 mono_error_set_exception_instance (error, exc);
1940 break;
1943 if (timeout != MONO_INFINITE_WAIT) {
1944 gint64 elapsed;
1946 elapsed = mono_msec_ticks () - start;
1947 if (elapsed >= timeout) {
1948 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1949 break;
1952 timeoutLeft = timeout - elapsed;
1956 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1958 return map_native_wait_result_to_managed (ret, numhandles);
1961 gint32
1962 ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (gpointer toSignal, gpointer toWait, gint32 ms, MonoError *error)
1964 MonoW32HandleWaitRet ret;
1965 MonoInternalThread *thread = mono_thread_internal_current ();
1967 if (ms == -1)
1968 ms = MONO_INFINITE_WAIT;
1970 if (mono_thread_current_check_pending_interrupt ())
1971 return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
1973 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1975 MONO_ENTER_GC_SAFE;
1976 #ifdef HOST_WIN32
1977 ret = mono_w32handle_convert_wait_ret (mono_win32_signal_object_and_wait (toSignal, toWait, ms, TRUE), 1);
1978 #else
1979 ret = mono_w32handle_signal_and_wait (toSignal, toWait, ms, TRUE);
1980 #endif
1981 MONO_EXIT_GC_SAFE;
1983 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1985 return map_native_wait_result_to_managed (ret, 1);
1988 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
1990 return mono_atomic_inc_i32 (location);
1993 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
1995 #if SIZEOF_VOID_P == 4
1996 if (G_UNLIKELY ((size_t)location & 0x7)) {
1997 gint64 ret;
1998 mono_interlocked_lock ();
1999 (*location)++;
2000 ret = *location;
2001 mono_interlocked_unlock ();
2002 return ret;
2004 #endif
2005 return mono_atomic_inc_i64 (location);
2008 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
2010 return mono_atomic_dec_i32(location);
2013 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
2015 #if SIZEOF_VOID_P == 4
2016 if (G_UNLIKELY ((size_t)location & 0x7)) {
2017 gint64 ret;
2018 mono_interlocked_lock ();
2019 (*location)--;
2020 ret = *location;
2021 mono_interlocked_unlock ();
2022 return ret;
2024 #endif
2025 return mono_atomic_dec_i64 (location);
2028 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location, gint32 value)
2030 return mono_atomic_xchg_i32(location, value);
2033 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location, MonoObject *value)
2035 MonoObject *res;
2036 res = (MonoObject *) mono_atomic_xchg_ptr((gpointer *) location, value);
2037 mono_gc_wbarrier_generic_nostore (location);
2038 return res;
2041 gpointer ves_icall_System_Threading_Interlocked_Exchange_IntPtr (gpointer *location, gpointer value)
2043 return mono_atomic_xchg_ptr(location, value);
2046 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location, gfloat value)
2048 IntFloatUnion val, ret;
2050 val.fval = value;
2051 ret.ival = mono_atomic_xchg_i32((gint32 *) location, val.ival);
2053 return ret.fval;
2056 gint64
2057 ves_icall_System_Threading_Interlocked_Exchange_Long (gint64 *location, gint64 value)
2059 #if SIZEOF_VOID_P == 4
2060 if (G_UNLIKELY ((size_t)location & 0x7)) {
2061 gint64 ret;
2062 mono_interlocked_lock ();
2063 ret = *location;
2064 *location = value;
2065 mono_interlocked_unlock ();
2066 return ret;
2068 #endif
2069 return mono_atomic_xchg_i64 (location, value);
2072 gdouble
2073 ves_icall_System_Threading_Interlocked_Exchange_Double (gdouble *location, gdouble value)
2075 LongDoubleUnion val, ret;
2077 val.fval = value;
2078 ret.ival = (gint64)mono_atomic_xchg_i64((gint64 *) location, val.ival);
2080 return ret.fval;
2083 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location, gint32 value, gint32 comparand)
2085 return mono_atomic_cas_i32(location, value, comparand);
2088 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int_Success(gint32 *location, gint32 value, gint32 comparand, MonoBoolean *success)
2090 gint32 r = mono_atomic_cas_i32(location, value, comparand);
2091 *success = r == comparand;
2092 return r;
2095 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location, MonoObject *value, MonoObject *comparand)
2097 MonoObject *res;
2098 res = (MonoObject *) mono_atomic_cas_ptr((gpointer *) location, value, comparand);
2099 mono_gc_wbarrier_generic_nostore (location);
2100 return res;
2103 gpointer ves_icall_System_Threading_Interlocked_CompareExchange_IntPtr(gpointer *location, gpointer value, gpointer comparand)
2105 return mono_atomic_cas_ptr(location, value, comparand);
2108 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location, gfloat value, gfloat comparand)
2110 IntFloatUnion val, ret, cmp;
2112 val.fval = value;
2113 cmp.fval = comparand;
2114 ret.ival = mono_atomic_cas_i32((gint32 *) location, val.ival, cmp.ival);
2116 return ret.fval;
2119 gdouble
2120 ves_icall_System_Threading_Interlocked_CompareExchange_Double (gdouble *location, gdouble value, gdouble comparand)
2122 #if SIZEOF_VOID_P == 8
2123 LongDoubleUnion val, comp, ret;
2125 val.fval = value;
2126 comp.fval = comparand;
2127 ret.ival = (gint64)mono_atomic_cas_ptr((gpointer *) location, (gpointer)val.ival, (gpointer)comp.ival);
2129 return ret.fval;
2130 #else
2131 gdouble old;
2133 mono_interlocked_lock ();
2134 old = *location;
2135 if (old == comparand)
2136 *location = value;
2137 mono_interlocked_unlock ();
2139 return old;
2140 #endif
2143 gint64
2144 ves_icall_System_Threading_Interlocked_CompareExchange_Long (gint64 *location, gint64 value, gint64 comparand)
2146 #if SIZEOF_VOID_P == 4
2147 if (G_UNLIKELY ((size_t)location & 0x7)) {
2148 gint64 old;
2149 mono_interlocked_lock ();
2150 old = *location;
2151 if (old == comparand)
2152 *location = value;
2153 mono_interlocked_unlock ();
2154 return old;
2156 #endif
2157 return mono_atomic_cas_i64 (location, value, comparand);
2160 MonoObject*
2161 ves_icall_System_Threading_Interlocked_CompareExchange_T (MonoObject **location, MonoObject *value, MonoObject *comparand)
2163 MonoObject *res;
2164 res = (MonoObject *)mono_atomic_cas_ptr ((volatile gpointer *)location, value, comparand);
2165 mono_gc_wbarrier_generic_nostore (location);
2166 return res;
2169 MonoObject*
2170 ves_icall_System_Threading_Interlocked_Exchange_T (MonoObject **location, MonoObject *value)
2172 MonoObject *res;
2173 MONO_CHECK_NULL (location, NULL);
2174 res = (MonoObject *)mono_atomic_xchg_ptr ((volatile gpointer *)location, value);
2175 mono_gc_wbarrier_generic_nostore (location);
2176 return res;
2179 gint32
2180 ves_icall_System_Threading_Interlocked_Add_Int (gint32 *location, gint32 value)
2182 return mono_atomic_add_i32 (location, value);
2185 gint64
2186 ves_icall_System_Threading_Interlocked_Add_Long (gint64 *location, gint64 value)
2188 #if SIZEOF_VOID_P == 4
2189 if (G_UNLIKELY ((size_t)location & 0x7)) {
2190 gint64 ret;
2191 mono_interlocked_lock ();
2192 *location += value;
2193 ret = *location;
2194 mono_interlocked_unlock ();
2195 return ret;
2197 #endif
2198 return mono_atomic_add_i64 (location, value);
2201 gint64
2202 ves_icall_System_Threading_Interlocked_Read_Long (gint64 *location)
2204 #if SIZEOF_VOID_P == 4
2205 if (G_UNLIKELY ((size_t)location & 0x7)) {
2206 gint64 ret;
2207 mono_interlocked_lock ();
2208 ret = *location;
2209 mono_interlocked_unlock ();
2210 return ret;
2212 #endif
2213 return mono_atomic_load_i64 (location);
2216 void
2217 ves_icall_System_Threading_Thread_MemoryBarrier (void)
2219 mono_memory_barrier ();
2222 void
2223 ves_icall_System_Threading_Thread_ClrState (MonoInternalThread* this_obj, guint32 state)
2225 mono_thread_clr_state (this_obj, (MonoThreadState)state);
2227 if (state & ThreadState_Background) {
2228 /* If the thread changes the background mode, the main thread has to
2229 * be notified, since it has to rebuild the list of threads to
2230 * wait for.
2232 mono_os_event_set (&background_change_event);
2236 void
2237 ves_icall_System_Threading_Thread_SetState (MonoInternalThread* this_obj, guint32 state)
2239 mono_thread_set_state (this_obj, (MonoThreadState)state);
2241 if (state & ThreadState_Background) {
2242 /* If the thread changes the background mode, the main thread has to
2243 * be notified, since it has to rebuild the list of threads to
2244 * wait for.
2246 mono_os_event_set (&background_change_event);
2250 guint32
2251 ves_icall_System_Threading_Thread_GetState (MonoInternalThread* this_obj)
2253 guint32 state;
2255 LOCK_THREAD (this_obj);
2257 state = this_obj->state;
2259 UNLOCK_THREAD (this_obj);
2261 return state;
2264 void ves_icall_System_Threading_Thread_Interrupt_internal (MonoThread *this_obj)
2266 MonoInternalThread *current;
2267 gboolean throw_;
2268 MonoInternalThread *thread = this_obj->internal_thread;
2270 LOCK_THREAD (thread);
2272 current = mono_thread_internal_current ();
2274 thread->thread_interrupt_requested = TRUE;
2275 throw_ = current != thread && (thread->state & ThreadState_WaitSleepJoin);
2277 UNLOCK_THREAD (thread);
2279 if (throw_) {
2280 async_abort_internal (thread, FALSE);
2285 * mono_thread_current_check_pending_interrupt:
2286 * Checks if there's a interruption request and set the pending exception if so.
2287 * \returns true if a pending exception was set
2289 gboolean
2290 mono_thread_current_check_pending_interrupt (void)
2292 MonoInternalThread *thread = mono_thread_internal_current ();
2293 gboolean throw_ = FALSE;
2295 LOCK_THREAD (thread);
2297 if (thread->thread_interrupt_requested) {
2298 throw_ = TRUE;
2299 thread->thread_interrupt_requested = FALSE;
2302 UNLOCK_THREAD (thread);
2304 if (throw_)
2305 mono_set_pending_exception (mono_get_exception_thread_interrupted ());
2306 return throw_;
2309 static gboolean
2310 request_thread_abort (MonoInternalThread *thread, MonoObject *state, gboolean appdomain_unload)
2312 LOCK_THREAD (thread);
2314 if (thread->state & (ThreadState_AbortRequested | ThreadState_Stopped))
2316 UNLOCK_THREAD (thread);
2317 return FALSE;
2320 if ((thread->state & ThreadState_Unstarted) != 0) {
2321 thread->state |= ThreadState_Aborted;
2322 UNLOCK_THREAD (thread);
2323 return FALSE;
2326 thread->state |= ThreadState_AbortRequested;
2327 if (appdomain_unload)
2328 thread->flags |= MONO_THREAD_FLAG_APPDOMAIN_ABORT;
2329 else
2330 thread->flags &= ~MONO_THREAD_FLAG_APPDOMAIN_ABORT;
2332 if (thread->abort_state_handle)
2333 mono_gchandle_free (thread->abort_state_handle);
2334 if (state) {
2335 thread->abort_state_handle = mono_gchandle_new (state, FALSE);
2336 g_assert (thread->abort_state_handle);
2337 } else {
2338 thread->abort_state_handle = 0;
2340 thread->abort_exc = NULL;
2342 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));
2344 /* During shutdown, we can't wait for other threads */
2345 if (!shutting_down)
2346 /* Make sure the thread is awake */
2347 mono_thread_resume (thread);
2349 UNLOCK_THREAD (thread);
2350 return TRUE;
2353 void
2354 ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject *state)
2356 if (!request_thread_abort (thread, state, FALSE))
2357 return;
2359 if (thread == mono_thread_internal_current ()) {
2360 MonoError error;
2361 self_abort_internal (&error);
2362 mono_error_set_pending_exception (&error);
2363 } else {
2364 async_abort_internal (thread, TRUE);
2369 * mono_thread_internal_abort:
2370 * Request thread \p thread to be aborted.
2371 * \p thread MUST NOT be the current thread.
2373 void
2374 mono_thread_internal_abort (MonoInternalThread *thread, gboolean appdomain_unload)
2376 g_assert (thread != mono_thread_internal_current ());
2378 if (!request_thread_abort (thread, NULL, appdomain_unload))
2379 return;
2380 async_abort_internal (thread, TRUE);
2383 void
2384 ves_icall_System_Threading_Thread_ResetAbort (MonoThread *this_obj)
2386 MonoInternalThread *thread = mono_thread_internal_current ();
2387 gboolean was_aborting, is_domain_abort;
2389 LOCK_THREAD (thread);
2390 was_aborting = thread->state & ThreadState_AbortRequested;
2391 is_domain_abort = thread->flags & MONO_THREAD_FLAG_APPDOMAIN_ABORT;
2393 if (was_aborting && !is_domain_abort)
2394 thread->state &= ~ThreadState_AbortRequested;
2395 UNLOCK_THREAD (thread);
2397 if (!was_aborting) {
2398 const char *msg = "Unable to reset abort because no abort was requested";
2399 mono_set_pending_exception (mono_get_exception_thread_state (msg));
2400 return;
2401 } else if (is_domain_abort) {
2402 /* Silently ignore abort resets in unloading appdomains */
2403 return;
2406 mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
2407 thread->abort_exc = NULL;
2408 if (thread->abort_state_handle) {
2409 mono_gchandle_free (thread->abort_state_handle);
2410 /* This is actually not necessary - the handle
2411 only counts if the exception is set */
2412 thread->abort_state_handle = 0;
2416 void
2417 mono_thread_internal_reset_abort (MonoInternalThread *thread)
2419 LOCK_THREAD (thread);
2421 thread->state &= ~ThreadState_AbortRequested;
2423 if (thread->abort_exc) {
2424 mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
2425 thread->abort_exc = NULL;
2426 if (thread->abort_state_handle) {
2427 mono_gchandle_free (thread->abort_state_handle);
2428 /* This is actually not necessary - the handle
2429 only counts if the exception is set */
2430 thread->abort_state_handle = 0;
2434 UNLOCK_THREAD (thread);
2437 MonoObject*
2438 ves_icall_System_Threading_Thread_GetAbortExceptionState (MonoThread *this_obj)
2440 MonoError error;
2441 MonoInternalThread *thread = this_obj->internal_thread;
2442 MonoObject *state, *deserialized = NULL;
2443 MonoDomain *domain;
2445 if (!thread->abort_state_handle)
2446 return NULL;
2448 state = mono_gchandle_get_target (thread->abort_state_handle);
2449 g_assert (state);
2451 domain = mono_domain_get ();
2452 if (mono_object_domain (state) == domain)
2453 return state;
2455 deserialized = mono_object_xdomain_representation (state, domain, &error);
2457 if (!deserialized) {
2458 MonoException *invalid_op_exc = mono_get_exception_invalid_operation ("Thread.ExceptionState cannot access an ExceptionState from a different AppDomain");
2459 if (!is_ok (&error)) {
2460 MonoObject *exc = (MonoObject*)mono_error_convert_to_exception (&error);
2461 MONO_OBJECT_SETREF (invalid_op_exc, inner_ex, exc);
2463 mono_set_pending_exception (invalid_op_exc);
2464 return NULL;
2467 return deserialized;
2470 static gboolean
2471 mono_thread_suspend (MonoInternalThread *thread)
2473 LOCK_THREAD (thread);
2475 if (thread->state & (ThreadState_Unstarted | ThreadState_Aborted | ThreadState_Stopped))
2477 UNLOCK_THREAD (thread);
2478 return FALSE;
2481 if (thread->state & (ThreadState_Suspended | ThreadState_SuspendRequested | ThreadState_AbortRequested))
2483 UNLOCK_THREAD (thread);
2484 return TRUE;
2487 thread->state |= ThreadState_SuspendRequested;
2488 mono_os_event_reset (thread->suspended);
2490 if (thread == mono_thread_internal_current ()) {
2491 /* calls UNLOCK_THREAD (thread) */
2492 self_suspend_internal ();
2493 } else {
2494 /* calls UNLOCK_THREAD (thread) */
2495 async_suspend_internal (thread, FALSE);
2498 return TRUE;
2501 void
2502 ves_icall_System_Threading_Thread_Suspend (MonoThread *this_obj)
2504 if (!mono_thread_suspend (this_obj->internal_thread)) {
2505 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
2506 return;
2510 /* LOCKING: LOCK_THREAD(thread) must be held */
2511 static gboolean
2512 mono_thread_resume (MonoInternalThread *thread)
2514 if ((thread->state & ThreadState_SuspendRequested) != 0) {
2515 // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (1) thread %p\n", thread_get_tid (thread));
2516 thread->state &= ~ThreadState_SuspendRequested;
2517 mono_os_event_set (thread->suspended);
2518 return TRUE;
2521 if ((thread->state & ThreadState_Suspended) == 0 ||
2522 (thread->state & ThreadState_Unstarted) != 0 ||
2523 (thread->state & ThreadState_Aborted) != 0 ||
2524 (thread->state & ThreadState_Stopped) != 0)
2526 // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (2) thread %p\n", thread_get_tid (thread));
2527 return FALSE;
2530 // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (3) thread %p\n", thread_get_tid (thread));
2532 mono_os_event_set (thread->suspended);
2534 if (!thread->self_suspended) {
2535 UNLOCK_THREAD (thread);
2537 /* Awake the thread */
2538 if (!mono_thread_info_resume (thread_get_tid (thread)))
2539 return FALSE;
2541 LOCK_THREAD (thread);
2544 thread->state &= ~ThreadState_Suspended;
2546 return TRUE;
2549 void
2550 ves_icall_System_Threading_Thread_Resume (MonoThread *thread)
2552 if (!thread->internal_thread) {
2553 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
2554 } else {
2555 LOCK_THREAD (thread->internal_thread);
2556 if (!mono_thread_resume (thread->internal_thread))
2557 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
2558 UNLOCK_THREAD (thread->internal_thread);
2562 static gboolean
2563 mono_threads_is_critical_method (MonoMethod *method)
2565 switch (method->wrapper_type) {
2566 case MONO_WRAPPER_RUNTIME_INVOKE:
2567 case MONO_WRAPPER_XDOMAIN_INVOKE:
2568 case MONO_WRAPPER_XDOMAIN_DISPATCH:
2569 return TRUE;
2571 return FALSE;
2574 static gboolean
2575 find_wrapper (MonoMethod *m, gint no, gint ilo, gboolean managed, gpointer data)
2577 if (managed)
2578 return TRUE;
2580 if (mono_threads_is_critical_method (m)) {
2581 *((gboolean*)data) = TRUE;
2582 return TRUE;
2584 return FALSE;
2587 static gboolean
2588 is_running_protected_wrapper (void)
2590 gboolean found = FALSE;
2591 mono_stack_walk (find_wrapper, &found);
2592 return found;
2596 * mono_thread_stop:
2598 void
2599 mono_thread_stop (MonoThread *thread)
2601 MonoInternalThread *internal = thread->internal_thread;
2603 if (!request_thread_abort (internal, NULL, FALSE))
2604 return;
2606 if (internal == mono_thread_internal_current ()) {
2607 MonoError error;
2608 self_abort_internal (&error);
2610 This function is part of the embeding API and has no way to return the exception
2611 to be thrown. So what we do is keep the old behavior and raise the exception.
2613 mono_error_raise_exception_deprecated (&error); /* OK to throw, see note */
2614 } else {
2615 async_abort_internal (internal, TRUE);
2619 gint8
2620 ves_icall_System_Threading_Thread_VolatileRead1 (void *ptr)
2622 gint8 tmp = *(volatile gint8 *)ptr;
2623 mono_memory_barrier ();
2624 return tmp;
2627 gint16
2628 ves_icall_System_Threading_Thread_VolatileRead2 (void *ptr)
2630 gint16 tmp = *(volatile gint16 *)ptr;
2631 mono_memory_barrier ();
2632 return tmp;
2635 gint32
2636 ves_icall_System_Threading_Thread_VolatileRead4 (void *ptr)
2638 gint32 tmp = *(volatile gint32 *)ptr;
2639 mono_memory_barrier ();
2640 return tmp;
2643 gint64
2644 ves_icall_System_Threading_Thread_VolatileRead8 (void *ptr)
2646 gint64 tmp = *(volatile gint64 *)ptr;
2647 mono_memory_barrier ();
2648 return tmp;
2651 void *
2652 ves_icall_System_Threading_Thread_VolatileReadIntPtr (void *ptr)
2654 volatile void *tmp = *(volatile void **)ptr;
2655 mono_memory_barrier ();
2656 return (void *) tmp;
2659 void *
2660 ves_icall_System_Threading_Thread_VolatileReadObject (void *ptr)
2662 volatile MonoObject *tmp = *(volatile MonoObject **)ptr;
2663 mono_memory_barrier ();
2664 return (MonoObject *) tmp;
2667 double
2668 ves_icall_System_Threading_Thread_VolatileReadDouble (void *ptr)
2670 double tmp = *(volatile double *)ptr;
2671 mono_memory_barrier ();
2672 return tmp;
2675 float
2676 ves_icall_System_Threading_Thread_VolatileReadFloat (void *ptr)
2678 float tmp = *(volatile float *)ptr;
2679 mono_memory_barrier ();
2680 return tmp;
2683 gint8
2684 ves_icall_System_Threading_Volatile_Read1 (void *ptr)
2686 return mono_atomic_load_i8 ((volatile gint8 *)ptr);
2689 gint16
2690 ves_icall_System_Threading_Volatile_Read2 (void *ptr)
2692 return mono_atomic_load_i16 ((volatile gint16 *)ptr);
2695 gint32
2696 ves_icall_System_Threading_Volatile_Read4 (void *ptr)
2698 return mono_atomic_load_i32 ((volatile gint32 *)ptr);
2701 gint64
2702 ves_icall_System_Threading_Volatile_Read8 (void *ptr)
2704 #if SIZEOF_VOID_P == 4
2705 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2706 gint64 val;
2707 mono_interlocked_lock ();
2708 val = *(gint64*)ptr;
2709 mono_interlocked_unlock ();
2710 return val;
2712 #endif
2713 return mono_atomic_load_i64 ((volatile gint64 *)ptr);
2716 void *
2717 ves_icall_System_Threading_Volatile_ReadIntPtr (void *ptr)
2719 return mono_atomic_load_ptr ((volatile gpointer *)ptr);
2722 double
2723 ves_icall_System_Threading_Volatile_ReadDouble (void *ptr)
2725 LongDoubleUnion u;
2727 #if SIZEOF_VOID_P == 4
2728 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2729 double val;
2730 mono_interlocked_lock ();
2731 val = *(double*)ptr;
2732 mono_interlocked_unlock ();
2733 return val;
2735 #endif
2737 u.ival = mono_atomic_load_i64 ((volatile gint64 *)ptr);
2739 return u.fval;
2742 float
2743 ves_icall_System_Threading_Volatile_ReadFloat (void *ptr)
2745 IntFloatUnion u;
2747 u.ival = mono_atomic_load_i32 ((volatile gint32 *)ptr);
2749 return u.fval;
2752 MonoObject*
2753 ves_icall_System_Threading_Volatile_Read_T (void *ptr)
2755 return (MonoObject *)mono_atomic_load_ptr ((volatile gpointer *)ptr);
2758 void
2759 ves_icall_System_Threading_Thread_VolatileWrite1 (void *ptr, gint8 value)
2761 mono_memory_barrier ();
2762 *(volatile gint8 *)ptr = value;
2765 void
2766 ves_icall_System_Threading_Thread_VolatileWrite2 (void *ptr, gint16 value)
2768 mono_memory_barrier ();
2769 *(volatile gint16 *)ptr = value;
2772 void
2773 ves_icall_System_Threading_Thread_VolatileWrite4 (void *ptr, gint32 value)
2775 mono_memory_barrier ();
2776 *(volatile gint32 *)ptr = value;
2779 void
2780 ves_icall_System_Threading_Thread_VolatileWrite8 (void *ptr, gint64 value)
2782 mono_memory_barrier ();
2783 *(volatile gint64 *)ptr = value;
2786 void
2787 ves_icall_System_Threading_Thread_VolatileWriteIntPtr (void *ptr, void *value)
2789 mono_memory_barrier ();
2790 *(volatile void **)ptr = value;
2793 void
2794 ves_icall_System_Threading_Thread_VolatileWriteObject (void *ptr, MonoObject *value)
2796 mono_memory_barrier ();
2797 mono_gc_wbarrier_generic_store (ptr, value);
2800 void
2801 ves_icall_System_Threading_Thread_VolatileWriteDouble (void *ptr, double value)
2803 mono_memory_barrier ();
2804 *(volatile double *)ptr = value;
2807 void
2808 ves_icall_System_Threading_Thread_VolatileWriteFloat (void *ptr, float value)
2810 mono_memory_barrier ();
2811 *(volatile float *)ptr = value;
2814 void
2815 ves_icall_System_Threading_Volatile_Write1 (void *ptr, gint8 value)
2817 mono_atomic_store_i8 ((volatile gint8 *)ptr, value);
2820 void
2821 ves_icall_System_Threading_Volatile_Write2 (void *ptr, gint16 value)
2823 mono_atomic_store_i16 ((volatile gint16 *)ptr, value);
2826 void
2827 ves_icall_System_Threading_Volatile_Write4 (void *ptr, gint32 value)
2829 mono_atomic_store_i32 ((volatile gint32 *)ptr, value);
2832 void
2833 ves_icall_System_Threading_Volatile_Write8 (void *ptr, gint64 value)
2835 #if SIZEOF_VOID_P == 4
2836 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2837 mono_interlocked_lock ();
2838 *(gint64*)ptr = value;
2839 mono_interlocked_unlock ();
2840 return;
2842 #endif
2844 mono_atomic_store_i64 ((volatile gint64 *)ptr, value);
2847 void
2848 ves_icall_System_Threading_Volatile_WriteIntPtr (void *ptr, void *value)
2850 mono_atomic_store_ptr ((volatile gpointer *)ptr, value);
2853 void
2854 ves_icall_System_Threading_Volatile_WriteDouble (void *ptr, double value)
2856 LongDoubleUnion u;
2858 #if SIZEOF_VOID_P == 4
2859 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2860 mono_interlocked_lock ();
2861 *(double*)ptr = value;
2862 mono_interlocked_unlock ();
2863 return;
2865 #endif
2867 u.fval = value;
2869 mono_atomic_store_i64 ((volatile gint64 *)ptr, u.ival);
2872 void
2873 ves_icall_System_Threading_Volatile_WriteFloat (void *ptr, float value)
2875 IntFloatUnion u;
2877 u.fval = value;
2879 mono_atomic_store_i32 ((volatile gint32 *)ptr, u.ival);
2882 void
2883 ves_icall_System_Threading_Volatile_Write_T (void *ptr, MonoObject *value)
2885 mono_gc_wbarrier_generic_store_atomic (ptr, value);
2888 static void
2889 free_context (void *user_data)
2891 ContextStaticData *data = user_data;
2893 mono_threads_lock ();
2896 * There is no guarantee that, by the point this reference queue callback
2897 * has been invoked, the GC handle associated with the object will fail to
2898 * resolve as one might expect. So if we don't free and remove the GC
2899 * handle here, free_context_static_data_helper () could end up resolving
2900 * a GC handle to an actually-dead context which would contain a pointer
2901 * to an already-freed static data segment, resulting in a crash when
2902 * accessing it.
2904 g_hash_table_remove (contexts, GUINT_TO_POINTER (data->gc_handle));
2906 mono_threads_unlock ();
2908 mono_gchandle_free (data->gc_handle);
2909 mono_free_static_data (data->static_data);
2910 g_free (data);
2913 void
2914 mono_threads_register_app_context (MonoAppContext *ctx, MonoError *error)
2916 error_init (error);
2917 mono_threads_lock ();
2919 //g_print ("Registering context %d in domain %d\n", ctx->context_id, ctx->domain_id);
2921 if (!contexts)
2922 contexts = g_hash_table_new (NULL, NULL);
2924 if (!context_queue)
2925 context_queue = mono_gc_reference_queue_new (free_context);
2927 gpointer gch = GUINT_TO_POINTER (mono_gchandle_new_weakref (&ctx->obj, FALSE));
2928 g_hash_table_insert (contexts, gch, gch);
2931 * We use this intermediate structure to contain a duplicate pointer to
2932 * the static data because we can't rely on being able to resolve the GC
2933 * handle in the reference queue callback.
2935 ContextStaticData *data = g_new0 (ContextStaticData, 1);
2936 data->gc_handle = GPOINTER_TO_UINT (gch);
2937 ctx->data = data;
2939 context_adjust_static_data (ctx);
2940 mono_gc_reference_queue_add (context_queue, &ctx->obj, data);
2942 mono_threads_unlock ();
2944 MONO_PROFILER_RAISE (context_loaded, (ctx));
2947 void
2948 ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppContextHandle ctx, MonoError *error)
2950 error_init (error);
2951 mono_threads_register_app_context (MONO_HANDLE_RAW (ctx), error); /* FIXME use handles in mono_threads_register_app_context */
2954 void
2955 mono_threads_release_app_context (MonoAppContext* ctx, MonoError *error)
2958 * NOTE: Since finalizers are unreliable for the purposes of ensuring
2959 * cleanup in exceptional circumstances, we don't actually do any
2960 * cleanup work here. We instead do this via a reference queue.
2963 //g_print ("Releasing context %d in domain %d\n", ctx->context_id, ctx->domain_id);
2965 MONO_PROFILER_RAISE (context_unloaded, (ctx));
2968 void
2969 ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContextHandle ctx, MonoError *error)
2971 error_init (error);
2972 mono_threads_release_app_context (MONO_HANDLE_RAW (ctx), error); /* FIXME use handles in mono_threads_release_app_context */
2975 void mono_thread_init (MonoThreadStartCB start_cb,
2976 MonoThreadAttachCB attach_cb)
2978 mono_coop_mutex_init_recursive (&threads_mutex);
2980 mono_os_mutex_init_recursive(&interlocked_mutex);
2981 mono_os_mutex_init_recursive(&joinable_threads_mutex);
2983 mono_os_event_init (&background_change_event, FALSE);
2985 mono_init_static_data_info (&thread_static_info);
2986 mono_init_static_data_info (&context_static_info);
2988 mono_thread_start_cb = start_cb;
2989 mono_thread_attach_cb = attach_cb;
2992 static gpointer
2993 thread_attach (MonoThreadInfo *info)
2995 return mono_gc_thread_attach (info);
2998 static void
2999 thread_detach (MonoThreadInfo *info)
3001 MonoInternalThread *internal;
3002 guint32 gchandle;
3004 /* If a delegate is passed to native code and invoked on a thread we dont
3005 * know about, marshal will register it with mono_threads_attach_coop, but
3006 * we have no way of knowing when that thread goes away. SGen has a TSD
3007 * so we assume that if the domain is still registered, we can detach
3008 * the thread */
3010 g_assert (info);
3011 g_assert (mono_thread_info_is_current (info));
3013 if (!mono_thread_info_try_get_internal_thread_gchandle (info, &gchandle))
3014 return;
3016 internal = (MonoInternalThread*) mono_gchandle_get_target (gchandle);
3017 g_assert (internal);
3019 mono_thread_detach_internal (internal);
3022 static void
3023 thread_detach_with_lock (MonoThreadInfo *info)
3025 mono_gc_thread_detach_with_lock (info);
3028 static gboolean
3029 thread_in_critical_region (MonoThreadInfo *info)
3031 return mono_gc_thread_in_critical_region (info);
3034 static gboolean
3035 ip_in_critical_region (MonoDomain *domain, gpointer ip)
3037 MonoJitInfo *ji;
3038 MonoMethod *method;
3041 * We pass false for 'try_aot' so this becomes async safe.
3042 * It won't find aot methods whose jit info is not yet loaded,
3043 * so we preload their jit info in the JIT.
3045 ji = mono_jit_info_table_find_internal (domain, ip, FALSE, FALSE);
3046 if (!ji)
3047 return FALSE;
3049 method = mono_jit_info_get_method (ji);
3050 g_assert (method);
3052 return mono_gc_is_critical_method (method);
3055 void
3056 mono_thread_callbacks_init (void)
3058 MonoThreadInfoCallbacks cb;
3060 memset (&cb, 0, sizeof(cb));
3061 cb.thread_attach = thread_attach;
3062 cb.thread_detach = thread_detach;
3063 cb.thread_detach_with_lock = thread_detach_with_lock;
3064 cb.ip_in_critical_region = ip_in_critical_region;
3065 cb.thread_in_critical_region = thread_in_critical_region;
3066 mono_thread_info_callbacks_init (&cb);
3070 * mono_thread_cleanup:
3072 void
3073 mono_thread_cleanup (void)
3075 mono_threads_join_threads ();
3077 #if !defined(RUN_IN_SUBTHREAD) && !defined(HOST_WIN32)
3078 /* The main thread must abandon any held mutexes (particularly
3079 * important for named mutexes as they are shared across
3080 * processes, see bug 74680.) This will happen when the
3081 * thread exits, but if it's not running in a subthread it
3082 * won't exit in time.
3084 mono_w32mutex_abandon ();
3085 #endif
3087 #if 0
3088 /* This stuff needs more testing, it seems one of these
3089 * critical sections can be locked when mono_thread_cleanup is
3090 * called.
3092 mono_coop_mutex_destroy (&threads_mutex);
3093 mono_os_mutex_destroy (&interlocked_mutex);
3094 mono_os_mutex_destroy (&delayed_free_table_mutex);
3095 mono_os_mutex_destroy (&small_id_mutex);
3096 mono_os_event_destroy (&background_change_event);
3097 #endif
3100 void
3101 mono_threads_install_cleanup (MonoThreadCleanupFunc func)
3103 mono_thread_cleanup_fn = func;
3107 * mono_thread_set_manage_callback:
3109 void
3110 mono_thread_set_manage_callback (MonoThread *thread, MonoThreadManageCallback func)
3112 thread->internal_thread->manage_callback = func;
3115 G_GNUC_UNUSED
3116 static void print_tids (gpointer key, gpointer value, gpointer user)
3118 /* GPOINTER_TO_UINT breaks horribly if sizeof(void *) >
3119 * sizeof(uint) and a cast to uint would overflow
3121 /* Older versions of glib don't have G_GSIZE_FORMAT, so just
3122 * print this as a pointer.
3124 g_message ("Waiting for: %p", key);
3127 struct wait_data
3129 MonoThreadHandle *handles[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
3130 MonoInternalThread *threads[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
3131 guint32 num;
3134 static void
3135 wait_for_tids (struct wait_data *wait, guint32 timeout, gboolean check_state_change)
3137 guint32 i;
3138 MonoThreadInfoWaitRet ret;
3140 THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num));
3142 /* Add the thread state change event, so it wakes
3143 * up if a thread changes to background mode. */
3145 MONO_ENTER_GC_SAFE;
3146 if (check_state_change)
3147 ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, &background_change_event, FALSE, timeout, TRUE);
3148 else
3149 ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, NULL, TRUE, timeout, TRUE);
3150 MONO_EXIT_GC_SAFE;
3152 if (ret == MONO_THREAD_INFO_WAIT_RET_FAILED) {
3153 /* See the comment in build_wait_tids() */
3154 THREAD_DEBUG (g_message ("%s: Wait failed", __func__));
3155 return;
3158 for( i = 0; i < wait->num; i++)
3159 mono_threads_close_thread_handle (wait->handles [i]);
3161 if (ret == MONO_THREAD_INFO_WAIT_RET_TIMEOUT)
3162 return;
3164 if (ret < wait->num) {
3165 MonoInternalThread *internal;
3167 internal = wait->threads [ret];
3169 mono_threads_lock ();
3170 if (mono_g_hash_table_lookup (threads, (gpointer) internal->tid) == internal)
3171 g_error ("%s: failed to call mono_thread_detach_internal on thread %p, InternalThread: %p", __func__, internal->tid, internal);
3172 mono_threads_unlock ();
3176 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
3178 struct wait_data *wait=(struct wait_data *)user;
3180 if(wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS - 1) {
3181 MonoInternalThread *thread=(MonoInternalThread *)value;
3183 /* Ignore background threads, we abort them later */
3184 /* Do not lock here since it is not needed and the caller holds threads_lock */
3185 if (thread->state & ThreadState_Background) {
3186 THREAD_DEBUG (g_message ("%s: ignoring background thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3187 return; /* just leave, ignore */
3190 if (mono_gc_is_finalizer_internal_thread (thread)) {
3191 THREAD_DEBUG (g_message ("%s: ignoring finalizer thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3192 return;
3195 if (thread == mono_thread_internal_current ()) {
3196 THREAD_DEBUG (g_message ("%s: ignoring current thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3197 return;
3200 if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread)) {
3201 THREAD_DEBUG (g_message ("%s: ignoring main thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3202 return;
3205 if (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE) {
3206 THREAD_DEBUG (g_message ("%s: ignoring thread %" G_GSIZE_FORMAT "with DONT_MANAGE flag set.", __func__, (gsize)thread->tid));
3207 return;
3210 THREAD_DEBUG (g_message ("%s: Invoking mono_thread_manage callback on thread %p", __func__, thread));
3211 if ((thread->manage_callback == NULL) || (thread->manage_callback (thread->root_domain_thread) == TRUE)) {
3212 wait->handles[wait->num]=mono_threads_open_thread_handle (thread->handle);
3213 wait->threads[wait->num]=thread;
3214 wait->num++;
3216 THREAD_DEBUG (g_message ("%s: adding thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3217 } else {
3218 THREAD_DEBUG (g_message ("%s: ignoring (because of callback) thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3222 } else {
3223 /* Just ignore the rest, we can't do anything with
3224 * them yet
3229 static void
3230 abort_threads (gpointer key, gpointer value, gpointer user)
3232 struct wait_data *wait=(struct wait_data *)user;
3233 MonoNativeThreadId self = mono_native_thread_id_get ();
3234 MonoInternalThread *thread = (MonoInternalThread *)value;
3236 if (wait->num >= MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS)
3237 return;
3239 if (mono_native_thread_id_equals (thread_get_tid (thread), self))
3240 return;
3241 if (mono_gc_is_finalizer_internal_thread (thread))
3242 return;
3244 if ((thread->flags & MONO_THREAD_FLAG_DONT_MANAGE))
3245 return;
3247 wait->handles[wait->num] = mono_threads_open_thread_handle (thread->handle);
3248 wait->threads[wait->num] = thread;
3249 wait->num++;
3251 THREAD_DEBUG (g_print ("%s: Aborting id: %"G_GSIZE_FORMAT"\n", __func__, (gsize)thread->tid));
3252 mono_thread_internal_abort (thread, FALSE);
3255 /**
3256 * mono_threads_set_shutting_down:
3258 * Is called by a thread that wants to shut down Mono. If the runtime is already
3259 * shutting down, the calling thread is suspended/stopped, and this function never
3260 * returns.
3262 void
3263 mono_threads_set_shutting_down (void)
3265 MonoInternalThread *current_thread = mono_thread_internal_current ();
3267 mono_threads_lock ();
3269 if (shutting_down) {
3270 mono_threads_unlock ();
3272 /* Make sure we're properly suspended/stopped */
3274 LOCK_THREAD (current_thread);
3276 if (current_thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
3277 UNLOCK_THREAD (current_thread);
3278 mono_thread_execute_interruption ();
3279 } else {
3280 UNLOCK_THREAD (current_thread);
3283 /*since we're killing the thread, detach it.*/
3284 mono_thread_detach_internal (current_thread);
3286 /* Wake up other threads potentially waiting for us */
3287 mono_thread_info_exit (0);
3288 } else {
3289 shutting_down = TRUE;
3291 /* Not really a background state change, but this will
3292 * interrupt the main thread if it is waiting for all
3293 * the other threads.
3295 mono_os_event_set (&background_change_event);
3297 mono_threads_unlock ();
3302 * mono_thread_manage:
3304 void
3305 mono_thread_manage (void)
3307 struct wait_data wait_data;
3308 struct wait_data *wait = &wait_data;
3310 memset (wait, 0, sizeof (struct wait_data));
3311 /* join each thread that's still running */
3312 THREAD_DEBUG (g_message ("%s: Joining each running thread...", __func__));
3314 mono_threads_lock ();
3315 if(threads==NULL) {
3316 THREAD_DEBUG (g_message("%s: No threads", __func__));
3317 mono_threads_unlock ();
3318 return;
3320 mono_threads_unlock ();
3322 do {
3323 mono_threads_lock ();
3324 if (shutting_down) {
3325 /* somebody else is shutting down */
3326 mono_threads_unlock ();
3327 break;
3329 THREAD_DEBUG (g_message ("%s: There are %d threads to join", __func__, mono_g_hash_table_size (threads));
3330 mono_g_hash_table_foreach (threads, print_tids, NULL));
3332 mono_os_event_reset (&background_change_event);
3333 wait->num=0;
3334 /* We must zero all InternalThread pointers to avoid making the GC unhappy. */
3335 memset (wait->threads, 0, MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
3336 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
3337 mono_threads_unlock ();
3338 if (wait->num > 0)
3339 /* Something to wait for */
3340 wait_for_tids (wait, MONO_INFINITE_WAIT, TRUE);
3341 THREAD_DEBUG (g_message ("%s: I have %d threads after waiting.", __func__, wait->num));
3342 } while(wait->num>0);
3344 /* Mono is shutting down, so just wait for the end */
3345 if (!mono_runtime_try_shutdown ()) {
3346 /*FIXME mono_thread_suspend probably should call mono_thread_execute_interruption when self interrupting. */
3347 mono_thread_suspend (mono_thread_internal_current ());
3348 mono_thread_execute_interruption ();
3352 * Remove everything but the finalizer thread and self.
3353 * Also abort all the background threads
3354 * */
3355 do {
3356 mono_threads_lock ();
3358 wait->num = 0;
3359 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
3360 memset (wait->threads, 0, MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
3361 mono_g_hash_table_foreach (threads, abort_threads, wait);
3363 mono_threads_unlock ();
3365 THREAD_DEBUG (g_message ("%s: wait->num is now %d", __func__, wait->num));
3366 if (wait->num > 0) {
3367 /* Something to wait for */
3368 wait_for_tids (wait, MONO_INFINITE_WAIT, FALSE);
3370 } while (wait->num > 0);
3373 * give the subthreads a chance to really quit (this is mainly needed
3374 * to get correct user and system times from getrusage/wait/time(1)).
3375 * This could be removed if we avoid pthread_detach() and use pthread_join().
3377 mono_thread_info_yield ();
3380 static void
3381 collect_threads_for_suspend (gpointer key, gpointer value, gpointer user_data)
3383 MonoInternalThread *thread = (MonoInternalThread*)value;
3384 struct wait_data *wait = (struct wait_data*)user_data;
3387 * We try to exclude threads early, to avoid running into the MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
3388 * limitation.
3389 * This needs no locking.
3391 if ((thread->state & ThreadState_Suspended) != 0 ||
3392 (thread->state & ThreadState_Stopped) != 0)
3393 return;
3395 if (wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
3396 wait->handles [wait->num] = mono_threads_open_thread_handle (thread->handle);
3397 wait->threads [wait->num] = thread;
3398 wait->num++;
3403 * mono_thread_suspend_all_other_threads:
3405 * Suspend all managed threads except the finalizer thread and this thread. It is
3406 * not possible to resume them later.
3408 void mono_thread_suspend_all_other_threads (void)
3410 struct wait_data wait_data;
3411 struct wait_data *wait = &wait_data;
3412 int i;
3413 MonoNativeThreadId self = mono_native_thread_id_get ();
3414 guint32 eventidx = 0;
3415 gboolean starting, finished;
3417 memset (wait, 0, sizeof (struct wait_data));
3419 * The other threads could be in an arbitrary state at this point, i.e.
3420 * they could be starting up, shutting down etc. This means that there could be
3421 * threads which are not even in the threads hash table yet.
3425 * First we set a barrier which will be checked by all threads before they
3426 * are added to the threads hash table, and they will exit if the flag is set.
3427 * This ensures that no threads could be added to the hash later.
3428 * We will use shutting_down as the barrier for now.
3430 g_assert (shutting_down);
3433 * We make multiple calls to WaitForMultipleObjects since:
3434 * - we can only wait for MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS threads
3435 * - some threads could exit without becoming suspended
3437 finished = FALSE;
3438 while (!finished) {
3440 * Make a copy of the hashtable since we can't do anything with
3441 * threads while threads_mutex is held.
3443 wait->num = 0;
3444 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
3445 memset (wait->threads, 0, MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
3446 mono_threads_lock ();
3447 mono_g_hash_table_foreach (threads, collect_threads_for_suspend, wait);
3448 mono_threads_unlock ();
3450 eventidx = 0;
3451 /* Get the suspended events that we'll be waiting for */
3452 for (i = 0; i < wait->num; ++i) {
3453 MonoInternalThread *thread = wait->threads [i];
3455 if (mono_native_thread_id_equals (thread_get_tid (thread), self)
3456 || mono_gc_is_finalizer_internal_thread (thread)
3457 || (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)
3459 mono_threads_close_thread_handle (wait->handles [i]);
3460 wait->threads [i] = NULL;
3461 continue;
3464 LOCK_THREAD (thread);
3466 if (thread->state & (ThreadState_Suspended | ThreadState_Stopped)) {
3467 UNLOCK_THREAD (thread);
3468 mono_threads_close_thread_handle (wait->handles [i]);
3469 wait->threads [i] = NULL;
3470 continue;
3473 ++eventidx;
3475 /* Convert abort requests into suspend requests */
3476 if ((thread->state & ThreadState_AbortRequested) != 0)
3477 thread->state &= ~ThreadState_AbortRequested;
3479 thread->state |= ThreadState_SuspendRequested;
3480 mono_os_event_reset (thread->suspended);
3482 /* Signal the thread to suspend + calls UNLOCK_THREAD (thread) */
3483 async_suspend_internal (thread, TRUE);
3485 mono_threads_close_thread_handle (wait->handles [i]);
3486 wait->threads [i] = NULL;
3488 if (eventidx <= 0) {
3490 * If there are threads which are starting up, we wait until they
3491 * are suspended when they try to register in the threads hash.
3492 * This is guaranteed to finish, since the threads which can create new
3493 * threads get suspended after a while.
3494 * FIXME: The finalizer thread can still create new threads.
3496 mono_threads_lock ();
3497 if (threads_starting_up)
3498 starting = mono_g_hash_table_size (threads_starting_up) > 0;
3499 else
3500 starting = FALSE;
3501 mono_threads_unlock ();
3502 if (starting)
3503 mono_thread_info_sleep (100, NULL);
3504 else
3505 finished = TRUE;
3510 typedef struct {
3511 MonoInternalThread *thread;
3512 MonoStackFrameInfo *frames;
3513 int nframes, max_frames;
3514 int nthreads, max_threads;
3515 MonoInternalThread **threads;
3516 } ThreadDumpUserData;
3518 static gboolean thread_dump_requested;
3520 /* This needs to be async safe */
3521 static gboolean
3522 collect_frame (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
3524 ThreadDumpUserData *ud = (ThreadDumpUserData *)data;
3526 if (ud->nframes < ud->max_frames) {
3527 memcpy (&ud->frames [ud->nframes], frame, sizeof (MonoStackFrameInfo));
3528 ud->nframes ++;
3531 return FALSE;
3534 /* This needs to be async safe */
3535 static SuspendThreadResult
3536 get_thread_dump (MonoThreadInfo *info, gpointer ud)
3538 ThreadDumpUserData *user_data = (ThreadDumpUserData *)ud;
3539 MonoInternalThread *thread = user_data->thread;
3541 #if 0
3542 /* This no longer works with remote unwinding */
3543 g_string_append_printf (text, " tid=0x%p this=0x%p ", (gpointer)(gsize)thread->tid, thread);
3544 mono_thread_internal_describe (thread, text);
3545 g_string_append (text, "\n");
3546 #endif
3548 if (thread == mono_thread_internal_current ())
3549 mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (collect_frame, NULL, MONO_UNWIND_SIGNAL_SAFE, ud);
3550 else
3551 mono_get_eh_callbacks ()->mono_walk_stack_with_state (collect_frame, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, ud);
3553 return MonoResumeThread;
3556 typedef struct {
3557 int nthreads, max_threads;
3558 MonoInternalThread **threads;
3559 } CollectThreadsUserData;
3561 static void
3562 collect_thread (gpointer key, gpointer value, gpointer user)
3564 CollectThreadsUserData *ud = (CollectThreadsUserData *)user;
3565 MonoInternalThread *thread = (MonoInternalThread *)value;
3567 if (ud->nthreads < ud->max_threads)
3568 ud->threads [ud->nthreads ++] = thread;
3572 * Collect running threads into the THREADS array.
3573 * THREADS should be an array allocated on the stack.
3575 static int
3576 collect_threads (MonoInternalThread **thread_array, int max_threads)
3578 CollectThreadsUserData ud;
3580 memset (&ud, 0, sizeof (ud));
3581 /* This array contains refs, but its on the stack, so its ok */
3582 ud.threads = thread_array;
3583 ud.max_threads = max_threads;
3585 mono_threads_lock ();
3586 mono_g_hash_table_foreach (threads, collect_thread, &ud);
3587 mono_threads_unlock ();
3589 return ud.nthreads;
3592 static void
3593 dump_thread (MonoInternalThread *thread, ThreadDumpUserData *ud)
3595 GString* text = g_string_new (0);
3596 char *name;
3597 GError *error = NULL;
3598 int i;
3600 ud->thread = thread;
3601 ud->nframes = 0;
3603 /* Collect frames for the thread */
3604 if (thread == mono_thread_internal_current ()) {
3605 get_thread_dump (mono_thread_info_current (), ud);
3606 } else {
3607 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, get_thread_dump, ud);
3611 * Do all the non async-safe work outside of get_thread_dump.
3613 if (thread->name) {
3614 name = g_utf16_to_utf8 (thread->name, thread->name_len, NULL, NULL, &error);
3615 g_assert (!error);
3616 g_string_append_printf (text, "\n\"%s\"", name);
3617 g_free (name);
3619 else if (thread->threadpool_thread) {
3620 g_string_append (text, "\n\"<threadpool thread>\"");
3621 } else {
3622 g_string_append (text, "\n\"<unnamed thread>\"");
3625 for (i = 0; i < ud->nframes; ++i) {
3626 MonoStackFrameInfo *frame = &ud->frames [i];
3627 MonoMethod *method = NULL;
3629 if (frame->type == FRAME_TYPE_MANAGED)
3630 method = mono_jit_info_get_method (frame->ji);
3632 if (method) {
3633 gchar *location = mono_debug_print_stack_frame (method, frame->native_offset, frame->domain);
3634 g_string_append_printf (text, " %s\n", location);
3635 g_free (location);
3636 } else {
3637 g_string_append_printf (text, " at <unknown> <0x%05x>\n", frame->native_offset);
3641 fprintf (stdout, "%s", text->str);
3643 #if PLATFORM_WIN32 && TARGET_WIN32 && _DEBUG
3644 OutputDebugStringA(text->str);
3645 #endif
3647 g_string_free (text, TRUE);
3648 fflush (stdout);
3651 void
3652 mono_threads_perform_thread_dump (void)
3654 ThreadDumpUserData ud;
3655 MonoInternalThread *thread_array [128];
3656 int tindex, nthreads;
3658 if (!thread_dump_requested)
3659 return;
3661 printf ("Full thread dump:\n");
3663 /* Make a copy of the threads hash to avoid doing work inside threads_lock () */
3664 nthreads = collect_threads (thread_array, 128);
3666 memset (&ud, 0, sizeof (ud));
3667 ud.frames = g_new0 (MonoStackFrameInfo, 256);
3668 ud.max_frames = 256;
3670 for (tindex = 0; tindex < nthreads; ++tindex)
3671 dump_thread (thread_array [tindex], &ud);
3673 g_free (ud.frames);
3675 thread_dump_requested = FALSE;
3678 /* Obtain the thread dump of all threads */
3679 static gboolean
3680 mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_frames, MonoError *error)
3683 ThreadDumpUserData ud;
3684 MonoInternalThread *thread_array [128];
3685 MonoDomain *domain = mono_domain_get ();
3686 MonoDebugSourceLocation *location;
3687 int tindex, nthreads;
3689 error_init (error);
3691 *out_threads = NULL;
3692 *out_stack_frames = NULL;
3694 /* Make a copy of the threads hash to avoid doing work inside threads_lock () */
3695 nthreads = collect_threads (thread_array, 128);
3697 memset (&ud, 0, sizeof (ud));
3698 ud.frames = g_new0 (MonoStackFrameInfo, 256);
3699 ud.max_frames = 256;
3701 *out_threads = mono_array_new_checked (domain, mono_defaults.thread_class, nthreads, error);
3702 goto_if_nok (error, leave);
3703 *out_stack_frames = mono_array_new_checked (domain, mono_defaults.array_class, nthreads, error);
3704 goto_if_nok (error, leave);
3706 for (tindex = 0; tindex < nthreads; ++tindex) {
3707 MonoInternalThread *thread = thread_array [tindex];
3708 MonoArray *thread_frames;
3709 int i;
3711 ud.thread = thread;
3712 ud.nframes = 0;
3714 /* Collect frames for the thread */
3715 if (thread == mono_thread_internal_current ()) {
3716 get_thread_dump (mono_thread_info_current (), &ud);
3717 } else {
3718 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, get_thread_dump, &ud);
3721 mono_array_setref_fast (*out_threads, tindex, mono_thread_current_for_thread (thread));
3723 thread_frames = mono_array_new_checked (domain, mono_defaults.stack_frame_class, ud.nframes, error);
3724 goto_if_nok (error, leave);
3725 mono_array_setref_fast (*out_stack_frames, tindex, thread_frames);
3727 for (i = 0; i < ud.nframes; ++i) {
3728 MonoStackFrameInfo *frame = &ud.frames [i];
3729 MonoMethod *method = NULL;
3730 MonoStackFrame *sf = (MonoStackFrame *)mono_object_new_checked (domain, mono_defaults.stack_frame_class, error);
3731 goto_if_nok (error, leave);
3733 sf->native_offset = frame->native_offset;
3735 if (frame->type == FRAME_TYPE_MANAGED)
3736 method = mono_jit_info_get_method (frame->ji);
3738 if (method) {
3739 sf->method_address = (gsize) frame->ji->code_start;
3741 MonoReflectionMethod *rm = mono_method_get_object_checked (domain, method, NULL, error);
3742 goto_if_nok (error, leave);
3743 MONO_OBJECT_SETREF (sf, method, rm);
3745 location = mono_debug_lookup_source_location (method, frame->native_offset, domain);
3746 if (location) {
3747 sf->il_offset = location->il_offset;
3749 if (location && location->source_file) {
3750 MonoString *filename = mono_string_new_checked (domain, location->source_file, error);
3751 goto_if_nok (error, leave);
3752 MONO_OBJECT_SETREF (sf, filename, filename);
3753 sf->line = location->row;
3754 sf->column = location->column;
3756 mono_debug_free_source_location (location);
3757 } else {
3758 sf->il_offset = -1;
3761 mono_array_setref (thread_frames, i, sf);
3765 leave:
3766 g_free (ud.frames);
3767 return is_ok (error);
3771 * mono_threads_request_thread_dump:
3773 * Ask all threads except the current to print their stacktrace to stdout.
3775 void
3776 mono_threads_request_thread_dump (void)
3778 /*The new thread dump code runs out of the finalizer thread. */
3779 thread_dump_requested = TRUE;
3780 mono_gc_finalize_notify ();
3783 struct ref_stack {
3784 gpointer *refs;
3785 gint allocated; /* +1 so that refs [allocated] == NULL */
3786 gint bottom;
3789 typedef struct ref_stack RefStack;
3791 static RefStack *
3792 ref_stack_new (gint initial_size)
3794 RefStack *rs;
3796 initial_size = MAX (initial_size, 16) + 1;
3797 rs = g_new0 (RefStack, 1);
3798 rs->refs = g_new0 (gpointer, initial_size);
3799 rs->allocated = initial_size;
3800 return rs;
3803 static void
3804 ref_stack_destroy (gpointer ptr)
3806 RefStack *rs = (RefStack *)ptr;
3808 if (rs != NULL) {
3809 g_free (rs->refs);
3810 g_free (rs);
3814 static void
3815 ref_stack_push (RefStack *rs, gpointer ptr)
3817 g_assert (rs != NULL);
3819 if (rs->bottom >= rs->allocated) {
3820 rs->refs = (void **)g_realloc (rs->refs, rs->allocated * 2 * sizeof (gpointer) + 1);
3821 rs->allocated <<= 1;
3822 rs->refs [rs->allocated] = NULL;
3824 rs->refs [rs->bottom++] = ptr;
3827 static void
3828 ref_stack_pop (RefStack *rs)
3830 if (rs == NULL || rs->bottom == 0)
3831 return;
3833 rs->bottom--;
3834 rs->refs [rs->bottom] = NULL;
3837 static gboolean
3838 ref_stack_find (RefStack *rs, gpointer ptr)
3840 gpointer *refs;
3842 if (rs == NULL)
3843 return FALSE;
3845 for (refs = rs->refs; refs && *refs; refs++) {
3846 if (*refs == ptr)
3847 return TRUE;
3849 return FALSE;
3853 * mono_thread_push_appdomain_ref:
3855 * Register that the current thread may have references to objects in domain
3856 * @domain on its stack. Each call to this function should be paired with a
3857 * call to pop_appdomain_ref.
3859 void
3860 mono_thread_push_appdomain_ref (MonoDomain *domain)
3862 MonoInternalThread *thread = mono_thread_internal_current ();
3864 if (thread) {
3865 /* printf ("PUSH REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, domain->friendly_name); */
3866 SPIN_LOCK (thread->lock_thread_id);
3867 if (thread->appdomain_refs == NULL)
3868 thread->appdomain_refs = ref_stack_new (16);
3869 ref_stack_push ((RefStack *)thread->appdomain_refs, domain);
3870 SPIN_UNLOCK (thread->lock_thread_id);
3874 void
3875 mono_thread_pop_appdomain_ref (void)
3877 MonoInternalThread *thread = mono_thread_internal_current ();
3879 if (thread) {
3880 /* printf ("POP REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, ((MonoDomain*)(thread->appdomain_refs->data))->friendly_name); */
3881 SPIN_LOCK (thread->lock_thread_id);
3882 ref_stack_pop ((RefStack *)thread->appdomain_refs);
3883 SPIN_UNLOCK (thread->lock_thread_id);
3887 gboolean
3888 mono_thread_internal_has_appdomain_ref (MonoInternalThread *thread, MonoDomain *domain)
3890 gboolean res;
3891 SPIN_LOCK (thread->lock_thread_id);
3892 res = ref_stack_find ((RefStack *)thread->appdomain_refs, domain);
3893 SPIN_UNLOCK (thread->lock_thread_id);
3894 return res;
3897 gboolean
3898 mono_thread_has_appdomain_ref (MonoThread *thread, MonoDomain *domain)
3900 return mono_thread_internal_has_appdomain_ref (thread->internal_thread, domain);
3903 typedef struct abort_appdomain_data {
3904 struct wait_data wait;
3905 MonoDomain *domain;
3906 } abort_appdomain_data;
3908 static void
3909 collect_appdomain_thread (gpointer key, gpointer value, gpointer user_data)
3911 MonoInternalThread *thread = (MonoInternalThread*)value;
3912 abort_appdomain_data *data = (abort_appdomain_data*)user_data;
3913 MonoDomain *domain = data->domain;
3915 if (mono_thread_internal_has_appdomain_ref (thread, domain)) {
3916 /* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread->tid, domain->friendly_name); */
3918 if(data->wait.num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
3919 data->wait.handles [data->wait.num] = mono_threads_open_thread_handle (thread->handle);
3920 data->wait.threads [data->wait.num] = thread;
3921 data->wait.num++;
3922 } else {
3923 /* Just ignore the rest, we can't do anything with
3924 * them yet
3931 * mono_threads_abort_appdomain_threads:
3933 * Abort threads which has references to the given appdomain.
3935 gboolean
3936 mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
3938 abort_appdomain_data user_data;
3939 gint64 start_time;
3940 int orig_timeout = timeout;
3941 int i;
3943 THREAD_DEBUG (g_message ("%s: starting abort", __func__));
3945 start_time = mono_msec_ticks ();
3946 do {
3947 mono_threads_lock ();
3949 user_data.domain = domain;
3950 user_data.wait.num = 0;
3951 /* This shouldn't take any locks */
3952 mono_g_hash_table_foreach (threads, collect_appdomain_thread, &user_data);
3953 mono_threads_unlock ();
3955 if (user_data.wait.num > 0) {
3956 /* Abort the threads outside the threads lock */
3957 for (i = 0; i < user_data.wait.num; ++i)
3958 mono_thread_internal_abort (user_data.wait.threads [i], TRUE);
3961 * We should wait for the threads either to abort, or to leave the
3962 * domain. We can't do the latter, so we wait with a timeout.
3964 wait_for_tids (&user_data.wait, 100, FALSE);
3967 /* Update remaining time */
3968 timeout -= mono_msec_ticks () - start_time;
3969 start_time = mono_msec_ticks ();
3971 if (orig_timeout != -1 && timeout < 0)
3972 return FALSE;
3974 while (user_data.wait.num > 0);
3976 THREAD_DEBUG (g_message ("%s: abort done", __func__));
3978 return TRUE;
3981 void
3982 mono_thread_self_abort (void)
3984 MonoError error;
3985 self_abort_internal (&error);
3986 mono_error_set_pending_exception (&error);
3990 * mono_thread_get_undeniable_exception:
3992 * Return an exception which needs to be raised when leaving a catch clause.
3993 * This is used for undeniable exception propagation.
3995 MonoException*
3996 mono_thread_get_undeniable_exception (void)
3998 MonoInternalThread *thread = mono_thread_internal_current ();
4000 if (!(thread && thread->abort_exc && !is_running_protected_wrapper ()))
4001 return NULL;
4003 // We don't want to have our exception effect calls made by
4004 // the catching block
4006 if (!mono_get_eh_callbacks ()->mono_above_abort_threshold ())
4007 return NULL;
4010 * FIXME: Clear the abort exception and return an AppDomainUnloaded
4011 * exception if the thread no longer references a dying appdomain.
4013 thread->abort_exc->trace_ips = NULL;
4014 thread->abort_exc->stack_trace = NULL;
4015 return thread->abort_exc;
4018 #if MONO_SMALL_CONFIG
4019 #define NUM_STATIC_DATA_IDX 4
4020 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
4021 64, 256, 1024, 4096
4023 #else
4024 #define NUM_STATIC_DATA_IDX 8
4025 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
4026 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
4028 #endif
4030 static MonoBitSet *thread_reference_bitmaps [NUM_STATIC_DATA_IDX];
4031 static MonoBitSet *context_reference_bitmaps [NUM_STATIC_DATA_IDX];
4033 static void
4034 mark_slots (void *addr, MonoBitSet **bitmaps, MonoGCMarkFunc mark_func, void *gc_data)
4036 gpointer *static_data = (gpointer *)addr;
4038 for (int i = 0; i < NUM_STATIC_DATA_IDX; ++i) {
4039 void **ptr = (void **)static_data [i];
4041 if (!ptr)
4042 continue;
4044 MONO_BITSET_FOREACH (bitmaps [i], idx, {
4045 void **p = ptr + idx;
4047 if (*p)
4048 mark_func ((MonoObject**)p, gc_data);
4053 static void
4054 mark_tls_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
4056 mark_slots (addr, thread_reference_bitmaps, mark_func, gc_data);
4059 static void
4060 mark_ctx_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
4062 mark_slots (addr, context_reference_bitmaps, mark_func, gc_data);
4066 * mono_alloc_static_data
4068 * Allocate memory blocks for storing threads or context static data
4070 static void
4071 mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean threadlocal)
4073 guint idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4074 int i;
4076 gpointer* static_data = *static_data_ptr;
4077 if (!static_data) {
4078 static MonoGCDescriptor tls_desc = MONO_GC_DESCRIPTOR_NULL;
4079 static MonoGCDescriptor ctx_desc = MONO_GC_DESCRIPTOR_NULL;
4081 if (mono_gc_user_markers_supported ()) {
4082 if (tls_desc == MONO_GC_DESCRIPTOR_NULL)
4083 tls_desc = mono_gc_make_root_descr_user (mark_tls_slots);
4085 if (ctx_desc == MONO_GC_DESCRIPTOR_NULL)
4086 ctx_desc = mono_gc_make_root_descr_user (mark_ctx_slots);
4089 static_data = (void **)mono_gc_alloc_fixed (static_data_size [0], threadlocal ? tls_desc : ctx_desc,
4090 threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC,
4091 threadlocal ? "managed thread-static variables" : "managed context-static variables");
4092 *static_data_ptr = static_data;
4093 static_data [0] = static_data;
4096 for (i = 1; i <= idx; ++i) {
4097 if (static_data [i])
4098 continue;
4100 if (mono_gc_user_markers_supported ())
4101 static_data [i] = g_malloc0 (static_data_size [i]);
4102 else
4103 static_data [i] = mono_gc_alloc_fixed (static_data_size [i], MONO_GC_DESCRIPTOR_NULL,
4104 threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC,
4105 threadlocal ? "managed thread-static variables" : "managed context-static variables");
4109 static void
4110 mono_free_static_data (gpointer* static_data)
4112 int i;
4113 for (i = 1; i < NUM_STATIC_DATA_IDX; ++i) {
4114 gpointer p = static_data [i];
4115 if (!p)
4116 continue;
4118 * At this point, the static data pointer array is still registered with the
4119 * GC, so must ensure that mark_tls_slots() will not encounter any invalid
4120 * data. Freeing the individual arrays without first nulling their slots
4121 * would make it possible for mark_tls/ctx_slots() to encounter a pointer to
4122 * such an already freed array. See bug #13813.
4124 static_data [i] = NULL;
4125 mono_memory_write_barrier ();
4126 if (mono_gc_user_markers_supported ())
4127 g_free (p);
4128 else
4129 mono_gc_free_fixed (p);
4131 mono_gc_free_fixed (static_data);
4135 * mono_init_static_data_info
4137 * Initializes static data counters
4139 static void mono_init_static_data_info (StaticDataInfo *static_data)
4141 static_data->idx = 0;
4142 static_data->offset = 0;
4143 static_data->freelist = NULL;
4147 * mono_alloc_static_data_slot
4149 * Generates an offset for static data. static_data contains the counters
4150 * used to generate it.
4152 static guint32
4153 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align)
4155 if (!static_data->idx && !static_data->offset) {
4157 * we use the first chunk of the first allocation also as
4158 * an array for the rest of the data
4160 static_data->offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX;
4162 static_data->offset += align - 1;
4163 static_data->offset &= ~(align - 1);
4164 if (static_data->offset + size >= static_data_size [static_data->idx]) {
4165 static_data->idx ++;
4166 g_assert (size <= static_data_size [static_data->idx]);
4167 g_assert (static_data->idx < NUM_STATIC_DATA_IDX);
4168 static_data->offset = 0;
4170 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (static_data->idx, static_data->offset, 0);
4171 static_data->offset += size;
4172 return offset;
4176 * LOCKING: requires that threads_mutex is held
4178 static void
4179 context_adjust_static_data (MonoAppContext *ctx)
4181 if (context_static_info.offset || context_static_info.idx > 0) {
4182 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (context_static_info.idx, context_static_info.offset, 0);
4183 mono_alloc_static_data (&ctx->static_data, offset, FALSE);
4184 ctx->data->static_data = ctx->static_data;
4189 * LOCKING: requires that threads_mutex is held
4191 static void
4192 alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
4194 MonoInternalThread *thread = (MonoInternalThread *)value;
4195 guint32 offset = GPOINTER_TO_UINT (user);
4197 mono_alloc_static_data (&(thread->static_data), offset, TRUE);
4201 * LOCKING: requires that threads_mutex is held
4203 static void
4204 alloc_context_static_data_helper (gpointer key, gpointer value, gpointer user)
4206 MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (GPOINTER_TO_INT (key));
4208 if (!ctx)
4209 return;
4211 guint32 offset = GPOINTER_TO_UINT (user);
4212 mono_alloc_static_data (&ctx->static_data, offset, FALSE);
4213 ctx->data->static_data = ctx->static_data;
4216 static StaticDataFreeList*
4217 search_slot_in_freelist (StaticDataInfo *static_data, guint32 size, guint32 align)
4219 StaticDataFreeList* prev = NULL;
4220 StaticDataFreeList* tmp = static_data->freelist;
4221 while (tmp) {
4222 if (tmp->size == size) {
4223 if (prev)
4224 prev->next = tmp->next;
4225 else
4226 static_data->freelist = tmp->next;
4227 return tmp;
4229 prev = tmp;
4230 tmp = tmp->next;
4232 return NULL;
4235 #if SIZEOF_VOID_P == 4
4236 #define ONE_P 1
4237 #else
4238 #define ONE_P 1ll
4239 #endif
4241 static void
4242 update_reference_bitmap (MonoBitSet **sets, guint32 offset, uintptr_t *bitmap, int numbits)
4244 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4245 if (!sets [idx])
4246 sets [idx] = mono_bitset_new (static_data_size [idx] / sizeof (uintptr_t), 0);
4247 MonoBitSet *rb = sets [idx];
4248 offset = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
4249 offset /= sizeof (uintptr_t);
4250 /* offset is now the bitmap offset */
4251 for (int i = 0; i < numbits; ++i) {
4252 if (bitmap [i / sizeof (uintptr_t)] & (ONE_P << (i & (sizeof (uintptr_t) * 8 -1))))
4253 mono_bitset_set_fast (rb, offset + i);
4257 static void
4258 clear_reference_bitmap (MonoBitSet **sets, guint32 offset, guint32 size)
4260 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4261 MonoBitSet *rb = sets [idx];
4262 offset = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
4263 offset /= sizeof (uintptr_t);
4264 /* offset is now the bitmap offset */
4265 for (int i = 0; i < size / sizeof (uintptr_t); i++)
4266 mono_bitset_clear_fast (rb, offset + i);
4269 guint32
4270 mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align, uintptr_t *bitmap, int numbits)
4272 g_assert (static_type == SPECIAL_STATIC_THREAD || static_type == SPECIAL_STATIC_CONTEXT);
4274 StaticDataInfo *info;
4275 MonoBitSet **sets;
4277 if (static_type == SPECIAL_STATIC_THREAD) {
4278 info = &thread_static_info;
4279 sets = thread_reference_bitmaps;
4280 } else {
4281 info = &context_static_info;
4282 sets = context_reference_bitmaps;
4285 mono_threads_lock ();
4287 StaticDataFreeList *item = search_slot_in_freelist (info, size, align);
4288 guint32 offset;
4290 if (item) {
4291 offset = item->offset;
4292 g_free (item);
4293 } else {
4294 offset = mono_alloc_static_data_slot (info, size, align);
4297 update_reference_bitmap (sets, offset, bitmap, numbits);
4299 if (static_type == SPECIAL_STATIC_THREAD) {
4300 /* This can be called during startup */
4301 if (threads != NULL)
4302 mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
4303 } else {
4304 if (contexts != NULL)
4305 g_hash_table_foreach (contexts, alloc_context_static_data_helper, GUINT_TO_POINTER (offset));
4307 ACCESS_SPECIAL_STATIC_OFFSET (offset, type) = SPECIAL_STATIC_OFFSET_TYPE_CONTEXT;
4310 mono_threads_unlock ();
4312 return offset;
4315 gpointer
4316 mono_get_special_static_data_for_thread (MonoInternalThread *thread, guint32 offset)
4318 guint32 static_type = ACCESS_SPECIAL_STATIC_OFFSET (offset, type);
4320 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4321 return get_thread_static_data (thread, offset);
4322 } else {
4323 return get_context_static_data (thread->current_appcontext, offset);
4327 gpointer
4328 mono_get_special_static_data (guint32 offset)
4330 return mono_get_special_static_data_for_thread (mono_thread_internal_current (), offset);
4333 typedef struct {
4334 guint32 offset;
4335 guint32 size;
4336 } OffsetSize;
4339 * LOCKING: requires that threads_mutex is held
4341 static void
4342 free_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
4344 MonoInternalThread *thread = (MonoInternalThread *)value;
4345 OffsetSize *data = (OffsetSize *)user;
4346 int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index);
4347 int off = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, offset);
4348 char *ptr;
4350 if (!thread->static_data || !thread->static_data [idx])
4351 return;
4352 ptr = ((char*) thread->static_data [idx]) + off;
4353 mono_gc_bzero_atomic (ptr, data->size);
4357 * LOCKING: requires that threads_mutex is held
4359 static void
4360 free_context_static_data_helper (gpointer key, gpointer value, gpointer user)
4362 MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (GPOINTER_TO_INT (key));
4364 if (!ctx)
4365 return;
4367 OffsetSize *data = (OffsetSize *)user;
4368 int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index);
4369 int off = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, offset);
4370 char *ptr;
4372 if (!ctx->static_data || !ctx->static_data [idx])
4373 return;
4375 ptr = ((char*) ctx->static_data [idx]) + off;
4376 mono_gc_bzero_atomic (ptr, data->size);
4379 static void
4380 do_free_special_slot (guint32 offset, guint32 size)
4382 guint32 static_type = ACCESS_SPECIAL_STATIC_OFFSET (offset, type);
4383 MonoBitSet **sets;
4384 StaticDataInfo *info;
4386 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4387 info = &thread_static_info;
4388 sets = thread_reference_bitmaps;
4389 } else {
4390 info = &context_static_info;
4391 sets = context_reference_bitmaps;
4394 guint32 data_offset = offset;
4395 ACCESS_SPECIAL_STATIC_OFFSET (data_offset, type) = 0;
4396 OffsetSize data = { data_offset, size };
4398 clear_reference_bitmap (sets, data.offset, data.size);
4400 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4401 if (threads != NULL)
4402 mono_g_hash_table_foreach (threads, free_thread_static_data_helper, &data);
4403 } else {
4404 if (contexts != NULL)
4405 g_hash_table_foreach (contexts, free_context_static_data_helper, &data);
4408 if (!mono_runtime_is_shutting_down ()) {
4409 StaticDataFreeList *item = g_new0 (StaticDataFreeList, 1);
4411 item->offset = offset;
4412 item->size = size;
4414 item->next = info->freelist;
4415 info->freelist = item;
4419 static void
4420 do_free_special (gpointer key, gpointer value, gpointer data)
4422 MonoClassField *field = (MonoClassField *)key;
4423 guint32 offset = GPOINTER_TO_UINT (value);
4424 gint32 align;
4425 guint32 size;
4426 size = mono_type_size (field->type, &align);
4427 do_free_special_slot (offset, size);
4430 void
4431 mono_alloc_special_static_data_free (GHashTable *special_static_fields)
4433 mono_threads_lock ();
4435 g_hash_table_foreach (special_static_fields, do_free_special, NULL);
4437 mono_threads_unlock ();
4440 #ifdef HOST_WIN32
4441 static void CALLBACK dummy_apc (ULONG_PTR param)
4444 #endif
4447 * mono_thread_execute_interruption
4449 * Performs the operation that the requested thread state requires (abort,
4450 * suspend or stop)
4452 static MonoException*
4453 mono_thread_execute_interruption (void)
4455 MonoInternalThread *thread = mono_thread_internal_current ();
4456 MonoThread *sys_thread = mono_thread_current ();
4458 LOCK_THREAD (thread);
4460 /* MonoThread::interruption_requested can only be changed with atomics */
4461 if (!mono_thread_clear_interruption_requested (thread)) {
4462 UNLOCK_THREAD (thread);
4463 return NULL;
4466 /* this will consume pending APC calls */
4467 #ifdef HOST_WIN32
4468 MONO_ENTER_GC_SAFE;
4469 mono_win32_wait_for_single_object_ex (GetCurrentThread (), 0, TRUE);
4470 MONO_EXIT_GC_SAFE;
4471 #endif
4473 /* Clear the interrupted flag of the thread so it can wait again */
4474 mono_thread_info_clear_self_interrupt ();
4476 /* If there's a pending exception and an AbortRequested, the pending exception takes precedence */
4477 if (sys_thread->pending_exception) {
4478 MonoException *exc;
4480 exc = sys_thread->pending_exception;
4481 sys_thread->pending_exception = NULL;
4483 UNLOCK_THREAD (thread);
4484 return exc;
4485 } else if (thread->state & (ThreadState_AbortRequested)) {
4486 UNLOCK_THREAD (thread);
4487 g_assert (sys_thread->pending_exception == NULL);
4488 if (thread->abort_exc == NULL) {
4490 * This might be racy, but it has to be called outside the lock
4491 * since it calls managed code.
4493 MONO_OBJECT_SETREF (thread, abort_exc, mono_get_exception_thread_abort ());
4495 return thread->abort_exc;
4496 } else if (thread->state & (ThreadState_SuspendRequested)) {
4497 /* calls UNLOCK_THREAD (thread) */
4498 self_suspend_internal ();
4499 return NULL;
4500 } else if (thread->thread_interrupt_requested) {
4502 thread->thread_interrupt_requested = FALSE;
4503 UNLOCK_THREAD (thread);
4505 return(mono_get_exception_thread_interrupted ());
4508 UNLOCK_THREAD (thread);
4510 return NULL;
4514 * mono_thread_request_interruption
4516 * A signal handler can call this method to request the interruption of a
4517 * thread. The result of the interruption will depend on the current state of
4518 * the thread. If the result is an exception that needs to be throw, it is
4519 * provided as return value.
4521 MonoException*
4522 mono_thread_request_interruption (gboolean running_managed)
4524 MonoInternalThread *thread = mono_thread_internal_current ();
4526 /* The thread may already be stopping */
4527 if (thread == NULL)
4528 return NULL;
4530 if (!mono_thread_set_interruption_requested (thread))
4531 return NULL;
4533 if (!running_managed || is_running_protected_wrapper ()) {
4534 /* Can't stop while in unmanaged code. Increase the global interruption
4535 request count. When exiting the unmanaged method the count will be
4536 checked and the thread will be interrupted. */
4538 /* this will awake the thread if it is in WaitForSingleObject
4539 or similar */
4540 #ifdef HOST_WIN32
4541 mono_win32_interrupt_wait (thread->thread_info, thread->native_handle, (DWORD)thread->tid);
4542 #else
4543 mono_thread_info_self_interrupt ();
4544 #endif
4545 return NULL;
4547 else {
4548 return mono_thread_execute_interruption ();
4552 /*This function should be called by a thread after it has exited all of
4553 * its handle blocks at interruption time.*/
4554 MonoException*
4555 mono_thread_resume_interruption (gboolean exec)
4557 MonoInternalThread *thread = mono_thread_internal_current ();
4558 gboolean still_aborting;
4560 /* The thread may already be stopping */
4561 if (thread == NULL)
4562 return NULL;
4564 LOCK_THREAD (thread);
4565 still_aborting = (thread->state & (ThreadState_AbortRequested)) != 0;
4566 UNLOCK_THREAD (thread);
4568 /*This can happen if the protected block called Thread::ResetAbort*/
4569 if (!still_aborting)
4570 return NULL;
4572 if (!mono_thread_set_interruption_requested (thread))
4573 return NULL;
4575 mono_thread_info_self_interrupt ();
4577 if (exec)
4578 return mono_thread_execute_interruption ();
4579 else
4580 return NULL;
4583 gboolean mono_thread_interruption_requested ()
4585 if (thread_interruption_requested) {
4586 MonoInternalThread *thread = mono_thread_internal_current ();
4587 /* The thread may already be stopping */
4588 if (thread != NULL)
4589 return mono_thread_get_interruption_requested (thread);
4591 return FALSE;
4594 static MonoException*
4595 mono_thread_interruption_checkpoint_request (gboolean bypass_abort_protection)
4597 MonoInternalThread *thread = mono_thread_internal_current ();
4599 /* The thread may already be stopping */
4600 if (!thread)
4601 return NULL;
4602 if (!mono_thread_get_interruption_requested (thread))
4603 return NULL;
4604 if (!bypass_abort_protection && !mono_thread_current ()->pending_exception && is_running_protected_wrapper ())
4605 return NULL;
4607 return mono_thread_execute_interruption ();
4611 * Performs the interruption of the current thread, if one has been requested,
4612 * and the thread is not running a protected wrapper.
4613 * Return the exception which needs to be thrown, if any.
4615 MonoException*
4616 mono_thread_interruption_checkpoint (void)
4618 return mono_thread_interruption_checkpoint_request (FALSE);
4622 * Performs the interruption of the current thread, if one has been requested.
4623 * Return the exception which needs to be thrown, if any.
4625 MonoException*
4626 mono_thread_force_interruption_checkpoint_noraise (void)
4628 return mono_thread_interruption_checkpoint_request (TRUE);
4632 * mono_set_pending_exception:
4634 * Set the pending exception of the current thread to EXC.
4635 * The exception will be thrown when execution returns to managed code.
4637 void
4638 mono_set_pending_exception (MonoException *exc)
4640 MonoThread *thread = mono_thread_current ();
4642 /* The thread may already be stopping */
4643 if (thread == NULL)
4644 return;
4646 MONO_OBJECT_SETREF (thread, pending_exception, exc);
4648 mono_thread_request_interruption (FALSE);
4652 * mono_thread_interruption_request_flag:
4654 * Returns the address of a flag that will be non-zero if an interruption has
4655 * been requested for a thread. The thread to interrupt may not be the current
4656 * thread, so an additional call to mono_thread_interruption_requested() or
4657 * mono_thread_interruption_checkpoint() is allways needed if the flag is not
4658 * zero.
4660 gint32* mono_thread_interruption_request_flag ()
4662 return &thread_interruption_requested;
4665 void
4666 mono_thread_init_apartment_state (void)
4668 #ifdef HOST_WIN32
4669 MonoInternalThread* thread = mono_thread_internal_current ();
4671 /* Positive return value indicates success, either
4672 * S_OK if this is first CoInitialize call, or
4673 * S_FALSE if CoInitialize already called, but with same
4674 * threading model. A negative value indicates failure,
4675 * probably due to trying to change the threading model.
4677 if (CoInitializeEx(NULL, (thread->apartment_state == ThreadApartmentState_STA)
4678 ? COINIT_APARTMENTTHREADED
4679 : COINIT_MULTITHREADED) < 0) {
4680 thread->apartment_state = ThreadApartmentState_Unknown;
4682 #endif
4685 void
4686 mono_thread_cleanup_apartment_state (void)
4688 #ifdef HOST_WIN32
4689 MonoInternalThread* thread = mono_thread_internal_current ();
4691 if (thread && thread->apartment_state != ThreadApartmentState_Unknown) {
4692 CoUninitialize ();
4694 #endif
4697 void
4698 mono_thread_set_state (MonoInternalThread *thread, MonoThreadState state)
4700 LOCK_THREAD (thread);
4701 thread->state |= state;
4702 UNLOCK_THREAD (thread);
4706 * mono_thread_test_and_set_state:
4707 * Test if current state of \p thread include \p test. If it does not, OR \p set into the state.
4708 * \returns TRUE if \p set was OR'd in.
4710 gboolean
4711 mono_thread_test_and_set_state (MonoInternalThread *thread, MonoThreadState test, MonoThreadState set)
4713 LOCK_THREAD (thread);
4715 if ((thread->state & test) != 0) {
4716 UNLOCK_THREAD (thread);
4717 return FALSE;
4720 thread->state |= set;
4721 UNLOCK_THREAD (thread);
4723 return TRUE;
4726 void
4727 mono_thread_clr_state (MonoInternalThread *thread, MonoThreadState state)
4729 LOCK_THREAD (thread);
4730 thread->state &= ~state;
4731 UNLOCK_THREAD (thread);
4734 gboolean
4735 mono_thread_test_state (MonoInternalThread *thread, MonoThreadState test)
4737 gboolean ret = FALSE;
4739 LOCK_THREAD (thread);
4741 if ((thread->state & test) != 0) {
4742 ret = TRUE;
4745 UNLOCK_THREAD (thread);
4747 return ret;
4750 static void
4751 self_interrupt_thread (void *_unused)
4753 MonoException *exc;
4754 MonoThreadInfo *info;
4756 exc = mono_thread_execute_interruption ();
4757 if (!exc) {
4758 if (mono_threads_is_coop_enabled ()) {
4759 /* We can return from an async call in coop, as
4760 * it's simply called when exiting the safepoint */
4761 return;
4764 g_error ("%s: we can't resume from an async call", __func__);
4767 info = mono_thread_info_current ();
4769 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. */
4772 static gboolean
4773 mono_jit_info_match (MonoJitInfo *ji, gpointer ip)
4775 if (!ji)
4776 return FALSE;
4777 return ji->code_start <= ip && (char*)ip < (char*)ji->code_start + ji->code_size;
4780 static gboolean
4781 last_managed (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
4783 MonoJitInfo **dest = (MonoJitInfo **)data;
4784 *dest = frame->ji;
4785 return TRUE;
4788 static MonoJitInfo*
4789 mono_thread_info_get_last_managed (MonoThreadInfo *info)
4791 MonoJitInfo *ji = NULL;
4792 if (!info)
4793 return NULL;
4796 * The suspended thread might be holding runtime locks. Make sure we don't try taking
4797 * any runtime locks while unwinding. In coop case we shouldn't safepoint in regions
4798 * where we hold runtime locks.
4800 if (!mono_threads_is_coop_enabled ())
4801 mono_thread_info_set_is_async_context (TRUE);
4802 mono_get_eh_callbacks ()->mono_walk_stack_with_state (last_managed, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, &ji);
4803 if (!mono_threads_is_coop_enabled ())
4804 mono_thread_info_set_is_async_context (FALSE);
4805 return ji;
4808 typedef struct {
4809 MonoInternalThread *thread;
4810 gboolean install_async_abort;
4811 MonoThreadInfoInterruptToken *interrupt_token;
4812 } AbortThreadData;
4814 static SuspendThreadResult
4815 async_abort_critical (MonoThreadInfo *info, gpointer ud)
4817 AbortThreadData *data = (AbortThreadData *)ud;
4818 MonoInternalThread *thread = data->thread;
4819 MonoJitInfo *ji = NULL;
4820 gboolean protected_wrapper;
4821 gboolean running_managed;
4823 if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (mono_thread_info_get_suspend_state (info)))
4824 return MonoResumeThread;
4826 /*someone is already interrupting it*/
4827 if (!mono_thread_set_interruption_requested (thread))
4828 return MonoResumeThread;
4830 ji = mono_thread_info_get_last_managed (info);
4831 protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
4832 running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
4834 if (!protected_wrapper && running_managed) {
4835 /*We are in managed code*/
4836 /*Set the thread to call */
4837 if (data->install_async_abort)
4838 mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
4839 return MonoResumeThread;
4840 } else {
4842 * This will cause waits to be broken.
4843 * It will also prevent the thread from entering a wait, so if the thread returns
4844 * from the wait before it receives the abort signal, it will just spin in the wait
4845 * functions in the io-layer until the signal handler calls QueueUserAPC which will
4846 * make it return.
4848 data->interrupt_token = mono_thread_info_prepare_interrupt (info);
4850 return MonoResumeThread;
4854 static void
4855 async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort)
4857 AbortThreadData data;
4859 g_assert (thread != mono_thread_internal_current ());
4861 data.thread = thread;
4862 data.install_async_abort = install_async_abort;
4863 data.interrupt_token = NULL;
4865 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), TRUE, async_abort_critical, &data);
4866 if (data.interrupt_token)
4867 mono_thread_info_finish_interrupt (data.interrupt_token);
4868 /*FIXME we need to wait for interruption to complete -- figure out how much into interruption we should wait for here*/
4871 static void
4872 self_abort_internal (MonoError *error)
4874 MonoException *exc;
4876 error_init (error);
4878 /* FIXME this is insanely broken, it doesn't cause interruption to happen synchronously
4879 * since passing FALSE to mono_thread_request_interruption makes sure it returns NULL */
4882 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.
4884 exc = mono_thread_request_interruption (TRUE);
4885 if (exc)
4886 mono_error_set_exception_instance (error, exc);
4887 else
4888 mono_thread_info_self_interrupt ();
4891 typedef struct {
4892 MonoInternalThread *thread;
4893 gboolean interrupt;
4894 MonoThreadInfoInterruptToken *interrupt_token;
4895 } SuspendThreadData;
4897 static SuspendThreadResult
4898 async_suspend_critical (MonoThreadInfo *info, gpointer ud)
4900 SuspendThreadData *data = (SuspendThreadData *)ud;
4901 MonoInternalThread *thread = data->thread;
4902 MonoJitInfo *ji = NULL;
4903 gboolean protected_wrapper;
4904 gboolean running_managed;
4906 ji = mono_thread_info_get_last_managed (info);
4907 protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
4908 running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
4910 if (running_managed && !protected_wrapper) {
4911 if (mono_threads_is_coop_enabled ()) {
4912 mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
4913 return MonoResumeThread;
4914 } else {
4915 thread->state &= ~ThreadState_SuspendRequested;
4916 thread->state |= ThreadState_Suspended;
4917 return KeepSuspended;
4919 } else {
4920 mono_thread_set_interruption_requested (thread);
4921 if (data->interrupt)
4922 data->interrupt_token = mono_thread_info_prepare_interrupt ((MonoThreadInfo *)thread->thread_info);
4924 return MonoResumeThread;
4928 /* LOCKING: called with @thread synch_cs held, and releases it */
4929 static void
4930 async_suspend_internal (MonoInternalThread *thread, gboolean interrupt)
4932 SuspendThreadData data;
4934 g_assert (thread != mono_thread_internal_current ());
4936 // MOSTLY_ASYNC_SAFE_PRINTF ("ASYNC SUSPEND thread %p\n", thread_get_tid (thread));
4938 thread->self_suspended = FALSE;
4940 data.thread = thread;
4941 data.interrupt = interrupt;
4942 data.interrupt_token = NULL;
4944 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), interrupt, async_suspend_critical, &data);
4945 if (data.interrupt_token)
4946 mono_thread_info_finish_interrupt (data.interrupt_token);
4948 UNLOCK_THREAD (thread);
4951 /* LOCKING: called with @thread synch_cs held, and releases it */
4952 static void
4953 self_suspend_internal (void)
4955 MonoInternalThread *thread;
4956 MonoOSEvent *event;
4957 MonoOSEventWaitRet res;
4959 thread = mono_thread_internal_current ();
4961 // MOSTLY_ASYNC_SAFE_PRINTF ("SELF SUSPEND thread %p\n", thread_get_tid (thread));
4963 thread->self_suspended = TRUE;
4965 thread->state &= ~ThreadState_SuspendRequested;
4966 thread->state |= ThreadState_Suspended;
4968 UNLOCK_THREAD (thread);
4970 event = thread->suspended;
4972 MONO_ENTER_GC_SAFE;
4973 res = mono_os_event_wait_one (event, MONO_INFINITE_WAIT, TRUE);
4974 g_assert (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0 || res == MONO_OS_EVENT_WAIT_RET_ALERTED);
4975 MONO_EXIT_GC_SAFE;
4978 static void
4979 suspend_for_shutdown_async_call (gpointer unused)
4981 for (;;)
4982 mono_thread_info_yield ();
4985 static SuspendThreadResult
4986 suspend_for_shutdown_critical (MonoThreadInfo *info, gpointer unused)
4988 mono_thread_info_setup_async_call (info, suspend_for_shutdown_async_call, NULL);
4989 return MonoResumeThread;
4992 void
4993 mono_thread_internal_suspend_for_shutdown (MonoInternalThread *thread)
4995 g_assert (thread != mono_thread_internal_current ());
4997 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, suspend_for_shutdown_critical, NULL);
5001 * mono_thread_is_foreign:
5002 * \param thread the thread to query
5004 * This function allows one to determine if a thread was created by the mono runtime and has
5005 * a well defined lifecycle or it's a foreign one, created by the native environment.
5007 * \returns TRUE if \p thread was not created by the runtime.
5009 mono_bool
5010 mono_thread_is_foreign (MonoThread *thread)
5012 MonoThreadInfo *info = (MonoThreadInfo *)thread->internal_thread->thread_info;
5013 return info->runtime_thread == FALSE;
5016 #ifndef HOST_WIN32
5017 static void
5018 threads_native_thread_join_lock (gpointer tid, gpointer value)
5020 pthread_t thread = (pthread_t)tid;
5021 if (thread != pthread_self ()) {
5022 MONO_ENTER_GC_SAFE;
5023 /* This shouldn't block */
5024 mono_threads_join_lock ();
5025 mono_native_thread_join (thread);
5026 mono_threads_join_unlock ();
5027 MONO_EXIT_GC_SAFE;
5030 static void
5031 threads_native_thread_join_nolock (gpointer tid, gpointer value)
5033 pthread_t thread = (pthread_t)tid;
5034 MONO_ENTER_GC_SAFE;
5035 mono_native_thread_join (thread);
5036 MONO_EXIT_GC_SAFE;
5039 static void
5040 threads_add_joinable_thread_nolock (gpointer tid)
5042 g_hash_table_insert (joinable_threads, tid, tid);
5044 #else
5045 static void
5046 threads_native_thread_join_lock (gpointer tid, gpointer value)
5048 MonoNativeThreadId thread_id = (MonoNativeThreadId)(guint64)tid;
5049 HANDLE thread_handle = (HANDLE)value;
5050 if (thread_id != GetCurrentThreadId () && thread_handle != NULL && thread_handle != INVALID_HANDLE_VALUE) {
5051 MONO_ENTER_GC_SAFE;
5052 /* This shouldn't block */
5053 mono_threads_join_lock ();
5054 mono_native_thread_join_handle (thread_handle, TRUE);
5055 mono_threads_join_unlock ();
5056 MONO_EXIT_GC_SAFE;
5060 static void
5061 threads_native_thread_join_nolock (gpointer tid, gpointer value)
5063 HANDLE thread_handle = (HANDLE)value;
5064 MONO_ENTER_GC_SAFE;
5065 mono_native_thread_join_handle (thread_handle, TRUE);
5066 MONO_EXIT_GC_SAFE;
5069 static void
5070 threads_add_joinable_thread_nolock (gpointer tid)
5072 g_hash_table_insert (joinable_threads, tid, (gpointer)OpenThread (SYNCHRONIZE, TRUE, (MonoNativeThreadId)(guint64)tid));
5074 #endif
5076 void
5077 mono_threads_add_joinable_runtime_thread (gpointer thread_info)
5079 g_assert (thread_info);
5080 MonoThreadInfo *mono_thread_info = (MonoThreadInfo*)thread_info;
5082 if (mono_thread_info->runtime_thread) {
5083 if (mono_atomic_cas_i32 (&mono_thread_info->thread_pending_native_join, TRUE, FALSE) == FALSE)
5084 mono_threads_add_joinable_thread ((gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (mono_thread_info_get_tid (mono_thread_info))));
5089 * mono_add_joinable_thread:
5091 * Add TID to the list of joinable threads.
5092 * LOCKING: Acquires the threads lock.
5094 void
5095 mono_threads_add_joinable_thread (gpointer tid)
5098 * We cannot detach from threads because it causes problems like
5099 * 2fd16f60/r114307. So we collect them and join them when
5100 * we have time (in the finalizer thread).
5102 joinable_threads_lock ();
5103 if (!joinable_threads)
5104 joinable_threads = g_hash_table_new (NULL, NULL);
5106 gpointer orig_key;
5107 gpointer value;
5108 if (!g_hash_table_lookup_extended (joinable_threads, tid, &orig_key, &value)) {
5109 threads_add_joinable_thread_nolock (tid);
5110 UnlockedIncrement (&joinable_thread_count);
5112 joinable_threads_unlock ();
5114 mono_gc_finalize_notify ();
5118 * mono_threads_join_threads:
5120 * Join all joinable threads. This is called from the finalizer thread.
5121 * LOCKING: Acquires the threads lock.
5123 void
5124 mono_threads_join_threads (void)
5126 GHashTableIter iter;
5127 gpointer key;
5128 gpointer value;
5129 gboolean found;
5131 /* Fastpath */
5132 if (!UnlockedRead (&joinable_thread_count))
5133 return;
5135 while (TRUE) {
5136 joinable_threads_lock ();
5137 found = FALSE;
5138 if (g_hash_table_size (joinable_threads)) {
5139 g_hash_table_iter_init (&iter, joinable_threads);
5140 g_hash_table_iter_next (&iter, &key, (void**)&value);
5141 g_hash_table_remove (joinable_threads, key);
5142 UnlockedDecrement (&joinable_thread_count);
5143 found = TRUE;
5145 joinable_threads_unlock ();
5146 if (found)
5147 threads_native_thread_join_lock (key, value);
5148 else
5149 break;
5154 * mono_thread_join:
5156 * Wait for thread TID to exit.
5157 * LOCKING: Acquires the threads lock.
5159 void
5160 mono_thread_join (gpointer tid)
5162 gboolean found = FALSE;
5163 gpointer orig_key;
5164 gpointer value;
5166 joinable_threads_lock ();
5167 if (!joinable_threads)
5168 joinable_threads = g_hash_table_new (NULL, NULL);
5170 if (g_hash_table_lookup_extended (joinable_threads, tid, &orig_key, &value)) {
5171 g_hash_table_remove (joinable_threads, tid);
5172 UnlockedDecrement (&joinable_thread_count);
5173 found = TRUE;
5175 joinable_threads_unlock ();
5177 if (!found)
5178 return;
5180 threads_native_thread_join_nolock (tid, value);
5183 void
5184 mono_thread_internal_unhandled_exception (MonoObject* exc)
5186 MonoClass *klass = exc->vtable->klass;
5187 if (is_threadabort_exception (klass)) {
5188 mono_thread_internal_reset_abort (mono_thread_internal_current ());
5189 } else if (!is_appdomainunloaded_exception (klass)
5190 && mono_runtime_unhandled_exception_policy_get () == MONO_UNHANDLED_POLICY_CURRENT) {
5191 mono_unhandled_exception (exc);
5192 if (mono_environment_exitcode_get () == 1) {
5193 mono_environment_exitcode_set (255);
5194 mono_invoke_unhandled_exception_hook (exc);
5195 g_assert_not_reached ();
5200 void
5201 ves_icall_System_Threading_Thread_GetStackTraces (MonoArray **out_threads, MonoArray **out_stack_traces)
5203 MonoError error;
5204 mono_threads_get_thread_dump (out_threads, out_stack_traces, &error);
5205 mono_error_set_pending_exception (&error);
5209 * mono_threads_attach_coop: called by native->managed wrappers
5211 * - @dummy:
5212 * - blocking mode: contains gc unsafe transition cookie
5213 * - non-blocking mode: contains random data
5214 * - @return: the original domain which needs to be restored, or NULL.
5216 gpointer
5217 mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
5219 MonoDomain *orig;
5220 MonoThreadInfo *info;
5221 gboolean external;
5223 orig = mono_domain_get ();
5225 if (!domain) {
5226 /* Happens when called from AOTed code which is only used in the root domain. */
5227 domain = mono_get_root_domain ();
5228 g_assert (domain);
5231 /* On coop, when we detached, we moved the thread from RUNNING->BLOCKING.
5232 * If we try to reattach we do a BLOCKING->RUNNING transition. If the thread
5233 * is fresh, mono_thread_attach() will do a STARTING->RUNNING transition so
5234 * we're only responsible for making the cookie. */
5235 if (mono_threads_is_blocking_transition_enabled ())
5236 external = !(info = mono_thread_info_current_unchecked ()) || !mono_thread_info_is_live (info);
5238 if (!mono_thread_internal_current ()) {
5239 mono_thread_attach_full (domain, FALSE);
5241 // #678164
5242 mono_thread_set_state (mono_thread_internal_current (), ThreadState_Background);
5245 if (orig != domain)
5246 mono_domain_set (domain, TRUE);
5248 if (mono_threads_is_blocking_transition_enabled ()) {
5249 if (external) {
5250 /* mono_thread_attach put the thread in RUNNING mode from STARTING, but we need to
5251 * return the right cookie. */
5252 *dummy = mono_threads_enter_gc_unsafe_region_cookie ();
5253 } else {
5254 /* thread state (BLOCKING|RUNNING) -> RUNNING */
5255 *dummy = mono_threads_enter_gc_unsafe_region (dummy);
5259 return orig;
5263 * mono_threads_detach_coop: called by native->managed wrappers
5265 * - @cookie: the original domain which needs to be restored, or NULL.
5266 * - @dummy:
5267 * - blocking mode: contains gc unsafe transition cookie
5268 * - non-blocking mode: contains random data
5270 void
5271 mono_threads_detach_coop (gpointer cookie, gpointer *dummy)
5273 MonoDomain *domain, *orig;
5275 orig = (MonoDomain*) cookie;
5277 domain = mono_domain_get ();
5278 g_assert (domain);
5280 if (mono_threads_is_blocking_transition_enabled ()) {
5281 /* it won't do anything if cookie is NULL
5282 * thread state RUNNING -> (RUNNING|BLOCKING) */
5283 mono_threads_exit_gc_unsafe_region (*dummy, dummy);
5286 if (orig != domain) {
5287 if (!orig)
5288 mono_domain_unset ();
5289 else
5290 mono_domain_set (orig, TRUE);
5294 #if 0
5295 /* Returns TRUE if the current thread is ready to be interrupted. */
5296 gboolean
5297 mono_threads_is_ready_to_be_interrupted (void)
5299 MonoInternalThread *thread;
5301 thread = mono_thread_internal_current ();
5302 LOCK_THREAD (thread);
5303 if (thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
5304 UNLOCK_THREAD (thread);
5305 return FALSE;
5308 if (mono_thread_get_abort_prot_block_count (thread) || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ()) {
5309 UNLOCK_THREAD (thread);
5310 return FALSE;
5313 UNLOCK_THREAD (thread);
5314 return TRUE;
5316 #endif
5318 void
5319 mono_thread_internal_describe (MonoInternalThread *internal, GString *text)
5321 g_string_append_printf (text, ", thread handle : %p", internal->handle);
5323 if (internal->thread_info) {
5324 g_string_append (text, ", state : ");
5325 mono_thread_info_describe_interrupt_token ((MonoThreadInfo*) internal->thread_info, text);
5328 if (internal->owned_mutexes) {
5329 int i;
5331 g_string_append (text, ", owns : [");
5332 for (i = 0; i < internal->owned_mutexes->len; i++)
5333 g_string_append_printf (text, i == 0 ? "%p" : ", %p", g_ptr_array_index (internal->owned_mutexes, i));
5334 g_string_append (text, "]");
5338 gboolean
5339 mono_thread_internal_is_current (MonoInternalThread *internal)
5341 g_assert (internal);
5342 return mono_native_thread_id_equals (mono_native_thread_id_get (), MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid));