2 * threads.c: Thread support internal calls
5 * Dick Porter (dick@ximian.com)
6 * Paolo Molaro (lupus@ximian.com)
7 * Patrik Torstensson (patrik.torstensson@labs2.com)
9 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
10 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
11 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
12 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
20 #include <mono/metadata/object.h>
21 #include <mono/metadata/domain-internals.h>
22 #include <mono/metadata/profiler-private.h>
23 #include <mono/metadata/threads.h>
24 #include <mono/metadata/threads-types.h>
25 #include <mono/metadata/exception.h>
26 #include <mono/metadata/environment.h>
27 #include <mono/metadata/monitor.h>
28 #include <mono/metadata/gc-internals.h>
29 #include <mono/metadata/marshal.h>
30 #include <mono/metadata/runtime.h>
31 #include <mono/io-layer/io-layer.h>
32 #include <mono/metadata/object-internals.h>
33 #include <mono/metadata/mono-debug-debugger.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/hazard-pointer.h>
41 #include <mono/utils/mono-tls.h>
42 #include <mono/utils/atomic.h>
43 #include <mono/utils/mono-memory-model.h>
44 #include <mono/utils/mono-threads-coop.h>
45 #include <mono/utils/mono-error-internals.h>
47 #include <mono/metadata/gc-internals.h>
48 #include <mono/metadata/reflection-internals.h>
54 #if defined(PLATFORM_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
55 #define USE_TKILL_ON_ANDROID 1
58 #ifdef PLATFORM_ANDROID
61 #ifdef USE_TKILL_ON_ANDROID
62 extern int tkill (pid_t tid, int signal);
66 /*#define THREAD_DEBUG(a) do { a; } while (0)*/
67 #define THREAD_DEBUG(a)
68 /*#define THREAD_WAIT_DEBUG(a) do { a; } while (0)*/
69 #define THREAD_WAIT_DEBUG(a)
70 /*#define LIBGC_DEBUG(a) do { a; } while (0)*/
71 #define LIBGC_DEBUG(a)
73 #define SPIN_TRYLOCK(i) (InterlockedCompareExchange (&(i), 1, 0) == 0)
74 #define SPIN_LOCK(i) do { \
75 if (SPIN_TRYLOCK (i)) \
79 #define SPIN_UNLOCK(i) i = 0
81 #define LOCK_THREAD(thread) lock_thread((thread))
82 #define UNLOCK_THREAD(thread) unlock_thread((thread))
84 /* Provide this for systems with glib < 2.6 */
85 #ifndef G_GSIZE_FORMAT
86 # if GLIB_SIZEOF_LONG == 8
87 # define G_GSIZE_FORMAT "lu"
89 # define G_GSIZE_FORMAT "u"
95 guint32 (*func)(void *);
111 typedef struct _StaticDataFreeList StaticDataFreeList;
112 struct _StaticDataFreeList {
113 StaticDataFreeList *next;
121 StaticDataFreeList *freelist;
124 /* Number of cached culture objects in the MonoThread->cached_culture_info array
125 * (per-type): we use the first NUM entries for CultureInfo and the last for
126 * UICultureInfo. So the size of the array is really NUM_CACHED_CULTURES * 2.
128 #define NUM_CACHED_CULTURES 4
129 #define CULTURES_START_IDX 0
130 #define UICULTURES_START_IDX NUM_CACHED_CULTURES
132 /* Controls access to the 'threads' hash table */
133 static void mono_threads_lock (void);
134 static void mono_threads_unlock (void);
135 static MonoCoopMutex threads_mutex;
137 /* Controls access to the 'joinable_threads' hash table */
138 #define joinable_threads_lock() mono_os_mutex_lock (&joinable_threads_mutex)
139 #define joinable_threads_unlock() mono_os_mutex_unlock (&joinable_threads_mutex)
140 static mono_mutex_t joinable_threads_mutex;
142 /* Holds current status of static data heap */
143 static StaticDataInfo thread_static_info;
144 static StaticDataInfo context_static_info;
146 /* The hash of existing threads (key is thread ID, value is
147 * MonoInternalThread*) that need joining before exit
149 static MonoGHashTable *threads=NULL;
151 /* List of app context GC handles.
152 * Added to from ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext ().
154 static GHashTable *contexts = NULL;
156 /* Cleanup queue for contexts. */
157 static MonoReferenceQueue *context_queue;
160 * Threads which are starting up and they are not in the 'threads' hash yet.
161 * When handle_store is called for a thread, it will be removed from this hash table.
162 * Protected by mono_threads_lock ().
164 static MonoGHashTable *threads_starting_up = NULL;
166 /* The TLS key that holds the MonoObject assigned to each thread */
167 static MonoNativeTlsKey current_object_key;
170 /* Protected by the threads lock */
171 static GHashTable *joinable_threads;
172 static int joinable_thread_count;
174 #ifdef MONO_HAVE_FAST_TLS
175 /* we need to use both the Tls* functions and __thread because
176 * the gc needs to see all the threads
178 MONO_FAST_TLS_DECLARE(tls_current_object);
179 #define SET_CURRENT_OBJECT(x) do { \
180 MONO_FAST_TLS_SET (tls_current_object, x); \
181 mono_native_tls_set_value (current_object_key, x); \
183 #define GET_CURRENT_OBJECT() ((MonoInternalThread*) MONO_FAST_TLS_GET (tls_current_object))
185 #define SET_CURRENT_OBJECT(x) mono_native_tls_set_value (current_object_key, x)
186 #define GET_CURRENT_OBJECT() (MonoInternalThread*) mono_native_tls_get_value (current_object_key)
189 /* function called at thread start */
190 static MonoThreadStartCB mono_thread_start_cb = NULL;
192 /* function called at thread attach */
193 static MonoThreadAttachCB mono_thread_attach_cb = NULL;
195 /* function called at thread cleanup */
196 static MonoThreadCleanupFunc mono_thread_cleanup_fn = NULL;
198 /* The default stack size for each thread */
199 static guint32 default_stacksize = 0;
200 #define default_stacksize_for_thread(thread) ((thread)->stack_size? (thread)->stack_size: default_stacksize)
202 static void thread_adjust_static_data (MonoInternalThread *thread);
203 static void context_adjust_static_data (MonoAppContext *ctx);
204 static void mono_free_static_data (gpointer* static_data);
205 static void mono_init_static_data_info (StaticDataInfo *static_data);
206 static guint32 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align);
207 static gboolean mono_thread_resume (MonoInternalThread* thread);
208 static void async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort);
209 static void self_abort_internal (void);
210 static void async_suspend_internal (MonoInternalThread *thread, gboolean interrupt);
211 static void self_suspend_internal (void);
213 static MonoException* mono_thread_execute_interruption (void);
214 static void ref_stack_destroy (gpointer rs);
216 /* Spin lock for InterlockedXXX 64 bit functions */
217 #define mono_interlocked_lock() mono_os_mutex_lock (&interlocked_mutex)
218 #define mono_interlocked_unlock() mono_os_mutex_unlock (&interlocked_mutex)
219 static mono_mutex_t interlocked_mutex;
221 /* global count of thread interruptions requested */
222 static gint32 thread_interruption_requested = 0;
224 /* Event signaled when a thread changes its background mode */
225 static HANDLE background_change_event;
227 static gboolean shutting_down = FALSE;
229 static gint32 managed_thread_id_counter = 0;
231 /* Class lazy loading functions */
232 static GENERATE_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, System, AppDomainUnloadedException)
235 mono_threads_lock (void)
237 mono_locks_coop_acquire (&threads_mutex, ThreadsLock);
241 mono_threads_unlock (void)
243 mono_locks_coop_release (&threads_mutex, ThreadsLock);
248 get_next_managed_thread_id (void)
250 return InterlockedIncrement (&managed_thread_id_counter);
254 mono_thread_get_tls_key (void)
256 return current_object_key;
260 mono_thread_get_tls_offset (void)
265 if (current_object_key)
266 offset = current_object_key;
268 MONO_THREAD_VAR_OFFSET (tls_current_object,offset);
273 static inline MonoNativeThreadId
274 thread_get_tid (MonoInternalThread *thread)
276 /* We store the tid as a guint64 to keep the object layout constant between platforms */
277 return MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid);
280 /* handle_store() and handle_remove() manage the array of threads that
281 * still need to be waited for when the main thread exits.
283 * If handle_store() returns FALSE the thread must not be started
284 * because Mono is shutting down.
286 static gboolean handle_store(MonoThread *thread, gboolean force_attach)
288 mono_threads_lock ();
290 THREAD_DEBUG (g_message ("%s: thread %p ID %"G_GSIZE_FORMAT, __func__, thread, (gsize)thread->internal_thread->tid));
292 if (threads_starting_up)
293 mono_g_hash_table_remove (threads_starting_up, thread);
295 if (shutting_down && !force_attach) {
296 mono_threads_unlock ();
301 MONO_GC_REGISTER_ROOT_FIXED (threads, MONO_ROOT_SOURCE_THREADING, "threads table");
302 threads=mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_THREADING, "threads table");
305 /* We don't need to duplicate thread->handle, because it is
306 * only closed when the thread object is finalized by the GC.
308 g_assert (thread->internal_thread);
309 mono_g_hash_table_insert(threads, (gpointer)(gsize)(thread->internal_thread->tid),
310 thread->internal_thread);
312 mono_threads_unlock ();
317 static gboolean handle_remove(MonoInternalThread *thread)
320 gsize tid = thread->tid;
322 THREAD_DEBUG (g_message ("%s: thread ID %"G_GSIZE_FORMAT, __func__, tid));
324 mono_threads_lock ();
327 /* We have to check whether the thread object for the
328 * tid is still the same in the table because the
329 * thread might have been destroyed and the tid reused
330 * in the meantime, in which case the tid would be in
331 * the table, but with another thread object.
333 if (mono_g_hash_table_lookup (threads, (gpointer)tid) == thread) {
334 mono_g_hash_table_remove (threads, (gpointer)tid);
343 mono_threads_unlock ();
345 /* Don't close the handle here, wait for the object finalizer
346 * to do it. Otherwise, the following race condition applies:
348 * 1) Thread exits (and handle_remove() closes the handle)
350 * 2) Some other handle is reassigned the same slot
352 * 3) Another thread tries to join the first thread, and
353 * blocks waiting for the reassigned handle to be signalled
354 * (which might never happen). This is possible, because the
355 * thread calling Join() still has a reference to the first
361 static void ensure_synch_cs_set (MonoInternalThread *thread)
363 MonoCoopMutex *synch_cs;
365 if (thread->synch_cs != NULL) {
369 synch_cs = g_new0 (MonoCoopMutex, 1);
370 mono_coop_mutex_init_recursive (synch_cs);
372 if (InterlockedCompareExchangePointer ((gpointer *)&thread->synch_cs,
373 synch_cs, NULL) != NULL) {
374 /* Another thread must have installed this CS */
375 mono_coop_mutex_destroy (synch_cs);
381 lock_thread (MonoInternalThread *thread)
383 if (!thread->synch_cs)
384 ensure_synch_cs_set (thread);
386 g_assert (thread->synch_cs);
388 mono_coop_mutex_lock (thread->synch_cs);
392 unlock_thread (MonoInternalThread *thread)
394 mono_coop_mutex_unlock (thread->synch_cs);
398 * NOTE: this function can be called also for threads different from the current one:
399 * make sure no code called from it will ever assume it is run on the thread that is
400 * getting cleaned up.
402 static void thread_cleanup (MonoInternalThread *thread)
404 g_assert (thread != NULL);
406 if (thread->abort_state_handle) {
407 mono_gchandle_free (thread->abort_state_handle);
408 thread->abort_state_handle = 0;
410 thread->abort_exc = NULL;
411 thread->current_appcontext = NULL;
414 * This is necessary because otherwise we might have
415 * cross-domain references which will not get cleaned up when
416 * the target domain is unloaded.
418 if (thread->cached_culture_info) {
420 for (i = 0; i < NUM_CACHED_CULTURES * 2; ++i)
421 mono_array_set (thread->cached_culture_info, MonoObject*, i, NULL);
425 * thread->synch_cs can be NULL if this was called after
426 * ves_icall_System_Threading_InternalThread_Thread_free_internal.
427 * This can happen only during shutdown.
428 * The shutting_down flag is not always set, so we can't assert on it.
430 if (thread->synch_cs)
431 LOCK_THREAD (thread);
433 thread->state |= ThreadState_Stopped;
434 thread->state &= ~ThreadState_Background;
436 if (thread->synch_cs)
437 UNLOCK_THREAD (thread);
440 An interruption request has leaked to cleanup. Adjust the global counter.
442 This can happen is the abort source thread finds the abortee (this) thread
443 in unmanaged code. If this thread never trips back to managed code or check
444 the local flag it will be left set and positively unbalance the global counter.
446 Leaving the counter unbalanced will cause a performance degradation since all threads
447 will now keep checking their local flags all the time.
449 if (InterlockedExchange (&thread->interruption_requested, 0))
450 InterlockedDecrement (&thread_interruption_requested);
452 /* if the thread is not in the hash it has been removed already */
453 if (!handle_remove (thread)) {
454 if (thread == mono_thread_internal_current ()) {
455 mono_domain_unset ();
456 mono_memory_barrier ();
458 /* This needs to be called even if handle_remove () fails */
459 if (mono_thread_cleanup_fn)
460 mono_thread_cleanup_fn (thread_get_tid (thread));
463 mono_release_type_locks (thread);
465 /* Can happen when we attach the profiler helper thread in order to heapshot. */
466 if (!mono_thread_info_lookup (MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid))->tools_thread)
467 mono_profiler_thread_end (thread->tid);
469 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
471 if (thread == mono_thread_internal_current ()) {
473 * This will signal async signal handlers that the thread has exited.
474 * The profiler callback needs this to be set, so it cannot be done earlier.
476 mono_domain_unset ();
477 mono_memory_barrier ();
480 if (thread == mono_thread_internal_current ())
481 mono_thread_pop_appdomain_ref ();
483 thread->cached_culture_info = NULL;
485 mono_free_static_data (thread->static_data);
486 thread->static_data = NULL;
487 ref_stack_destroy (thread->appdomain_refs);
488 thread->appdomain_refs = NULL;
490 if (mono_thread_cleanup_fn)
491 mono_thread_cleanup_fn (thread_get_tid (thread));
493 if (mono_gc_is_moving ()) {
494 MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref);
495 thread->thread_pinning_ref = NULL;
501 * A special static data offset (guint32) consists of 3 parts:
503 * [0] 6-bit index into the array of chunks.
504 * [6] 25-bit offset into the array.
505 * [31] Bit indicating thread or context static.
510 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
521 } SpecialStaticOffset;
523 #define SPECIAL_STATIC_OFFSET_TYPE_THREAD 0
524 #define SPECIAL_STATIC_OFFSET_TYPE_CONTEXT 1
526 #define MAKE_SPECIAL_STATIC_OFFSET(index, offset, type) \
527 ((SpecialStaticOffset) { .fields = { (index), (offset), (type) } }.raw)
528 #define ACCESS_SPECIAL_STATIC_OFFSET(x,f) \
529 (((SpecialStaticOffset *) &(x))->fields.f)
532 get_thread_static_data (MonoInternalThread *thread, guint32 offset)
534 g_assert (ACCESS_SPECIAL_STATIC_OFFSET (offset, type) == SPECIAL_STATIC_OFFSET_TYPE_THREAD);
536 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
537 int off = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
539 return ((char *) thread->static_data [idx]) + off;
543 get_context_static_data (MonoAppContext *ctx, guint32 offset)
545 g_assert (ACCESS_SPECIAL_STATIC_OFFSET (offset, type) == SPECIAL_STATIC_OFFSET_TYPE_CONTEXT);
547 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
548 int off = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
550 return ((char *) ctx->static_data [idx]) + off;
554 get_current_thread_ptr_for_domain (MonoDomain *domain, MonoInternalThread *thread)
556 static MonoClassField *current_thread_field = NULL;
560 if (!current_thread_field) {
561 current_thread_field = mono_class_get_field_from_name (mono_defaults.thread_class, "current_thread");
562 g_assert (current_thread_field);
565 mono_class_vtable (domain, mono_defaults.thread_class);
566 mono_domain_lock (domain);
567 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, current_thread_field));
568 mono_domain_unlock (domain);
571 return (MonoThread **)get_thread_static_data (thread, offset);
575 set_current_thread_for_domain (MonoDomain *domain, MonoInternalThread *thread, MonoThread *current)
577 MonoThread **current_thread_ptr = get_current_thread_ptr_for_domain (domain, thread);
579 g_assert (current->obj.vtable->domain == domain);
581 g_assert (!*current_thread_ptr);
582 *current_thread_ptr = current;
586 create_thread_object (MonoDomain *domain, MonoError *error)
588 MonoVTable *vt = mono_class_vtable (domain, mono_defaults.thread_class);
589 MonoThread *t = (MonoThread*)mono_object_new_mature (vt, error);
594 new_thread_with_internal (MonoDomain *domain, MonoInternalThread *internal, MonoError *error)
598 thread = create_thread_object (domain, error);
599 if (!mono_error_ok (error))
602 MONO_OBJECT_SETREF (thread, internal_thread, internal);
607 static MonoInternalThread*
608 create_internal_thread (MonoError *error)
610 MonoInternalThread *thread;
613 vt = mono_class_vtable (mono_get_root_domain (), mono_defaults.internal_thread_class);
614 thread = (MonoInternalThread*) mono_object_new_mature (vt, error);
615 if (!mono_error_ok (error))
618 thread->synch_cs = g_new0 (MonoCoopMutex, 1);
619 mono_coop_mutex_init_recursive (thread->synch_cs);
621 thread->apartment_state = ThreadApartmentState_Unknown;
622 thread->managed_id = get_next_managed_thread_id ();
623 if (mono_gc_is_moving ()) {
624 thread->thread_pinning_ref = thread;
625 MONO_GC_REGISTER_ROOT_PINNING (thread->thread_pinning_ref, MONO_ROOT_SOURCE_THREADING, "thread pinning reference");
632 init_root_domain_thread (MonoInternalThread *thread, MonoThread *candidate, MonoError *error)
634 MonoDomain *domain = mono_get_root_domain ();
636 mono_error_init (error);
637 if (!candidate || candidate->obj.vtable->domain != domain) {
638 candidate = new_thread_with_internal (domain, thread, error);
639 return_val_if_nok (error, FALSE);
641 set_current_thread_for_domain (domain, thread, candidate);
642 g_assert (!thread->root_domain_thread);
643 MONO_OBJECT_SETREF (thread, root_domain_thread, candidate);
647 static guint32 WINAPI start_wrapper_internal(void *data)
650 MonoThreadInfo *info;
651 StartInfo *start_info = (StartInfo *)data;
652 guint32 (*start_func)(void *);
656 * We don't create a local to hold start_info->obj, so hopefully it won't get pinned during a
659 MonoInternalThread *internal = start_info->obj->internal_thread;
660 MonoObject *start_delegate = start_info->delegate;
661 MonoDomain *domain = start_info->obj->obj.vtable->domain;
663 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper", __func__, mono_native_thread_id_get ()));
665 /* We can be sure start_info->obj->tid and
666 * start_info->obj->handle have been set, because the thread
667 * was created suspended, and these values were set before the
671 info = mono_thread_info_current ();
673 internal->thread_info = info;
674 internal->small_id = info->small_id;
678 SET_CURRENT_OBJECT (internal);
680 /* Every thread references the appdomain which created it */
681 mono_thread_push_appdomain_ref (domain);
683 if (!mono_domain_set (domain, FALSE)) {
684 /* No point in raising an appdomain_unloaded exception here */
685 /* FIXME: Cleanup here */
686 mono_thread_pop_appdomain_ref ();
690 start_func = start_info->func;
691 start_arg = start_info->obj->start_obj;
693 start_arg = start_info->start_arg;
695 /* We have to do this here because mono_thread_new_init()
696 requires that root_domain_thread is set up. */
697 thread_adjust_static_data (internal);
698 init_root_domain_thread (internal, start_info->obj, &error);
699 mono_error_raise_exception (&error); /* FIXME don't raise here */
701 /* This MUST be called before any managed code can be
702 * executed, as it calls the callback function that (for the
703 * jit) sets the lmf marker.
705 mono_thread_new_init (tid, &tid, start_func);
706 internal->stack_ptr = &tid;
707 if (domain != mono_get_root_domain ())
708 set_current_thread_for_domain (domain, internal, start_info->obj);
710 LIBGC_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT",%d) Setting thread stack to %p", __func__, mono_native_thread_id_get (), getpid (), thread->stack_ptr));
712 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, mono_native_thread_id_get (), internal));
714 /* On 2.0 profile (and higher), set explicitly since state might have been
716 if (internal->apartment_state == ThreadApartmentState_Unknown)
717 internal->apartment_state = ThreadApartmentState_MTA;
719 mono_thread_init_apartment_state ();
721 if(internal->start_notify!=NULL) {
722 /* Let the thread that called Start() know we're
725 ReleaseSemaphore (internal->start_notify, 1, NULL);
729 THREAD_DEBUG (g_message ("%s: start_wrapper for %"G_GSIZE_FORMAT, __func__,
733 * Call this after calling start_notify, since the profiler callback might want
734 * to lock the thread, and the lock is held by thread_start () which waits for
737 mono_profiler_thread_start (tid);
739 /* if the name was set before starting, we didn't invoke the profiler callback */
740 if (internal->name) {
741 char *tname = g_utf16_to_utf8 (internal->name, internal->name_len, NULL, NULL, NULL);
742 mono_profiler_thread_name (internal->tid, tname);
743 mono_native_thread_set_name (MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid), tname);
747 /* start_func is set only for unmanaged start functions */
749 start_func (start_arg);
752 g_assert (start_delegate != NULL);
753 args [0] = start_arg;
754 /* we may want to handle the exception here. See comment below on unhandled exceptions */
755 mono_runtime_delegate_invoke_checked (start_delegate, args, &error);
756 mono_error_raise_exception (&error); /* FIXME don't raise here */
759 /* If the thread calls ExitThread at all, this remaining code
760 * will not be executed, but the main thread will eventually
761 * call thread_cleanup() on this thread's behalf.
764 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper terminating", __func__, mono_native_thread_id_get ()));
766 /* Do any cleanup needed for apartment state. This
767 * cannot be done in thread_cleanup since thread_cleanup could be
768 * called for a thread other than the current thread.
769 * mono_thread_cleanup_apartment_state cleans up apartment
770 * for the current thead */
771 mono_thread_cleanup_apartment_state ();
773 thread_cleanup (internal);
777 /* Remove the reference to the thread object in the TLS data,
778 * so the thread object can be finalized. This won't be
779 * reached if the thread threw an uncaught exception, so those
780 * thread handles will stay referenced :-( (This is due to
781 * missing support for scanning thread-specific data in the
782 * Boehm GC - the io-layer keeps a GC-visible hash of pointers
785 SET_CURRENT_OBJECT (NULL);
790 static guint32 WINAPI start_wrapper(void *data)
794 /* Avoid scanning the frames above this frame during a GC */
795 mono_gc_set_stack_end ((void*)&dummy);
797 return start_wrapper_internal (data);
803 * Common thread creation code.
804 * LOCKING: Acquires the threads lock.
807 create_thread (MonoThread *thread, MonoInternalThread *internal, StartInfo *start_info, gboolean threadpool_thread, guint32 stack_size,
810 HANDLE thread_handle;
811 MonoNativeThreadId tid;
812 guint32 create_flags;
815 * Join joinable threads to prevent running out of threads since the finalizer
816 * thread might be blocked/backlogged.
818 mono_threads_join_threads ();
820 mono_error_init (error);
822 mono_threads_lock ();
825 mono_threads_unlock ();
828 if (threads_starting_up == NULL) {
829 MONO_GC_REGISTER_ROOT_FIXED (threads_starting_up, MONO_ROOT_SOURCE_THREADING, "starting threads table");
830 threads_starting_up = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_VALUE_GC, MONO_ROOT_SOURCE_THREADING, "starting threads table");
832 mono_g_hash_table_insert (threads_starting_up, thread, thread);
833 mono_threads_unlock ();
835 internal->start_notify = CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
836 if (!internal->start_notify) {
837 mono_threads_lock ();
838 mono_g_hash_table_remove (threads_starting_up, thread);
839 mono_threads_unlock ();
840 g_warning ("%s: CreateSemaphore error 0x%x", __func__, GetLastError ());
846 stack_size = default_stacksize_for_thread (internal);
848 /* Create suspended, so we can do some housekeeping before the thread
851 create_flags = CREATE_SUSPENDED;
853 thread_handle = mono_threads_create_thread ((LPTHREAD_START_ROUTINE)start_wrapper, start_info,
854 stack_size, create_flags, &tid);
856 if (thread_handle == NULL) {
857 /* The thread couldn't be created, so set an exception */
858 mono_threads_lock ();
859 mono_g_hash_table_remove (threads_starting_up, thread);
860 mono_threads_unlock ();
862 mono_error_set_execution_engine (error, "Couldn't create thread. Error 0x%x", GetLastError());
865 THREAD_DEBUG (g_message ("%s: Started thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread_handle));
867 internal->handle = thread_handle;
868 internal->tid = MONO_NATIVE_THREAD_ID_TO_UINT (tid);
870 internal->threadpool_thread = threadpool_thread;
871 if (threadpool_thread)
872 mono_thread_set_state (internal, ThreadState_Background);
874 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Launching thread %p (%"G_GSIZE_FORMAT")", __func__, mono_native_thread_id_get (), internal, (gsize)internal->tid));
876 /* Only store the handle when the thread is about to be
877 * launched, to avoid the main thread deadlocking while trying
878 * to clean up a thread that will never be signalled.
880 if (!handle_store (thread, FALSE))
883 mono_thread_info_resume (tid);
885 if (internal->start_notify) {
887 * Wait for the thread to set up its TLS data etc, so
888 * theres no potential race condition if someone tries
889 * to look up the data believing the thread has
892 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") waiting for thread %p (%"G_GSIZE_FORMAT") to start", __func__, mono_native_thread_id_get (), internal, (gsize)internal->tid));
895 WaitForSingleObjectEx (internal->start_notify, INFINITE, FALSE);
898 CloseHandle (internal->start_notify);
899 internal->start_notify = NULL;
902 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));
907 void mono_thread_new_init (intptr_t tid, gpointer stack_start, gpointer func)
909 if (mono_thread_start_cb) {
910 mono_thread_start_cb (tid, stack_start, func);
914 void mono_threads_set_default_stacksize (guint32 stacksize)
916 default_stacksize = stacksize;
919 guint32 mono_threads_get_default_stacksize (void)
921 return default_stacksize;
925 * mono_thread_create_internal:
927 * ARG should not be a GC reference.
930 mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gboolean threadpool_thread, guint32 stack_size, MonoError *error)
933 MonoInternalThread *internal;
934 StartInfo *start_info;
937 mono_error_init (error);
939 thread = create_thread_object (domain, error);
940 return_val_if_nok (error, NULL);
942 internal = create_internal_thread (error);
943 return_val_if_nok (error, NULL);
945 MONO_OBJECT_SETREF (thread, internal_thread, internal);
947 start_info = g_new0 (StartInfo, 1);
948 start_info->func = (guint32 (*)(void *))func;
949 start_info->obj = thread;
950 start_info->start_arg = arg;
952 res = create_thread (thread, internal, start_info, threadpool_thread, stack_size, error);
953 return_val_if_nok (error, NULL);
955 /* Check that the managed and unmanaged layout of MonoInternalThread matches */
956 #ifndef MONO_CROSS_COMPILE
957 if (mono_check_corlib_version () == NULL)
958 g_assert (((char*)&internal->unused2 - (char*)internal) == mono_defaults.internal_thread_class->fields [mono_defaults.internal_thread_class->field.count - 1].offset);
965 mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
968 if (!mono_thread_create_checked (domain, func, arg, &error))
969 mono_error_cleanup (&error);
973 mono_thread_create_checked (MonoDomain *domain, gpointer func, gpointer arg, MonoError *error)
975 return (NULL != mono_thread_create_internal (domain, func, arg, FALSE, 0, error));
979 mono_thread_attach (MonoDomain *domain)
982 MonoThread *thread = mono_thread_attach_full (domain, FALSE, &error);
983 mono_error_raise_exception (&error);
989 mono_thread_attach_full (MonoDomain *domain, gboolean force_attach, MonoError *error)
991 MonoThreadInfo *info;
992 MonoInternalThread *thread;
993 MonoThread *current_thread;
994 HANDLE thread_handle;
995 MonoNativeThreadId tid;
997 mono_error_init (error);
999 if ((thread = mono_thread_internal_current ())) {
1000 if (domain != mono_domain_get ())
1001 mono_domain_set (domain, TRUE);
1002 /* Already attached */
1003 return mono_thread_current ();
1006 if (!mono_gc_register_thread (&domain)) {
1007 g_error ("Thread %"G_GSIZE_FORMAT" calling into managed code is not registered with the GC. On UNIX, this can be fixed by #include-ing <gc.h> before <pthread.h> in the file containing the thread creation code.", mono_native_thread_id_get ());
1010 thread = create_internal_thread (error);
1011 if (!mono_error_ok (error))
1014 thread_handle = mono_thread_info_open_handle ();
1015 g_assert (thread_handle);
1017 tid=mono_native_thread_id_get ();
1019 thread->handle = thread_handle;
1020 thread->tid = MONO_NATIVE_THREAD_ID_TO_UINT (tid);
1021 thread->stack_ptr = &tid;
1023 THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread_handle));
1025 info = mono_thread_info_current ();
1027 thread->thread_info = info;
1028 thread->small_id = info->small_id;
1030 current_thread = new_thread_with_internal (domain, thread, error);
1031 if (!mono_error_ok (error))
1034 if (!handle_store (current_thread, force_attach)) {
1035 /* Mono is shutting down, so just wait for the end */
1037 mono_thread_info_sleep (10000, NULL);
1040 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, mono_native_thread_id_get (), thread));
1042 SET_CURRENT_OBJECT (thread);
1043 mono_domain_set (domain, TRUE);
1045 thread_adjust_static_data (thread);
1047 init_root_domain_thread (thread, current_thread, error);
1048 return_val_if_nok (error, NULL);
1050 if (domain != mono_get_root_domain ())
1051 set_current_thread_for_domain (domain, thread, current_thread);
1054 if (mono_thread_attach_cb) {
1058 mono_thread_info_get_stack_bounds (&staddr, &stsize);
1061 mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), &tid);
1063 mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), staddr + stsize);
1066 /* Can happen when we attach the profiler helper thread in order to heapshot. */
1067 if (!info->tools_thread)
1068 // FIXME: Need a separate callback
1069 mono_profiler_thread_start (MONO_NATIVE_THREAD_ID_TO_UINT (tid));
1071 return current_thread;
1075 mono_thread_detach_internal (MonoInternalThread *thread)
1077 g_return_if_fail (thread != NULL);
1079 THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
1081 thread_cleanup (thread);
1083 SET_CURRENT_OBJECT (NULL);
1084 mono_domain_unset ();
1086 /* Don't need to CloseHandle this thread, even though we took a
1087 * reference in mono_thread_attach (), because the GC will do it
1088 * when the Thread object is finalised.
1093 mono_thread_detach (MonoThread *thread)
1096 mono_thread_detach_internal (thread->internal_thread);
1100 * mono_thread_detach_if_exiting:
1102 * Detach the current thread from the runtime if it is exiting, i.e. it is running pthread dtors.
1103 * This should be used at the end of embedding code which calls into managed code, and which
1104 * can be called from pthread dtors, like dealloc: implementations in objective-c.
1107 mono_thread_detach_if_exiting (void)
1109 if (mono_thread_info_is_exiting ()) {
1110 MonoInternalThread *thread;
1112 thread = mono_thread_internal_current ();
1114 mono_thread_detach_internal (thread);
1115 mono_thread_info_detach ();
1123 MonoInternalThread *thread = mono_thread_internal_current ();
1125 THREAD_DEBUG (g_message ("%s: mono_thread_exit for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
1127 thread_cleanup (thread);
1128 SET_CURRENT_OBJECT (NULL);
1129 mono_domain_unset ();
1131 /* we could add a callback here for embedders to use. */
1132 if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread))
1133 exit (mono_environment_exitcode_get ());
1134 mono_thread_info_exit ();
1138 ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this_obj)
1141 MonoInternalThread *internal;
1143 internal = create_internal_thread (&error);
1144 if (mono_error_set_pending_exception (&error))
1147 internal->state = ThreadState_Unstarted;
1149 InterlockedCompareExchangePointer ((volatile gpointer *)&this_obj->internal_thread, internal, NULL);
1153 ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this_obj,
1157 StartInfo *start_info;
1158 MonoInternalThread *internal;
1161 THREAD_DEBUG (g_message("%s: Trying to start a new thread: this (%p) start (%p)", __func__, this_obj, start));
1163 if (!this_obj->internal_thread)
1164 ves_icall_System_Threading_Thread_ConstructInternalThread (this_obj);
1165 internal = this_obj->internal_thread;
1167 LOCK_THREAD (internal);
1169 if ((internal->state & ThreadState_Unstarted) == 0) {
1170 UNLOCK_THREAD (internal);
1171 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has already been started."));
1175 if ((internal->state & ThreadState_Aborted) != 0) {
1176 UNLOCK_THREAD (internal);
1179 /* This is freed in start_wrapper */
1180 start_info = g_new0 (StartInfo, 1);
1181 start_info->func = NULL;
1182 start_info->start_arg = NULL;
1183 start_info->delegate = start;
1184 start_info->obj = this_obj;
1185 g_assert (this_obj->obj.vtable->domain == mono_domain_get ());
1187 res = create_thread (this_obj, internal, start_info, FALSE, 0, &error);
1189 mono_error_cleanup (&error);
1190 UNLOCK_THREAD (internal);
1194 internal->state &= ~ThreadState_Unstarted;
1196 THREAD_DEBUG (g_message ("%s: Started thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread));
1198 UNLOCK_THREAD (internal);
1199 return internal->handle;
1203 * This is called from the finalizer of the internal thread object.
1206 ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThread *this_obj, HANDLE thread)
1208 THREAD_DEBUG (g_message ("%s: Closing thread %p, handle %p", __func__, this, thread));
1211 * Since threads keep a reference to their thread object while running, by the time this function is called,
1212 * the thread has already exited/detached, i.e. thread_cleanup () has ran. The exception is during shutdown,
1213 * when thread_cleanup () can be called after this.
1216 CloseHandle (thread);
1218 if (this_obj->synch_cs) {
1219 MonoCoopMutex *synch_cs = this_obj->synch_cs;
1220 this_obj->synch_cs = NULL;
1221 mono_coop_mutex_destroy (synch_cs);
1225 if (this_obj->name) {
1226 void *name = this_obj->name;
1227 this_obj->name = NULL;
1233 ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
1236 MonoInternalThread *thread = mono_thread_internal_current ();
1238 THREAD_DEBUG (g_message ("%s: Sleeping for %d ms", __func__, ms));
1240 mono_thread_current_check_pending_interrupt ();
1243 gboolean alerted = FALSE;
1245 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1247 res = mono_thread_info_sleep (ms, &alerted);
1249 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1252 MonoException* exc = mono_thread_execute_interruption ();
1254 mono_raise_exception (exc);
1266 void ves_icall_System_Threading_Thread_SpinWait_nop (void)
1271 ves_icall_System_Threading_Thread_GetDomainID (void)
1273 return mono_domain_get()->domain_id;
1277 ves_icall_System_Threading_Thread_Yield (void)
1279 return mono_thread_info_yield ();
1283 * mono_thread_get_name:
1285 * Return the name of the thread. NAME_LEN is set to the length of the name.
1286 * Return NULL if the thread has no name. The returned memory is owned by the
1290 mono_thread_get_name (MonoInternalThread *this_obj, guint32 *name_len)
1294 LOCK_THREAD (this_obj);
1296 if (!this_obj->name) {
1300 *name_len = this_obj->name_len;
1301 res = g_new (gunichar2, this_obj->name_len);
1302 memcpy (res, this_obj->name, sizeof (gunichar2) * this_obj->name_len);
1305 UNLOCK_THREAD (this_obj);
1311 * mono_thread_get_name_utf8:
1313 * Return the name of the thread in UTF-8.
1314 * Return NULL if the thread has no name.
1315 * The returned memory is owned by the caller.
1318 mono_thread_get_name_utf8 (MonoThread *thread)
1323 MonoInternalThread *internal = thread->internal_thread;
1324 if (internal == NULL)
1327 LOCK_THREAD (internal);
1329 char *tname = g_utf16_to_utf8 (internal->name, internal->name_len, NULL, NULL, NULL);
1331 UNLOCK_THREAD (internal);
1337 * mono_thread_get_managed_id:
1339 * Return the Thread.ManagedThreadId value of `thread`.
1340 * Returns -1 if `thread` is NULL.
1343 mono_thread_get_managed_id (MonoThread *thread)
1348 MonoInternalThread *internal = thread->internal_thread;
1349 if (internal == NULL)
1352 int32_t id = internal->managed_id;
1358 ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThread *this_obj)
1363 mono_error_init (&error);
1365 LOCK_THREAD (this_obj);
1367 if (!this_obj->name)
1370 str = mono_string_new_utf16_checked (mono_domain_get (), this_obj->name, this_obj->name_len, &error);
1372 UNLOCK_THREAD (this_obj);
1374 if (mono_error_set_pending_exception (&error))
1381 mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, MonoError *error)
1383 LOCK_THREAD (this_obj);
1385 mono_error_init (error);
1387 if ((this_obj->flags & MONO_THREAD_FLAG_NAME_SET)) {
1388 UNLOCK_THREAD (this_obj);
1390 mono_error_set_invalid_operation (error, "Thread.Name can only be set once.");
1393 if (this_obj->name) {
1394 g_free (this_obj->name);
1395 this_obj->name_len = 0;
1398 this_obj->name = g_new (gunichar2, mono_string_length (name));
1399 memcpy (this_obj->name, mono_string_chars (name), mono_string_length (name) * 2);
1400 this_obj->name_len = mono_string_length (name);
1403 this_obj->flags |= MONO_THREAD_FLAG_NAME_SET;
1406 this_obj->name = NULL;
1409 UNLOCK_THREAD (this_obj);
1411 if (this_obj->name && this_obj->tid) {
1412 char *tname = mono_string_to_utf8 (name);
1413 mono_profiler_thread_name (this_obj->tid, tname);
1414 mono_native_thread_set_name (thread_get_tid (this_obj), tname);
1420 ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj, MonoString *name)
1423 mono_thread_set_name_internal (this_obj, name, TRUE, &error);
1424 mono_error_set_pending_exception (&error);
1428 * ves_icall_System_Threading_Thread_GetPriority_internal:
1429 * @param this_obj: The MonoInternalThread on which to operate.
1431 * Gets the priority of the given thread.
1432 * @return: The priority of the given thread.
1435 ves_icall_System_Threading_Thread_GetPriority (MonoThread *this_obj)
1438 MonoInternalThread *internal = this_obj->internal_thread;
1440 LOCK_THREAD (internal);
1441 priority = GetThreadPriority (internal->handle) + 2;
1442 UNLOCK_THREAD (internal);
1447 * ves_icall_System_Threading_Thread_SetPriority_internal:
1448 * @param this_obj: The MonoInternalThread on which to operate.
1449 * @param priority: The priority to set.
1451 * Sets the priority of the given thread.
1454 ves_icall_System_Threading_Thread_SetPriority (MonoThread *this_obj, int priority)
1456 MonoInternalThread *internal = this_obj->internal_thread;
1458 LOCK_THREAD (internal);
1459 SetThreadPriority (internal->handle, priority - 2);
1460 UNLOCK_THREAD (internal);
1463 /* If the array is already in the requested domain, we just return it,
1464 otherwise we return a copy in that domain. */
1466 byte_array_to_domain (MonoArray *arr, MonoDomain *domain, MonoError *error)
1470 mono_error_init (error);
1474 if (mono_object_domain (arr) == domain)
1477 copy = mono_array_new_checked (domain, mono_defaults.byte_class, arr->max_length, error);
1478 memmove (mono_array_addr (copy, guint8, 0), mono_array_addr (arr, guint8, 0), arr->max_length);
1483 ves_icall_System_Threading_Thread_ByteArrayToRootDomain (MonoArray *arr)
1486 MonoArray *result = byte_array_to_domain (arr, mono_get_root_domain (), &error);
1487 mono_error_set_pending_exception (&error);
1492 ves_icall_System_Threading_Thread_ByteArrayToCurrentDomain (MonoArray *arr)
1495 MonoArray *result = byte_array_to_domain (arr, mono_domain_get (), &error);
1496 mono_error_set_pending_exception (&error);
1501 mono_thread_current (void)
1504 MonoDomain *domain = mono_domain_get ();
1505 MonoInternalThread *internal = mono_thread_internal_current ();
1506 MonoThread **current_thread_ptr;
1508 g_assert (internal);
1509 current_thread_ptr = get_current_thread_ptr_for_domain (domain, internal);
1511 if (!*current_thread_ptr) {
1512 g_assert (domain != mono_get_root_domain ());
1513 *current_thread_ptr = new_thread_with_internal (domain, internal, &error);
1514 mono_error_raise_exception (&error); /* FIXME don't raise here */
1516 return *current_thread_ptr;
1519 /* Return the thread object belonging to INTERNAL in the current domain */
1521 mono_thread_current_for_thread (MonoInternalThread *internal)
1524 MonoDomain *domain = mono_domain_get ();
1525 MonoThread **current_thread_ptr;
1527 g_assert (internal);
1528 current_thread_ptr = get_current_thread_ptr_for_domain (domain, internal);
1530 if (!*current_thread_ptr) {
1531 g_assert (domain != mono_get_root_domain ());
1532 *current_thread_ptr = new_thread_with_internal (domain, internal, &error);
1533 mono_error_raise_exception (&error); /* FIXME don't raise here */
1535 return *current_thread_ptr;
1539 mono_thread_internal_current (void)
1541 MonoInternalThread *res = GET_CURRENT_OBJECT ();
1542 THREAD_DEBUG (g_message ("%s: returning %p", __func__, res));
1547 ves_icall_System_Threading_Thread_Join_internal(MonoThread *this_obj, int ms)
1549 MonoInternalThread *thread = this_obj->internal_thread;
1550 HANDLE handle = thread->handle;
1551 MonoInternalThread *cur_thread = mono_thread_internal_current ();
1554 mono_thread_current_check_pending_interrupt ();
1556 LOCK_THREAD (thread);
1558 if ((thread->state & ThreadState_Unstarted) != 0) {
1559 UNLOCK_THREAD (thread);
1561 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started."));
1565 UNLOCK_THREAD (thread);
1570 THREAD_DEBUG (g_message ("%s: joining thread handle %p, %d ms", __func__, handle, ms));
1572 mono_thread_set_state (cur_thread, ThreadState_WaitSleepJoin);
1575 ret=WaitForSingleObjectEx (handle, ms, TRUE);
1578 mono_thread_clr_state (cur_thread, ThreadState_WaitSleepJoin);
1580 if(ret==WAIT_OBJECT_0) {
1581 THREAD_DEBUG (g_message ("%s: join successful", __func__));
1586 THREAD_DEBUG (g_message ("%s: join failed", __func__));
1592 mono_wait_uninterrupted (MonoInternalThread *thread, gboolean multiple, guint32 numhandles, gpointer *handles, gboolean waitall, gint32 ms, gboolean alertable)
1600 start = (ms == -1) ? 0 : mono_100ns_ticks ();
1604 ret = WaitForMultipleObjectsEx (numhandles, handles, waitall, wait, alertable);
1606 ret = WaitForSingleObjectEx (handles [0], ms, alertable);
1609 if (ret != WAIT_IO_COMPLETION)
1612 exc = mono_thread_execute_interruption ();
1614 mono_raise_exception (exc);
1619 /* Re-calculate ms according to the time passed */
1620 diff_ms = (gint32)((mono_100ns_ticks () - start) / 10000);
1621 if (diff_ms >= ms) {
1625 wait = ms - diff_ms;
1631 gint32 ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms)
1637 MonoObject *waitHandle;
1638 MonoInternalThread *thread = mono_thread_internal_current ();
1640 /* Do this WaitSleepJoin check before creating objects */
1641 mono_thread_current_check_pending_interrupt ();
1643 /* We fail in managed if the array has more than 64 elements */
1644 numhandles = (guint32)mono_array_length(mono_handles);
1645 handles = g_new0(HANDLE, numhandles);
1647 for(i = 0; i < numhandles; i++) {
1648 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
1649 handles [i] = mono_wait_handle_get_handle ((MonoWaitHandle *) waitHandle);
1656 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1658 ret = mono_wait_uninterrupted (thread, TRUE, numhandles, handles, TRUE, ms, TRUE);
1660 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1664 /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
1665 return ret == WAIT_FAILED ? 0x7fffffff : ret;
1668 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms)
1670 HANDLE handles [MAXIMUM_WAIT_OBJECTS];
1671 uintptr_t numhandles;
1674 MonoObject *waitHandle;
1675 MonoInternalThread *thread = mono_thread_internal_current ();
1677 /* Do this WaitSleepJoin check before creating objects */
1678 mono_thread_current_check_pending_interrupt ();
1680 numhandles = mono_array_length(mono_handles);
1681 if (numhandles > MAXIMUM_WAIT_OBJECTS)
1684 for(i = 0; i < numhandles; i++) {
1685 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
1686 handles [i] = mono_wait_handle_get_handle ((MonoWaitHandle *) waitHandle);
1693 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1695 ret = mono_wait_uninterrupted (thread, TRUE, numhandles, handles, FALSE, ms, TRUE);
1697 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1699 THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") returning %d", __func__, mono_native_thread_id_get (), ret));
1702 * These need to be here. See MSDN dos on WaitForMultipleObjects.
1704 if (ret >= WAIT_OBJECT_0 && ret <= WAIT_OBJECT_0 + numhandles - 1) {
1705 return ret - WAIT_OBJECT_0;
1707 else if (ret >= WAIT_ABANDONED_0 && ret <= WAIT_ABANDONED_0 + numhandles - 1) {
1708 return ret - WAIT_ABANDONED_0;
1711 /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
1712 return ret == WAIT_FAILED ? 0x7fffffff : ret;
1716 gint32 ves_icall_System_Threading_WaitHandle_WaitOne_internal(HANDLE handle, gint32 ms)
1719 MonoInternalThread *thread = mono_thread_internal_current ();
1721 THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") waiting for %p, %d ms", __func__, mono_native_thread_id_get (), handle, ms));
1727 mono_thread_current_check_pending_interrupt ();
1729 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1731 ret = mono_wait_uninterrupted (thread, FALSE, 1, &handle, FALSE, ms, TRUE);
1733 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1735 /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
1736 return ret == WAIT_FAILED ? 0x7fffffff : ret;
1740 ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (HANDLE toSignal, HANDLE toWait, gint32 ms)
1743 MonoInternalThread *thread = mono_thread_internal_current ();
1748 mono_thread_current_check_pending_interrupt ();
1750 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1753 ret = SignalObjectAndWait (toSignal, toWait, ms, TRUE);
1756 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1758 /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
1759 return ret == WAIT_FAILED ? 0x7fffffff : ret;
1762 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created)
1769 mutex = CreateMutex (NULL, owned, NULL);
1771 mutex = CreateMutex (NULL, owned, mono_string_chars (name));
1773 if (GetLastError () == ERROR_ALREADY_EXISTS) {
1781 MonoBoolean ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) {
1782 return(ReleaseMutex (handle));
1785 HANDLE ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name,
1791 *error = ERROR_SUCCESS;
1793 ret = OpenMutex (rights, FALSE, mono_string_chars (name));
1795 *error = GetLastError ();
1802 HANDLE ves_icall_System_Threading_Semaphore_CreateSemaphore_internal (gint32 initialCount, gint32 maximumCount, MonoString *name, MonoBoolean *created)
1809 sem = CreateSemaphore (NULL, initialCount, maximumCount, NULL);
1811 sem = CreateSemaphore (NULL, initialCount, maximumCount,
1812 mono_string_chars (name));
1814 if (GetLastError () == ERROR_ALREADY_EXISTS) {
1822 gint32 ves_icall_System_Threading_Semaphore_ReleaseSemaphore_internal (HANDLE handle, gint32 releaseCount, MonoBoolean *fail)
1826 *fail = !ReleaseSemaphore (handle, releaseCount, &prevcount);
1831 HANDLE ves_icall_System_Threading_Semaphore_OpenSemaphore_internal (MonoString *name, gint32 rights, gint32 *error)
1835 *error = ERROR_SUCCESS;
1837 ret = OpenSemaphore (rights, FALSE, mono_string_chars (name));
1839 *error = GetLastError ();
1845 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual, MonoBoolean initial, MonoString *name, MonoBoolean *created)
1852 event = CreateEvent (NULL, manual, initial, NULL);
1854 event = CreateEvent (NULL, manual, initial,
1855 mono_string_chars (name));
1857 if (GetLastError () == ERROR_ALREADY_EXISTS) {
1865 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
1866 return (SetEvent(handle));
1869 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
1870 return (ResetEvent(handle));
1874 ves_icall_System_Threading_Events_CloseEvent_internal (HANDLE handle) {
1875 CloseHandle (handle);
1878 HANDLE ves_icall_System_Threading_Events_OpenEvent_internal (MonoString *name,
1884 *error = ERROR_SUCCESS;
1886 ret = OpenEvent (rights, FALSE, mono_string_chars (name));
1888 *error = GetLastError ();
1894 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
1896 return InterlockedIncrement (location);
1899 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
1901 #if SIZEOF_VOID_P == 4
1902 if (G_UNLIKELY ((size_t)location & 0x7)) {
1904 mono_interlocked_lock ();
1907 mono_interlocked_unlock ();
1911 return InterlockedIncrement64 (location);
1914 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
1916 return InterlockedDecrement(location);
1919 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
1921 #if SIZEOF_VOID_P == 4
1922 if (G_UNLIKELY ((size_t)location & 0x7)) {
1924 mono_interlocked_lock ();
1927 mono_interlocked_unlock ();
1931 return InterlockedDecrement64 (location);
1934 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location, gint32 value)
1936 return InterlockedExchange(location, value);
1939 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location, MonoObject *value)
1942 res = (MonoObject *) InterlockedExchangePointer((gpointer *) location, value);
1943 mono_gc_wbarrier_generic_nostore (location);
1947 gpointer ves_icall_System_Threading_Interlocked_Exchange_IntPtr (gpointer *location, gpointer value)
1949 return InterlockedExchangePointer(location, value);
1952 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location, gfloat value)
1954 IntFloatUnion val, ret;
1957 ret.ival = InterlockedExchange((gint32 *) location, val.ival);
1963 ves_icall_System_Threading_Interlocked_Exchange_Long (gint64 *location, gint64 value)
1965 #if SIZEOF_VOID_P == 4
1966 if (G_UNLIKELY ((size_t)location & 0x7)) {
1968 mono_interlocked_lock ();
1971 mono_interlocked_unlock ();
1975 return InterlockedExchange64 (location, value);
1979 ves_icall_System_Threading_Interlocked_Exchange_Double (gdouble *location, gdouble value)
1981 LongDoubleUnion val, ret;
1984 ret.ival = (gint64)InterlockedExchange64((gint64 *) location, val.ival);
1989 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location, gint32 value, gint32 comparand)
1991 return InterlockedCompareExchange(location, value, comparand);
1994 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int_Success(gint32 *location, gint32 value, gint32 comparand, MonoBoolean *success)
1996 gint32 r = InterlockedCompareExchange(location, value, comparand);
1997 *success = r == comparand;
2001 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location, MonoObject *value, MonoObject *comparand)
2004 res = (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location, value, comparand);
2005 mono_gc_wbarrier_generic_nostore (location);
2009 gpointer ves_icall_System_Threading_Interlocked_CompareExchange_IntPtr(gpointer *location, gpointer value, gpointer comparand)
2011 return InterlockedCompareExchangePointer(location, value, comparand);
2014 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location, gfloat value, gfloat comparand)
2016 IntFloatUnion val, ret, cmp;
2019 cmp.fval = comparand;
2020 ret.ival = InterlockedCompareExchange((gint32 *) location, val.ival, cmp.ival);
2026 ves_icall_System_Threading_Interlocked_CompareExchange_Double (gdouble *location, gdouble value, gdouble comparand)
2028 #if SIZEOF_VOID_P == 8
2029 LongDoubleUnion val, comp, ret;
2032 comp.fval = comparand;
2033 ret.ival = (gint64)InterlockedCompareExchangePointer((gpointer *) location, (gpointer)val.ival, (gpointer)comp.ival);
2039 mono_interlocked_lock ();
2041 if (old == comparand)
2043 mono_interlocked_unlock ();
2050 ves_icall_System_Threading_Interlocked_CompareExchange_Long (gint64 *location, gint64 value, gint64 comparand)
2052 #if SIZEOF_VOID_P == 4
2053 if (G_UNLIKELY ((size_t)location & 0x7)) {
2055 mono_interlocked_lock ();
2057 if (old == comparand)
2059 mono_interlocked_unlock ();
2063 return InterlockedCompareExchange64 (location, value, comparand);
2067 ves_icall_System_Threading_Interlocked_CompareExchange_T (MonoObject **location, MonoObject *value, MonoObject *comparand)
2070 res = (MonoObject *)InterlockedCompareExchangePointer ((volatile gpointer *)location, value, comparand);
2071 mono_gc_wbarrier_generic_nostore (location);
2076 ves_icall_System_Threading_Interlocked_Exchange_T (MonoObject **location, MonoObject *value)
2079 MONO_CHECK_NULL (location, NULL);
2080 res = (MonoObject *)InterlockedExchangePointer ((volatile gpointer *)location, value);
2081 mono_gc_wbarrier_generic_nostore (location);
2086 ves_icall_System_Threading_Interlocked_Add_Int (gint32 *location, gint32 value)
2088 return InterlockedAdd (location, value);
2092 ves_icall_System_Threading_Interlocked_Add_Long (gint64 *location, gint64 value)
2094 #if SIZEOF_VOID_P == 4
2095 if (G_UNLIKELY ((size_t)location & 0x7)) {
2097 mono_interlocked_lock ();
2100 mono_interlocked_unlock ();
2104 return InterlockedAdd64 (location, value);
2108 ves_icall_System_Threading_Interlocked_Read_Long (gint64 *location)
2110 #if SIZEOF_VOID_P == 4
2111 if (G_UNLIKELY ((size_t)location & 0x7)) {
2113 mono_interlocked_lock ();
2115 mono_interlocked_unlock ();
2119 return InterlockedRead64 (location);
2123 ves_icall_System_Threading_Thread_MemoryBarrier (void)
2125 mono_memory_barrier ();
2129 ves_icall_System_Threading_Thread_ClrState (MonoInternalThread* this_obj, guint32 state)
2131 mono_thread_clr_state (this_obj, (MonoThreadState)state);
2133 if (state & ThreadState_Background) {
2134 /* If the thread changes the background mode, the main thread has to
2135 * be notified, since it has to rebuild the list of threads to
2138 SetEvent (background_change_event);
2143 ves_icall_System_Threading_Thread_SetState (MonoInternalThread* this_obj, guint32 state)
2145 mono_thread_set_state (this_obj, (MonoThreadState)state);
2147 if (state & ThreadState_Background) {
2148 /* If the thread changes the background mode, the main thread has to
2149 * be notified, since it has to rebuild the list of threads to
2152 SetEvent (background_change_event);
2157 ves_icall_System_Threading_Thread_GetState (MonoInternalThread* this_obj)
2161 LOCK_THREAD (this_obj);
2163 state = this_obj->state;
2165 UNLOCK_THREAD (this_obj);
2170 void ves_icall_System_Threading_Thread_Interrupt_internal (MonoThread *this_obj)
2172 MonoInternalThread *current;
2174 MonoInternalThread *thread = this_obj->internal_thread;
2176 LOCK_THREAD (thread);
2178 current = mono_thread_internal_current ();
2180 thread->thread_interrupt_requested = TRUE;
2181 throw_ = current != thread && (thread->state & ThreadState_WaitSleepJoin);
2183 UNLOCK_THREAD (thread);
2186 async_abort_internal (thread, FALSE);
2190 void mono_thread_current_check_pending_interrupt ()
2192 MonoInternalThread *thread = mono_thread_internal_current ();
2193 gboolean throw_ = FALSE;
2195 LOCK_THREAD (thread);
2197 if (thread->thread_interrupt_requested) {
2199 thread->thread_interrupt_requested = FALSE;
2202 UNLOCK_THREAD (thread);
2205 mono_raise_exception (mono_get_exception_thread_interrupted ());
2210 ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject *state)
2212 LOCK_THREAD (thread);
2214 if ((thread->state & ThreadState_AbortRequested) != 0 ||
2215 (thread->state & ThreadState_StopRequested) != 0 ||
2216 (thread->state & ThreadState_Stopped) != 0)
2218 UNLOCK_THREAD (thread);
2222 if ((thread->state & ThreadState_Unstarted) != 0) {
2223 thread->state |= ThreadState_Aborted;
2224 UNLOCK_THREAD (thread);
2228 thread->state |= ThreadState_AbortRequested;
2229 if (thread->abort_state_handle)
2230 mono_gchandle_free (thread->abort_state_handle);
2232 thread->abort_state_handle = mono_gchandle_new (state, FALSE);
2233 g_assert (thread->abort_state_handle);
2235 thread->abort_state_handle = 0;
2237 thread->abort_exc = NULL;
2239 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));
2241 /* During shutdown, we can't wait for other threads */
2243 /* Make sure the thread is awake */
2244 mono_thread_resume (thread);
2246 UNLOCK_THREAD (thread);
2248 if (thread == mono_thread_internal_current ())
2249 self_abort_internal ();
2251 async_abort_internal (thread, TRUE);
2255 ves_icall_System_Threading_Thread_ResetAbort (MonoThread *this_obj)
2257 MonoInternalThread *thread = mono_thread_internal_current ();
2258 gboolean was_aborting;
2260 LOCK_THREAD (thread);
2261 was_aborting = thread->state & ThreadState_AbortRequested;
2262 thread->state &= ~ThreadState_AbortRequested;
2263 UNLOCK_THREAD (thread);
2265 if (!was_aborting) {
2266 const char *msg = "Unable to reset abort because no abort was requested";
2267 mono_set_pending_exception (mono_get_exception_thread_state (msg));
2270 thread->abort_exc = NULL;
2271 if (thread->abort_state_handle) {
2272 mono_gchandle_free (thread->abort_state_handle);
2273 /* This is actually not necessary - the handle
2274 only counts if the exception is set */
2275 thread->abort_state_handle = 0;
2280 mono_thread_internal_reset_abort (MonoInternalThread *thread)
2282 LOCK_THREAD (thread);
2284 thread->state &= ~ThreadState_AbortRequested;
2286 if (thread->abort_exc) {
2287 thread->abort_exc = NULL;
2288 if (thread->abort_state_handle) {
2289 mono_gchandle_free (thread->abort_state_handle);
2290 /* This is actually not necessary - the handle
2291 only counts if the exception is set */
2292 thread->abort_state_handle = 0;
2296 UNLOCK_THREAD (thread);
2300 ves_icall_System_Threading_Thread_GetAbortExceptionState (MonoThread *this_obj)
2303 MonoInternalThread *thread = this_obj->internal_thread;
2304 MonoObject *state, *deserialized = NULL;
2307 if (!thread->abort_state_handle)
2310 state = mono_gchandle_get_target (thread->abort_state_handle);
2313 domain = mono_domain_get ();
2314 if (mono_object_domain (state) == domain)
2317 deserialized = mono_object_xdomain_representation (state, domain, &error);
2319 if (!deserialized) {
2320 MonoException *invalid_op_exc = mono_get_exception_invalid_operation ("Thread.ExceptionState cannot access an ExceptionState from a different AppDomain");
2321 if (!is_ok (&error)) {
2322 MonoObject *exc = (MonoObject*)mono_error_convert_to_exception (&error);
2323 MONO_OBJECT_SETREF (invalid_op_exc, inner_ex, exc);
2325 mono_set_pending_exception (invalid_op_exc);
2329 return deserialized;
2333 mono_thread_suspend (MonoInternalThread *thread)
2335 LOCK_THREAD (thread);
2337 if ((thread->state & ThreadState_Unstarted) != 0 ||
2338 (thread->state & ThreadState_Aborted) != 0 ||
2339 (thread->state & ThreadState_Stopped) != 0)
2341 UNLOCK_THREAD (thread);
2345 if ((thread->state & ThreadState_Suspended) != 0 ||
2346 (thread->state & ThreadState_SuspendRequested) != 0 ||
2347 (thread->state & ThreadState_StopRequested) != 0)
2349 UNLOCK_THREAD (thread);
2353 thread->state |= ThreadState_SuspendRequested;
2355 if (thread == mono_thread_internal_current ()) {
2356 /* calls UNLOCK_THREAD (thread) */
2357 self_suspend_internal ();
2359 /* calls UNLOCK_THREAD (thread) */
2360 async_suspend_internal (thread, FALSE);
2367 ves_icall_System_Threading_Thread_Suspend (MonoThread *this_obj)
2369 if (!mono_thread_suspend (this_obj->internal_thread)) {
2370 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
2375 /* LOCKING: LOCK_THREAD(thread) must be held */
2377 mono_thread_resume (MonoInternalThread *thread)
2379 if ((thread->state & ThreadState_SuspendRequested) != 0) {
2380 thread->state &= ~ThreadState_SuspendRequested;
2384 if ((thread->state & ThreadState_Suspended) == 0 ||
2385 (thread->state & ThreadState_Unstarted) != 0 ||
2386 (thread->state & ThreadState_Aborted) != 0 ||
2387 (thread->state & ThreadState_Stopped) != 0)
2392 UNLOCK_THREAD (thread);
2394 /* Awake the thread */
2395 if (!mono_thread_info_resume (thread_get_tid (thread)))
2398 LOCK_THREAD (thread);
2400 thread->state &= ~ThreadState_Suspended;
2406 ves_icall_System_Threading_Thread_Resume (MonoThread *thread)
2408 if (!thread->internal_thread) {
2409 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
2411 LOCK_THREAD (thread->internal_thread);
2412 if (!mono_thread_resume (thread->internal_thread))
2413 mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
2414 UNLOCK_THREAD (thread->internal_thread);
2419 mono_threads_is_critical_method (MonoMethod *method)
2421 switch (method->wrapper_type) {
2422 case MONO_WRAPPER_RUNTIME_INVOKE:
2423 case MONO_WRAPPER_XDOMAIN_INVOKE:
2424 case MONO_WRAPPER_XDOMAIN_DISPATCH:
2431 find_wrapper (MonoMethod *m, gint no, gint ilo, gboolean managed, gpointer data)
2436 if (mono_threads_is_critical_method (m)) {
2437 *((gboolean*)data) = TRUE;
2444 is_running_protected_wrapper (void)
2446 gboolean found = FALSE;
2447 mono_stack_walk (find_wrapper, &found);
2451 void mono_thread_internal_stop (MonoInternalThread *thread)
2453 LOCK_THREAD (thread);
2455 if ((thread->state & ThreadState_StopRequested) != 0 ||
2456 (thread->state & ThreadState_Stopped) != 0)
2458 UNLOCK_THREAD (thread);
2462 /* Make sure the thread is awake */
2463 mono_thread_resume (thread);
2465 thread->state |= ThreadState_StopRequested;
2466 thread->state &= ~ThreadState_AbortRequested;
2468 UNLOCK_THREAD (thread);
2470 if (thread == mono_thread_internal_current ())
2471 self_abort_internal ();
2473 async_abort_internal (thread, TRUE);
2476 void mono_thread_stop (MonoThread *thread)
2478 mono_thread_internal_stop (thread->internal_thread);
2482 ves_icall_System_Threading_Thread_VolatileRead1 (void *ptr)
2484 gint8 tmp = *(volatile gint8 *)ptr;
2485 mono_memory_barrier ();
2490 ves_icall_System_Threading_Thread_VolatileRead2 (void *ptr)
2492 gint16 tmp = *(volatile gint16 *)ptr;
2493 mono_memory_barrier ();
2498 ves_icall_System_Threading_Thread_VolatileRead4 (void *ptr)
2500 gint32 tmp = *(volatile gint32 *)ptr;
2501 mono_memory_barrier ();
2506 ves_icall_System_Threading_Thread_VolatileRead8 (void *ptr)
2508 gint64 tmp = *(volatile gint64 *)ptr;
2509 mono_memory_barrier ();
2514 ves_icall_System_Threading_Thread_VolatileReadIntPtr (void *ptr)
2516 volatile void *tmp = *(volatile void **)ptr;
2517 mono_memory_barrier ();
2518 return (void *) tmp;
2522 ves_icall_System_Threading_Thread_VolatileReadObject (void *ptr)
2524 volatile MonoObject *tmp = *(volatile MonoObject **)ptr;
2525 mono_memory_barrier ();
2526 return (MonoObject *) tmp;
2530 ves_icall_System_Threading_Thread_VolatileReadDouble (void *ptr)
2532 double tmp = *(volatile double *)ptr;
2533 mono_memory_barrier ();
2538 ves_icall_System_Threading_Thread_VolatileReadFloat (void *ptr)
2540 float tmp = *(volatile float *)ptr;
2541 mono_memory_barrier ();
2546 ves_icall_System_Threading_Volatile_Read1 (void *ptr)
2548 return InterlockedRead8 ((volatile gint8 *)ptr);
2552 ves_icall_System_Threading_Volatile_Read2 (void *ptr)
2554 return InterlockedRead16 ((volatile gint16 *)ptr);
2558 ves_icall_System_Threading_Volatile_Read4 (void *ptr)
2560 return InterlockedRead ((volatile gint32 *)ptr);
2564 ves_icall_System_Threading_Volatile_Read8 (void *ptr)
2566 #if SIZEOF_VOID_P == 4
2567 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2569 mono_interlocked_lock ();
2570 val = *(gint64*)ptr;
2571 mono_interlocked_unlock ();
2575 return InterlockedRead64 ((volatile gint64 *)ptr);
2579 ves_icall_System_Threading_Volatile_ReadIntPtr (void *ptr)
2581 return InterlockedReadPointer ((volatile gpointer *)ptr);
2585 ves_icall_System_Threading_Volatile_ReadDouble (void *ptr)
2589 #if SIZEOF_VOID_P == 4
2590 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2592 mono_interlocked_lock ();
2593 val = *(double*)ptr;
2594 mono_interlocked_unlock ();
2599 u.ival = InterlockedRead64 ((volatile gint64 *)ptr);
2605 ves_icall_System_Threading_Volatile_ReadFloat (void *ptr)
2609 u.ival = InterlockedRead ((volatile gint32 *)ptr);
2615 ves_icall_System_Threading_Volatile_Read_T (void *ptr)
2617 return (MonoObject *)InterlockedReadPointer ((volatile gpointer *)ptr);
2621 ves_icall_System_Threading_Thread_VolatileWrite1 (void *ptr, gint8 value)
2623 mono_memory_barrier ();
2624 *(volatile gint8 *)ptr = value;
2628 ves_icall_System_Threading_Thread_VolatileWrite2 (void *ptr, gint16 value)
2630 mono_memory_barrier ();
2631 *(volatile gint16 *)ptr = value;
2635 ves_icall_System_Threading_Thread_VolatileWrite4 (void *ptr, gint32 value)
2637 mono_memory_barrier ();
2638 *(volatile gint32 *)ptr = value;
2642 ves_icall_System_Threading_Thread_VolatileWrite8 (void *ptr, gint64 value)
2644 mono_memory_barrier ();
2645 *(volatile gint64 *)ptr = value;
2649 ves_icall_System_Threading_Thread_VolatileWriteIntPtr (void *ptr, void *value)
2651 mono_memory_barrier ();
2652 *(volatile void **)ptr = value;
2656 ves_icall_System_Threading_Thread_VolatileWriteObject (void *ptr, MonoObject *value)
2658 mono_memory_barrier ();
2659 mono_gc_wbarrier_generic_store (ptr, value);
2663 ves_icall_System_Threading_Thread_VolatileWriteDouble (void *ptr, double value)
2665 mono_memory_barrier ();
2666 *(volatile double *)ptr = value;
2670 ves_icall_System_Threading_Thread_VolatileWriteFloat (void *ptr, float value)
2672 mono_memory_barrier ();
2673 *(volatile float *)ptr = value;
2677 ves_icall_System_Threading_Volatile_Write1 (void *ptr, gint8 value)
2679 InterlockedWrite8 ((volatile gint8 *)ptr, value);
2683 ves_icall_System_Threading_Volatile_Write2 (void *ptr, gint16 value)
2685 InterlockedWrite16 ((volatile gint16 *)ptr, value);
2689 ves_icall_System_Threading_Volatile_Write4 (void *ptr, gint32 value)
2691 InterlockedWrite ((volatile gint32 *)ptr, value);
2695 ves_icall_System_Threading_Volatile_Write8 (void *ptr, gint64 value)
2697 #if SIZEOF_VOID_P == 4
2698 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2699 mono_interlocked_lock ();
2700 *(gint64*)ptr = value;
2701 mono_interlocked_unlock ();
2706 InterlockedWrite64 ((volatile gint64 *)ptr, value);
2710 ves_icall_System_Threading_Volatile_WriteIntPtr (void *ptr, void *value)
2712 InterlockedWritePointer ((volatile gpointer *)ptr, value);
2716 ves_icall_System_Threading_Volatile_WriteDouble (void *ptr, double value)
2720 #if SIZEOF_VOID_P == 4
2721 if (G_UNLIKELY ((size_t)ptr & 0x7)) {
2722 mono_interlocked_lock ();
2723 *(double*)ptr = value;
2724 mono_interlocked_unlock ();
2731 InterlockedWrite64 ((volatile gint64 *)ptr, u.ival);
2735 ves_icall_System_Threading_Volatile_WriteFloat (void *ptr, float value)
2741 InterlockedWrite ((volatile gint32 *)ptr, u.ival);
2745 ves_icall_System_Threading_Volatile_Write_T (void *ptr, MonoObject *value)
2747 mono_gc_wbarrier_generic_store_atomic (ptr, value);
2751 free_context (void *user_data)
2753 ContextStaticData *data = user_data;
2755 mono_threads_lock ();
2758 * There is no guarantee that, by the point this reference queue callback
2759 * has been invoked, the GC handle associated with the object will fail to
2760 * resolve as one might expect. So if we don't free and remove the GC
2761 * handle here, free_context_static_data_helper () could end up resolving
2762 * a GC handle to an actually-dead context which would contain a pointer
2763 * to an already-freed static data segment, resulting in a crash when
2766 g_hash_table_remove (contexts, GUINT_TO_POINTER (data->gc_handle));
2768 mono_threads_unlock ();
2770 mono_gchandle_free (data->gc_handle);
2771 mono_free_static_data (data->static_data);
2776 ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppContext *ctx)
2778 mono_threads_lock ();
2780 //g_print ("Registering context %d in domain %d\n", ctx->context_id, ctx->domain_id);
2783 contexts = g_hash_table_new (NULL, NULL);
2786 context_queue = mono_gc_reference_queue_new (free_context);
2788 gpointer gch = GUINT_TO_POINTER (mono_gchandle_new_weakref (&ctx->obj, FALSE));
2789 g_hash_table_insert (contexts, gch, gch);
2792 * We use this intermediate structure to contain a duplicate pointer to
2793 * the static data because we can't rely on being able to resolve the GC
2794 * handle in the reference queue callback.
2796 ContextStaticData *data = g_new0 (ContextStaticData, 1);
2797 data->gc_handle = GPOINTER_TO_UINT (gch);
2800 context_adjust_static_data (ctx);
2801 mono_gc_reference_queue_add (context_queue, &ctx->obj, data);
2803 mono_threads_unlock ();
2805 mono_profiler_context_loaded (ctx);
2809 ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContext *ctx)
2812 * NOTE: Since finalizers are unreliable for the purposes of ensuring
2813 * cleanup in exceptional circumstances, we don't actually do any
2814 * cleanup work here. We instead do this via a reference queue.
2817 //g_print ("Releasing context %d in domain %d\n", ctx->context_id, ctx->domain_id);
2819 mono_profiler_context_unloaded (ctx);
2823 mono_thread_init_tls (void)
2825 MONO_FAST_TLS_INIT (tls_current_object);
2826 mono_native_tls_alloc (¤t_object_key, NULL);
2829 void mono_thread_init (MonoThreadStartCB start_cb,
2830 MonoThreadAttachCB attach_cb)
2832 mono_coop_mutex_init_recursive (&threads_mutex);
2834 mono_os_mutex_init_recursive(&interlocked_mutex);
2835 mono_os_mutex_init_recursive(&joinable_threads_mutex);
2837 background_change_event = CreateEvent (NULL, TRUE, FALSE, NULL);
2838 g_assert(background_change_event != NULL);
2840 mono_init_static_data_info (&thread_static_info);
2841 mono_init_static_data_info (&context_static_info);
2843 THREAD_DEBUG (g_message ("%s: Allocated current_object_key %d", __func__, current_object_key));
2845 mono_thread_start_cb = start_cb;
2846 mono_thread_attach_cb = attach_cb;
2848 /* Get a pseudo handle to the current process. This is just a
2849 * kludge so that wapi can build a process handle if needed.
2850 * As a pseudo handle is returned, we don't need to clean
2853 GetCurrentProcess ();
2856 void mono_thread_cleanup (void)
2858 #if !defined(HOST_WIN32) && !defined(RUN_IN_SUBTHREAD)
2859 MonoThreadInfo *info;
2861 /* The main thread must abandon any held mutexes (particularly
2862 * important for named mutexes as they are shared across
2863 * processes, see bug 74680.) This will happen when the
2864 * thread exits, but if it's not running in a subthread it
2865 * won't exit in time.
2867 info = mono_thread_info_current ();
2868 wapi_thread_handle_set_exited (info->handle, mono_environment_exitcode_get ());
2872 /* This stuff needs more testing, it seems one of these
2873 * critical sections can be locked when mono_thread_cleanup is
2876 mono_coop_mutex_destroy (&threads_mutex);
2877 mono_os_mutex_destroy (&interlocked_mutex);
2878 mono_os_mutex_destroy (&delayed_free_table_mutex);
2879 mono_os_mutex_destroy (&small_id_mutex);
2880 CloseHandle (background_change_event);
2883 mono_native_tls_free (current_object_key);
2887 mono_threads_install_cleanup (MonoThreadCleanupFunc func)
2889 mono_thread_cleanup_fn = func;
2893 mono_thread_set_manage_callback (MonoThread *thread, MonoThreadManageCallback func)
2895 thread->internal_thread->manage_callback = func;
2899 static void print_tids (gpointer key, gpointer value, gpointer user)
2901 /* GPOINTER_TO_UINT breaks horribly if sizeof(void *) >
2902 * sizeof(uint) and a cast to uint would overflow
2904 /* Older versions of glib don't have G_GSIZE_FORMAT, so just
2905 * print this as a pointer.
2907 g_message ("Waiting for: %p", key);
2912 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
2913 MonoInternalThread *threads[MAXIMUM_WAIT_OBJECTS];
2917 static void wait_for_tids (struct wait_data *wait, guint32 timeout)
2921 THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num));
2924 ret=WaitForMultipleObjectsEx(wait->num, wait->handles, TRUE, timeout, TRUE);
2927 if(ret==WAIT_FAILED) {
2928 /* See the comment in build_wait_tids() */
2929 THREAD_DEBUG (g_message ("%s: Wait failed", __func__));
2933 for(i=0; i<wait->num; i++)
2934 CloseHandle (wait->handles[i]);
2936 if (ret == WAIT_TIMEOUT)
2939 for(i=0; i<wait->num; i++) {
2940 gsize tid = wait->threads[i]->tid;
2943 * On !win32, when the thread handle becomes signalled, it just means the thread has exited user code,
2944 * it can still run io-layer etc. code. So wait for it to really exit.
2945 * FIXME: This won't join threads which are not in the joinable_hash yet.
2947 mono_thread_join ((gpointer)tid);
2949 mono_threads_lock ();
2950 if(mono_g_hash_table_lookup (threads, (gpointer)tid)!=NULL) {
2951 /* This thread must have been killed, because
2952 * it hasn't cleaned itself up. (It's just
2953 * possible that the thread exited before the
2954 * parent thread had a chance to store the
2955 * handle, and now there is another pointer to
2956 * the already-exited thread stored. In this
2957 * case, we'll just get two
2958 * mono_profiler_thread_end() calls for the
2962 mono_threads_unlock ();
2963 THREAD_DEBUG (g_message ("%s: cleaning up after thread %p (%"G_GSIZE_FORMAT")", __func__, wait->threads[i], tid));
2964 thread_cleanup (wait->threads[i]);
2966 mono_threads_unlock ();
2971 static void wait_for_tids_or_state_change (struct wait_data *wait, guint32 timeout)
2973 guint32 i, ret, count;
2975 THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num));
2977 /* Add the thread state change event, so it wakes up if a thread changes
2978 * to background mode.
2981 if (count < MAXIMUM_WAIT_OBJECTS) {
2982 wait->handles [count] = background_change_event;
2987 ret=WaitForMultipleObjectsEx (count, wait->handles, FALSE, timeout, TRUE);
2990 if(ret==WAIT_FAILED) {
2991 /* See the comment in build_wait_tids() */
2992 THREAD_DEBUG (g_message ("%s: Wait failed", __func__));
2996 for(i=0; i<wait->num; i++)
2997 CloseHandle (wait->handles[i]);
2999 if (ret == WAIT_TIMEOUT)
3002 if (ret < wait->num) {
3003 gsize tid = wait->threads[ret]->tid;
3004 mono_threads_lock ();
3005 if (mono_g_hash_table_lookup (threads, (gpointer)tid)!=NULL) {
3006 /* See comment in wait_for_tids about thread cleanup */
3007 mono_threads_unlock ();
3008 THREAD_DEBUG (g_message ("%s: cleaning up after thread %"G_GSIZE_FORMAT, __func__, tid));
3009 thread_cleanup (wait->threads [ret]);
3011 mono_threads_unlock ();
3015 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
3017 struct wait_data *wait=(struct wait_data *)user;
3019 if(wait->num<MAXIMUM_WAIT_OBJECTS) {
3021 MonoInternalThread *thread=(MonoInternalThread *)value;
3023 /* Ignore background threads, we abort them later */
3024 /* Do not lock here since it is not needed and the caller holds threads_lock */
3025 if (thread->state & ThreadState_Background) {
3026 THREAD_DEBUG (g_message ("%s: ignoring background thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3027 return; /* just leave, ignore */
3030 if (mono_gc_is_finalizer_internal_thread (thread)) {
3031 THREAD_DEBUG (g_message ("%s: ignoring finalizer thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3035 if (thread == mono_thread_internal_current ()) {
3036 THREAD_DEBUG (g_message ("%s: ignoring current thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3040 if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread)) {
3041 THREAD_DEBUG (g_message ("%s: ignoring main thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3045 if (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE) {
3046 THREAD_DEBUG (g_message ("%s: ignoring thread %" G_GSIZE_FORMAT "with DONT_MANAGE flag set.", __func__, (gsize)thread->tid));
3050 handle = mono_threads_open_thread_handle (thread->handle, thread_get_tid (thread));
3051 if (handle == NULL) {
3052 THREAD_DEBUG (g_message ("%s: ignoring unopenable thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3056 THREAD_DEBUG (g_message ("%s: Invoking mono_thread_manage callback on thread %p", __func__, thread));
3057 if ((thread->manage_callback == NULL) || (thread->manage_callback (thread->root_domain_thread) == TRUE)) {
3058 wait->handles[wait->num]=handle;
3059 wait->threads[wait->num]=thread;
3062 THREAD_DEBUG (g_message ("%s: adding thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3064 THREAD_DEBUG (g_message ("%s: ignoring (because of callback) thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
3069 /* Just ignore the rest, we can't do anything with
3076 remove_and_abort_threads (gpointer key, gpointer value, gpointer user)
3078 struct wait_data *wait=(struct wait_data *)user;
3079 MonoNativeThreadId self = mono_native_thread_id_get ();
3080 MonoInternalThread *thread = (MonoInternalThread *)value;
3083 if (wait->num >= MAXIMUM_WAIT_OBJECTS)
3086 /* The finalizer thread is not a background thread */
3087 if (!mono_native_thread_id_equals (thread_get_tid (thread), self)
3088 && (thread->state & ThreadState_Background) != 0
3089 && (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE) == 0
3091 handle = mono_threads_open_thread_handle (thread->handle, thread_get_tid (thread));
3095 wait->handles[wait->num] = handle;
3096 wait->threads[wait->num] = thread;
3099 THREAD_DEBUG (g_print ("%s: Aborting id: %"G_GSIZE_FORMAT"\n", __func__, (gsize)thread->tid));
3100 mono_thread_internal_stop (thread);
3104 return !mono_native_thread_id_equals (thread_get_tid (thread), self)
3105 && !mono_gc_is_finalizer_internal_thread (thread);
3109 * mono_threads_set_shutting_down:
3111 * Is called by a thread that wants to shut down Mono. If the runtime is already
3112 * shutting down, the calling thread is suspended/stopped, and this function never
3116 mono_threads_set_shutting_down (void)
3118 MonoInternalThread *current_thread = mono_thread_internal_current ();
3120 mono_threads_lock ();
3122 if (shutting_down) {
3123 mono_threads_unlock ();
3125 /* Make sure we're properly suspended/stopped */
3127 LOCK_THREAD (current_thread);
3129 if ((current_thread->state & ThreadState_SuspendRequested) ||
3130 (current_thread->state & ThreadState_AbortRequested) ||
3131 (current_thread->state & ThreadState_StopRequested)) {
3132 UNLOCK_THREAD (current_thread);
3133 mono_thread_execute_interruption ();
3135 current_thread->state |= ThreadState_Stopped;
3136 UNLOCK_THREAD (current_thread);
3139 /*since we're killing the thread, unset the current domain.*/
3140 mono_domain_unset ();
3142 /* Wake up other threads potentially waiting for us */
3143 mono_thread_info_exit ();
3145 shutting_down = TRUE;
3147 /* Not really a background state change, but this will
3148 * interrupt the main thread if it is waiting for all
3149 * the other threads.
3151 SetEvent (background_change_event);
3153 mono_threads_unlock ();
3157 void mono_thread_manage (void)
3159 struct wait_data wait_data;
3160 struct wait_data *wait = &wait_data;
3162 memset (wait, 0, sizeof (struct wait_data));
3163 /* join each thread that's still running */
3164 THREAD_DEBUG (g_message ("%s: Joining each running thread...", __func__));
3166 mono_threads_lock ();
3168 THREAD_DEBUG (g_message("%s: No threads", __func__));
3169 mono_threads_unlock ();
3172 mono_threads_unlock ();
3175 mono_threads_lock ();
3176 if (shutting_down) {
3177 /* somebody else is shutting down */
3178 mono_threads_unlock ();
3181 THREAD_DEBUG (g_message ("%s: There are %d threads to join", __func__, mono_g_hash_table_size (threads));
3182 mono_g_hash_table_foreach (threads, print_tids, NULL));
3184 ResetEvent (background_change_event);
3186 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
3187 memset (wait->threads, 0, MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
3188 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
3189 mono_threads_unlock ();
3191 /* Something to wait for */
3192 wait_for_tids_or_state_change (wait, INFINITE);
3194 THREAD_DEBUG (g_message ("%s: I have %d threads after waiting.", __func__, wait->num));
3195 } while(wait->num>0);
3197 /* Mono is shutting down, so just wait for the end */
3198 if (!mono_runtime_try_shutdown ()) {
3199 /*FIXME mono_thread_suspend probably should call mono_thread_execute_interruption when self interrupting. */
3200 mono_thread_suspend (mono_thread_internal_current ());
3201 mono_thread_execute_interruption ();
3205 * Remove everything but the finalizer thread and self.
3206 * Also abort all the background threads
3209 mono_threads_lock ();
3212 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
3213 memset (wait->threads, 0, MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
3214 mono_g_hash_table_foreach_remove (threads, remove_and_abort_threads, wait);
3216 mono_threads_unlock ();
3218 THREAD_DEBUG (g_message ("%s: wait->num is now %d", __func__, wait->num));
3220 /* Something to wait for */
3221 wait_for_tids (wait, INFINITE);
3223 } while (wait->num > 0);
3226 * give the subthreads a chance to really quit (this is mainly needed
3227 * to get correct user and system times from getrusage/wait/time(1)).
3228 * This could be removed if we avoid pthread_detach() and use pthread_join().
3230 mono_thread_info_yield ();
3234 collect_threads_for_suspend (gpointer key, gpointer value, gpointer user_data)
3236 MonoInternalThread *thread = (MonoInternalThread*)value;
3237 struct wait_data *wait = (struct wait_data*)user_data;
3241 * We try to exclude threads early, to avoid running into the MAXIMUM_WAIT_OBJECTS
3243 * This needs no locking.
3245 if ((thread->state & ThreadState_Suspended) != 0 ||
3246 (thread->state & ThreadState_Stopped) != 0)
3249 if (wait->num<MAXIMUM_WAIT_OBJECTS) {
3250 handle = mono_threads_open_thread_handle (thread->handle, thread_get_tid (thread));
3254 wait->handles [wait->num] = handle;
3255 wait->threads [wait->num] = thread;
3261 * mono_thread_suspend_all_other_threads:
3263 * Suspend all managed threads except the finalizer thread and this thread. It is
3264 * not possible to resume them later.
3266 void mono_thread_suspend_all_other_threads (void)
3268 struct wait_data wait_data;
3269 struct wait_data *wait = &wait_data;
3271 MonoNativeThreadId self = mono_native_thread_id_get ();
3272 guint32 eventidx = 0;
3273 gboolean starting, finished;
3275 memset (wait, 0, sizeof (struct wait_data));
3277 * The other threads could be in an arbitrary state at this point, i.e.
3278 * they could be starting up, shutting down etc. This means that there could be
3279 * threads which are not even in the threads hash table yet.
3283 * First we set a barrier which will be checked by all threads before they
3284 * are added to the threads hash table, and they will exit if the flag is set.
3285 * This ensures that no threads could be added to the hash later.
3286 * We will use shutting_down as the barrier for now.
3288 g_assert (shutting_down);
3291 * We make multiple calls to WaitForMultipleObjects since:
3292 * - we can only wait for MAXIMUM_WAIT_OBJECTS threads
3293 * - some threads could exit without becoming suspended
3298 * Make a copy of the hashtable since we can't do anything with
3299 * threads while threads_mutex is held.
3302 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
3303 memset (wait->threads, 0, MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
3304 mono_threads_lock ();
3305 mono_g_hash_table_foreach (threads, collect_threads_for_suspend, wait);
3306 mono_threads_unlock ();
3309 /* Get the suspended events that we'll be waiting for */
3310 for (i = 0; i < wait->num; ++i) {
3311 MonoInternalThread *thread = wait->threads [i];
3313 if (mono_native_thread_id_equals (thread_get_tid (thread), self)
3314 || mono_gc_is_finalizer_internal_thread (thread)
3315 || (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)
3317 //CloseHandle (wait->handles [i]);
3318 wait->threads [i] = NULL; /* ignore this thread in next loop */
3322 LOCK_THREAD (thread);
3324 if ((thread->state & ThreadState_Suspended) != 0 ||
3325 (thread->state & ThreadState_StopRequested) != 0 ||
3326 (thread->state & ThreadState_Stopped) != 0) {
3327 UNLOCK_THREAD (thread);
3328 CloseHandle (wait->handles [i]);
3329 wait->threads [i] = NULL; /* ignore this thread in next loop */
3335 /* Convert abort requests into suspend requests */
3336 if ((thread->state & ThreadState_AbortRequested) != 0)
3337 thread->state &= ~ThreadState_AbortRequested;
3339 thread->state |= ThreadState_SuspendRequested;
3341 /* Signal the thread to suspend + calls UNLOCK_THREAD (thread) */
3342 async_suspend_internal (thread, TRUE);
3344 if (eventidx <= 0) {
3346 * If there are threads which are starting up, we wait until they
3347 * are suspended when they try to register in the threads hash.
3348 * This is guaranteed to finish, since the threads which can create new
3349 * threads get suspended after a while.
3350 * FIXME: The finalizer thread can still create new threads.
3352 mono_threads_lock ();
3353 if (threads_starting_up)
3354 starting = mono_g_hash_table_size (threads_starting_up) > 0;
3357 mono_threads_unlock ();
3359 mono_thread_info_sleep (100, NULL);
3367 MonoInternalThread *thread;
3368 MonoStackFrameInfo *frames;
3369 int nframes, max_frames;
3370 int nthreads, max_threads;
3371 MonoInternalThread **threads;
3372 } ThreadDumpUserData;
3374 static gboolean thread_dump_requested;
3376 /* This needs to be async safe */
3378 collect_frame (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
3380 ThreadDumpUserData *ud = (ThreadDumpUserData *)data;
3382 if (ud->nframes < ud->max_frames) {
3383 memcpy (&ud->frames [ud->nframes], frame, sizeof (MonoStackFrameInfo));
3390 /* This needs to be async safe */
3391 static SuspendThreadResult
3392 get_thread_dump (MonoThreadInfo *info, gpointer ud)
3394 ThreadDumpUserData *user_data = (ThreadDumpUserData *)ud;
3395 MonoInternalThread *thread = user_data->thread;
3398 /* This no longer works with remote unwinding */
3400 wapi_desc = wapi_current_thread_desc ();
3401 g_string_append_printf (text, " tid=0x%p this=0x%p %s\n", (gpointer)(gsize)thread->tid, thread, wapi_desc);
3406 if (thread == mono_thread_internal_current ())
3407 mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (collect_frame, NULL, MONO_UNWIND_SIGNAL_SAFE, ud);
3409 mono_get_eh_callbacks ()->mono_walk_stack_with_state (collect_frame, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, ud);
3411 return MonoResumeThread;
3415 int nthreads, max_threads;
3416 MonoInternalThread **threads;
3417 } CollectThreadsUserData;
3420 collect_thread (gpointer key, gpointer value, gpointer user)
3422 CollectThreadsUserData *ud = (CollectThreadsUserData *)user;
3423 MonoInternalThread *thread = (MonoInternalThread *)value;
3425 if (ud->nthreads < ud->max_threads)
3426 ud->threads [ud->nthreads ++] = thread;
3430 * Collect running threads into the THREADS array.
3431 * THREADS should be an array allocated on the stack.
3434 collect_threads (MonoInternalThread **thread_array, int max_threads)
3436 CollectThreadsUserData ud;
3438 memset (&ud, 0, sizeof (ud));
3439 /* This array contains refs, but its on the stack, so its ok */
3440 ud.threads = thread_array;
3441 ud.max_threads = max_threads;
3443 mono_threads_lock ();
3444 mono_g_hash_table_foreach (threads, collect_thread, &ud);
3445 mono_threads_unlock ();
3451 dump_thread (MonoInternalThread *thread, ThreadDumpUserData *ud)
3453 GString* text = g_string_new (0);
3455 GError *error = NULL;
3458 ud->thread = thread;
3461 /* Collect frames for the thread */
3462 if (thread == mono_thread_internal_current ()) {
3463 get_thread_dump (mono_thread_info_current (), ud);
3465 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, get_thread_dump, ud);
3469 * Do all the non async-safe work outside of get_thread_dump.
3472 name = g_utf16_to_utf8 (thread->name, thread->name_len, NULL, NULL, &error);
3474 g_string_append_printf (text, "\n\"%s\"", name);
3477 else if (thread->threadpool_thread) {
3478 g_string_append (text, "\n\"<threadpool thread>\"");
3480 g_string_append (text, "\n\"<unnamed thread>\"");
3483 for (i = 0; i < ud->nframes; ++i) {
3484 MonoStackFrameInfo *frame = &ud->frames [i];
3485 MonoMethod *method = NULL;
3487 if (frame->type == FRAME_TYPE_MANAGED)
3488 method = mono_jit_info_get_method (frame->ji);
3491 gchar *location = mono_debug_print_stack_frame (method, frame->native_offset, frame->domain);
3492 g_string_append_printf (text, " %s\n", location);
3495 g_string_append_printf (text, " at <unknown> <0x%05x>\n", frame->native_offset);
3499 fprintf (stdout, "%s", text->str);
3501 #if PLATFORM_WIN32 && TARGET_WIN32 && _DEBUG
3502 OutputDebugStringA(text->str);
3505 g_string_free (text, TRUE);
3510 mono_threads_perform_thread_dump (void)
3512 ThreadDumpUserData ud;
3513 MonoInternalThread *thread_array [128];
3514 int tindex, nthreads;
3516 if (!thread_dump_requested)
3519 printf ("Full thread dump:\n");
3521 /* Make a copy of the threads hash to avoid doing work inside threads_lock () */
3522 nthreads = collect_threads (thread_array, 128);
3524 memset (&ud, 0, sizeof (ud));
3525 ud.frames = g_new0 (MonoStackFrameInfo, 256);
3526 ud.max_frames = 256;
3528 for (tindex = 0; tindex < nthreads; ++tindex)
3529 dump_thread (thread_array [tindex], &ud);
3533 thread_dump_requested = FALSE;
3536 /* Obtain the thread dump of all threads */
3538 mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_frames)
3542 ThreadDumpUserData ud;
3543 MonoInternalThread *thread_array [128];
3544 MonoDomain *domain = mono_domain_get ();
3545 MonoDebugSourceLocation *location;
3546 int tindex, nthreads;
3548 mono_error_init (&error);
3550 *out_threads = NULL;
3551 *out_stack_frames = NULL;
3553 /* Make a copy of the threads hash to avoid doing work inside threads_lock () */
3554 nthreads = collect_threads (thread_array, 128);
3556 memset (&ud, 0, sizeof (ud));
3557 ud.frames = g_new0 (MonoStackFrameInfo, 256);
3558 ud.max_frames = 256;
3560 *out_threads = mono_array_new_checked (domain, mono_defaults.thread_class, nthreads, &error);
3561 if (!is_ok (&error))
3563 *out_stack_frames = mono_array_new_checked (domain, mono_defaults.array_class, nthreads, &error);
3564 if (!is_ok (&error))
3567 for (tindex = 0; tindex < nthreads; ++tindex) {
3568 MonoInternalThread *thread = thread_array [tindex];
3569 MonoArray *thread_frames;
3575 /* Collect frames for the thread */
3576 if (thread == mono_thread_internal_current ()) {
3577 get_thread_dump (mono_thread_info_current (), &ud);
3579 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, get_thread_dump, &ud);
3582 mono_array_setref_fast (*out_threads, tindex, mono_thread_current_for_thread (thread));
3584 thread_frames = mono_array_new_checked (domain, mono_defaults.stack_frame_class, ud.nframes, &error);
3585 if (!is_ok (&error))
3587 mono_array_setref_fast (*out_stack_frames, tindex, thread_frames);
3589 for (i = 0; i < ud.nframes; ++i) {
3590 MonoStackFrameInfo *frame = &ud.frames [i];
3591 MonoMethod *method = NULL;
3592 MonoStackFrame *sf = (MonoStackFrame *)mono_object_new_checked (domain, mono_defaults.stack_frame_class, &error);
3593 if (!mono_error_ok (&error))
3596 sf->native_offset = frame->native_offset;
3598 if (frame->type == FRAME_TYPE_MANAGED)
3599 method = mono_jit_info_get_method (frame->ji);
3602 sf->method_address = (gsize) frame->ji->code_start;
3604 MonoReflectionMethod *rm = mono_method_get_object_checked (domain, method, NULL, &error);
3605 mono_error_raise_exception (&error); /* FIXME don't raise here */
3606 MONO_OBJECT_SETREF (sf, method, rm);
3608 location = mono_debug_lookup_source_location (method, frame->native_offset, domain);
3610 sf->il_offset = location->il_offset;
3612 if (location && location->source_file) {
3613 MONO_OBJECT_SETREF (sf, filename, mono_string_new (domain, location->source_file));
3614 sf->line = location->row;
3615 sf->column = location->column;
3617 mono_debug_free_source_location (location);
3622 mono_array_setref (thread_frames, i, sf);
3628 mono_error_raise_exception (&error); /* FIXME don't raise here */
3632 * mono_threads_request_thread_dump:
3634 * Ask all threads except the current to print their stacktrace to stdout.
3637 mono_threads_request_thread_dump (void)
3639 /*The new thread dump code runs out of the finalizer thread. */
3640 thread_dump_requested = TRUE;
3641 mono_gc_finalize_notify ();
3646 gint allocated; /* +1 so that refs [allocated] == NULL */
3650 typedef struct ref_stack RefStack;
3653 ref_stack_new (gint initial_size)
3657 initial_size = MAX (initial_size, 16) + 1;
3658 rs = g_new0 (RefStack, 1);
3659 rs->refs = g_new0 (gpointer, initial_size);
3660 rs->allocated = initial_size;
3665 ref_stack_destroy (gpointer ptr)
3667 RefStack *rs = (RefStack *)ptr;
3676 ref_stack_push (RefStack *rs, gpointer ptr)
3678 g_assert (rs != NULL);
3680 if (rs->bottom >= rs->allocated) {
3681 rs->refs = (void **)g_realloc (rs->refs, rs->allocated * 2 * sizeof (gpointer) + 1);
3682 rs->allocated <<= 1;
3683 rs->refs [rs->allocated] = NULL;
3685 rs->refs [rs->bottom++] = ptr;
3689 ref_stack_pop (RefStack *rs)
3691 if (rs == NULL || rs->bottom == 0)
3695 rs->refs [rs->bottom] = NULL;
3699 ref_stack_find (RefStack *rs, gpointer ptr)
3706 for (refs = rs->refs; refs && *refs; refs++) {
3714 * mono_thread_push_appdomain_ref:
3716 * Register that the current thread may have references to objects in domain
3717 * @domain on its stack. Each call to this function should be paired with a
3718 * call to pop_appdomain_ref.
3721 mono_thread_push_appdomain_ref (MonoDomain *domain)
3723 MonoInternalThread *thread = mono_thread_internal_current ();
3726 /* printf ("PUSH REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, domain->friendly_name); */
3727 SPIN_LOCK (thread->lock_thread_id);
3728 if (thread->appdomain_refs == NULL)
3729 thread->appdomain_refs = ref_stack_new (16);
3730 ref_stack_push ((RefStack *)thread->appdomain_refs, domain);
3731 SPIN_UNLOCK (thread->lock_thread_id);
3736 mono_thread_pop_appdomain_ref (void)
3738 MonoInternalThread *thread = mono_thread_internal_current ();
3741 /* printf ("POP REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, ((MonoDomain*)(thread->appdomain_refs->data))->friendly_name); */
3742 SPIN_LOCK (thread->lock_thread_id);
3743 ref_stack_pop ((RefStack *)thread->appdomain_refs);
3744 SPIN_UNLOCK (thread->lock_thread_id);
3749 mono_thread_internal_has_appdomain_ref (MonoInternalThread *thread, MonoDomain *domain)
3752 SPIN_LOCK (thread->lock_thread_id);
3753 res = ref_stack_find ((RefStack *)thread->appdomain_refs, domain);
3754 SPIN_UNLOCK (thread->lock_thread_id);
3759 mono_thread_has_appdomain_ref (MonoThread *thread, MonoDomain *domain)
3761 return mono_thread_internal_has_appdomain_ref (thread->internal_thread, domain);
3764 typedef struct abort_appdomain_data {
3765 struct wait_data wait;
3767 } abort_appdomain_data;
3770 collect_appdomain_thread (gpointer key, gpointer value, gpointer user_data)
3772 MonoInternalThread *thread = (MonoInternalThread*)value;
3773 abort_appdomain_data *data = (abort_appdomain_data*)user_data;
3774 MonoDomain *domain = data->domain;
3776 if (mono_thread_internal_has_appdomain_ref (thread, domain)) {
3777 /* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread->tid, domain->friendly_name); */
3779 if(data->wait.num<MAXIMUM_WAIT_OBJECTS) {
3780 HANDLE handle = mono_threads_open_thread_handle (thread->handle, thread_get_tid (thread));
3783 data->wait.handles [data->wait.num] = handle;
3784 data->wait.threads [data->wait.num] = thread;
3787 /* Just ignore the rest, we can't do anything with
3795 * mono_threads_abort_appdomain_threads:
3797 * Abort threads which has references to the given appdomain.
3800 mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
3802 #ifdef __native_client__
3806 abort_appdomain_data user_data;
3808 int orig_timeout = timeout;
3811 THREAD_DEBUG (g_message ("%s: starting abort", __func__));
3813 start_time = mono_msec_ticks ();
3815 mono_threads_lock ();
3817 user_data.domain = domain;
3818 user_data.wait.num = 0;
3819 /* This shouldn't take any locks */
3820 mono_g_hash_table_foreach (threads, collect_appdomain_thread, &user_data);
3821 mono_threads_unlock ();
3823 if (user_data.wait.num > 0) {
3824 /* Abort the threads outside the threads lock */
3825 for (i = 0; i < user_data.wait.num; ++i)
3826 ves_icall_System_Threading_Thread_Abort (user_data.wait.threads [i], NULL);
3829 * We should wait for the threads either to abort, or to leave the
3830 * domain. We can't do the latter, so we wait with a timeout.
3832 wait_for_tids (&user_data.wait, 100);
3835 /* Update remaining time */
3836 timeout -= mono_msec_ticks () - start_time;
3837 start_time = mono_msec_ticks ();
3839 if (orig_timeout != -1 && timeout < 0)
3842 while (user_data.wait.num > 0);
3844 THREAD_DEBUG (g_message ("%s: abort done", __func__));
3850 clear_cached_culture (gpointer key, gpointer value, gpointer user_data)
3852 MonoInternalThread *thread = (MonoInternalThread*)value;
3853 MonoDomain *domain = (MonoDomain*)user_data;
3856 /* No locking needed here */
3857 /* FIXME: why no locking? writes to the cache are protected with synch_cs above */
3859 if (thread->cached_culture_info) {
3860 for (i = 0; i < NUM_CACHED_CULTURES * 2; ++i) {
3861 MonoObject *obj = mono_array_get (thread->cached_culture_info, MonoObject*, i);
3862 if (obj && obj->vtable->domain == domain)
3863 mono_array_set (thread->cached_culture_info, MonoObject*, i, NULL);
3869 * mono_threads_clear_cached_culture:
3871 * Clear the cached_current_culture from all threads if it is in the
3875 mono_threads_clear_cached_culture (MonoDomain *domain)
3877 mono_threads_lock ();
3878 mono_g_hash_table_foreach (threads, clear_cached_culture, domain);
3879 mono_threads_unlock ();
3883 * mono_thread_get_undeniable_exception:
3885 * Return an exception which needs to be raised when leaving a catch clause.
3886 * This is used for undeniable exception propagation.
3889 mono_thread_get_undeniable_exception (void)
3891 MonoInternalThread *thread = mono_thread_internal_current ();
3893 if (thread && thread->abort_exc && !is_running_protected_wrapper ()) {
3895 * FIXME: Clear the abort exception and return an AppDomainUnloaded
3896 * exception if the thread no longer references a dying appdomain.
3898 thread->abort_exc->trace_ips = NULL;
3899 thread->abort_exc->stack_trace = NULL;
3900 return thread->abort_exc;
3906 #if MONO_SMALL_CONFIG
3907 #define NUM_STATIC_DATA_IDX 4
3908 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
3912 #define NUM_STATIC_DATA_IDX 8
3913 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
3914 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
3918 static MonoBitSet *thread_reference_bitmaps [NUM_STATIC_DATA_IDX];
3919 static MonoBitSet *context_reference_bitmaps [NUM_STATIC_DATA_IDX];
3922 mark_slots (void *addr, MonoBitSet **bitmaps, MonoGCMarkFunc mark_func, void *gc_data)
3924 gpointer *static_data = (gpointer *)addr;
3926 for (int i = 0; i < NUM_STATIC_DATA_IDX; ++i) {
3927 void **ptr = (void **)static_data [i];
3932 MONO_BITSET_FOREACH (bitmaps [i], idx, {
3933 void **p = ptr + idx;
3936 mark_func ((MonoObject**)p, gc_data);
3942 mark_tls_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
3944 mark_slots (addr, thread_reference_bitmaps, mark_func, gc_data);
3948 mark_ctx_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
3950 mark_slots (addr, context_reference_bitmaps, mark_func, gc_data);
3954 * mono_alloc_static_data
3956 * Allocate memory blocks for storing threads or context static data
3959 mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean threadlocal)
3961 guint idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
3964 gpointer* static_data = *static_data_ptr;
3966 static MonoGCDescriptor tls_desc = MONO_GC_DESCRIPTOR_NULL;
3967 static MonoGCDescriptor ctx_desc = MONO_GC_DESCRIPTOR_NULL;
3969 if (mono_gc_user_markers_supported ()) {
3970 if (tls_desc == MONO_GC_DESCRIPTOR_NULL)
3971 tls_desc = mono_gc_make_root_descr_user (mark_tls_slots);
3973 if (ctx_desc == MONO_GC_DESCRIPTOR_NULL)
3974 ctx_desc = mono_gc_make_root_descr_user (mark_ctx_slots);
3977 static_data = (void **)mono_gc_alloc_fixed (static_data_size [0], threadlocal ? tls_desc : ctx_desc,
3978 threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC,
3979 threadlocal ? "managed thread-static variables" : "managed context-static variables");
3980 *static_data_ptr = static_data;
3981 static_data [0] = static_data;
3984 for (i = 1; i <= idx; ++i) {
3985 if (static_data [i])
3988 if (mono_gc_user_markers_supported ())
3989 static_data [i] = g_malloc0 (static_data_size [i]);
3991 static_data [i] = mono_gc_alloc_fixed (static_data_size [i], MONO_GC_DESCRIPTOR_NULL,
3992 threadlocal ? MONO_ROOT_SOURCE_THREAD_STATIC : MONO_ROOT_SOURCE_CONTEXT_STATIC,
3993 threadlocal ? "managed thread-static variables" : "managed context-static variables");
3998 mono_free_static_data (gpointer* static_data)
4001 for (i = 1; i < NUM_STATIC_DATA_IDX; ++i) {
4002 gpointer p = static_data [i];
4006 * At this point, the static data pointer array is still registered with the
4007 * GC, so must ensure that mark_tls_slots() will not encounter any invalid
4008 * data. Freeing the individual arrays without first nulling their slots
4009 * would make it possible for mark_tls/ctx_slots() to encounter a pointer to
4010 * such an already freed array. See bug #13813.
4012 static_data [i] = NULL;
4013 mono_memory_write_barrier ();
4014 if (mono_gc_user_markers_supported ())
4017 mono_gc_free_fixed (p);
4019 mono_gc_free_fixed (static_data);
4023 * mono_init_static_data_info
4025 * Initializes static data counters
4027 static void mono_init_static_data_info (StaticDataInfo *static_data)
4029 static_data->idx = 0;
4030 static_data->offset = 0;
4031 static_data->freelist = NULL;
4035 * mono_alloc_static_data_slot
4037 * Generates an offset for static data. static_data contains the counters
4038 * used to generate it.
4041 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align)
4043 if (!static_data->idx && !static_data->offset) {
4045 * we use the first chunk of the first allocation also as
4046 * an array for the rest of the data
4048 static_data->offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX;
4050 static_data->offset += align - 1;
4051 static_data->offset &= ~(align - 1);
4052 if (static_data->offset + size >= static_data_size [static_data->idx]) {
4053 static_data->idx ++;
4054 g_assert (size <= static_data_size [static_data->idx]);
4055 g_assert (static_data->idx < NUM_STATIC_DATA_IDX);
4056 static_data->offset = 0;
4058 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (static_data->idx, static_data->offset, 0);
4059 static_data->offset += size;
4064 * ensure thread static fields already allocated are valid for thread
4065 * This function is called when a thread is created or on thread attach.
4068 thread_adjust_static_data (MonoInternalThread *thread)
4070 mono_threads_lock ();
4071 if (thread_static_info.offset || thread_static_info.idx > 0) {
4072 /* get the current allocated size */
4073 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (thread_static_info.idx, thread_static_info.offset, 0);
4074 mono_alloc_static_data (&thread->static_data, offset, TRUE);
4076 mono_threads_unlock ();
4080 * LOCKING: requires that threads_mutex is held
4083 context_adjust_static_data (MonoAppContext *ctx)
4085 if (context_static_info.offset || context_static_info.idx > 0) {
4086 guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (context_static_info.idx, context_static_info.offset, 0);
4087 mono_alloc_static_data (&ctx->static_data, offset, FALSE);
4088 ctx->data->static_data = ctx->static_data;
4093 * LOCKING: requires that threads_mutex is held
4096 alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
4098 MonoInternalThread *thread = (MonoInternalThread *)value;
4099 guint32 offset = GPOINTER_TO_UINT (user);
4101 mono_alloc_static_data (&(thread->static_data), offset, TRUE);
4105 * LOCKING: requires that threads_mutex is held
4108 alloc_context_static_data_helper (gpointer key, gpointer value, gpointer user)
4110 MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (GPOINTER_TO_INT (key));
4115 guint32 offset = GPOINTER_TO_UINT (user);
4116 mono_alloc_static_data (&ctx->static_data, offset, FALSE);
4117 ctx->data->static_data = ctx->static_data;
4120 static StaticDataFreeList*
4121 search_slot_in_freelist (StaticDataInfo *static_data, guint32 size, guint32 align)
4123 StaticDataFreeList* prev = NULL;
4124 StaticDataFreeList* tmp = static_data->freelist;
4126 if (tmp->size == size) {
4128 prev->next = tmp->next;
4130 static_data->freelist = tmp->next;
4139 #if SIZEOF_VOID_P == 4
4146 update_reference_bitmap (MonoBitSet **sets, guint32 offset, uintptr_t *bitmap, int numbits)
4148 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4150 sets [idx] = mono_bitset_new (static_data_size [idx] / sizeof (uintptr_t), 0);
4151 MonoBitSet *rb = sets [idx];
4152 offset = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
4153 offset /= sizeof (uintptr_t);
4154 /* offset is now the bitmap offset */
4155 for (int i = 0; i < numbits; ++i) {
4156 if (bitmap [i / sizeof (uintptr_t)] & (ONE_P << (i & (sizeof (uintptr_t) * 8 -1))))
4157 mono_bitset_set_fast (rb, offset + i);
4162 clear_reference_bitmap (MonoBitSet **sets, guint32 offset, guint32 size)
4164 int idx = ACCESS_SPECIAL_STATIC_OFFSET (offset, index);
4165 MonoBitSet *rb = sets [idx];
4166 offset = ACCESS_SPECIAL_STATIC_OFFSET (offset, offset);
4167 offset /= sizeof (uintptr_t);
4168 /* offset is now the bitmap offset */
4169 for (int i = 0; i < size / sizeof (uintptr_t); i++)
4170 mono_bitset_clear_fast (rb, offset + i);
4174 mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align, uintptr_t *bitmap, int numbits)
4176 g_assert (static_type == SPECIAL_STATIC_THREAD || static_type == SPECIAL_STATIC_CONTEXT);
4178 StaticDataInfo *info;
4181 if (static_type == SPECIAL_STATIC_THREAD) {
4182 info = &thread_static_info;
4183 sets = thread_reference_bitmaps;
4185 info = &context_static_info;
4186 sets = context_reference_bitmaps;
4189 mono_threads_lock ();
4191 StaticDataFreeList *item = search_slot_in_freelist (info, size, align);
4195 offset = item->offset;
4198 offset = mono_alloc_static_data_slot (info, size, align);
4201 update_reference_bitmap (sets, offset, bitmap, numbits);
4203 if (static_type == SPECIAL_STATIC_THREAD) {
4204 /* This can be called during startup */
4205 if (threads != NULL)
4206 mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
4208 if (contexts != NULL)
4209 g_hash_table_foreach (contexts, alloc_context_static_data_helper, GUINT_TO_POINTER (offset));
4211 ACCESS_SPECIAL_STATIC_OFFSET (offset, type) = SPECIAL_STATIC_OFFSET_TYPE_CONTEXT;
4214 mono_threads_unlock ();
4220 mono_get_special_static_data_for_thread (MonoInternalThread *thread, guint32 offset)
4222 guint32 static_type = ACCESS_SPECIAL_STATIC_OFFSET (offset, type);
4224 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4225 return get_thread_static_data (thread, offset);
4227 return get_context_static_data (thread->current_appcontext, offset);
4232 mono_get_special_static_data (guint32 offset)
4234 return mono_get_special_static_data_for_thread (mono_thread_internal_current (), offset);
4243 * LOCKING: requires that threads_mutex is held
4246 free_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
4248 MonoInternalThread *thread = (MonoInternalThread *)value;
4249 OffsetSize *data = (OffsetSize *)user;
4250 int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index);
4251 int off = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, offset);
4254 if (!thread->static_data || !thread->static_data [idx])
4256 ptr = ((char*) thread->static_data [idx]) + off;
4257 mono_gc_bzero_atomic (ptr, data->size);
4261 * LOCKING: requires that threads_mutex is held
4264 free_context_static_data_helper (gpointer key, gpointer value, gpointer user)
4266 MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (GPOINTER_TO_INT (key));
4271 OffsetSize *data = (OffsetSize *)user;
4272 int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index);
4273 int off = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, offset);
4276 if (!ctx->static_data || !ctx->static_data [idx])
4279 ptr = ((char*) ctx->static_data [idx]) + off;
4280 mono_gc_bzero_atomic (ptr, data->size);
4284 do_free_special_slot (guint32 offset, guint32 size)
4286 guint32 static_type = ACCESS_SPECIAL_STATIC_OFFSET (offset, type);
4288 StaticDataInfo *info;
4290 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4291 info = &thread_static_info;
4292 sets = thread_reference_bitmaps;
4294 info = &context_static_info;
4295 sets = context_reference_bitmaps;
4298 guint32 data_offset = offset;
4299 ACCESS_SPECIAL_STATIC_OFFSET (data_offset, type) = 0;
4300 OffsetSize data = { data_offset, size };
4302 clear_reference_bitmap (sets, data.offset, data.size);
4304 if (static_type == SPECIAL_STATIC_OFFSET_TYPE_THREAD) {
4305 if (threads != NULL)
4306 mono_g_hash_table_foreach (threads, free_thread_static_data_helper, &data);
4308 if (contexts != NULL)
4309 g_hash_table_foreach (contexts, free_context_static_data_helper, &data);
4312 if (!mono_runtime_is_shutting_down ()) {
4313 StaticDataFreeList *item = g_new0 (StaticDataFreeList, 1);
4315 item->offset = offset;
4318 item->next = info->freelist;
4319 info->freelist = item;
4324 do_free_special (gpointer key, gpointer value, gpointer data)
4326 MonoClassField *field = (MonoClassField *)key;
4327 guint32 offset = GPOINTER_TO_UINT (value);
4330 size = mono_type_size (field->type, &align);
4331 do_free_special_slot (offset, size);
4335 mono_alloc_special_static_data_free (GHashTable *special_static_fields)
4337 mono_threads_lock ();
4339 g_hash_table_foreach (special_static_fields, do_free_special, NULL);
4341 mono_threads_unlock ();
4345 mono_special_static_data_free_slot (guint32 offset, guint32 size)
4347 /* Only ever called for ThreadLocal instances */
4348 g_assert (ACCESS_SPECIAL_STATIC_OFFSET (offset, type) == SPECIAL_STATIC_OFFSET_TYPE_THREAD);
4350 mono_threads_lock ();
4351 do_free_special_slot (offset, size);
4352 mono_threads_unlock ();
4356 static void CALLBACK dummy_apc (ULONG_PTR param)
4362 * mono_thread_execute_interruption
4364 * Performs the operation that the requested thread state requires (abort,
4367 static MonoException*
4368 mono_thread_execute_interruption (void)
4370 MonoInternalThread *thread = mono_thread_internal_current ();
4371 MonoThread *sys_thread = mono_thread_current ();
4373 LOCK_THREAD (thread);
4375 /* MonoThread::interruption_requested can only be changed with atomics */
4376 if (InterlockedCompareExchange (&thread->interruption_requested, FALSE, TRUE)) {
4377 /* this will consume pending APC calls */
4379 WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
4381 InterlockedDecrement (&thread_interruption_requested);
4383 /* Clear the interrupted flag of the thread so it can wait again */
4384 mono_thread_info_clear_self_interrupt ();
4387 /* If there's a pending exception and an AbortRequested, the pending exception takes precedence */
4388 if (sys_thread->pending_exception) {
4391 exc = sys_thread->pending_exception;
4392 sys_thread->pending_exception = NULL;
4394 UNLOCK_THREAD (thread);
4396 } else if ((thread->state & ThreadState_AbortRequested) != 0) {
4397 UNLOCK_THREAD (thread);
4398 g_assert (sys_thread->pending_exception == NULL);
4399 if (thread->abort_exc == NULL) {
4401 * This might be racy, but it has to be called outside the lock
4402 * since it calls managed code.
4404 MONO_OBJECT_SETREF (thread, abort_exc, mono_get_exception_thread_abort ());
4406 return thread->abort_exc;
4408 else if ((thread->state & ThreadState_SuspendRequested) != 0) {
4409 /* calls UNLOCK_THREAD (thread) */
4410 self_suspend_internal ();
4413 else if ((thread->state & ThreadState_StopRequested) != 0) {
4414 /* FIXME: do this through the JIT? */
4416 UNLOCK_THREAD (thread);
4418 mono_thread_exit ();
4420 } else if (thread->thread_interrupt_requested) {
4422 thread->thread_interrupt_requested = FALSE;
4423 UNLOCK_THREAD (thread);
4425 return(mono_get_exception_thread_interrupted ());
4428 UNLOCK_THREAD (thread);
4434 * mono_thread_request_interruption
4436 * A signal handler can call this method to request the interruption of a
4437 * thread. The result of the interruption will depend on the current state of
4438 * the thread. If the result is an exception that needs to be throw, it is
4439 * provided as return value.
4442 mono_thread_request_interruption (gboolean running_managed)
4444 MonoInternalThread *thread = mono_thread_internal_current ();
4446 /* The thread may already be stopping */
4451 if (thread->interrupt_on_stop &&
4452 thread->state & ThreadState_StopRequested &&
4453 thread->state & ThreadState_Background)
4457 if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
4459 InterlockedIncrement (&thread_interruption_requested);
4461 if (!running_managed || is_running_protected_wrapper ()) {
4462 /* Can't stop while in unmanaged code. Increase the global interruption
4463 request count. When exiting the unmanaged method the count will be
4464 checked and the thread will be interrupted. */
4466 /* this will awake the thread if it is in WaitForSingleObject
4468 /* Our implementation of this function ignores the func argument */
4470 QueueUserAPC ((PAPCFUNC)dummy_apc, thread->handle, (ULONG_PTR)NULL);
4472 mono_thread_info_self_interrupt ();
4477 return mono_thread_execute_interruption ();
4481 /*This function should be called by a thread after it has exited all of
4482 * its handle blocks at interruption time.*/
4484 mono_thread_resume_interruption (void)
4486 MonoInternalThread *thread = mono_thread_internal_current ();
4487 gboolean still_aborting;
4489 /* The thread may already be stopping */
4493 LOCK_THREAD (thread);
4494 still_aborting = (thread->state & ThreadState_AbortRequested) != 0;
4495 UNLOCK_THREAD (thread);
4497 /*This can happen if the protected block called Thread::ResetAbort*/
4498 if (!still_aborting)
4501 if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
4503 InterlockedIncrement (&thread_interruption_requested);
4505 mono_thread_info_self_interrupt ();
4507 return mono_thread_execute_interruption ();
4510 gboolean mono_thread_interruption_requested ()
4512 if (thread_interruption_requested) {
4513 MonoInternalThread *thread = mono_thread_internal_current ();
4514 /* The thread may already be stopping */
4516 return (thread->interruption_requested);
4521 static MonoException*
4522 mono_thread_interruption_checkpoint_request (gboolean bypass_abort_protection)
4524 MonoInternalThread *thread = mono_thread_internal_current ();
4526 /* The thread may already be stopping */
4530 if (thread->interruption_requested && (bypass_abort_protection || !is_running_protected_wrapper ())) {
4531 MonoException* exc = mono_thread_execute_interruption ();
4539 * Performs the interruption of the current thread, if one has been requested,
4540 * and the thread is not running a protected wrapper.
4541 * Return the exception which needs to be thrown, if any.
4544 mono_thread_interruption_checkpoint (void)
4546 return mono_thread_interruption_checkpoint_request (FALSE);
4550 * Performs the interruption of the current thread, if one has been requested.
4551 * Return the exception which needs to be thrown, if any.
4554 mono_thread_force_interruption_checkpoint_noraise (void)
4556 return mono_thread_interruption_checkpoint_request (TRUE);
4560 * mono_set_pending_exception:
4562 * Set the pending exception of the current thread to EXC.
4563 * The exception will be thrown when execution returns to managed code.
4566 mono_set_pending_exception (MonoException *exc)
4568 MonoThread *thread = mono_thread_current ();
4570 /* The thread may already be stopping */
4574 MONO_OBJECT_SETREF (thread, pending_exception, exc);
4576 mono_thread_request_interruption (FALSE);
4580 * mono_thread_interruption_request_flag:
4582 * Returns the address of a flag that will be non-zero if an interruption has
4583 * been requested for a thread. The thread to interrupt may not be the current
4584 * thread, so an additional call to mono_thread_interruption_requested() or
4585 * mono_thread_interruption_checkpoint() is allways needed if the flag is not
4588 gint32* mono_thread_interruption_request_flag ()
4590 return &thread_interruption_requested;
4594 mono_thread_init_apartment_state (void)
4597 MonoInternalThread* thread = mono_thread_internal_current ();
4599 /* Positive return value indicates success, either
4600 * S_OK if this is first CoInitialize call, or
4601 * S_FALSE if CoInitialize already called, but with same
4602 * threading model. A negative value indicates failure,
4603 * probably due to trying to change the threading model.
4605 if (CoInitializeEx(NULL, (thread->apartment_state == ThreadApartmentState_STA)
4606 ? COINIT_APARTMENTTHREADED
4607 : COINIT_MULTITHREADED) < 0) {
4608 thread->apartment_state = ThreadApartmentState_Unknown;
4614 mono_thread_cleanup_apartment_state (void)
4617 MonoInternalThread* thread = mono_thread_internal_current ();
4619 if (thread && thread->apartment_state != ThreadApartmentState_Unknown) {
4626 mono_thread_set_state (MonoInternalThread *thread, MonoThreadState state)
4628 LOCK_THREAD (thread);
4629 thread->state |= state;
4630 UNLOCK_THREAD (thread);
4634 mono_thread_clr_state (MonoInternalThread *thread, MonoThreadState state)
4636 LOCK_THREAD (thread);
4637 thread->state &= ~state;
4638 UNLOCK_THREAD (thread);
4642 mono_thread_test_state (MonoInternalThread *thread, MonoThreadState test)
4644 gboolean ret = FALSE;
4646 LOCK_THREAD (thread);
4648 if ((thread->state & test) != 0) {
4652 UNLOCK_THREAD (thread);
4657 static gboolean has_tls_get = FALSE;
4660 mono_runtime_set_has_tls_get (gboolean val)
4666 mono_runtime_has_tls_get (void)
4672 self_interrupt_thread (void *_unused)
4674 MonoThreadInfo *info = mono_thread_info_current ();
4675 MonoException *exc = mono_thread_execute_interruption ();
4676 if (exc) /*We must use _with_context since we didn't trampoline into the runtime*/
4677 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. */
4678 g_assert_not_reached (); /*this MUST not happen since we can't resume from an async call*/
4682 mono_jit_info_match (MonoJitInfo *ji, gpointer ip)
4686 return ji->code_start <= ip && (char*)ip < (char*)ji->code_start + ji->code_size;
4690 last_managed (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
4692 MonoJitInfo **dest = (MonoJitInfo **)data;
4698 mono_thread_info_get_last_managed (MonoThreadInfo *info)
4700 MonoJitInfo *ji = NULL;
4705 * The suspended thread might be holding runtime locks. Make sure we don't try taking
4706 * any runtime locks while unwinding. In coop case we shouldn't safepoint in regions
4707 * where we hold runtime locks.
4709 if (!mono_threads_is_coop_enabled ())
4710 mono_thread_info_set_is_async_context (TRUE);
4711 mono_get_eh_callbacks ()->mono_walk_stack_with_state (last_managed, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, &ji);
4712 if (!mono_threads_is_coop_enabled ())
4713 mono_thread_info_set_is_async_context (FALSE);
4718 MonoInternalThread *thread;
4719 gboolean install_async_abort;
4720 MonoThreadInfoInterruptToken *interrupt_token;
4723 static SuspendThreadResult
4724 async_abort_critical (MonoThreadInfo *info, gpointer ud)
4726 AbortThreadData *data = (AbortThreadData *)ud;
4727 MonoInternalThread *thread = data->thread;
4728 MonoJitInfo *ji = NULL;
4729 gboolean protected_wrapper;
4730 gboolean running_managed;
4732 if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (mono_thread_info_get_suspend_state (info)))
4733 return MonoResumeThread;
4735 /*someone is already interrupting it*/
4736 if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
4737 return MonoResumeThread;
4739 InterlockedIncrement (&thread_interruption_requested);
4741 ji = mono_thread_info_get_last_managed (info);
4742 protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
4743 running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
4745 if (!protected_wrapper && running_managed) {
4746 /*We are in managed code*/
4747 /*Set the thread to call */
4748 if (data->install_async_abort)
4749 mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
4750 return MonoResumeThread;
4753 * This will cause waits to be broken.
4754 * It will also prevent the thread from entering a wait, so if the thread returns
4755 * from the wait before it receives the abort signal, it will just spin in the wait
4756 * functions in the io-layer until the signal handler calls QueueUserAPC which will
4759 data->interrupt_token = mono_thread_info_prepare_interrupt (info);
4761 return MonoResumeThread;
4766 async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort)
4768 AbortThreadData data;
4770 g_assert (thread != mono_thread_internal_current ());
4772 data.thread = thread;
4773 data.install_async_abort = install_async_abort;
4774 data.interrupt_token = NULL;
4776 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), TRUE, async_abort_critical, &data);
4777 if (data.interrupt_token)
4778 mono_thread_info_finish_interrupt (data.interrupt_token);
4779 /*FIXME we need to wait for interruption to complete -- figure out how much into interruption we should wait for here*/
4783 self_abort_internal (void)
4787 /* FIXME this is insanely broken, it doesn't cause interruption to happen synchronously
4788 * since passing FALSE to mono_thread_request_interruption makes sure it returns NULL */
4790 exc = mono_thread_request_interruption (TRUE);
4792 mono_raise_exception (exc);
4794 mono_thread_info_self_interrupt ();
4798 MonoInternalThread *thread;
4800 MonoThreadInfoInterruptToken *interrupt_token;
4801 } SuspendThreadData;
4803 static SuspendThreadResult
4804 async_suspend_critical (MonoThreadInfo *info, gpointer ud)
4806 SuspendThreadData *data = (SuspendThreadData *)ud;
4807 MonoInternalThread *thread = data->thread;
4808 MonoJitInfo *ji = NULL;
4809 gboolean protected_wrapper;
4810 gboolean running_managed;
4812 ji = mono_thread_info_get_last_managed (info);
4813 protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
4814 running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
4816 if (running_managed && !protected_wrapper) {
4817 thread->state &= ~ThreadState_SuspendRequested;
4818 thread->state |= ThreadState_Suspended;
4819 return KeepSuspended;
4821 if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 0)
4822 InterlockedIncrement (&thread_interruption_requested);
4823 if (data->interrupt)
4824 data->interrupt_token = mono_thread_info_prepare_interrupt ((MonoThreadInfo *)thread->thread_info);
4826 return MonoResumeThread;
4830 /* LOCKING: called with @thread synch_cs held, and releases it */
4832 async_suspend_internal (MonoInternalThread *thread, gboolean interrupt)
4834 SuspendThreadData data;
4836 g_assert (thread != mono_thread_internal_current ());
4838 data.thread = thread;
4839 data.interrupt = interrupt;
4840 data.interrupt_token = NULL;
4842 mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), interrupt, async_suspend_critical, &data);
4843 if (data.interrupt_token)
4844 mono_thread_info_finish_interrupt (data.interrupt_token);
4846 UNLOCK_THREAD (thread);
4849 /* LOCKING: called with @thread synch_cs held, and releases it */
4851 self_suspend_internal (void)
4853 MonoInternalThread *thread;
4855 thread = mono_thread_internal_current ();
4857 mono_thread_info_begin_self_suspend ();
4858 thread->state &= ~ThreadState_SuspendRequested;
4859 thread->state |= ThreadState_Suspended;
4861 UNLOCK_THREAD (thread);
4863 mono_thread_info_end_self_suspend ();
4867 * mono_thread_is_foreign:
4868 * @thread: the thread to query
4870 * This function allows one to determine if a thread was created by the mono runtime and has
4871 * a well defined lifecycle or it's a foreigh one, created by the native environment.
4873 * Returns: TRUE if @thread was not created by the runtime.
4876 mono_thread_is_foreign (MonoThread *thread)
4878 MonoThreadInfo *info = (MonoThreadInfo *)thread->internal_thread->thread_info;
4879 return info->runtime_thread == FALSE;
4883 * mono_add_joinable_thread:
4885 * Add TID to the list of joinable threads.
4886 * LOCKING: Acquires the threads lock.
4889 mono_threads_add_joinable_thread (gpointer tid)
4893 * We cannot detach from threads because it causes problems like
4894 * 2fd16f60/r114307. So we collect them and join them when
4895 * we have time (in he finalizer thread).
4897 joinable_threads_lock ();
4898 if (!joinable_threads)
4899 joinable_threads = g_hash_table_new (NULL, NULL);
4900 g_hash_table_insert (joinable_threads, tid, tid);
4901 joinable_thread_count ++;
4902 joinable_threads_unlock ();
4904 mono_gc_finalize_notify ();
4909 * mono_threads_join_threads:
4911 * Join all joinable threads. This is called from the finalizer thread.
4912 * LOCKING: Acquires the threads lock.
4915 mono_threads_join_threads (void)
4918 GHashTableIter iter;
4925 if (!joinable_thread_count)
4929 joinable_threads_lock ();
4931 if (g_hash_table_size (joinable_threads)) {
4932 g_hash_table_iter_init (&iter, joinable_threads);
4933 g_hash_table_iter_next (&iter, &key, (void**)&tid);
4934 thread = (pthread_t)tid;
4935 g_hash_table_remove (joinable_threads, key);
4936 joinable_thread_count --;
4939 joinable_threads_unlock ();
4941 if (thread != pthread_self ())
4942 /* This shouldn't block */
4943 pthread_join (thread, NULL);
4954 * Wait for thread TID to exit.
4955 * LOCKING: Acquires the threads lock.
4958 mono_thread_join (gpointer tid)
4962 gboolean found = FALSE;
4964 joinable_threads_lock ();
4965 if (!joinable_threads)
4966 joinable_threads = g_hash_table_new (NULL, NULL);
4967 if (g_hash_table_lookup (joinable_threads, tid)) {
4968 g_hash_table_remove (joinable_threads, tid);
4969 joinable_thread_count --;
4972 joinable_threads_unlock ();
4975 thread = (pthread_t)tid;
4976 pthread_join (thread, NULL);
4981 mono_thread_internal_check_for_interruption_critical (MonoInternalThread *thread)
4983 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
4984 mono_thread_interruption_checkpoint ();
4987 static inline gboolean
4988 is_appdomainunloaded_exception (MonoClass *klass)
4990 return klass == mono_class_get_appdomain_unloaded_exception_class ();
4993 static inline gboolean
4994 is_threadabort_exception (MonoClass *klass)
4996 return klass == mono_defaults.threadabortexception_class;
5000 mono_thread_internal_unhandled_exception (MonoObject* exc)
5002 if (mono_runtime_unhandled_exception_policy_get () == MONO_UNHANDLED_POLICY_CURRENT) {
5003 MonoClass *klass = exc->vtable->klass;
5004 if (is_threadabort_exception (klass)) {
5005 mono_thread_internal_reset_abort (mono_thread_internal_current ());
5006 } else if (!is_appdomainunloaded_exception (klass)) {
5007 mono_unhandled_exception (exc);
5008 if (mono_environment_exitcode_get () == 1)
5015 ves_icall_System_Threading_Thread_GetStackTraces (MonoArray **out_threads, MonoArray **out_stack_traces)
5017 mono_threads_get_thread_dump (out_threads, out_stack_traces);
5021 * mono_threads_attach_coop: called by native->managed wrappers
5025 * - @return: the original domain which needs to be restored, or NULL.
5028 * - @dummy: contains the original domain
5029 * - @return: a cookie containing current MonoThreadInfo*.
5032 mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
5036 gboolean fresh_thread;
5039 /* Happens when called from AOTed code which is only used in the root domain. */
5040 domain = mono_get_root_domain ();
5045 /* On coop, when we detached, we moved the thread from RUNNING->BLOCKING.
5046 * If we try to reattach we do a BLOCKING->RUNNING transition. If the thread
5047 * is fresh, mono_thread_attach() will do a STARTING->RUNNING transition so
5048 * we're only responsible for making the cookie. */
5049 if (mono_threads_is_coop_enabled ()) {
5050 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
5051 fresh_thread = !info || !mono_thread_info_is_live (info);
5054 if (!mono_thread_internal_current ()) {
5055 mono_thread_attach_full (domain, FALSE, &error);
5056 mono_error_assert_ok (&error);
5059 mono_thread_set_state (mono_thread_internal_current (), ThreadState_Background);
5062 orig = mono_domain_get ();
5064 mono_domain_set (domain, TRUE);
5066 if (!mono_threads_is_coop_enabled ())
5067 return orig != domain ? orig : NULL;
5071 /* mono_thread_attach put the thread in RUNNING mode from STARTING, but we need to
5072 * return the right cookie. */
5073 return mono_threads_enter_gc_unsafe_region_cookie (mono_thread_info_current ());
5076 /* thread state (BLOCKING|RUNNING) -> RUNNING */
5077 return mono_threads_enter_gc_unsafe_region (dummy);
5082 * mono_threads_detach_coop: called by native->managed wrappers
5085 * - @cookie: the original domain which needs to be restored, or NULL.
5089 * - @cookie: contains current MonoThreadInfo* if it was in BLOCKING mode, NULL otherwise
5090 * - @dummy: contains the original domain
5093 mono_threads_detach_coop (gpointer cookie, gpointer *dummy)
5095 MonoDomain *domain, *orig;
5097 if (!mono_threads_is_coop_enabled ()) {
5098 orig = (MonoDomain*) cookie;
5100 mono_domain_set (orig, TRUE);
5102 orig = (MonoDomain*) *dummy;
5104 domain = mono_domain_get ();
5107 /* it won't do anything if cookie is NULL
5108 * thread state RUNNING -> (RUNNING|BLOCKING) */
5109 mono_threads_exit_gc_unsafe_region (cookie, dummy);
5111 if (orig != domain) {
5113 mono_domain_unset ();
5115 mono_domain_set (orig, TRUE);