Get rid of the macros in mono-tls.h to fix many warnings.
[mono-project/dkf.git] / mono / metadata / threads.c
blobcd17b07dc9e6105dbdd9cc59280b122473c64568
1 /*
2 * threads.c: Thread support internal calls
4 * Author:
5 * Dick Porter (dick@ximian.com)
6 * Paolo Molaro (lupus@ximian.com)
7 * Patrik Torstensson (patrik.torstensson@labs2.com)
9 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
10 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
13 #include <config.h>
15 #include <glib.h>
16 #include <signal.h>
17 #include <string.h>
19 #if defined(__OpenBSD__)
20 #include <pthread.h>
21 #include <pthread_np.h>
22 #endif
24 #include <mono/metadata/object.h>
25 #include <mono/metadata/domain-internals.h>
26 #include <mono/metadata/profiler-private.h>
27 #include <mono/metadata/threads.h>
28 #include <mono/metadata/threadpool.h>
29 #include <mono/metadata/threads-types.h>
30 #include <mono/metadata/exception.h>
31 #include <mono/metadata/environment.h>
32 #include <mono/metadata/monitor.h>
33 #include <mono/metadata/gc-internal.h>
34 #include <mono/metadata/marshal.h>
35 #include <mono/io-layer/io-layer.h>
36 #ifndef HOST_WIN32
37 #include <mono/io-layer/threads.h>
38 #endif
39 #include <mono/metadata/object-internals.h>
40 #include <mono/metadata/mono-debug-debugger.h>
41 #include <mono/utils/mono-compiler.h>
42 #include <mono/utils/mono-mmap.h>
43 #include <mono/utils/mono-membar.h>
44 #include <mono/utils/mono-time.h>
45 #include <mono/utils/mono-threads.h>
46 #include <mono/utils/hazard-pointer.h>
47 #include <mono/utils/mono-tls.h>
49 #include <mono/metadata/gc-internal.h>
51 #ifdef PLATFORM_ANDROID
52 #include <errno.h>
54 extern int tkill (pid_t tid, int signal);
55 #endif
57 /*#define THREAD_DEBUG(a) do { a; } while (0)*/
58 #define THREAD_DEBUG(a)
59 /*#define THREAD_WAIT_DEBUG(a) do { a; } while (0)*/
60 #define THREAD_WAIT_DEBUG(a)
61 /*#define LIBGC_DEBUG(a) do { a; } while (0)*/
62 #define LIBGC_DEBUG(a)
64 #define SPIN_TRYLOCK(i) (InterlockedCompareExchange (&(i), 1, 0) == 0)
65 #define SPIN_LOCK(i) do { \
66 if (SPIN_TRYLOCK (i)) \
67 break; \
68 } while (1)
70 #define SPIN_UNLOCK(i) i = 0
72 /* Provide this for systems with glib < 2.6 */
73 #ifndef G_GSIZE_FORMAT
74 # if GLIB_SIZEOF_LONG == 8
75 # define G_GSIZE_FORMAT "lu"
76 # else
77 # define G_GSIZE_FORMAT "u"
78 # endif
79 #endif
81 struct StartInfo
83 guint32 (*func)(void *);
84 MonoThread *obj;
85 MonoObject *delegate;
86 void *start_arg;
89 typedef union {
90 gint32 ival;
91 gfloat fval;
92 } IntFloatUnion;
94 typedef union {
95 gint64 ival;
96 gdouble fval;
97 } LongDoubleUnion;
99 typedef struct _MonoThreadDomainTls MonoThreadDomainTls;
100 struct _MonoThreadDomainTls {
101 MonoThreadDomainTls *next;
102 guint32 offset;
103 guint32 size;
106 typedef struct {
107 int idx;
108 int offset;
109 MonoThreadDomainTls *freelist;
110 } StaticDataInfo;
112 /* Number of cached culture objects in the MonoThread->cached_culture_info array
113 * (per-type): we use the first NUM entries for CultureInfo and the last for
114 * UICultureInfo. So the size of the array is really NUM_CACHED_CULTURES * 2.
116 #define NUM_CACHED_CULTURES 4
117 #define CULTURES_START_IDX 0
118 #define UICULTURES_START_IDX NUM_CACHED_CULTURES
120 /* Controls access to the 'threads' hash table */
121 #define mono_threads_lock() EnterCriticalSection (&threads_mutex)
122 #define mono_threads_unlock() LeaveCriticalSection (&threads_mutex)
123 static CRITICAL_SECTION threads_mutex;
125 /* Controls access to context static data */
126 #define mono_contexts_lock() EnterCriticalSection (&contexts_mutex)
127 #define mono_contexts_unlock() LeaveCriticalSection (&contexts_mutex)
128 static CRITICAL_SECTION contexts_mutex;
130 /* Holds current status of static data heap */
131 static StaticDataInfo thread_static_info;
132 static StaticDataInfo context_static_info;
134 /* The hash of existing threads (key is thread ID, value is
135 * MonoInternalThread*) that need joining before exit
137 static MonoGHashTable *threads=NULL;
140 * Threads which are starting up and they are not in the 'threads' hash yet.
141 * When handle_store is called for a thread, it will be removed from this hash table.
142 * Protected by mono_threads_lock ().
144 static MonoGHashTable *threads_starting_up = NULL;
146 /* Maps a MonoThread to its start argument */
147 /* Protected by mono_threads_lock () */
148 static MonoGHashTable *thread_start_args = NULL;
150 /* The TLS key that holds the MonoObject assigned to each thread */
151 static MonoNativeTlsKey current_object_key;
153 #ifdef MONO_HAVE_FAST_TLS
154 /* we need to use both the Tls* functions and __thread because
155 * the gc needs to see all the threads
157 MONO_FAST_TLS_DECLARE(tls_current_object);
158 #define SET_CURRENT_OBJECT(x) do { \
159 MONO_FAST_TLS_SET (tls_current_object, x); \
160 mono_native_tls_set_value (current_object_key, x); \
161 } while (FALSE)
162 #define GET_CURRENT_OBJECT() ((MonoInternalThread*) MONO_FAST_TLS_GET (tls_current_object))
163 #else
164 #define SET_CURRENT_OBJECT(x) mono_native_tls_set_value (current_object_key, x)
165 #define GET_CURRENT_OBJECT() (MonoInternalThread*) mono_native_tls_get_value (current_object_key)
166 #endif
168 /* function called at thread start */
169 static MonoThreadStartCB mono_thread_start_cb = NULL;
171 /* function called at thread attach */
172 static MonoThreadAttachCB mono_thread_attach_cb = NULL;
174 /* function called at thread cleanup */
175 static MonoThreadCleanupFunc mono_thread_cleanup_fn = NULL;
177 /* function called to notify the runtime about a pending exception on the current thread */
178 static MonoThreadNotifyPendingExcFunc mono_thread_notify_pending_exc_fn = NULL;
180 /* The default stack size for each thread */
181 static guint32 default_stacksize = 0;
182 #define default_stacksize_for_thread(thread) ((thread)->stack_size? (thread)->stack_size: default_stacksize)
184 static void thread_adjust_static_data (MonoInternalThread *thread);
185 static void mono_free_static_data (gpointer* static_data, gboolean threadlocal);
186 static void mono_init_static_data_info (StaticDataInfo *static_data);
187 static guint32 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align);
188 static gboolean mono_thread_resume (MonoInternalThread* thread);
189 static void mono_thread_start (MonoThread *thread);
190 static void signal_thread_state_change (MonoInternalThread *thread);
191 static void abort_thread_internal (MonoInternalThread *thread, gboolean can_raise_exception, gboolean install_async_abort);
192 static void suspend_thread_internal (MonoInternalThread *thread, gboolean interrupt);
193 static void self_suspend_internal (MonoInternalThread *thread);
194 static gboolean resume_thread_internal (MonoInternalThread *thread);
196 static MonoException* mono_thread_execute_interruption (MonoInternalThread *thread);
197 static void ref_stack_destroy (gpointer rs);
199 /* Spin lock for InterlockedXXX 64 bit functions */
200 #define mono_interlocked_lock() EnterCriticalSection (&interlocked_mutex)
201 #define mono_interlocked_unlock() LeaveCriticalSection (&interlocked_mutex)
202 static CRITICAL_SECTION interlocked_mutex;
204 /* global count of thread interruptions requested */
205 static gint32 thread_interruption_requested = 0;
207 /* Event signaled when a thread changes its background mode */
208 static HANDLE background_change_event;
210 static gboolean shutting_down = FALSE;
212 static gint32 managed_thread_id_counter = 0;
214 static guint32
215 get_next_managed_thread_id (void)
217 return InterlockedIncrement (&managed_thread_id_counter);
220 MonoNativeTlsKey
221 mono_thread_get_tls_key (void)
223 return current_object_key;
226 gint32
227 mono_thread_get_tls_offset (void)
229 int offset;
230 MONO_THREAD_VAR_OFFSET (tls_current_object,offset);
231 return offset;
234 /* handle_store() and handle_remove() manage the array of threads that
235 * still need to be waited for when the main thread exits.
237 * If handle_store() returns FALSE the thread must not be started
238 * because Mono is shutting down.
240 static gboolean handle_store(MonoThread *thread)
242 mono_threads_lock ();
244 THREAD_DEBUG (g_message ("%s: thread %p ID %"G_GSIZE_FORMAT, __func__, thread, (gsize)thread->internal_thread->tid));
246 if (threads_starting_up)
247 mono_g_hash_table_remove (threads_starting_up, thread);
249 if (shutting_down) {
250 mono_threads_unlock ();
251 return FALSE;
254 if(threads==NULL) {
255 MONO_GC_REGISTER_ROOT_FIXED (threads);
256 threads=mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC);
259 /* We don't need to duplicate thread->handle, because it is
260 * only closed when the thread object is finalized by the GC.
262 g_assert (thread->internal_thread);
263 mono_g_hash_table_insert(threads, (gpointer)(gsize)(thread->internal_thread->tid),
264 thread->internal_thread);
266 mono_threads_unlock ();
268 return TRUE;
271 static gboolean handle_remove(MonoInternalThread *thread)
273 gboolean ret;
274 gsize tid = thread->tid;
276 THREAD_DEBUG (g_message ("%s: thread ID %"G_GSIZE_FORMAT, __func__, tid));
278 mono_threads_lock ();
280 if (threads) {
281 /* We have to check whether the thread object for the
282 * tid is still the same in the table because the
283 * thread might have been destroyed and the tid reused
284 * in the meantime, in which case the tid would be in
285 * the table, but with another thread object.
287 if (mono_g_hash_table_lookup (threads, (gpointer)tid) == thread) {
288 mono_g_hash_table_remove (threads, (gpointer)tid);
289 ret = TRUE;
290 } else {
291 ret = FALSE;
294 else
295 ret = FALSE;
297 mono_threads_unlock ();
299 /* Don't close the handle here, wait for the object finalizer
300 * to do it. Otherwise, the following race condition applies:
302 * 1) Thread exits (and handle_remove() closes the handle)
304 * 2) Some other handle is reassigned the same slot
306 * 3) Another thread tries to join the first thread, and
307 * blocks waiting for the reassigned handle to be signalled
308 * (which might never happen). This is possible, because the
309 * thread calling Join() still has a reference to the first
310 * thread's object.
312 return ret;
315 static void ensure_synch_cs_set (MonoInternalThread *thread)
317 CRITICAL_SECTION *synch_cs;
319 if (thread->synch_cs != NULL) {
320 return;
323 synch_cs = g_new0 (CRITICAL_SECTION, 1);
324 InitializeCriticalSection (synch_cs);
326 if (InterlockedCompareExchangePointer ((gpointer *)&thread->synch_cs,
327 synch_cs, NULL) != NULL) {
328 /* Another thread must have installed this CS */
329 DeleteCriticalSection (synch_cs);
330 g_free (synch_cs);
335 * NOTE: this function can be called also for threads different from the current one:
336 * make sure no code called from it will ever assume it is run on the thread that is
337 * getting cleaned up.
339 static void thread_cleanup (MonoInternalThread *thread)
341 g_assert (thread != NULL);
343 if (thread->abort_state_handle) {
344 mono_gchandle_free (thread->abort_state_handle);
345 thread->abort_state_handle = 0;
347 thread->abort_exc = NULL;
348 thread->current_appcontext = NULL;
351 * This is necessary because otherwise we might have
352 * cross-domain references which will not get cleaned up when
353 * the target domain is unloaded.
355 if (thread->cached_culture_info) {
356 int i;
357 for (i = 0; i < NUM_CACHED_CULTURES * 2; ++i)
358 mono_array_set (thread->cached_culture_info, MonoObject*, i, NULL);
361 /* if the thread is not in the hash it has been removed already */
362 if (!handle_remove (thread)) {
363 /* This needs to be called even if handle_remove () fails */
364 if (mono_thread_cleanup_fn)
365 mono_thread_cleanup_fn (thread);
366 return;
368 mono_release_type_locks (thread);
370 EnterCriticalSection (thread->synch_cs);
372 thread->state |= ThreadState_Stopped;
373 thread->state &= ~ThreadState_Background;
375 LeaveCriticalSection (thread->synch_cs);
377 mono_profiler_thread_end (thread->tid);
379 if (thread == mono_thread_internal_current ())
380 mono_thread_pop_appdomain_ref ();
382 thread->cached_culture_info = NULL;
384 mono_free_static_data (thread->static_data, TRUE);
385 thread->static_data = NULL;
386 ref_stack_destroy (thread->appdomain_refs);
387 thread->appdomain_refs = NULL;
389 if (mono_thread_cleanup_fn)
390 mono_thread_cleanup_fn (thread);
392 MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref);
395 static gpointer
396 get_thread_static_data (MonoInternalThread *thread, guint32 offset)
398 int idx;
399 g_assert ((offset & 0x80000000) == 0);
400 offset &= 0x7fffffff;
401 idx = (offset >> 24) - 1;
402 return ((char*) thread->static_data [idx]) + (offset & 0xffffff);
405 static MonoThread**
406 get_current_thread_ptr_for_domain (MonoDomain *domain, MonoInternalThread *thread)
408 static MonoClassField *current_thread_field = NULL;
410 guint32 offset;
412 if (!current_thread_field) {
413 current_thread_field = mono_class_get_field_from_name (mono_defaults.thread_class, "current_thread");
414 g_assert (current_thread_field);
417 mono_class_vtable (domain, mono_defaults.thread_class);
418 mono_domain_lock (domain);
419 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, current_thread_field));
420 mono_domain_unlock (domain);
421 g_assert (offset);
423 return get_thread_static_data (thread, offset);
426 static void
427 set_current_thread_for_domain (MonoDomain *domain, MonoInternalThread *thread, MonoThread *current)
429 MonoThread **current_thread_ptr = get_current_thread_ptr_for_domain (domain, thread);
431 g_assert (current->obj.vtable->domain == domain);
433 g_assert (!*current_thread_ptr);
434 *current_thread_ptr = current;
437 static MonoInternalThread*
438 create_internal_thread_object (void)
440 MonoVTable *vt = mono_class_vtable (mono_get_root_domain (), mono_defaults.internal_thread_class);
441 return (MonoInternalThread*)mono_gc_alloc_mature (vt);
444 static MonoThread*
445 create_thread_object (MonoDomain *domain)
447 MonoVTable *vt = mono_class_vtable (domain, mono_defaults.thread_class);
448 return (MonoThread*)mono_gc_alloc_mature (vt);
451 static MonoThread*
452 new_thread_with_internal (MonoDomain *domain, MonoInternalThread *internal)
454 MonoThread *thread = create_thread_object (domain);
455 MONO_OBJECT_SETREF (thread, internal_thread, internal);
456 return thread;
459 static void
460 init_root_domain_thread (MonoInternalThread *thread, MonoThread *candidate)
462 MonoDomain *domain = mono_get_root_domain ();
464 if (!candidate || candidate->obj.vtable->domain != domain)
465 candidate = new_thread_with_internal (domain, thread);
466 set_current_thread_for_domain (domain, thread, candidate);
467 g_assert (!thread->root_domain_thread);
468 MONO_OBJECT_SETREF (thread, root_domain_thread, candidate);
471 static guint32 WINAPI start_wrapper_internal(void *data)
473 MonoThreadInfo *info;
474 struct StartInfo *start_info=(struct StartInfo *)data;
475 guint32 (*start_func)(void *);
476 void *start_arg;
477 gsize tid;
479 * We don't create a local to hold start_info->obj, so hopefully it won't get pinned during a
480 * GC stack walk.
482 MonoInternalThread *internal = start_info->obj->internal_thread;
483 MonoObject *start_delegate = start_info->delegate;
484 MonoDomain *domain = start_info->obj->obj.vtable->domain;
486 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper", __func__, GetCurrentThreadId ()));
488 /* We can be sure start_info->obj->tid and
489 * start_info->obj->handle have been set, because the thread
490 * was created suspended, and these values were set before the
491 * thread resumed
494 info = mono_thread_info_current ();
495 g_assert (info);
496 internal->thread_info = info;
499 tid=internal->tid;
501 SET_CURRENT_OBJECT (internal);
503 mono_monitor_init_tls ();
505 /* Every thread references the appdomain which created it */
506 mono_thread_push_appdomain_ref (domain);
508 if (!mono_domain_set (domain, FALSE)) {
509 /* No point in raising an appdomain_unloaded exception here */
510 /* FIXME: Cleanup here */
511 mono_thread_pop_appdomain_ref ();
512 return 0;
515 start_func = start_info->func;
516 start_arg = start_info->start_arg;
518 /* We have to do this here because mono_thread_new_init()
519 requires that root_domain_thread is set up. */
520 thread_adjust_static_data (internal);
521 init_root_domain_thread (internal, start_info->obj);
523 /* This MUST be called before any managed code can be
524 * executed, as it calls the callback function that (for the
525 * jit) sets the lmf marker.
527 mono_thread_new_init (tid, &tid, start_func);
528 internal->stack_ptr = &tid;
530 LIBGC_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT",%d) Setting thread stack to %p", __func__, GetCurrentThreadId (), getpid (), thread->stack_ptr));
532 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, GetCurrentThreadId (), internal));
534 /* On 2.0 profile (and higher), set explicitly since state might have been
535 Unknown */
536 if (internal->apartment_state == ThreadApartmentState_Unknown)
537 internal->apartment_state = ThreadApartmentState_MTA;
539 mono_thread_init_apartment_state ();
541 if(internal->start_notify!=NULL) {
542 /* Let the thread that called Start() know we're
543 * ready
545 ReleaseSemaphore (internal->start_notify, 1, NULL);
548 mono_threads_lock ();
549 mono_g_hash_table_remove (thread_start_args, start_info->obj);
550 mono_threads_unlock ();
552 mono_thread_set_execution_context (start_info->obj->ec_to_set);
553 start_info->obj->ec_to_set = NULL;
555 g_free (start_info);
556 THREAD_DEBUG (g_message ("%s: start_wrapper for %"G_GSIZE_FORMAT, __func__,
557 internal->tid));
560 * Call this after calling start_notify, since the profiler callback might want
561 * to lock the thread, and the lock is held by thread_start () which waits for
562 * start_notify.
564 mono_profiler_thread_start (tid);
566 /* start_func is set only for unmanaged start functions */
567 if (start_func) {
568 start_func (start_arg);
569 } else {
570 void *args [1];
571 g_assert (start_delegate != NULL);
572 args [0] = start_arg;
573 /* we may want to handle the exception here. See comment below on unhandled exceptions */
574 mono_runtime_delegate_invoke (start_delegate, args, NULL);
577 /* If the thread calls ExitThread at all, this remaining code
578 * will not be executed, but the main thread will eventually
579 * call thread_cleanup() on this thread's behalf.
582 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper terminating", __func__, GetCurrentThreadId ()));
584 thread_cleanup (internal);
586 /* Do any cleanup needed for apartment state. This
587 * cannot be done in thread_cleanup since thread_cleanup could be
588 * called for a thread other than the current thread.
589 * mono_thread_cleanup_apartment_state cleans up apartment
590 * for the current thead */
591 mono_thread_cleanup_apartment_state ();
593 /* Remove the reference to the thread object in the TLS data,
594 * so the thread object can be finalized. This won't be
595 * reached if the thread threw an uncaught exception, so those
596 * thread handles will stay referenced :-( (This is due to
597 * missing support for scanning thread-specific data in the
598 * Boehm GC - the io-layer keeps a GC-visible hash of pointers
599 * to TLS data.)
601 SET_CURRENT_OBJECT (NULL);
602 mono_domain_unset ();
604 return(0);
607 static guint32 WINAPI start_wrapper(void *data)
609 #ifdef HAVE_SGEN_GC
610 volatile int dummy;
612 /* Avoid scanning the frames above this frame during a GC */
613 mono_gc_set_stack_end ((void*)&dummy);
614 #endif
616 return start_wrapper_internal (data);
619 void mono_thread_new_init (intptr_t tid, gpointer stack_start, gpointer func)
621 if (mono_thread_start_cb) {
622 mono_thread_start_cb (tid, stack_start, func);
626 void mono_threads_set_default_stacksize (guint32 stacksize)
628 default_stacksize = stacksize;
631 guint32 mono_threads_get_default_stacksize (void)
633 return default_stacksize;
637 * mono_create_thread:
639 * This is a wrapper around CreateThread which handles differences in the type of
640 * the the 'tid' argument.
642 gpointer mono_create_thread (WapiSecurityAttributes *security,
643 guint32 stacksize, WapiThreadStart start,
644 gpointer param, guint32 create, gsize *tid)
646 gpointer res;
648 #ifdef HOST_WIN32
649 DWORD real_tid;
651 res = CreateThread (security, stacksize, start, param, create, &real_tid);
652 if (tid)
653 *tid = real_tid;
654 #else
655 res = CreateThread (security, stacksize, start, param, create, tid);
656 #endif
658 return res;
662 * The thread start argument may be an object reference, and there is
663 * no ref to keep it alive when the new thread is started but not yet
664 * registered with the collector. So we store it in a GC tracked hash
665 * table.
667 * LOCKING: Assumes the threads lock is held.
669 static void
670 register_thread_start_argument (MonoThread *thread, struct StartInfo *start_info)
672 if (thread_start_args == NULL) {
673 MONO_GC_REGISTER_ROOT_FIXED (thread_start_args);
674 thread_start_args = mono_g_hash_table_new (NULL, NULL);
676 mono_g_hash_table_insert (thread_start_args, thread, start_info->start_arg);
679 MonoInternalThread* mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gboolean threadpool_thread, guint32 stack_size)
681 MonoThread *thread;
682 MonoInternalThread *internal;
683 HANDLE thread_handle;
684 struct StartInfo *start_info;
685 gsize tid;
687 thread = create_thread_object (domain);
688 internal = create_internal_thread_object ();
689 MONO_OBJECT_SETREF (thread, internal_thread, internal);
691 start_info=g_new0 (struct StartInfo, 1);
692 start_info->func = func;
693 start_info->obj = thread;
694 start_info->start_arg = arg;
696 mono_threads_lock ();
697 if (shutting_down) {
698 mono_threads_unlock ();
699 g_free (start_info);
700 return NULL;
702 if (threads_starting_up == NULL) {
703 MONO_GC_REGISTER_ROOT_FIXED (threads_starting_up);
704 threads_starting_up = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_VALUE_GC);
707 register_thread_start_argument (thread, start_info);
708 mono_g_hash_table_insert (threads_starting_up, thread, thread);
709 mono_threads_unlock ();
711 if (stack_size == 0)
712 stack_size = default_stacksize_for_thread (internal);
714 /* Create suspended, so we can do some housekeeping before the thread
715 * starts
717 thread_handle = mono_create_thread (NULL, stack_size, (LPTHREAD_START_ROUTINE)start_wrapper, start_info,
718 CREATE_SUSPENDED, &tid);
719 THREAD_DEBUG (g_message ("%s: Started thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread_handle));
720 if (thread_handle == NULL) {
721 /* The thread couldn't be created, so throw an exception */
722 mono_threads_lock ();
723 mono_g_hash_table_remove (threads_starting_up, thread);
724 mono_threads_unlock ();
725 g_free (start_info);
726 mono_raise_exception (mono_get_exception_execution_engine ("Couldn't create thread"));
727 return NULL;
730 internal->handle=thread_handle;
731 internal->tid=tid;
732 internal->apartment_state=ThreadApartmentState_Unknown;
733 internal->thread_pinning_ref = internal;
734 internal->managed_id = get_next_managed_thread_id ();
735 MONO_GC_REGISTER_ROOT (internal->thread_pinning_ref);
737 internal->synch_cs = g_new0 (CRITICAL_SECTION, 1);
738 InitializeCriticalSection (internal->synch_cs);
740 internal->threadpool_thread = threadpool_thread;
741 if (threadpool_thread)
742 mono_thread_set_state (internal, ThreadState_Background);
744 if (handle_store (thread))
745 ResumeThread (thread_handle);
747 return internal;
750 void
751 mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
753 mono_thread_create_internal (domain, func, arg, FALSE, 0);
757 * mono_thread_get_stack_bounds:
759 * Return the address and size of the current threads stack. Return NULL as the
760 * stack address if the stack address cannot be determined.
762 void
763 mono_thread_get_stack_bounds (guint8 **staddr, size_t *stsize)
765 #if defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
766 *staddr = (guint8*)pthread_get_stackaddr_np (pthread_self ());
767 *stsize = pthread_get_stacksize_np (pthread_self ());
769 /* staddr points to the start of the stack, not the end */
770 *staddr -= *stsize;
771 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
772 return;
773 /* FIXME: simplify the mess below */
774 #elif !defined(HOST_WIN32)
775 pthread_attr_t attr;
776 guint8 *current = (guint8*)&attr;
778 pthread_attr_init (&attr);
779 # ifdef HAVE_PTHREAD_GETATTR_NP
780 pthread_getattr_np (pthread_self(), &attr);
781 # else
782 # ifdef HAVE_PTHREAD_ATTR_GET_NP
783 pthread_attr_get_np (pthread_self(), &attr);
784 # elif defined(sun)
785 *staddr = NULL;
786 pthread_attr_getstacksize (&attr, &stsize);
787 # elif defined(__OpenBSD__)
788 stack_t ss;
789 int rslt;
791 rslt = pthread_stackseg_np(pthread_self(), &ss);
792 g_assert (rslt == 0);
794 *staddr = (guint8*)((size_t)ss.ss_sp - ss.ss_size);
795 *stsize = ss.ss_size;
796 # else
797 *staddr = NULL;
798 *stsize = 0;
799 return;
800 # endif
801 # endif
803 # if !defined(sun)
804 # if !defined(__OpenBSD__)
805 pthread_attr_getstack (&attr, (void**)staddr, stsize);
806 # endif
807 if (*staddr)
808 g_assert ((current > *staddr) && (current < *staddr + *stsize));
809 # endif
811 pthread_attr_destroy (&attr);
812 #else
813 *staddr = NULL;
814 *stsize = (size_t)-1;
815 #endif
817 /* When running under emacs, sometimes staddr is not aligned to a page size */
818 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
821 MonoThread *
822 mono_thread_attach (MonoDomain *domain)
824 MonoInternalThread *thread;
825 MonoThread *current_thread;
826 HANDLE thread_handle;
827 gsize tid;
829 if ((thread = mono_thread_internal_current ())) {
830 if (domain != mono_domain_get ())
831 mono_domain_set (domain, TRUE);
832 /* Already attached */
833 return mono_thread_current ();
836 if (!mono_gc_register_thread (&domain)) {
837 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.", GetCurrentThreadId ());
840 thread = create_internal_thread_object ();
842 thread_handle = GetCurrentThread ();
843 g_assert (thread_handle);
845 tid=GetCurrentThreadId ();
848 * The handle returned by GetCurrentThread () is a pseudo handle, so it can't be used to
849 * refer to the thread from other threads for things like aborting.
851 DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle,
852 THREAD_ALL_ACCESS, TRUE, 0);
854 thread->handle=thread_handle;
855 thread->tid=tid;
856 #ifdef PLATFORM_ANDROID
857 thread->android_tid = (gpointer) gettid ();
858 #endif
859 thread->apartment_state=ThreadApartmentState_Unknown;
860 thread->thread_pinning_ref = thread;
861 thread->managed_id = get_next_managed_thread_id ();
862 MONO_GC_REGISTER_ROOT (thread->thread_pinning_ref);
864 thread->stack_ptr = &tid;
866 thread->synch_cs = g_new0 (CRITICAL_SECTION, 1);
867 InitializeCriticalSection (thread->synch_cs);
869 THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread_handle));
871 current_thread = new_thread_with_internal (domain, thread);
873 if (!handle_store (current_thread)) {
874 /* Mono is shutting down, so just wait for the end */
875 for (;;)
876 Sleep (10000);
879 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, GetCurrentThreadId (), thread));
881 SET_CURRENT_OBJECT (thread);
882 mono_domain_set (domain, TRUE);
884 mono_monitor_init_tls ();
886 thread_adjust_static_data (thread);
888 init_root_domain_thread (thread, current_thread);
889 if (domain != mono_get_root_domain ())
890 set_current_thread_for_domain (domain, thread, current_thread);
893 if (mono_thread_attach_cb) {
894 guint8 *staddr;
895 size_t stsize;
897 mono_thread_get_stack_bounds (&staddr, &stsize);
899 if (staddr == NULL)
900 mono_thread_attach_cb (tid, &tid);
901 else
902 mono_thread_attach_cb (tid, staddr + stsize);
905 // FIXME: Need a separate callback
906 mono_profiler_thread_start (tid);
908 return current_thread;
911 void
912 mono_thread_detach (MonoThread *thread)
914 g_return_if_fail (thread != NULL);
916 THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->internal_thread->tid));
918 thread_cleanup (thread->internal_thread);
920 SET_CURRENT_OBJECT (NULL);
921 mono_domain_unset ();
923 /* Don't need to CloseHandle this thread, even though we took a
924 * reference in mono_thread_attach (), because the GC will do it
925 * when the Thread object is finalised.
929 void
930 mono_thread_exit ()
932 MonoInternalThread *thread = mono_thread_internal_current ();
934 THREAD_DEBUG (g_message ("%s: mono_thread_exit for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
936 thread_cleanup (thread);
937 SET_CURRENT_OBJECT (NULL);
938 mono_domain_unset ();
940 /* we could add a callback here for embedders to use. */
941 if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread))
942 exit (mono_environment_exitcode_get ());
943 ExitThread (-1);
946 void
947 ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this)
949 MonoInternalThread *internal = create_internal_thread_object ();
951 internal->state = ThreadState_Unstarted;
952 internal->apartment_state = ThreadApartmentState_Unknown;
953 internal->managed_id = get_next_managed_thread_id ();
955 InterlockedCompareExchangePointer ((gpointer)&this->internal_thread, internal, NULL);
958 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this,
959 MonoObject *start)
961 guint32 (*start_func)(void *);
962 struct StartInfo *start_info;
963 HANDLE thread;
964 gsize tid;
965 MonoInternalThread *internal;
967 THREAD_DEBUG (g_message("%s: Trying to start a new thread: this (%p) start (%p)", __func__, this, start));
969 if (!this->internal_thread)
970 ves_icall_System_Threading_Thread_ConstructInternalThread (this);
971 internal = this->internal_thread;
973 ensure_synch_cs_set (internal);
975 EnterCriticalSection (internal->synch_cs);
977 if ((internal->state & ThreadState_Unstarted) == 0) {
978 LeaveCriticalSection (internal->synch_cs);
979 mono_raise_exception (mono_get_exception_thread_state ("Thread has already been started."));
980 return NULL;
983 if ((internal->state & ThreadState_Aborted) != 0) {
984 LeaveCriticalSection (internal->synch_cs);
985 return this;
987 start_func = NULL;
989 /* This is freed in start_wrapper */
990 start_info = g_new0 (struct StartInfo, 1);
991 start_info->func = start_func;
992 start_info->start_arg = this->start_obj; /* FIXME: GC object stored in unmanaged memory */
993 start_info->delegate = start;
994 start_info->obj = this;
995 g_assert (this->obj.vtable->domain == mono_domain_get ());
997 internal->start_notify=CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
998 if (internal->start_notify==NULL) {
999 LeaveCriticalSection (internal->synch_cs);
1000 g_warning ("%s: CreateSemaphore error 0x%x", __func__, GetLastError ());
1001 g_free (start_info);
1002 return(NULL);
1005 mono_threads_lock ();
1006 register_thread_start_argument (this, start_info);
1007 if (threads_starting_up == NULL) {
1008 MONO_GC_REGISTER_ROOT_FIXED (threads_starting_up);
1009 threads_starting_up = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_VALUE_GC);
1011 mono_g_hash_table_insert (threads_starting_up, this, this);
1012 mono_threads_unlock ();
1014 thread=mono_create_thread(NULL, default_stacksize_for_thread (internal), (LPTHREAD_START_ROUTINE)start_wrapper, start_info,
1015 CREATE_SUSPENDED, &tid);
1016 if(thread==NULL) {
1017 LeaveCriticalSection (internal->synch_cs);
1018 mono_threads_lock ();
1019 mono_g_hash_table_remove (threads_starting_up, this);
1020 mono_threads_unlock ();
1021 g_warning("%s: CreateThread error 0x%x", __func__, GetLastError());
1022 return(NULL);
1025 internal->handle=thread;
1026 internal->tid=tid;
1027 internal->thread_pinning_ref = internal;
1028 MONO_GC_REGISTER_ROOT (internal->thread_pinning_ref);
1031 /* Don't call handle_store() here, delay it to Start.
1032 * We can't join a thread (trying to will just block
1033 * forever) until it actually starts running, so don't
1034 * store the handle till then.
1037 mono_thread_start (this);
1039 internal->state &= ~ThreadState_Unstarted;
1041 THREAD_DEBUG (g_message ("%s: Started thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread));
1043 LeaveCriticalSection (internal->synch_cs);
1044 return(thread);
1048 void ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThread *this, HANDLE thread)
1050 MONO_ARCH_SAVE_REGS;
1052 THREAD_DEBUG (g_message ("%s: Closing thread %p, handle %p", __func__, this, thread));
1054 if (thread)
1055 CloseHandle (thread);
1057 if (this->synch_cs) {
1058 CRITICAL_SECTION *synch_cs = this->synch_cs;
1059 this->synch_cs = NULL;
1060 DeleteCriticalSection (synch_cs);
1061 g_free (synch_cs);
1064 if (this->name) {
1065 void *name = this->name;
1066 this->name = NULL;
1067 g_free (name);
1071 static void mono_thread_start (MonoThread *thread)
1073 MonoInternalThread *internal = thread->internal_thread;
1075 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Launching thread %p (%"G_GSIZE_FORMAT")", __func__, GetCurrentThreadId (), internal, (gsize)internal->tid));
1077 /* Only store the handle when the thread is about to be
1078 * launched, to avoid the main thread deadlocking while trying
1079 * to clean up a thread that will never be signalled.
1081 if (!handle_store (thread))
1082 return;
1084 ResumeThread (internal->handle);
1086 if(internal->start_notify!=NULL) {
1087 /* Wait for the thread to set up its TLS data etc, so
1088 * theres no potential race condition if someone tries
1089 * to look up the data believing the thread has
1090 * started
1093 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") waiting for thread %p (%"G_GSIZE_FORMAT") to start", __func__, GetCurrentThreadId (), internal, (gsize)internal->tid));
1095 WaitForSingleObjectEx (internal->start_notify, INFINITE, FALSE);
1096 CloseHandle (internal->start_notify);
1097 internal->start_notify = NULL;
1100 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Done launching thread %p (%"G_GSIZE_FORMAT")", __func__, GetCurrentThreadId (), internal, (gsize)internal->tid));
1103 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
1105 guint32 res;
1106 MonoInternalThread *thread = mono_thread_internal_current ();
1108 THREAD_DEBUG (g_message ("%s: Sleeping for %d ms", __func__, ms));
1110 mono_thread_current_check_pending_interrupt ();
1112 while (TRUE) {
1113 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1115 res = SleepEx(ms,TRUE);
1117 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1119 if (res == WAIT_IO_COMPLETION) { /* we might have been interrupted */
1120 MonoException* exc = mono_thread_execute_interruption (thread);
1121 if (exc) {
1122 mono_raise_exception (exc);
1123 } else {
1124 // FIXME: !INFINITE
1125 if (ms != INFINITE)
1126 break;
1128 } else {
1129 break;
1134 void ves_icall_System_Threading_Thread_SpinWait_nop (void)
1138 gint32
1139 ves_icall_System_Threading_Thread_GetDomainID (void)
1141 MONO_ARCH_SAVE_REGS;
1143 return mono_domain_get()->domain_id;
1146 gboolean
1147 ves_icall_System_Threading_Thread_Yield (void)
1149 #ifdef HOST_WIN32
1150 return SwitchToThread ();
1151 #else
1152 return sched_yield () == 0;
1153 #endif
1157 * mono_thread_get_name:
1159 * Return the name of the thread. NAME_LEN is set to the length of the name.
1160 * Return NULL if the thread has no name. The returned memory is owned by the
1161 * caller.
1163 gunichar2*
1164 mono_thread_get_name (MonoInternalThread *this_obj, guint32 *name_len)
1166 gunichar2 *res;
1168 ensure_synch_cs_set (this_obj);
1170 EnterCriticalSection (this_obj->synch_cs);
1172 if (!this_obj->name) {
1173 *name_len = 0;
1174 res = NULL;
1175 } else {
1176 *name_len = this_obj->name_len;
1177 res = g_new (gunichar2, this_obj->name_len);
1178 memcpy (res, this_obj->name, sizeof (gunichar2) * this_obj->name_len);
1181 LeaveCriticalSection (this_obj->synch_cs);
1183 return res;
1186 MonoString*
1187 ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThread *this_obj)
1189 MonoString* str;
1191 ensure_synch_cs_set (this_obj);
1193 EnterCriticalSection (this_obj->synch_cs);
1195 if (!this_obj->name)
1196 str = NULL;
1197 else
1198 str = mono_string_new_utf16 (mono_domain_get (), this_obj->name, this_obj->name_len);
1200 LeaveCriticalSection (this_obj->synch_cs);
1202 return str;
1205 void
1206 ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj, MonoString *name)
1208 ensure_synch_cs_set (this_obj);
1210 EnterCriticalSection (this_obj->synch_cs);
1212 if (this_obj->name) {
1213 LeaveCriticalSection (this_obj->synch_cs);
1215 mono_raise_exception (mono_get_exception_invalid_operation ("Thread.Name can only be set once."));
1216 return;
1218 if (name) {
1219 this_obj->name = g_new (gunichar2, mono_string_length (name));
1220 memcpy (this_obj->name, mono_string_chars (name), mono_string_length (name) * 2);
1221 this_obj->name_len = mono_string_length (name);
1223 else
1224 this_obj->name = NULL;
1226 LeaveCriticalSection (this_obj->synch_cs);
1227 if (this_obj->name) {
1228 char *tname = mono_string_to_utf8 (name);
1229 mono_profiler_thread_name (this_obj->tid, tname);
1230 mono_free (tname);
1234 /* If the array is already in the requested domain, we just return it,
1235 otherwise we return a copy in that domain. */
1236 static MonoArray*
1237 byte_array_to_domain (MonoArray *arr, MonoDomain *domain)
1239 MonoArray *copy;
1241 if (!arr)
1242 return NULL;
1244 if (mono_object_domain (arr) == domain)
1245 return arr;
1247 copy = mono_array_new (domain, mono_defaults.byte_class, arr->max_length);
1248 mono_gc_memmove (mono_array_addr (copy, guint8, 0), mono_array_addr (arr, guint8, 0), arr->max_length);
1249 return copy;
1252 MonoArray*
1253 ves_icall_System_Threading_Thread_ByteArrayToRootDomain (MonoArray *arr)
1255 return byte_array_to_domain (arr, mono_get_root_domain ());
1258 MonoArray*
1259 ves_icall_System_Threading_Thread_ByteArrayToCurrentDomain (MonoArray *arr)
1261 return byte_array_to_domain (arr, mono_domain_get ());
1264 MonoThread *
1265 mono_thread_current (void)
1267 MonoDomain *domain = mono_domain_get ();
1268 MonoInternalThread *internal = mono_thread_internal_current ();
1269 MonoThread **current_thread_ptr;
1271 g_assert (internal);
1272 current_thread_ptr = get_current_thread_ptr_for_domain (domain, internal);
1274 if (!*current_thread_ptr) {
1275 g_assert (domain != mono_get_root_domain ());
1276 *current_thread_ptr = new_thread_with_internal (domain, internal);
1278 return *current_thread_ptr;
1281 MonoInternalThread*
1282 mono_thread_internal_current (void)
1284 MonoInternalThread *res = GET_CURRENT_OBJECT ();
1285 THREAD_DEBUG (g_message ("%s: returning %p", __func__, res));
1286 return res;
1289 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoInternalThread *this,
1290 int ms, HANDLE thread)
1292 MonoInternalThread *cur_thread = mono_thread_internal_current ();
1293 gboolean ret;
1295 mono_thread_current_check_pending_interrupt ();
1297 ensure_synch_cs_set (this);
1299 EnterCriticalSection (this->synch_cs);
1301 if ((this->state & ThreadState_Unstarted) != 0) {
1302 LeaveCriticalSection (this->synch_cs);
1304 mono_raise_exception (mono_get_exception_thread_state ("Thread has not been started."));
1305 return FALSE;
1308 LeaveCriticalSection (this->synch_cs);
1310 if(ms== -1) {
1311 ms=INFINITE;
1313 THREAD_DEBUG (g_message ("%s: joining thread handle %p, %d ms", __func__, thread, ms));
1315 mono_thread_set_state (cur_thread, ThreadState_WaitSleepJoin);
1317 ret=WaitForSingleObjectEx (thread, ms, TRUE);
1319 mono_thread_clr_state (cur_thread, ThreadState_WaitSleepJoin);
1321 if(ret==WAIT_OBJECT_0) {
1322 THREAD_DEBUG (g_message ("%s: join successful", __func__));
1324 return(TRUE);
1327 THREAD_DEBUG (g_message ("%s: join failed", __func__));
1329 return(FALSE);
1332 /* FIXME: exitContext isnt documented */
1333 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
1335 HANDLE *handles;
1336 guint32 numhandles;
1337 guint32 ret;
1338 guint32 i;
1339 MonoObject *waitHandle;
1340 MonoInternalThread *thread = mono_thread_internal_current ();
1342 /* Do this WaitSleepJoin check before creating objects */
1343 mono_thread_current_check_pending_interrupt ();
1345 numhandles = mono_array_length(mono_handles);
1346 handles = g_new0(HANDLE, numhandles);
1348 for(i = 0; i < numhandles; i++) {
1349 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
1350 handles [i] = mono_wait_handle_get_handle ((MonoWaitHandle *) waitHandle);
1353 if(ms== -1) {
1354 ms=INFINITE;
1357 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1359 ret=WaitForMultipleObjectsEx(numhandles, handles, TRUE, ms, TRUE);
1361 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1363 g_free(handles);
1365 if(ret==WAIT_FAILED) {
1366 THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Wait failed", __func__, GetCurrentThreadId ()));
1367 return(FALSE);
1368 } else if(ret==WAIT_TIMEOUT || ret == WAIT_IO_COMPLETION) {
1369 /* Do we want to try again if we get
1370 * WAIT_IO_COMPLETION? The documentation for
1371 * WaitHandle doesn't give any clues. (We'd have to
1372 * fiddle with the timeout if we retry.)
1374 THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Wait timed out", __func__, GetCurrentThreadId ()));
1375 return(FALSE);
1378 return(TRUE);
1381 /* FIXME: exitContext isnt documented */
1382 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
1384 HANDLE handles [MAXIMUM_WAIT_OBJECTS];
1385 guint32 numhandles;
1386 guint32 ret;
1387 guint32 i;
1388 MonoObject *waitHandle;
1389 MonoInternalThread *thread = mono_thread_internal_current ();
1390 guint32 start;
1392 /* Do this WaitSleepJoin check before creating objects */
1393 mono_thread_current_check_pending_interrupt ();
1395 numhandles = mono_array_length(mono_handles);
1396 if (numhandles > MAXIMUM_WAIT_OBJECTS)
1397 return WAIT_FAILED;
1399 for(i = 0; i < numhandles; i++) {
1400 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
1401 handles [i] = mono_wait_handle_get_handle ((MonoWaitHandle *) waitHandle);
1404 if(ms== -1) {
1405 ms=INFINITE;
1408 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1410 start = (ms == -1) ? 0 : mono_msec_ticks ();
1411 do {
1412 ret = WaitForMultipleObjectsEx (numhandles, handles, FALSE, ms, TRUE);
1413 if (ret != WAIT_IO_COMPLETION)
1414 break;
1415 if (ms != -1) {
1416 guint32 diff;
1418 diff = mono_msec_ticks () - start;
1419 ms -= diff;
1420 if (ms <= 0)
1421 break;
1423 } while (ms == -1 || ms > 0);
1425 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1427 THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") returning %d", __func__, GetCurrentThreadId (), ret));
1430 * These need to be here. See MSDN dos on WaitForMultipleObjects.
1432 if (ret >= WAIT_OBJECT_0 && ret <= WAIT_OBJECT_0 + numhandles - 1) {
1433 return ret - WAIT_OBJECT_0;
1435 else if (ret >= WAIT_ABANDONED_0 && ret <= WAIT_ABANDONED_0 + numhandles - 1) {
1436 return ret - WAIT_ABANDONED_0;
1438 else {
1439 return ret;
1443 /* FIXME: exitContext isnt documented */
1444 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
1446 guint32 ret;
1447 MonoInternalThread *thread = mono_thread_internal_current ();
1449 THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") waiting for %p, %d ms", __func__, GetCurrentThreadId (), handle, ms));
1451 if(ms== -1) {
1452 ms=INFINITE;
1455 mono_thread_current_check_pending_interrupt ();
1457 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1459 ret=WaitForSingleObjectEx (handle, ms, TRUE);
1461 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1463 if(ret==WAIT_FAILED) {
1464 THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Wait failed", __func__, GetCurrentThreadId ()));
1465 return(FALSE);
1466 } else if(ret==WAIT_TIMEOUT || ret == WAIT_IO_COMPLETION) {
1467 /* Do we want to try again if we get
1468 * WAIT_IO_COMPLETION? The documentation for
1469 * WaitHandle doesn't give any clues. (We'd have to
1470 * fiddle with the timeout if we retry.)
1472 THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Wait timed out", __func__, GetCurrentThreadId ()));
1473 return(FALSE);
1476 return(TRUE);
1479 gboolean
1480 ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (HANDLE toSignal, HANDLE toWait, gint32 ms, gboolean exitContext)
1482 guint32 ret;
1483 MonoInternalThread *thread = mono_thread_internal_current ();
1485 MONO_ARCH_SAVE_REGS;
1487 if (ms == -1)
1488 ms = INFINITE;
1490 mono_thread_current_check_pending_interrupt ();
1492 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1494 ret = SignalObjectAndWait (toSignal, toWait, ms, TRUE);
1496 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1498 return (!(ret == WAIT_TIMEOUT || ret == WAIT_IO_COMPLETION || ret == WAIT_FAILED));
1501 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created)
1503 HANDLE mutex;
1505 MONO_ARCH_SAVE_REGS;
1507 *created = TRUE;
1509 if (name == NULL) {
1510 mutex = CreateMutex (NULL, owned, NULL);
1511 } else {
1512 mutex = CreateMutex (NULL, owned, mono_string_chars (name));
1514 if (GetLastError () == ERROR_ALREADY_EXISTS) {
1515 *created = FALSE;
1519 return(mutex);
1522 MonoBoolean ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) {
1523 MONO_ARCH_SAVE_REGS;
1525 return(ReleaseMutex (handle));
1528 HANDLE ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name,
1529 gint32 rights,
1530 gint32 *error)
1532 HANDLE ret;
1534 MONO_ARCH_SAVE_REGS;
1536 *error = ERROR_SUCCESS;
1538 ret = OpenMutex (rights, FALSE, mono_string_chars (name));
1539 if (ret == NULL) {
1540 *error = GetLastError ();
1543 return(ret);
1547 HANDLE ves_icall_System_Threading_Semaphore_CreateSemaphore_internal (gint32 initialCount, gint32 maximumCount, MonoString *name, MonoBoolean *created)
1549 HANDLE sem;
1551 MONO_ARCH_SAVE_REGS;
1553 *created = TRUE;
1555 if (name == NULL) {
1556 sem = CreateSemaphore (NULL, initialCount, maximumCount, NULL);
1557 } else {
1558 sem = CreateSemaphore (NULL, initialCount, maximumCount,
1559 mono_string_chars (name));
1561 if (GetLastError () == ERROR_ALREADY_EXISTS) {
1562 *created = FALSE;
1566 return(sem);
1569 gint32 ves_icall_System_Threading_Semaphore_ReleaseSemaphore_internal (HANDLE handle, gint32 releaseCount, MonoBoolean *fail)
1571 gint32 prevcount;
1573 MONO_ARCH_SAVE_REGS;
1575 *fail = !ReleaseSemaphore (handle, releaseCount, &prevcount);
1577 return (prevcount);
1580 HANDLE ves_icall_System_Threading_Semaphore_OpenSemaphore_internal (MonoString *name, gint32 rights, gint32 *error)
1582 HANDLE ret;
1584 MONO_ARCH_SAVE_REGS;
1586 *error = ERROR_SUCCESS;
1588 ret = OpenSemaphore (rights, FALSE, mono_string_chars (name));
1589 if (ret == NULL) {
1590 *error = GetLastError ();
1593 return(ret);
1596 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual, MonoBoolean initial, MonoString *name, MonoBoolean *created)
1598 HANDLE event;
1600 MONO_ARCH_SAVE_REGS;
1602 *created = TRUE;
1604 if (name == NULL) {
1605 event = CreateEvent (NULL, manual, initial, NULL);
1606 } else {
1607 event = CreateEvent (NULL, manual, initial,
1608 mono_string_chars (name));
1610 if (GetLastError () == ERROR_ALREADY_EXISTS) {
1611 *created = FALSE;
1615 return(event);
1618 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
1619 MONO_ARCH_SAVE_REGS;
1621 return (SetEvent(handle));
1624 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
1625 MONO_ARCH_SAVE_REGS;
1627 return (ResetEvent(handle));
1630 void
1631 ves_icall_System_Threading_Events_CloseEvent_internal (HANDLE handle) {
1632 MONO_ARCH_SAVE_REGS;
1634 CloseHandle (handle);
1637 HANDLE ves_icall_System_Threading_Events_OpenEvent_internal (MonoString *name,
1638 gint32 rights,
1639 gint32 *error)
1641 HANDLE ret;
1643 MONO_ARCH_SAVE_REGS;
1645 *error = ERROR_SUCCESS;
1647 ret = OpenEvent (rights, FALSE, mono_string_chars (name));
1648 if (ret == NULL) {
1649 *error = GetLastError ();
1652 return(ret);
1655 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
1657 MONO_ARCH_SAVE_REGS;
1659 return InterlockedIncrement (location);
1662 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
1664 gint64 ret;
1666 MONO_ARCH_SAVE_REGS;
1668 mono_interlocked_lock ();
1670 ret = ++ *location;
1672 mono_interlocked_unlock ();
1675 return ret;
1678 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
1680 MONO_ARCH_SAVE_REGS;
1682 return InterlockedDecrement(location);
1685 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
1687 gint64 ret;
1689 MONO_ARCH_SAVE_REGS;
1691 mono_interlocked_lock ();
1693 ret = -- *location;
1695 mono_interlocked_unlock ();
1697 return ret;
1700 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location, gint32 value)
1702 MONO_ARCH_SAVE_REGS;
1704 return InterlockedExchange(location, value);
1707 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location, MonoObject *value)
1709 MonoObject *res;
1710 res = (MonoObject *) InterlockedExchangePointer((gpointer *) location, value);
1711 mono_gc_wbarrier_generic_nostore (location);
1712 return res;
1715 gpointer ves_icall_System_Threading_Interlocked_Exchange_IntPtr (gpointer *location, gpointer value)
1717 return InterlockedExchangePointer(location, value);
1720 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location, gfloat value)
1722 IntFloatUnion val, ret;
1724 MONO_ARCH_SAVE_REGS;
1726 val.fval = value;
1727 ret.ival = InterlockedExchange((gint32 *) location, val.ival);
1729 return ret.fval;
1732 gint64
1733 ves_icall_System_Threading_Interlocked_Exchange_Long (gint64 *location, gint64 value)
1735 #if SIZEOF_VOID_P == 8
1736 return (gint64) InterlockedExchangePointer((gpointer *) location, (gpointer)value);
1737 #else
1738 gint64 res;
1741 * According to MSDN, this function is only atomic with regards to the
1742 * other Interlocked functions on 32 bit platforms.
1744 mono_interlocked_lock ();
1745 res = *location;
1746 *location = value;
1747 mono_interlocked_unlock ();
1749 return res;
1750 #endif
1753 gdouble
1754 ves_icall_System_Threading_Interlocked_Exchange_Double (gdouble *location, gdouble value)
1756 #if SIZEOF_VOID_P == 8
1757 LongDoubleUnion val, ret;
1759 val.fval = value;
1760 ret.ival = (gint64)InterlockedExchangePointer((gpointer *) location, (gpointer)val.ival);
1762 return ret.fval;
1763 #else
1764 gdouble res;
1767 * According to MSDN, this function is only atomic with regards to the
1768 * other Interlocked functions on 32 bit platforms.
1770 mono_interlocked_lock ();
1771 res = *location;
1772 *location = value;
1773 mono_interlocked_unlock ();
1775 return res;
1776 #endif
1779 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location, gint32 value, gint32 comparand)
1781 MONO_ARCH_SAVE_REGS;
1783 return InterlockedCompareExchange(location, value, comparand);
1786 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location, MonoObject *value, MonoObject *comparand)
1788 MonoObject *res;
1789 res = (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location, value, comparand);
1790 mono_gc_wbarrier_generic_nostore (location);
1791 return res;
1794 gpointer ves_icall_System_Threading_Interlocked_CompareExchange_IntPtr(gpointer *location, gpointer value, gpointer comparand)
1796 return InterlockedCompareExchangePointer(location, value, comparand);
1799 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location, gfloat value, gfloat comparand)
1801 IntFloatUnion val, ret, cmp;
1803 MONO_ARCH_SAVE_REGS;
1805 val.fval = value;
1806 cmp.fval = comparand;
1807 ret.ival = InterlockedCompareExchange((gint32 *) location, val.ival, cmp.ival);
1809 return ret.fval;
1812 gdouble
1813 ves_icall_System_Threading_Interlocked_CompareExchange_Double (gdouble *location, gdouble value, gdouble comparand)
1815 #if SIZEOF_VOID_P == 8
1816 LongDoubleUnion val, comp, ret;
1818 val.fval = value;
1819 comp.fval = comparand;
1820 ret.ival = (gint64)InterlockedCompareExchangePointer((gpointer *) location, (gpointer)val.ival, (gpointer)comp.ival);
1822 return ret.fval;
1823 #else
1824 gdouble old;
1826 mono_interlocked_lock ();
1827 old = *location;
1828 if (old == comparand)
1829 *location = value;
1830 mono_interlocked_unlock ();
1832 return old;
1833 #endif
1836 gint64
1837 ves_icall_System_Threading_Interlocked_CompareExchange_Long (gint64 *location, gint64 value, gint64 comparand)
1839 #if SIZEOF_VOID_P == 8
1840 return (gint64)InterlockedCompareExchangePointer((gpointer *) location, (gpointer)value, (gpointer)comparand);
1841 #else
1842 gint64 old;
1844 mono_interlocked_lock ();
1845 old = *location;
1846 if (old == comparand)
1847 *location = value;
1848 mono_interlocked_unlock ();
1850 return old;
1851 #endif
1854 MonoObject*
1855 ves_icall_System_Threading_Interlocked_CompareExchange_T (MonoObject **location, MonoObject *value, MonoObject *comparand)
1857 MonoObject *res;
1858 res = InterlockedCompareExchangePointer ((gpointer *)location, value, comparand);
1859 mono_gc_wbarrier_generic_nostore (location);
1860 return res;
1863 MonoObject*
1864 ves_icall_System_Threading_Interlocked_Exchange_T (MonoObject **location, MonoObject *value)
1866 MonoObject *res;
1867 res = InterlockedExchangePointer ((gpointer *)location, value);
1868 mono_gc_wbarrier_generic_nostore (location);
1869 return res;
1872 gint32
1873 ves_icall_System_Threading_Interlocked_Add_Int (gint32 *location, gint32 value)
1875 #if SIZEOF_VOID_P == 8
1876 /* Should be implemented as a JIT intrinsic */
1877 mono_raise_exception (mono_get_exception_not_implemented (NULL));
1878 return 0;
1879 #else
1880 gint32 orig;
1882 mono_interlocked_lock ();
1883 orig = *location;
1884 *location = orig + value;
1885 mono_interlocked_unlock ();
1887 return orig + value;
1888 #endif
1891 gint64
1892 ves_icall_System_Threading_Interlocked_Add_Long (gint64 *location, gint64 value)
1894 #if SIZEOF_VOID_P == 8
1895 /* Should be implemented as a JIT intrinsic */
1896 mono_raise_exception (mono_get_exception_not_implemented (NULL));
1897 return 0;
1898 #else
1899 gint64 orig;
1901 mono_interlocked_lock ();
1902 orig = *location;
1903 *location = orig + value;
1904 mono_interlocked_unlock ();
1906 return orig + value;
1907 #endif
1910 gint64
1911 ves_icall_System_Threading_Interlocked_Read_Long (gint64 *location)
1913 #if SIZEOF_VOID_P == 8
1914 /* 64 bit reads are already atomic */
1915 return *location;
1916 #else
1917 gint64 res;
1919 mono_interlocked_lock ();
1920 res = *location;
1921 mono_interlocked_unlock ();
1923 return res;
1924 #endif
1927 void
1928 ves_icall_System_Threading_Thread_MemoryBarrier (void)
1930 mono_threads_lock ();
1931 mono_threads_unlock ();
1934 void
1935 ves_icall_System_Threading_Thread_ClrState (MonoInternalThread* this, guint32 state)
1937 mono_thread_clr_state (this, state);
1939 if (state & ThreadState_Background) {
1940 /* If the thread changes the background mode, the main thread has to
1941 * be notified, since it has to rebuild the list of threads to
1942 * wait for.
1944 SetEvent (background_change_event);
1948 void
1949 ves_icall_System_Threading_Thread_SetState (MonoInternalThread* this, guint32 state)
1951 mono_thread_set_state (this, state);
1953 if (state & ThreadState_Background) {
1954 /* If the thread changes the background mode, the main thread has to
1955 * be notified, since it has to rebuild the list of threads to
1956 * wait for.
1958 SetEvent (background_change_event);
1962 guint32
1963 ves_icall_System_Threading_Thread_GetState (MonoInternalThread* this)
1965 guint32 state;
1967 ensure_synch_cs_set (this);
1969 EnterCriticalSection (this->synch_cs);
1971 state = this->state;
1973 LeaveCriticalSection (this->synch_cs);
1975 return state;
1978 void ves_icall_System_Threading_Thread_Interrupt_internal (MonoInternalThread *this)
1980 MonoInternalThread *current;
1981 gboolean throw;
1983 ensure_synch_cs_set (this);
1985 current = mono_thread_internal_current ();
1987 EnterCriticalSection (this->synch_cs);
1989 this->thread_interrupt_requested = TRUE;
1990 throw = current != this && (this->state & ThreadState_WaitSleepJoin);
1992 LeaveCriticalSection (this->synch_cs);
1994 if (throw) {
1995 abort_thread_internal (this, TRUE, FALSE);
1999 void mono_thread_current_check_pending_interrupt ()
2001 MonoInternalThread *thread = mono_thread_internal_current ();
2002 gboolean throw = FALSE;
2004 mono_debugger_check_interruption ();
2006 ensure_synch_cs_set (thread);
2008 EnterCriticalSection (thread->synch_cs);
2010 if (thread->thread_interrupt_requested) {
2011 throw = TRUE;
2012 thread->thread_interrupt_requested = FALSE;
2015 LeaveCriticalSection (thread->synch_cs);
2017 if (throw) {
2018 mono_raise_exception (mono_get_exception_thread_interrupted ());
2022 int
2023 mono_thread_get_abort_signal (void)
2025 #ifdef HOST_WIN32
2026 return -1;
2027 #else
2028 #ifndef SIGRTMIN
2029 #ifdef SIGUSR1
2030 return SIGUSR1;
2031 #else
2032 return -1;
2033 #endif
2034 #else
2035 static int abort_signum = -1;
2036 int i;
2037 if (abort_signum != -1)
2038 return abort_signum;
2039 /* we try to avoid SIGRTMIN and any one that might have been set already, see bug #75387 */
2040 for (i = SIGRTMIN + 1; i < SIGRTMAX; ++i) {
2041 struct sigaction sinfo;
2042 sigaction (i, NULL, &sinfo);
2043 if (sinfo.sa_handler == SIG_DFL && (void*)sinfo.sa_sigaction == (void*)SIG_DFL) {
2044 abort_signum = i;
2045 return i;
2048 /* fallback to the old way */
2049 return SIGRTMIN;
2050 #endif
2051 #endif /* HOST_WIN32 */
2054 #ifdef HOST_WIN32
2055 static void CALLBACK interruption_request_apc (ULONG_PTR param)
2057 MonoException* exc = mono_thread_request_interruption (FALSE);
2058 if (exc) mono_raise_exception (exc);
2060 #endif /* HOST_WIN32 */
2063 * signal_thread_state_change
2065 * Tells the thread that his state has changed and it has to enter the new
2066 * state as soon as possible.
2068 static void signal_thread_state_change (MonoInternalThread *thread)
2070 if (thread == mono_thread_internal_current ()) {
2071 /* Do it synchronously */
2072 MonoException *exc = mono_thread_request_interruption (FALSE);
2073 if (exc)
2074 mono_raise_exception (exc);
2077 #ifdef HOST_WIN32
2078 QueueUserAPC ((PAPCFUNC)interruption_request_apc, thread->handle, NULL);
2079 #else
2080 /* fixme: store the state somewhere */
2081 mono_thread_kill (thread, mono_thread_get_abort_signal ());
2084 * This will cause waits to be broken.
2085 * It will also prevent the thread from entering a wait, so if the thread returns
2086 * from the wait before it receives the abort signal, it will just spin in the wait
2087 * functions in the io-layer until the signal handler calls QueueUserAPC which will
2088 * make it return.
2090 wapi_interrupt_thread (thread->handle);
2091 #endif /* HOST_WIN32 */
2094 void
2095 ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject *state)
2097 ensure_synch_cs_set (thread);
2099 EnterCriticalSection (thread->synch_cs);
2101 if ((thread->state & ThreadState_AbortRequested) != 0 ||
2102 (thread->state & ThreadState_StopRequested) != 0 ||
2103 (thread->state & ThreadState_Stopped) != 0)
2105 LeaveCriticalSection (thread->synch_cs);
2106 return;
2109 if ((thread->state & ThreadState_Unstarted) != 0) {
2110 thread->state |= ThreadState_Aborted;
2111 LeaveCriticalSection (thread->synch_cs);
2112 return;
2115 thread->state |= ThreadState_AbortRequested;
2116 if (thread->abort_state_handle)
2117 mono_gchandle_free (thread->abort_state_handle);
2118 if (state) {
2119 thread->abort_state_handle = mono_gchandle_new (state, FALSE);
2120 g_assert (thread->abort_state_handle);
2121 } else {
2122 thread->abort_state_handle = 0;
2124 thread->abort_exc = NULL;
2127 * abort_exc is set in mono_thread_execute_interruption(),
2128 * triggered by the call to signal_thread_state_change(),
2129 * below. There's a point between where we have
2130 * abort_state_handle set, but abort_exc NULL, but that's not
2131 * a problem.
2134 LeaveCriticalSection (thread->synch_cs);
2136 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Abort requested for %p (%"G_GSIZE_FORMAT")", __func__, GetCurrentThreadId (), thread, (gsize)thread->tid));
2138 /* During shutdown, we can't wait for other threads */
2139 if (!shutting_down)
2140 /* Make sure the thread is awake */
2141 mono_thread_resume (thread);
2143 abort_thread_internal (thread, TRUE, TRUE);
2146 void
2147 ves_icall_System_Threading_Thread_ResetAbort (void)
2149 MonoInternalThread *thread = mono_thread_internal_current ();
2150 gboolean was_aborting;
2152 ensure_synch_cs_set (thread);
2154 EnterCriticalSection (thread->synch_cs);
2155 was_aborting = thread->state & ThreadState_AbortRequested;
2156 thread->state &= ~ThreadState_AbortRequested;
2157 LeaveCriticalSection (thread->synch_cs);
2159 if (!was_aborting) {
2160 const char *msg = "Unable to reset abort because no abort was requested";
2161 mono_raise_exception (mono_get_exception_thread_state (msg));
2163 thread->abort_exc = NULL;
2164 if (thread->abort_state_handle) {
2165 mono_gchandle_free (thread->abort_state_handle);
2166 /* This is actually not necessary - the handle
2167 only counts if the exception is set */
2168 thread->abort_state_handle = 0;
2172 void
2173 mono_thread_internal_reset_abort (MonoInternalThread *thread)
2175 ensure_synch_cs_set (thread);
2177 EnterCriticalSection (thread->synch_cs);
2179 thread->state &= ~ThreadState_AbortRequested;
2181 if (thread->abort_exc) {
2182 thread->abort_exc = NULL;
2183 if (thread->abort_state_handle) {
2184 mono_gchandle_free (thread->abort_state_handle);
2185 /* This is actually not necessary - the handle
2186 only counts if the exception is set */
2187 thread->abort_state_handle = 0;
2191 LeaveCriticalSection (thread->synch_cs);
2194 MonoObject*
2195 ves_icall_System_Threading_Thread_GetAbortExceptionState (MonoThread *this)
2197 MonoInternalThread *thread = this->internal_thread;
2198 MonoObject *state, *deserialized = NULL, *exc;
2199 MonoDomain *domain;
2201 if (!thread->abort_state_handle)
2202 return NULL;
2204 state = mono_gchandle_get_target (thread->abort_state_handle);
2205 g_assert (state);
2207 domain = mono_domain_get ();
2208 if (mono_object_domain (state) == domain)
2209 return state;
2211 deserialized = mono_object_xdomain_representation (state, domain, &exc);
2213 if (!deserialized) {
2214 MonoException *invalid_op_exc = mono_get_exception_invalid_operation ("Thread.ExceptionState cannot access an ExceptionState from a different AppDomain");
2215 if (exc)
2216 MONO_OBJECT_SETREF (invalid_op_exc, inner_ex, exc);
2217 mono_raise_exception (invalid_op_exc);
2220 return deserialized;
2223 static gboolean
2224 mono_thread_suspend (MonoInternalThread *thread)
2226 ensure_synch_cs_set (thread);
2228 EnterCriticalSection (thread->synch_cs);
2230 if ((thread->state & ThreadState_Unstarted) != 0 ||
2231 (thread->state & ThreadState_Aborted) != 0 ||
2232 (thread->state & ThreadState_Stopped) != 0)
2234 LeaveCriticalSection (thread->synch_cs);
2235 return FALSE;
2238 if ((thread->state & ThreadState_Suspended) != 0 ||
2239 (thread->state & ThreadState_SuspendRequested) != 0 ||
2240 (thread->state & ThreadState_StopRequested) != 0)
2242 LeaveCriticalSection (thread->synch_cs);
2243 return TRUE;
2246 thread->state |= ThreadState_SuspendRequested;
2248 LeaveCriticalSection (thread->synch_cs);
2250 suspend_thread_internal (thread, FALSE);
2251 return TRUE;
2254 void
2255 ves_icall_System_Threading_Thread_Suspend (MonoInternalThread *thread)
2257 if (!mono_thread_suspend (thread))
2258 mono_raise_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
2261 static gboolean
2262 mono_thread_resume (MonoInternalThread *thread)
2264 ensure_synch_cs_set (thread);
2266 EnterCriticalSection (thread->synch_cs);
2268 if ((thread->state & ThreadState_SuspendRequested) != 0) {
2269 thread->state &= ~ThreadState_SuspendRequested;
2270 LeaveCriticalSection (thread->synch_cs);
2271 return TRUE;
2274 if ((thread->state & ThreadState_Suspended) == 0 ||
2275 (thread->state & ThreadState_Unstarted) != 0 ||
2276 (thread->state & ThreadState_Aborted) != 0 ||
2277 (thread->state & ThreadState_Stopped) != 0)
2279 LeaveCriticalSection (thread->synch_cs);
2280 return FALSE;
2283 return resume_thread_internal (thread);
2286 void
2287 ves_icall_System_Threading_Thread_Resume (MonoThread *thread)
2289 if (!thread->internal_thread || !mono_thread_resume (thread->internal_thread))
2290 mono_raise_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
2293 static gboolean
2294 mono_threads_is_critical_method (MonoMethod *method)
2296 switch (method->wrapper_type) {
2297 case MONO_WRAPPER_RUNTIME_INVOKE:
2298 case MONO_WRAPPER_XDOMAIN_INVOKE:
2299 case MONO_WRAPPER_XDOMAIN_DISPATCH:
2300 return TRUE;
2302 return FALSE;
2305 static gboolean
2306 find_wrapper (MonoMethod *m, gint no, gint ilo, gboolean managed, gpointer data)
2308 if (managed)
2309 return TRUE;
2311 if (mono_threads_is_critical_method (m)) {
2312 *((gboolean*)data) = TRUE;
2313 return TRUE;
2315 return FALSE;
2318 static gboolean
2319 is_running_protected_wrapper (void)
2321 gboolean found = FALSE;
2322 mono_stack_walk (find_wrapper, &found);
2323 return found;
2326 void mono_thread_internal_stop (MonoInternalThread *thread)
2328 ensure_synch_cs_set (thread);
2330 EnterCriticalSection (thread->synch_cs);
2332 if ((thread->state & ThreadState_StopRequested) != 0 ||
2333 (thread->state & ThreadState_Stopped) != 0)
2335 LeaveCriticalSection (thread->synch_cs);
2336 return;
2339 /* Make sure the thread is awake */
2340 mono_thread_resume (thread);
2342 thread->state |= ThreadState_StopRequested;
2343 thread->state &= ~ThreadState_AbortRequested;
2345 LeaveCriticalSection (thread->synch_cs);
2347 abort_thread_internal (thread, TRUE, TRUE);
2350 void mono_thread_stop (MonoThread *thread)
2352 mono_thread_internal_stop (thread->internal_thread);
2355 gint8
2356 ves_icall_System_Threading_Thread_VolatileRead1 (void *ptr)
2358 return *((volatile gint8 *) (ptr));
2361 gint16
2362 ves_icall_System_Threading_Thread_VolatileRead2 (void *ptr)
2364 return *((volatile gint16 *) (ptr));
2367 gint32
2368 ves_icall_System_Threading_Thread_VolatileRead4 (void *ptr)
2370 return *((volatile gint32 *) (ptr));
2373 gint64
2374 ves_icall_System_Threading_Thread_VolatileRead8 (void *ptr)
2376 return *((volatile gint64 *) (ptr));
2379 void *
2380 ves_icall_System_Threading_Thread_VolatileReadIntPtr (void *ptr)
2382 return (void *) *((volatile void **) ptr);
2385 void
2386 ves_icall_System_Threading_Thread_VolatileWrite1 (void *ptr, gint8 value)
2388 *((volatile gint8 *) ptr) = value;
2391 void
2392 ves_icall_System_Threading_Thread_VolatileWrite2 (void *ptr, gint16 value)
2394 *((volatile gint16 *) ptr) = value;
2397 void
2398 ves_icall_System_Threading_Thread_VolatileWrite4 (void *ptr, gint32 value)
2400 *((volatile gint32 *) ptr) = value;
2403 void
2404 ves_icall_System_Threading_Thread_VolatileWrite8 (void *ptr, gint64 value)
2406 *((volatile gint64 *) ptr) = value;
2409 void
2410 ves_icall_System_Threading_Thread_VolatileWriteIntPtr (void *ptr, void *value)
2412 *((volatile void **) ptr) = value;
2415 void
2416 ves_icall_System_Threading_Thread_VolatileWriteObject (void *ptr, void *value)
2418 mono_gc_wbarrier_generic_store (ptr, value);
2421 void mono_thread_init (MonoThreadStartCB start_cb,
2422 MonoThreadAttachCB attach_cb)
2424 InitializeCriticalSection(&threads_mutex);
2425 InitializeCriticalSection(&interlocked_mutex);
2426 InitializeCriticalSection(&contexts_mutex);
2428 background_change_event = CreateEvent (NULL, TRUE, FALSE, NULL);
2429 g_assert(background_change_event != NULL);
2431 mono_init_static_data_info (&thread_static_info);
2432 mono_init_static_data_info (&context_static_info);
2434 MONO_FAST_TLS_INIT (tls_current_object);
2435 mono_native_tls_alloc (&current_object_key, NULL);
2436 THREAD_DEBUG (g_message ("%s: Allocated current_object_key %d", __func__, current_object_key));
2438 mono_thread_start_cb = start_cb;
2439 mono_thread_attach_cb = attach_cb;
2441 /* Get a pseudo handle to the current process. This is just a
2442 * kludge so that wapi can build a process handle if needed.
2443 * As a pseudo handle is returned, we don't need to clean
2444 * anything up.
2446 GetCurrentProcess ();
2449 void mono_thread_cleanup (void)
2451 #if !defined(HOST_WIN32) && !defined(RUN_IN_SUBTHREAD)
2452 /* The main thread must abandon any held mutexes (particularly
2453 * important for named mutexes as they are shared across
2454 * processes, see bug 74680.) This will happen when the
2455 * thread exits, but if it's not running in a subthread it
2456 * won't exit in time.
2458 /* Using non-w32 API is a nasty kludge, but I couldn't find
2459 * anything in the documentation that would let me do this
2460 * here yet still be safe to call on windows.
2462 _wapi_thread_signal_self (mono_environment_exitcode_get ());
2463 #endif
2465 #if 0
2466 /* This stuff needs more testing, it seems one of these
2467 * critical sections can be locked when mono_thread_cleanup is
2468 * called.
2470 DeleteCriticalSection (&threads_mutex);
2471 DeleteCriticalSection (&interlocked_mutex);
2472 DeleteCriticalSection (&contexts_mutex);
2473 DeleteCriticalSection (&delayed_free_table_mutex);
2474 DeleteCriticalSection (&small_id_mutex);
2475 CloseHandle (background_change_event);
2476 #endif
2478 mono_native_tls_free (current_object_key);
2481 void
2482 mono_threads_install_cleanup (MonoThreadCleanupFunc func)
2484 mono_thread_cleanup_fn = func;
2487 void
2488 mono_thread_set_manage_callback (MonoThread *thread, MonoThreadManageCallback func)
2490 thread->internal_thread->manage_callback = func;
2493 void mono_threads_install_notify_pending_exc (MonoThreadNotifyPendingExcFunc func)
2495 mono_thread_notify_pending_exc_fn = func;
2498 G_GNUC_UNUSED
2499 static void print_tids (gpointer key, gpointer value, gpointer user)
2501 /* GPOINTER_TO_UINT breaks horribly if sizeof(void *) >
2502 * sizeof(uint) and a cast to uint would overflow
2504 /* Older versions of glib don't have G_GSIZE_FORMAT, so just
2505 * print this as a pointer.
2507 g_message ("Waiting for: %p", key);
2510 struct wait_data
2512 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
2513 MonoInternalThread *threads[MAXIMUM_WAIT_OBJECTS];
2514 guint32 num;
2517 static void wait_for_tids (struct wait_data *wait, guint32 timeout)
2519 guint32 i, ret;
2521 THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num));
2523 ret=WaitForMultipleObjectsEx(wait->num, wait->handles, TRUE, timeout, TRUE);
2525 if(ret==WAIT_FAILED) {
2526 /* See the comment in build_wait_tids() */
2527 THREAD_DEBUG (g_message ("%s: Wait failed", __func__));
2528 return;
2531 for(i=0; i<wait->num; i++)
2532 CloseHandle (wait->handles[i]);
2534 if (ret == WAIT_TIMEOUT)
2535 return;
2537 for(i=0; i<wait->num; i++) {
2538 gsize tid = wait->threads[i]->tid;
2540 mono_threads_lock ();
2541 if(mono_g_hash_table_lookup (threads, (gpointer)tid)!=NULL) {
2542 /* This thread must have been killed, because
2543 * it hasn't cleaned itself up. (It's just
2544 * possible that the thread exited before the
2545 * parent thread had a chance to store the
2546 * handle, and now there is another pointer to
2547 * the already-exited thread stored. In this
2548 * case, we'll just get two
2549 * mono_profiler_thread_end() calls for the
2550 * same thread.)
2553 mono_threads_unlock ();
2554 THREAD_DEBUG (g_message ("%s: cleaning up after thread %p (%"G_GSIZE_FORMAT")", __func__, wait->threads[i], tid));
2555 thread_cleanup (wait->threads[i]);
2556 } else {
2557 mono_threads_unlock ();
2562 static void wait_for_tids_or_state_change (struct wait_data *wait, guint32 timeout)
2564 guint32 i, ret, count;
2566 THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num));
2568 /* Add the thread state change event, so it wakes up if a thread changes
2569 * to background mode.
2571 count = wait->num;
2572 if (count < MAXIMUM_WAIT_OBJECTS) {
2573 wait->handles [count] = background_change_event;
2574 count++;
2577 ret=WaitForMultipleObjectsEx (count, wait->handles, FALSE, timeout, TRUE);
2579 if(ret==WAIT_FAILED) {
2580 /* See the comment in build_wait_tids() */
2581 THREAD_DEBUG (g_message ("%s: Wait failed", __func__));
2582 return;
2585 for(i=0; i<wait->num; i++)
2586 CloseHandle (wait->handles[i]);
2588 if (ret == WAIT_TIMEOUT)
2589 return;
2591 if (ret < wait->num) {
2592 gsize tid = wait->threads[ret]->tid;
2593 mono_threads_lock ();
2594 if (mono_g_hash_table_lookup (threads, (gpointer)tid)!=NULL) {
2595 /* See comment in wait_for_tids about thread cleanup */
2596 mono_threads_unlock ();
2597 THREAD_DEBUG (g_message ("%s: cleaning up after thread %"G_GSIZE_FORMAT, __func__, tid));
2598 thread_cleanup (wait->threads [ret]);
2599 } else
2600 mono_threads_unlock ();
2604 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
2606 struct wait_data *wait=(struct wait_data *)user;
2608 if(wait->num<MAXIMUM_WAIT_OBJECTS) {
2609 HANDLE handle;
2610 MonoInternalThread *thread=(MonoInternalThread *)value;
2612 /* Ignore background threads, we abort them later */
2613 /* Do not lock here since it is not needed and the caller holds threads_lock */
2614 if (thread->state & ThreadState_Background) {
2615 THREAD_DEBUG (g_message ("%s: ignoring background thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
2616 return; /* just leave, ignore */
2619 if (mono_gc_is_finalizer_internal_thread (thread)) {
2620 THREAD_DEBUG (g_message ("%s: ignoring finalizer thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
2621 return;
2624 if (thread == mono_thread_internal_current ()) {
2625 THREAD_DEBUG (g_message ("%s: ignoring current thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
2626 return;
2629 if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread)) {
2630 THREAD_DEBUG (g_message ("%s: ignoring main thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
2631 return;
2634 if (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE) {
2635 THREAD_DEBUG (g_message ("%s: ignoring thread %" G_GSIZE_FORMAT "with DONT_MANAGE flag set.", __func__, (gsize)thread->tid));
2636 return;
2639 handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
2640 if (handle == NULL) {
2641 THREAD_DEBUG (g_message ("%s: ignoring unopenable thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
2642 return;
2645 THREAD_DEBUG (g_message ("%s: Invoking mono_thread_manage callback on thread %p", __func__, thread));
2646 if ((thread->manage_callback == NULL) || (thread->manage_callback (thread->root_domain_thread) == TRUE)) {
2647 wait->handles[wait->num]=handle;
2648 wait->threads[wait->num]=thread;
2649 wait->num++;
2651 THREAD_DEBUG (g_message ("%s: adding thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
2652 } else {
2653 THREAD_DEBUG (g_message ("%s: ignoring (because of callback) thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
2657 } else {
2658 /* Just ignore the rest, we can't do anything with
2659 * them yet
2664 static gboolean
2665 remove_and_abort_threads (gpointer key, gpointer value, gpointer user)
2667 struct wait_data *wait=(struct wait_data *)user;
2668 gsize self = GetCurrentThreadId ();
2669 MonoInternalThread *thread = value;
2670 HANDLE handle;
2672 if (wait->num >= MAXIMUM_WAIT_OBJECTS)
2673 return FALSE;
2675 /* The finalizer thread is not a background thread */
2676 if (thread->tid != self && (thread->state & ThreadState_Background) != 0 &&
2677 !(thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)) {
2679 handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
2680 if (handle == NULL)
2681 return FALSE;
2683 /* printf ("A: %d\n", wait->num); */
2684 wait->handles[wait->num]=thread->handle;
2685 wait->threads[wait->num]=thread;
2686 wait->num++;
2688 THREAD_DEBUG (g_print ("%s: Aborting id: %"G_GSIZE_FORMAT"\n", __func__, (gsize)thread->tid));
2689 mono_thread_internal_stop (thread);
2690 return TRUE;
2693 return (thread->tid != self && !mono_gc_is_finalizer_internal_thread (thread));
2696 /**
2697 * mono_threads_set_shutting_down:
2699 * Is called by a thread that wants to shut down Mono. If the runtime is already
2700 * shutting down, the calling thread is suspended/stopped, and this function never
2701 * returns.
2703 void
2704 mono_threads_set_shutting_down (void)
2706 MonoInternalThread *current_thread = mono_thread_internal_current ();
2708 mono_threads_lock ();
2710 if (shutting_down) {
2711 mono_threads_unlock ();
2713 /* Make sure we're properly suspended/stopped */
2715 EnterCriticalSection (current_thread->synch_cs);
2717 if ((current_thread->state & ThreadState_SuspendRequested) ||
2718 (current_thread->state & ThreadState_AbortRequested) ||
2719 (current_thread->state & ThreadState_StopRequested)) {
2720 LeaveCriticalSection (current_thread->synch_cs);
2721 mono_thread_execute_interruption (current_thread);
2722 } else {
2723 current_thread->state |= ThreadState_Stopped;
2724 LeaveCriticalSection (current_thread->synch_cs);
2727 /*since we're killing the thread, unset the current domain.*/
2728 mono_domain_unset ();
2730 /* Wake up other threads potentially waiting for us */
2731 ExitThread (0);
2732 } else {
2733 shutting_down = TRUE;
2735 /* Not really a background state change, but this will
2736 * interrupt the main thread if it is waiting for all
2737 * the other threads.
2739 SetEvent (background_change_event);
2741 mono_threads_unlock ();
2745 /**
2746 * mono_threads_is_shutting_down:
2748 * Returns whether a thread has commenced shutdown of Mono. Note that
2749 * if the function returns FALSE the caller must not assume that
2750 * shutdown is not in progress, because the situation might have
2751 * changed since the function returned. For that reason this function
2752 * is of very limited utility.
2754 gboolean
2755 mono_threads_is_shutting_down (void)
2757 return shutting_down;
2760 void mono_thread_manage (void)
2762 struct wait_data wait_data;
2763 struct wait_data *wait = &wait_data;
2765 memset (wait, 0, sizeof (struct wait_data));
2766 /* join each thread that's still running */
2767 THREAD_DEBUG (g_message ("%s: Joining each running thread...", __func__));
2769 mono_threads_lock ();
2770 if(threads==NULL) {
2771 THREAD_DEBUG (g_message("%s: No threads", __func__));
2772 mono_threads_unlock ();
2773 return;
2775 mono_threads_unlock ();
2777 do {
2778 mono_threads_lock ();
2779 if (shutting_down) {
2780 /* somebody else is shutting down */
2781 mono_threads_unlock ();
2782 break;
2784 THREAD_DEBUG (g_message ("%s: There are %d threads to join", __func__, mono_g_hash_table_size (threads));
2785 mono_g_hash_table_foreach (threads, print_tids, NULL));
2787 ResetEvent (background_change_event);
2788 wait->num=0;
2789 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
2790 memset (wait->threads, 0, MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
2791 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
2792 mono_threads_unlock ();
2793 if(wait->num>0) {
2794 /* Something to wait for */
2795 wait_for_tids_or_state_change (wait, INFINITE);
2797 THREAD_DEBUG (g_message ("%s: I have %d threads after waiting.", __func__, wait->num));
2798 } while(wait->num>0);
2800 mono_threads_set_shutting_down ();
2802 /* No new threads will be created after this point */
2804 mono_runtime_set_shutting_down ();
2806 THREAD_DEBUG (g_message ("%s: threadpool cleanup", __func__));
2807 mono_thread_pool_cleanup ();
2810 * Remove everything but the finalizer thread and self.
2811 * Also abort all the background threads
2812 * */
2813 do {
2814 mono_threads_lock ();
2816 wait->num = 0;
2817 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
2818 memset (wait->threads, 0, MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
2819 mono_g_hash_table_foreach_remove (threads, remove_and_abort_threads, wait);
2821 mono_threads_unlock ();
2823 THREAD_DEBUG (g_message ("%s: wait->num is now %d", __func__, wait->num));
2824 if(wait->num>0) {
2825 /* Something to wait for */
2826 wait_for_tids (wait, INFINITE);
2828 } while (wait->num > 0);
2831 * give the subthreads a chance to really quit (this is mainly needed
2832 * to get correct user and system times from getrusage/wait/time(1)).
2833 * This could be removed if we avoid pthread_detach() and use pthread_join().
2835 #ifndef HOST_WIN32
2836 sched_yield ();
2837 #endif
2840 static void terminate_thread (gpointer key, gpointer value, gpointer user)
2842 MonoInternalThread *thread=(MonoInternalThread *)value;
2844 if(thread->tid != (gsize)user) {
2845 /*TerminateThread (thread->handle, -1);*/
2849 void mono_thread_abort_all_other_threads (void)
2851 gsize self = GetCurrentThreadId ();
2853 mono_threads_lock ();
2854 THREAD_DEBUG (g_message ("%s: There are %d threads to abort", __func__,
2855 mono_g_hash_table_size (threads));
2856 mono_g_hash_table_foreach (threads, print_tids, NULL));
2858 mono_g_hash_table_foreach (threads, terminate_thread, (gpointer)self);
2860 mono_threads_unlock ();
2863 static void
2864 collect_threads_for_suspend (gpointer key, gpointer value, gpointer user_data)
2866 MonoInternalThread *thread = (MonoInternalThread*)value;
2867 struct wait_data *wait = (struct wait_data*)user_data;
2868 HANDLE handle;
2871 * We try to exclude threads early, to avoid running into the MAXIMUM_WAIT_OBJECTS
2872 * limitation.
2873 * This needs no locking.
2875 if ((thread->state & ThreadState_Suspended) != 0 ||
2876 (thread->state & ThreadState_Stopped) != 0)
2877 return;
2879 if (wait->num<MAXIMUM_WAIT_OBJECTS) {
2880 handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
2881 if (handle == NULL)
2882 return;
2884 wait->handles [wait->num] = handle;
2885 wait->threads [wait->num] = thread;
2886 wait->num++;
2891 * mono_thread_suspend_all_other_threads:
2893 * Suspend all managed threads except the finalizer thread and this thread. It is
2894 * not possible to resume them later.
2896 void mono_thread_suspend_all_other_threads (void)
2898 struct wait_data wait_data;
2899 struct wait_data *wait = &wait_data;
2900 int i;
2901 gsize self = GetCurrentThreadId ();
2902 gpointer *events;
2903 guint32 eventidx = 0;
2904 gboolean starting, finished;
2906 memset (wait, 0, sizeof (struct wait_data));
2908 * The other threads could be in an arbitrary state at this point, i.e.
2909 * they could be starting up, shutting down etc. This means that there could be
2910 * threads which are not even in the threads hash table yet.
2914 * First we set a barrier which will be checked by all threads before they
2915 * are added to the threads hash table, and they will exit if the flag is set.
2916 * This ensures that no threads could be added to the hash later.
2917 * We will use shutting_down as the barrier for now.
2919 g_assert (shutting_down);
2922 * We make multiple calls to WaitForMultipleObjects since:
2923 * - we can only wait for MAXIMUM_WAIT_OBJECTS threads
2924 * - some threads could exit without becoming suspended
2926 finished = FALSE;
2927 while (!finished) {
2929 * Make a copy of the hashtable since we can't do anything with
2930 * threads while threads_mutex is held.
2932 wait->num = 0;
2933 /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
2934 memset (wait->threads, 0, MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
2935 mono_threads_lock ();
2936 mono_g_hash_table_foreach (threads, collect_threads_for_suspend, wait);
2937 mono_threads_unlock ();
2939 events = g_new0 (gpointer, wait->num);
2940 eventidx = 0;
2941 /* Get the suspended events that we'll be waiting for */
2942 for (i = 0; i < wait->num; ++i) {
2943 MonoInternalThread *thread = wait->threads [i];
2944 gboolean signal_suspend = FALSE;
2946 if ((thread->tid == self) || mono_gc_is_finalizer_internal_thread (thread) || (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)) {
2947 //CloseHandle (wait->handles [i]);
2948 wait->threads [i] = NULL; /* ignore this thread in next loop */
2949 continue;
2952 ensure_synch_cs_set (thread);
2954 EnterCriticalSection (thread->synch_cs);
2956 if (thread->suspended_event == NULL) {
2957 thread->suspended_event = CreateEvent (NULL, TRUE, FALSE, NULL);
2958 if (thread->suspended_event == NULL) {
2959 /* Forget this one and go on to the next */
2960 LeaveCriticalSection (thread->synch_cs);
2961 continue;
2965 if ((thread->state & ThreadState_Suspended) != 0 ||
2966 (thread->state & ThreadState_StopRequested) != 0 ||
2967 (thread->state & ThreadState_Stopped) != 0) {
2968 LeaveCriticalSection (thread->synch_cs);
2969 CloseHandle (wait->handles [i]);
2970 wait->threads [i] = NULL; /* ignore this thread in next loop */
2971 continue;
2974 if ((thread->state & ThreadState_SuspendRequested) == 0)
2975 signal_suspend = TRUE;
2977 events [eventidx++] = thread->suspended_event;
2979 /* Convert abort requests into suspend requests */
2980 if ((thread->state & ThreadState_AbortRequested) != 0)
2981 thread->state &= ~ThreadState_AbortRequested;
2983 thread->state |= ThreadState_SuspendRequested;
2985 LeaveCriticalSection (thread->synch_cs);
2987 /* Signal the thread to suspend */
2988 if (mono_thread_info_new_interrupt_enabled ())
2989 suspend_thread_internal (thread, TRUE);
2990 else if (signal_suspend)
2991 signal_thread_state_change (thread);
2994 /*Only wait on the suspend event if we are using the old path */
2995 if (eventidx > 0 && !mono_thread_info_new_interrupt_enabled ()) {
2996 WaitForMultipleObjectsEx (eventidx, events, TRUE, 100, FALSE);
2997 for (i = 0; i < wait->num; ++i) {
2998 MonoInternalThread *thread = wait->threads [i];
3000 if (thread == NULL)
3001 continue;
3003 ensure_synch_cs_set (thread);
3005 EnterCriticalSection (thread->synch_cs);
3006 if ((thread->state & ThreadState_Suspended) != 0) {
3007 CloseHandle (thread->suspended_event);
3008 thread->suspended_event = NULL;
3010 LeaveCriticalSection (thread->synch_cs);
3014 if (eventidx <= 0) {
3016 * If there are threads which are starting up, we wait until they
3017 * are suspended when they try to register in the threads hash.
3018 * This is guaranteed to finish, since the threads which can create new
3019 * threads get suspended after a while.
3020 * FIXME: The finalizer thread can still create new threads.
3022 mono_threads_lock ();
3023 if (threads_starting_up)
3024 starting = mono_g_hash_table_size (threads_starting_up) > 0;
3025 else
3026 starting = FALSE;
3027 mono_threads_unlock ();
3028 if (starting)
3029 Sleep (100);
3030 else
3031 finished = TRUE;
3034 g_free (events);
3038 static void
3039 collect_threads (gpointer key, gpointer value, gpointer user_data)
3041 MonoInternalThread *thread = (MonoInternalThread*)value;
3042 struct wait_data *wait = (struct wait_data*)user_data;
3043 HANDLE handle;
3045 if (wait->num<MAXIMUM_WAIT_OBJECTS) {
3046 handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
3047 if (handle == NULL)
3048 return;
3050 wait->handles [wait->num] = handle;
3051 wait->threads [wait->num] = thread;
3052 wait->num++;
3056 static gboolean thread_dump_requested;
3058 static G_GNUC_UNUSED gboolean
3059 print_stack_frame_to_string (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
3061 GString *p = (GString*)data;
3062 MonoMethod *method = NULL;
3063 if (frame->ji)
3064 method = frame->ji->method;
3066 if (method) {
3067 gchar *location = mono_debug_print_stack_frame (method, frame->native_offset, frame->domain);
3068 g_string_append_printf (p, " %s\n", location);
3069 g_free (location);
3070 } else
3071 g_string_append_printf (p, " at <unknown> <0x%05x>\n", frame->native_offset);
3073 return FALSE;
3076 static void
3077 print_thread_dump (MonoInternalThread *thread, MonoThreadInfo *info)
3079 GString* text = g_string_new (0);
3080 char *name;
3081 GError *error = NULL;
3083 if (thread->name) {
3084 name = g_utf16_to_utf8 (thread->name, thread->name_len, NULL, NULL, &error);
3085 g_assert (!error);
3086 g_string_append_printf (text, "\n\"%s\"", name);
3087 g_free (name);
3089 else if (thread->threadpool_thread)
3090 g_string_append (text, "\n\"<threadpool thread>\"");
3091 else
3092 g_string_append (text, "\n\"<unnamed thread>\"");
3094 #if 0
3095 /* This no longer works with remote unwinding */
3096 #ifndef HOST_WIN32
3097 wapi_desc = wapi_current_thread_desc ();
3098 g_string_append_printf (text, " tid=0x%p this=0x%p %s\n", (gpointer)(gsize)thread->tid, thread, wapi_desc);
3099 free (wapi_desc);
3100 #endif
3101 #endif
3103 mono_get_eh_callbacks ()->mono_walk_stack_with_state (print_stack_frame_to_string, &info->suspend_state, MONO_UNWIND_SIGNAL_SAFE, text);
3104 mono_thread_info_resume (mono_thread_info_get_tid (info));
3106 fprintf (stdout, "%s", text->str);
3108 #if PLATFORM_WIN32 && TARGET_WIN32 && _DEBUG
3109 OutputDebugStringA(text->str);
3110 #endif
3112 g_string_free (text, TRUE);
3113 fflush (stdout);
3116 static void
3117 dump_thread (gpointer key, gpointer value, gpointer user)
3119 MonoInternalThread *thread = (MonoInternalThread *)value;
3120 MonoThreadInfo *info;
3122 if (thread == mono_thread_internal_current ())
3123 return;
3126 FIXME This still can hang if we stop a thread during malloc.
3127 FIXME This can hang if we suspend on a critical method and the GC kicks in. A fix might be to have function
3128 that takes a callback and runs it with the target suspended.
3129 We probably should loop a bit around trying to get it to either managed code
3130 or WSJ state.
3132 info = mono_thread_info_safe_suspend_sync ((MonoNativeThreadId)(gpointer)(gsize)thread->tid, FALSE);
3134 if (!info)
3135 return;
3137 print_thread_dump (thread, info);
3140 void
3141 mono_threads_perform_thread_dump (void)
3143 if (!thread_dump_requested)
3144 return;
3146 printf ("Full thread dump:\n");
3149 * Make a copy of the hashtable since we can't do anything with
3150 * threads while threads_mutex is held.
3152 mono_threads_lock ();
3153 mono_g_hash_table_foreach (threads, dump_thread, NULL);
3154 mono_threads_unlock ();
3156 thread_dump_requested = FALSE;
3160 * mono_threads_request_thread_dump:
3162 * Ask all threads except the current to print their stacktrace to stdout.
3164 void
3165 mono_threads_request_thread_dump (void)
3167 struct wait_data wait_data;
3168 struct wait_data *wait = &wait_data;
3169 int i;
3171 /*The new thread dump code runs out of the finalizer thread. */
3172 if (mono_thread_info_new_interrupt_enabled ()) {
3173 thread_dump_requested = TRUE;
3174 mono_gc_finalize_notify ();
3175 return;
3179 memset (wait, 0, sizeof (struct wait_data));
3182 * Make a copy of the hashtable since we can't do anything with
3183 * threads while threads_mutex is held.
3185 mono_threads_lock ();
3186 mono_g_hash_table_foreach (threads, collect_threads, wait);
3187 mono_threads_unlock ();
3189 for (i = 0; i < wait->num; ++i) {
3190 MonoInternalThread *thread = wait->threads [i];
3192 if (!mono_gc_is_finalizer_internal_thread (thread) &&
3193 (thread != mono_thread_internal_current ()) &&
3194 !thread->thread_dump_requested) {
3195 thread->thread_dump_requested = TRUE;
3197 signal_thread_state_change (thread);
3200 CloseHandle (wait->handles [i]);
3204 struct ref_stack {
3205 gpointer *refs;
3206 gint allocated; /* +1 so that refs [allocated] == NULL */
3207 gint bottom;
3210 typedef struct ref_stack RefStack;
3212 static RefStack *
3213 ref_stack_new (gint initial_size)
3215 RefStack *rs;
3217 initial_size = MAX (initial_size, 16) + 1;
3218 rs = g_new0 (RefStack, 1);
3219 rs->refs = g_new0 (gpointer, initial_size);
3220 rs->allocated = initial_size;
3221 return rs;
3224 static void
3225 ref_stack_destroy (gpointer ptr)
3227 RefStack *rs = ptr;
3229 if (rs != NULL) {
3230 g_free (rs->refs);
3231 g_free (rs);
3235 static void
3236 ref_stack_push (RefStack *rs, gpointer ptr)
3238 g_assert (rs != NULL);
3240 if (rs->bottom >= rs->allocated) {
3241 rs->refs = g_realloc (rs->refs, rs->allocated * 2 * sizeof (gpointer) + 1);
3242 rs->allocated <<= 1;
3243 rs->refs [rs->allocated] = NULL;
3245 rs->refs [rs->bottom++] = ptr;
3248 static void
3249 ref_stack_pop (RefStack *rs)
3251 if (rs == NULL || rs->bottom == 0)
3252 return;
3254 rs->bottom--;
3255 rs->refs [rs->bottom] = NULL;
3258 static gboolean
3259 ref_stack_find (RefStack *rs, gpointer ptr)
3261 gpointer *refs;
3263 if (rs == NULL)
3264 return FALSE;
3266 for (refs = rs->refs; refs && *refs; refs++) {
3267 if (*refs == ptr)
3268 return TRUE;
3270 return FALSE;
3274 * mono_thread_push_appdomain_ref:
3276 * Register that the current thread may have references to objects in domain
3277 * @domain on its stack. Each call to this function should be paired with a
3278 * call to pop_appdomain_ref.
3280 void
3281 mono_thread_push_appdomain_ref (MonoDomain *domain)
3283 MonoInternalThread *thread = mono_thread_internal_current ();
3285 if (thread) {
3286 /* printf ("PUSH REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, domain->friendly_name); */
3287 SPIN_LOCK (thread->lock_thread_id);
3288 if (thread->appdomain_refs == NULL)
3289 thread->appdomain_refs = ref_stack_new (16);
3290 ref_stack_push (thread->appdomain_refs, domain);
3291 SPIN_UNLOCK (thread->lock_thread_id);
3295 void
3296 mono_thread_pop_appdomain_ref (void)
3298 MonoInternalThread *thread = mono_thread_internal_current ();
3300 if (thread) {
3301 /* printf ("POP REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, ((MonoDomain*)(thread->appdomain_refs->data))->friendly_name); */
3302 SPIN_LOCK (thread->lock_thread_id);
3303 ref_stack_pop (thread->appdomain_refs);
3304 SPIN_UNLOCK (thread->lock_thread_id);
3308 gboolean
3309 mono_thread_internal_has_appdomain_ref (MonoInternalThread *thread, MonoDomain *domain)
3311 gboolean res;
3312 SPIN_LOCK (thread->lock_thread_id);
3313 res = ref_stack_find (thread->appdomain_refs, domain);
3314 SPIN_UNLOCK (thread->lock_thread_id);
3315 return res;
3318 gboolean
3319 mono_thread_has_appdomain_ref (MonoThread *thread, MonoDomain *domain)
3321 return mono_thread_internal_has_appdomain_ref (thread->internal_thread, domain);
3324 typedef struct abort_appdomain_data {
3325 struct wait_data wait;
3326 MonoDomain *domain;
3327 } abort_appdomain_data;
3329 static void
3330 collect_appdomain_thread (gpointer key, gpointer value, gpointer user_data)
3332 MonoInternalThread *thread = (MonoInternalThread*)value;
3333 abort_appdomain_data *data = (abort_appdomain_data*)user_data;
3334 MonoDomain *domain = data->domain;
3336 if (mono_thread_internal_has_appdomain_ref (thread, domain)) {
3337 /* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread->tid, domain->friendly_name); */
3339 if(data->wait.num<MAXIMUM_WAIT_OBJECTS) {
3340 HANDLE handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
3341 if (handle == NULL)
3342 return;
3343 data->wait.handles [data->wait.num] = handle;
3344 data->wait.threads [data->wait.num] = thread;
3345 data->wait.num++;
3346 } else {
3347 /* Just ignore the rest, we can't do anything with
3348 * them yet
3355 * mono_threads_abort_appdomain_threads:
3357 * Abort threads which has references to the given appdomain.
3359 gboolean
3360 mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
3362 abort_appdomain_data user_data;
3363 guint32 start_time;
3364 int orig_timeout = timeout;
3365 int i;
3367 THREAD_DEBUG (g_message ("%s: starting abort", __func__));
3369 start_time = mono_msec_ticks ();
3370 do {
3371 mono_threads_lock ();
3373 user_data.domain = domain;
3374 user_data.wait.num = 0;
3375 /* This shouldn't take any locks */
3376 mono_g_hash_table_foreach (threads, collect_appdomain_thread, &user_data);
3377 mono_threads_unlock ();
3379 if (user_data.wait.num > 0) {
3380 /* Abort the threads outside the threads lock */
3381 for (i = 0; i < user_data.wait.num; ++i)
3382 ves_icall_System_Threading_Thread_Abort (user_data.wait.threads [i], NULL);
3385 * We should wait for the threads either to abort, or to leave the
3386 * domain. We can't do the latter, so we wait with a timeout.
3388 wait_for_tids (&user_data.wait, 100);
3391 /* Update remaining time */
3392 timeout -= mono_msec_ticks () - start_time;
3393 start_time = mono_msec_ticks ();
3395 if (orig_timeout != -1 && timeout < 0)
3396 return FALSE;
3398 while (user_data.wait.num > 0);
3400 THREAD_DEBUG (g_message ("%s: abort done", __func__));
3402 return TRUE;
3405 static void
3406 clear_cached_culture (gpointer key, gpointer value, gpointer user_data)
3408 MonoInternalThread *thread = (MonoInternalThread*)value;
3409 MonoDomain *domain = (MonoDomain*)user_data;
3410 int i;
3412 /* No locking needed here */
3413 /* FIXME: why no locking? writes to the cache are protected with synch_cs above */
3415 if (thread->cached_culture_info) {
3416 for (i = 0; i < NUM_CACHED_CULTURES * 2; ++i) {
3417 MonoObject *obj = mono_array_get (thread->cached_culture_info, MonoObject*, i);
3418 if (obj && obj->vtable->domain == domain)
3419 mono_array_set (thread->cached_culture_info, MonoObject*, i, NULL);
3425 * mono_threads_clear_cached_culture:
3427 * Clear the cached_current_culture from all threads if it is in the
3428 * given appdomain.
3430 void
3431 mono_threads_clear_cached_culture (MonoDomain *domain)
3433 mono_threads_lock ();
3434 mono_g_hash_table_foreach (threads, clear_cached_culture, domain);
3435 mono_threads_unlock ();
3439 * mono_thread_get_undeniable_exception:
3441 * Return an exception which needs to be raised when leaving a catch clause.
3442 * This is used for undeniable exception propagation.
3444 MonoException*
3445 mono_thread_get_undeniable_exception (void)
3447 MonoInternalThread *thread = mono_thread_internal_current ();
3449 if (thread && thread->abort_exc && !is_running_protected_wrapper ()) {
3451 * FIXME: Clear the abort exception and return an AppDomainUnloaded
3452 * exception if the thread no longer references a dying appdomain.
3454 thread->abort_exc->trace_ips = NULL;
3455 thread->abort_exc->stack_trace = NULL;
3456 return thread->abort_exc;
3459 return NULL;
3462 #if MONO_SMALL_CONFIG
3463 #define NUM_STATIC_DATA_IDX 4
3464 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
3465 64, 256, 1024, 4096
3467 #else
3468 #define NUM_STATIC_DATA_IDX 8
3469 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
3470 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
3472 #endif
3474 static uintptr_t* static_reference_bitmaps [NUM_STATIC_DATA_IDX];
3476 #ifdef HAVE_SGEN_GC
3477 static void
3478 mark_tls_slots (void *addr, MonoGCMarkFunc mark_func)
3480 int i;
3481 gpointer *static_data = addr;
3482 for (i = 0; i < NUM_STATIC_DATA_IDX; ++i) {
3483 int j, numwords;
3484 void **ptr;
3485 if (!static_data [i])
3486 continue;
3487 numwords = 1 + static_data_size [i] / sizeof (gpointer) / (sizeof(uintptr_t) * 8);
3488 ptr = static_data [i];
3489 for (j = 0; j < numwords; ++j, ptr += sizeof (uintptr_t) * 8) {
3490 uintptr_t bmap = static_reference_bitmaps [i][j];
3491 void ** p = ptr;
3492 while (bmap) {
3493 if ((bmap & 1) && *p) {
3494 mark_func (p);
3496 p++;
3497 bmap >>= 1;
3502 #endif
3505 * mono_alloc_static_data
3507 * Allocate memory blocks for storing threads or context static data
3509 static void
3510 mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean threadlocal)
3512 guint idx = (offset >> 24) - 1;
3513 int i;
3515 gpointer* static_data = *static_data_ptr;
3516 if (!static_data) {
3517 static void* tls_desc = NULL;
3518 #ifdef HAVE_SGEN_GC
3519 if (!tls_desc)
3520 tls_desc = mono_gc_make_root_descr_user (mark_tls_slots);
3521 #endif
3522 static_data = mono_gc_alloc_fixed (static_data_size [0], threadlocal?tls_desc:NULL);
3523 *static_data_ptr = static_data;
3524 static_data [0] = static_data;
3527 for (i = 1; i <= idx; ++i) {
3528 if (static_data [i])
3529 continue;
3530 #ifdef HAVE_SGEN_GC
3531 static_data [i] = threadlocal?g_malloc0 (static_data_size [i]):mono_gc_alloc_fixed (static_data_size [i], NULL);
3532 #else
3533 static_data [i] = mono_gc_alloc_fixed (static_data_size [i], NULL);
3534 #endif
3538 static void
3539 mono_free_static_data (gpointer* static_data, gboolean threadlocal)
3541 int i;
3542 for (i = 1; i < NUM_STATIC_DATA_IDX; ++i) {
3543 if (!static_data [i])
3544 continue;
3545 #ifdef HAVE_SGEN_GC
3546 if (threadlocal)
3547 g_free (static_data [i]);
3548 else
3549 mono_gc_free_fixed (static_data [i]);
3550 #else
3551 mono_gc_free_fixed (static_data [i]);
3552 #endif
3554 mono_gc_free_fixed (static_data);
3558 * mono_init_static_data_info
3560 * Initializes static data counters
3562 static void mono_init_static_data_info (StaticDataInfo *static_data)
3564 static_data->idx = 0;
3565 static_data->offset = 0;
3566 static_data->freelist = NULL;
3570 * mono_alloc_static_data_slot
3572 * Generates an offset for static data. static_data contains the counters
3573 * used to generate it.
3575 static guint32
3576 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align)
3578 guint32 offset;
3580 if (!static_data->idx && !static_data->offset) {
3582 * we use the first chunk of the first allocation also as
3583 * an array for the rest of the data
3585 static_data->offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX;
3587 static_data->offset += align - 1;
3588 static_data->offset &= ~(align - 1);
3589 if (static_data->offset + size >= static_data_size [static_data->idx]) {
3590 static_data->idx ++;
3591 g_assert (size <= static_data_size [static_data->idx]);
3592 g_assert (static_data->idx < NUM_STATIC_DATA_IDX);
3593 static_data->offset = 0;
3595 offset = static_data->offset | ((static_data->idx + 1) << 24);
3596 static_data->offset += size;
3597 return offset;
3601 * ensure thread static fields already allocated are valid for thread
3602 * This function is called when a thread is created or on thread attach.
3604 static void
3605 thread_adjust_static_data (MonoInternalThread *thread)
3607 guint32 offset;
3609 mono_threads_lock ();
3610 if (thread_static_info.offset || thread_static_info.idx > 0) {
3611 /* get the current allocated size */
3612 offset = thread_static_info.offset | ((thread_static_info.idx + 1) << 24);
3613 mono_alloc_static_data (&(thread->static_data), offset, TRUE);
3615 mono_threads_unlock ();
3618 static void
3619 alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
3621 MonoInternalThread *thread = value;
3622 guint32 offset = GPOINTER_TO_UINT (user);
3624 mono_alloc_static_data (&(thread->static_data), offset, TRUE);
3627 static MonoThreadDomainTls*
3628 search_tls_slot_in_freelist (StaticDataInfo *static_data, guint32 size, guint32 align)
3630 MonoThreadDomainTls* prev = NULL;
3631 MonoThreadDomainTls* tmp = static_data->freelist;
3632 while (tmp) {
3633 if (tmp->size == size) {
3634 if (prev)
3635 prev->next = tmp->next;
3636 else
3637 static_data->freelist = tmp->next;
3638 return tmp;
3640 tmp = tmp->next;
3642 return NULL;
3645 static void
3646 update_tls_reference_bitmap (guint32 offset, uintptr_t *bitmap, int max_set)
3648 int i;
3649 int idx = (offset >> 24) - 1;
3650 uintptr_t *rb;
3651 if (!static_reference_bitmaps [idx])
3652 static_reference_bitmaps [idx] = g_new0 (uintptr_t, 1 + static_data_size [idx] / sizeof(gpointer) / (sizeof(uintptr_t) * 8));
3653 rb = static_reference_bitmaps [idx];
3654 offset &= 0xffffff;
3655 offset /= sizeof (gpointer);
3656 /* offset is now the bitmap offset */
3657 for (i = 0; i < max_set; ++i) {
3658 if (bitmap [i / sizeof (uintptr_t)] & (1L << (i & (sizeof (uintptr_t) * 8 -1))))
3659 rb [(offset + i) / (sizeof (uintptr_t) * 8)] |= (1L << ((offset + i) & (sizeof (uintptr_t) * 8 -1)));
3663 static void
3664 clear_reference_bitmap (guint32 offset, guint32 size)
3666 int idx = (offset >> 24) - 1;
3667 uintptr_t *rb;
3668 rb = static_reference_bitmaps [idx];
3669 offset &= 0xffffff;
3670 offset /= sizeof (gpointer);
3671 size /= sizeof (gpointer);
3672 size += offset;
3673 /* offset is now the bitmap offset */
3674 for (; offset < size; ++offset)
3675 rb [offset / (sizeof (uintptr_t) * 8)] &= ~(1L << (offset & (sizeof (uintptr_t) * 8 -1)));
3679 * The offset for a special static variable is composed of three parts:
3680 * a bit that indicates the type of static data (0:thread, 1:context),
3681 * an index in the array of chunks of memory for the thread (thread->static_data)
3682 * and an offset in that chunk of mem. This allows allocating less memory in the
3683 * common case.
3686 guint32
3687 mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align, uintptr_t *bitmap, int max_set)
3689 guint32 offset;
3690 if (static_type == SPECIAL_STATIC_THREAD) {
3691 MonoThreadDomainTls *item;
3692 mono_threads_lock ();
3693 item = search_tls_slot_in_freelist (&thread_static_info, size, align);
3694 /*g_print ("TLS alloc: %d in domain %p (total: %d), cached: %p\n", size, mono_domain_get (), thread_static_info.offset, item);*/
3695 if (item) {
3696 offset = item->offset;
3697 g_free (item);
3698 } else {
3699 offset = mono_alloc_static_data_slot (&thread_static_info, size, align);
3701 update_tls_reference_bitmap (offset, bitmap, max_set);
3702 /* This can be called during startup */
3703 if (threads != NULL)
3704 mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
3705 mono_threads_unlock ();
3706 } else {
3707 g_assert (static_type == SPECIAL_STATIC_CONTEXT);
3708 mono_contexts_lock ();
3709 offset = mono_alloc_static_data_slot (&context_static_info, size, align);
3710 mono_contexts_unlock ();
3711 offset |= 0x80000000; /* Set the high bit to indicate context static data */
3713 return offset;
3716 gpointer
3717 mono_get_special_static_data_for_thread (MonoInternalThread *thread, guint32 offset)
3719 /* The high bit means either thread (0) or static (1) data. */
3721 guint32 static_type = (offset & 0x80000000);
3722 int idx;
3724 offset &= 0x7fffffff;
3725 idx = (offset >> 24) - 1;
3727 if (static_type == 0) {
3728 return get_thread_static_data (thread, offset);
3729 } else {
3730 /* Allocate static data block under demand, since we don't have a list
3731 // of contexts
3733 MonoAppContext *context = mono_context_get ();
3734 if (!context->static_data || !context->static_data [idx]) {
3735 mono_contexts_lock ();
3736 mono_alloc_static_data (&(context->static_data), offset, FALSE);
3737 mono_contexts_unlock ();
3739 return ((char*) context->static_data [idx]) + (offset & 0xffffff);
3743 gpointer
3744 mono_get_special_static_data (guint32 offset)
3746 return mono_get_special_static_data_for_thread (mono_thread_internal_current (), offset);
3749 typedef struct {
3750 guint32 offset;
3751 guint32 size;
3752 } TlsOffsetSize;
3754 static void
3755 free_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
3757 MonoInternalThread *thread = value;
3758 TlsOffsetSize *data = user;
3759 int idx = (data->offset >> 24) - 1;
3760 char *ptr;
3762 if (!thread->static_data || !thread->static_data [idx])
3763 return;
3764 ptr = ((char*) thread->static_data [idx]) + (data->offset & 0xffffff);
3765 mono_gc_bzero (ptr, data->size);
3768 static void
3769 do_free_special_slot (guint32 offset, guint32 size)
3771 guint32 static_type = (offset & 0x80000000);
3772 /*g_print ("free %s , size: %d, offset: %x\n", field->name, size, offset);*/
3773 if (static_type == 0) {
3774 TlsOffsetSize data;
3775 MonoThreadDomainTls *item = g_new0 (MonoThreadDomainTls, 1);
3776 data.offset = offset & 0x7fffffff;
3777 data.size = size;
3778 clear_reference_bitmap (data.offset, data.size);
3779 if (threads != NULL)
3780 mono_g_hash_table_foreach (threads, free_thread_static_data_helper, &data);
3781 item->offset = offset;
3782 item->size = size;
3784 if (!mono_runtime_is_shutting_down ()) {
3785 item->next = thread_static_info.freelist;
3786 thread_static_info.freelist = item;
3787 } else {
3788 /* We could be called during shutdown after mono_thread_cleanup () is called */
3789 g_free (item);
3791 } else {
3792 /* FIXME: free context static data as well */
3796 static void
3797 do_free_special (gpointer key, gpointer value, gpointer data)
3799 MonoClassField *field = key;
3800 guint32 offset = GPOINTER_TO_UINT (value);
3801 gint32 align;
3802 guint32 size;
3803 size = mono_type_size (field->type, &align);
3804 do_free_special_slot (offset, size);
3807 void
3808 mono_alloc_special_static_data_free (GHashTable *special_static_fields)
3810 mono_threads_lock ();
3811 g_hash_table_foreach (special_static_fields, do_free_special, NULL);
3812 mono_threads_unlock ();
3815 void
3816 mono_special_static_data_free_slot (guint32 offset, guint32 size)
3818 mono_threads_lock ();
3819 do_free_special_slot (offset, size);
3820 mono_threads_unlock ();
3824 * allocates room in the thread local area for storing an instance of the struct type
3825 * the allocation is kept track of in domain->tlsrec_list.
3827 uint32_t
3828 mono_thread_alloc_tls (MonoReflectionType *type)
3830 MonoDomain *domain = mono_domain_get ();
3831 MonoClass *klass;
3832 MonoTlsDataRecord *tlsrec;
3833 int max_set = 0;
3834 gsize *bitmap;
3835 gsize default_bitmap [4] = {0};
3836 uint32_t tls_offset;
3837 guint32 size;
3838 gint32 align;
3840 klass = mono_class_from_mono_type (type->type);
3841 /* TlsDatum is a struct, so we subtract the object header size offset */
3842 bitmap = mono_class_compute_bitmap (klass, default_bitmap, sizeof (default_bitmap) * 8, - (int)(sizeof (MonoObject) / sizeof (gpointer)), &max_set, FALSE);
3843 size = mono_type_size (type->type, &align);
3844 tls_offset = mono_alloc_special_static_data (SPECIAL_STATIC_THREAD, size, align, bitmap, max_set);
3845 if (bitmap != default_bitmap)
3846 g_free (bitmap);
3847 tlsrec = g_new0 (MonoTlsDataRecord, 1);
3848 tlsrec->tls_offset = tls_offset;
3849 tlsrec->size = size;
3850 mono_domain_lock (domain);
3851 tlsrec->next = domain->tlsrec_list;
3852 domain->tlsrec_list = tlsrec;
3853 mono_domain_unlock (domain);
3854 return tls_offset;
3857 void
3858 mono_thread_destroy_tls (uint32_t tls_offset)
3860 MonoTlsDataRecord *prev = NULL;
3861 MonoTlsDataRecord *cur;
3862 guint32 size = 0;
3863 MonoDomain *domain = mono_domain_get ();
3864 mono_domain_lock (domain);
3865 cur = domain->tlsrec_list;
3866 while (cur) {
3867 if (cur->tls_offset == tls_offset) {
3868 if (prev)
3869 prev->next = cur->next;
3870 else
3871 domain->tlsrec_list = cur->next;
3872 size = cur->size;
3873 g_free (cur);
3874 break;
3876 prev = cur;
3877 cur = cur->next;
3879 mono_domain_unlock (domain);
3880 if (size)
3881 mono_special_static_data_free_slot (tls_offset, size);
3885 * This is just to ensure cleanup: the finalizers should have taken care, so this is not perf-critical.
3887 void
3888 mono_thread_destroy_domain_tls (MonoDomain *domain)
3890 while (domain->tlsrec_list)
3891 mono_thread_destroy_tls (domain->tlsrec_list->tls_offset);
3894 static MonoClassField *local_slots = NULL;
3896 typedef struct {
3897 /* local tls data to get locals_slot from a thread */
3898 guint32 offset;
3899 int idx;
3900 /* index in the locals_slot array */
3901 int slot;
3902 } LocalSlotID;
3904 static void
3905 clear_local_slot (gpointer key, gpointer value, gpointer user_data)
3907 LocalSlotID *sid = user_data;
3908 MonoInternalThread *thread = (MonoInternalThread*)value;
3909 MonoArray *slots_array;
3911 * the static field is stored at: ((char*) thread->static_data [idx]) + (offset & 0xffffff);
3912 * it is for the right domain, so we need to check if it is allocated an initialized
3913 * for the current thread.
3915 /*g_print ("handling thread %p\n", thread);*/
3916 if (!thread->static_data || !thread->static_data [sid->idx])
3917 return;
3918 slots_array = *(MonoArray **)(((char*) thread->static_data [sid->idx]) + (sid->offset & 0xffffff));
3919 if (!slots_array || sid->slot >= mono_array_length (slots_array))
3920 return;
3921 mono_array_set (slots_array, MonoObject*, sid->slot, NULL);
3924 void
3925 mono_thread_free_local_slot_values (int slot, MonoBoolean thread_local)
3927 MonoDomain *domain;
3928 LocalSlotID sid;
3929 sid.slot = slot;
3930 if (thread_local) {
3931 void *addr = NULL;
3932 if (!local_slots) {
3933 local_slots = mono_class_get_field_from_name (mono_defaults.thread_class, "local_slots");
3934 if (!local_slots) {
3935 g_warning ("local_slots field not found in Thread class");
3936 return;
3939 domain = mono_domain_get ();
3940 mono_domain_lock (domain);
3941 if (domain->special_static_fields)
3942 addr = g_hash_table_lookup (domain->special_static_fields, local_slots);
3943 mono_domain_unlock (domain);
3944 if (!addr)
3945 return;
3946 /*g_print ("freeing slot %d at %p\n", slot, addr);*/
3947 sid.offset = GPOINTER_TO_UINT (addr);
3948 sid.offset &= 0x7fffffff;
3949 sid.idx = (sid.offset >> 24) - 1;
3950 mono_threads_lock ();
3951 mono_g_hash_table_foreach (threads, clear_local_slot, &sid);
3952 mono_threads_unlock ();
3953 } else {
3954 /* FIXME: clear the slot for MonoAppContexts, too */
3958 #ifdef HOST_WIN32
3959 static void CALLBACK dummy_apc (ULONG_PTR param)
3962 #else
3963 static guint32 dummy_apc (gpointer param)
3965 return 0;
3967 #endif
3970 * mono_thread_execute_interruption
3972 * Performs the operation that the requested thread state requires (abort,
3973 * suspend or stop)
3975 static MonoException* mono_thread_execute_interruption (MonoInternalThread *thread)
3977 ensure_synch_cs_set (thread);
3979 EnterCriticalSection (thread->synch_cs);
3981 /* MonoThread::interruption_requested can only be changed with atomics */
3982 if (InterlockedCompareExchange (&thread->interruption_requested, FALSE, TRUE)) {
3983 /* this will consume pending APC calls */
3984 WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
3985 InterlockedDecrement (&thread_interruption_requested);
3986 #ifndef HOST_WIN32
3987 /* Clear the interrupted flag of the thread so it can wait again */
3988 wapi_clear_interruption ();
3989 #endif
3992 if ((thread->state & ThreadState_AbortRequested) != 0) {
3993 LeaveCriticalSection (thread->synch_cs);
3994 if (thread->abort_exc == NULL) {
3996 * This might be racy, but it has to be called outside the lock
3997 * since it calls managed code.
3999 MONO_OBJECT_SETREF (thread, abort_exc, mono_get_exception_thread_abort ());
4001 return thread->abort_exc;
4003 else if ((thread->state & ThreadState_SuspendRequested) != 0) {
4004 self_suspend_internal (thread);
4005 return NULL;
4007 else if ((thread->state & ThreadState_StopRequested) != 0) {
4008 /* FIXME: do this through the JIT? */
4010 LeaveCriticalSection (thread->synch_cs);
4012 mono_thread_exit ();
4013 return NULL;
4014 } else if (thread->thread_interrupt_requested) {
4016 thread->thread_interrupt_requested = FALSE;
4017 LeaveCriticalSection (thread->synch_cs);
4019 return(mono_get_exception_thread_interrupted ());
4022 LeaveCriticalSection (thread->synch_cs);
4024 return NULL;
4028 * mono_thread_request_interruption
4030 * A signal handler can call this method to request the interruption of a
4031 * thread. The result of the interruption will depend on the current state of
4032 * the thread. If the result is an exception that needs to be throw, it is
4033 * provided as return value.
4035 MonoException*
4036 mono_thread_request_interruption (gboolean running_managed)
4038 MonoInternalThread *thread = mono_thread_internal_current ();
4040 /* The thread may already be stopping */
4041 if (thread == NULL)
4042 return NULL;
4044 #ifdef HOST_WIN32
4045 if (thread->interrupt_on_stop &&
4046 thread->state & ThreadState_StopRequested &&
4047 thread->state & ThreadState_Background)
4048 ExitThread (1);
4049 #endif
4051 if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
4052 return NULL;
4054 if (!running_managed || is_running_protected_wrapper ()) {
4055 /* Can't stop while in unmanaged code. Increase the global interruption
4056 request count. When exiting the unmanaged method the count will be
4057 checked and the thread will be interrupted. */
4059 InterlockedIncrement (&thread_interruption_requested);
4061 if (mono_thread_notify_pending_exc_fn && !running_managed)
4062 /* The JIT will notify the thread about the interruption */
4063 /* This shouldn't take any locks */
4064 mono_thread_notify_pending_exc_fn ();
4066 /* this will awake the thread if it is in WaitForSingleObject
4067 or similar */
4068 /* Our implementation of this function ignores the func argument */
4069 QueueUserAPC ((PAPCFUNC)dummy_apc, thread->handle, NULL);
4070 return NULL;
4072 else {
4073 return mono_thread_execute_interruption (thread);
4077 /*This function should be called by a thread after it has exited all of
4078 * its handle blocks at interruption time.*/
4079 MonoException*
4080 mono_thread_resume_interruption (void)
4082 MonoInternalThread *thread = mono_thread_internal_current ();
4083 gboolean still_aborting;
4085 /* The thread may already be stopping */
4086 if (thread == NULL)
4087 return NULL;
4089 ensure_synch_cs_set (thread);
4090 EnterCriticalSection (thread->synch_cs);
4091 still_aborting = (thread->state & ThreadState_AbortRequested) != 0;
4092 LeaveCriticalSection (thread->synch_cs);
4094 /*This can happen if the protected block called Thread::ResetAbort*/
4095 if (!still_aborting)
4096 return FALSE;
4098 if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
4099 return NULL;
4100 InterlockedIncrement (&thread_interruption_requested);
4102 #ifndef HOST_WIN32
4103 wapi_self_interrupt ();
4104 #endif
4105 return mono_thread_execute_interruption (thread);
4108 gboolean mono_thread_interruption_requested ()
4110 if (thread_interruption_requested) {
4111 MonoInternalThread *thread = mono_thread_internal_current ();
4112 /* The thread may already be stopping */
4113 if (thread != NULL)
4114 return (thread->interruption_requested);
4116 return FALSE;
4119 static void mono_thread_interruption_checkpoint_request (gboolean bypass_abort_protection)
4121 MonoInternalThread *thread = mono_thread_internal_current ();
4123 /* The thread may already be stopping */
4124 if (thread == NULL)
4125 return;
4127 mono_debugger_check_interruption ();
4129 if (thread->interruption_requested && (bypass_abort_protection || !is_running_protected_wrapper ())) {
4130 MonoException* exc = mono_thread_execute_interruption (thread);
4131 if (exc) mono_raise_exception (exc);
4136 * Performs the interruption of the current thread, if one has been requested,
4137 * and the thread is not running a protected wrapper.
4139 void mono_thread_interruption_checkpoint ()
4141 mono_thread_interruption_checkpoint_request (FALSE);
4145 * Performs the interruption of the current thread, if one has been requested.
4147 void mono_thread_force_interruption_checkpoint ()
4149 mono_thread_interruption_checkpoint_request (TRUE);
4153 * mono_thread_get_and_clear_pending_exception:
4155 * Return any pending exceptions for the current thread and clear it as a side effect.
4157 MonoException*
4158 mono_thread_get_and_clear_pending_exception (void)
4160 MonoInternalThread *thread = mono_thread_internal_current ();
4162 /* The thread may already be stopping */
4163 if (thread == NULL)
4164 return NULL;
4166 if (thread->interruption_requested && !is_running_protected_wrapper ()) {
4167 return mono_thread_execute_interruption (thread);
4170 if (thread->pending_exception) {
4171 MonoException *exc = thread->pending_exception;
4173 thread->pending_exception = NULL;
4174 return exc;
4177 return NULL;
4181 * mono_set_pending_exception:
4183 * Set the pending exception of the current thread to EXC. On platforms which
4184 * support it, the exception will be thrown when execution returns to managed code.
4185 * On other platforms, this function is equivalent to mono_raise_exception ().
4186 * Internal calls which report exceptions using this function instead of
4187 * raise_exception () might be called by JITted code using a more efficient calling
4188 * convention.
4190 void
4191 mono_set_pending_exception (MonoException *exc)
4193 MonoInternalThread *thread = mono_thread_internal_current ();
4195 /* The thread may already be stopping */
4196 if (thread == NULL)
4197 return;
4199 if (mono_thread_notify_pending_exc_fn) {
4200 MONO_OBJECT_SETREF (thread, pending_exception, exc);
4202 mono_thread_notify_pending_exc_fn ();
4203 } else {
4204 /* No way to notify the JIT about the exception, have to throw it now */
4205 mono_raise_exception (exc);
4210 * mono_thread_interruption_request_flag:
4212 * Returns the address of a flag that will be non-zero if an interruption has
4213 * been requested for a thread. The thread to interrupt may not be the current
4214 * thread, so an additional call to mono_thread_interruption_requested() or
4215 * mono_thread_interruption_checkpoint() is allways needed if the flag is not
4216 * zero.
4218 gint32* mono_thread_interruption_request_flag ()
4220 return &thread_interruption_requested;
4223 void
4224 mono_thread_init_apartment_state (void)
4226 #ifdef HOST_WIN32
4227 MonoInternalThread* thread = mono_thread_internal_current ();
4229 /* Positive return value indicates success, either
4230 * S_OK if this is first CoInitialize call, or
4231 * S_FALSE if CoInitialize already called, but with same
4232 * threading model. A negative value indicates failure,
4233 * probably due to trying to change the threading model.
4235 if (CoInitializeEx(NULL, (thread->apartment_state == ThreadApartmentState_STA)
4236 ? COINIT_APARTMENTTHREADED
4237 : COINIT_MULTITHREADED) < 0) {
4238 thread->apartment_state = ThreadApartmentState_Unknown;
4240 #endif
4243 void
4244 mono_thread_cleanup_apartment_state (void)
4246 #ifdef HOST_WIN32
4247 MonoInternalThread* thread = mono_thread_internal_current ();
4249 if (thread && thread->apartment_state != ThreadApartmentState_Unknown) {
4250 CoUninitialize ();
4252 #endif
4255 void
4256 mono_thread_set_state (MonoInternalThread *thread, MonoThreadState state)
4258 ensure_synch_cs_set (thread);
4260 EnterCriticalSection (thread->synch_cs);
4261 thread->state |= state;
4262 LeaveCriticalSection (thread->synch_cs);
4265 void
4266 mono_thread_clr_state (MonoInternalThread *thread, MonoThreadState state)
4268 ensure_synch_cs_set (thread);
4270 EnterCriticalSection (thread->synch_cs);
4271 thread->state &= ~state;
4272 LeaveCriticalSection (thread->synch_cs);
4275 gboolean
4276 mono_thread_test_state (MonoInternalThread *thread, MonoThreadState test)
4278 gboolean ret = FALSE;
4280 ensure_synch_cs_set (thread);
4282 EnterCriticalSection (thread->synch_cs);
4284 if ((thread->state & test) != 0) {
4285 ret = TRUE;
4288 LeaveCriticalSection (thread->synch_cs);
4290 return ret;
4293 static MonoClassField *execution_context_field;
4295 static MonoObject**
4296 get_execution_context_addr (void)
4298 MonoDomain *domain = mono_domain_get ();
4299 guint32 offset;
4301 if (!execution_context_field) {
4302 execution_context_field = mono_class_get_field_from_name (mono_defaults.thread_class,
4303 "_ec");
4304 g_assert (execution_context_field);
4307 g_assert (mono_class_try_get_vtable (domain, mono_defaults.appdomain_class));
4309 mono_domain_lock (domain);
4310 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, execution_context_field));
4311 mono_domain_unlock (domain);
4312 g_assert (offset);
4314 return (MonoObject**) mono_get_special_static_data (offset);
4317 MonoObject*
4318 mono_thread_get_execution_context (void)
4320 return *get_execution_context_addr ();
4323 void
4324 mono_thread_set_execution_context (MonoObject *ec)
4326 *get_execution_context_addr () = ec;
4329 static gboolean has_tls_get = FALSE;
4331 void
4332 mono_runtime_set_has_tls_get (gboolean val)
4334 has_tls_get = val;
4337 gboolean
4338 mono_runtime_has_tls_get (void)
4340 return has_tls_get;
4344 mono_thread_kill (MonoInternalThread *thread, int signal)
4346 #ifdef HOST_WIN32
4347 /* Win32 uses QueueUserAPC and callers of this are guarded */
4348 g_assert_not_reached ();
4349 #else
4350 # ifdef PTHREAD_POINTER_ID
4351 return pthread_kill ((gpointer)(gsize)(thread->tid), mono_thread_get_abort_signal ());
4352 # else
4353 # ifdef PLATFORM_ANDROID
4354 if (thread->android_tid != 0) {
4355 int ret;
4356 int old_errno = errno;
4358 ret = tkill ((pid_t) thread->android_tid, signal);
4359 if (ret < 0) {
4360 ret = errno;
4361 errno = old_errno;
4364 return ret;
4366 else
4367 return pthread_kill (thread->tid, mono_thread_get_abort_signal ());
4368 # else
4369 return pthread_kill (thread->tid, mono_thread_get_abort_signal ());
4370 # endif
4371 # endif
4372 #endif
4375 static void
4376 self_interrupt_thread (void *_unused)
4378 MonoThreadInfo *info = mono_thread_info_current ();
4379 MonoException *exc = mono_thread_execute_interruption (mono_thread_internal_current ());
4380 if (exc) /*We must use _with_context since we didn't trampoline into the runtime*/
4381 mono_raise_exception_with_context (exc, &info->suspend_state.ctx);
4382 g_assert_not_reached (); /*this MUST not happen since we can't resume from an async call*/
4385 static gboolean
4386 mono_jit_info_match (MonoJitInfo *ji, gpointer ip)
4388 if (!ji)
4389 return FALSE;
4390 return ji->code_start <= ip && (char*)ip < (char*)ji->code_start + ji->code_size;
4393 static gboolean
4394 last_managed (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
4396 MonoJitInfo **dest = data;
4397 *dest = frame->ji;
4398 return TRUE;
4401 static MonoJitInfo*
4402 mono_thread_info_get_last_managed (MonoThreadInfo *info)
4404 MonoJitInfo *ji = NULL;
4405 mono_get_eh_callbacks ()->mono_walk_stack_with_state (last_managed, &info->suspend_state, MONO_UNWIND_SIGNAL_SAFE, &ji);
4406 return ji;
4409 static void
4410 abort_thread_internal (MonoInternalThread *thread, gboolean can_raise_exception, gboolean install_async_abort)
4412 MonoJitInfo *ji;
4413 MonoThreadInfo *info = NULL;
4414 gboolean protected_wrapper;
4415 gboolean running_managed;
4417 if (!mono_thread_info_new_interrupt_enabled ()) {
4418 signal_thread_state_change (thread);
4419 return;
4423 FIXME this is insanely broken, it doesn't cause interruption to happen
4424 synchronously since passing FALSE to mono_thread_request_interruption makes sure it returns NULL
4426 if (thread == mono_thread_internal_current ()) {
4427 /* Do it synchronously */
4428 MonoException *exc = mono_thread_request_interruption (can_raise_exception);
4429 if (exc)
4430 mono_raise_exception (exc);
4431 #ifndef HOST_WIN32
4432 wapi_interrupt_thread (thread->handle);
4433 #endif
4434 return;
4437 /*FIXME we need to check 2 conditions here, request to interrupt this thread or if the target died*/
4438 if (!(info = mono_thread_info_safe_suspend_sync ((MonoNativeThreadId)(gsize)thread->tid, TRUE))) {
4439 return;
4442 if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (&info->suspend_state)) {
4443 mono_thread_info_resume (mono_thread_info_get_tid (info));
4444 return;
4447 /*someone is already interrupting it*/
4448 if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1) {
4449 mono_thread_info_resume (mono_thread_info_get_tid (info));
4450 return;
4453 ji = mono_thread_info_get_last_managed (info);
4454 protected_wrapper = ji && mono_threads_is_critical_method (ji->method);
4455 running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
4457 if (!protected_wrapper && running_managed) {
4458 /*We are in managed code*/
4459 /*Set the thread to call */
4460 if (install_async_abort)
4461 mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
4462 mono_thread_info_resume (mono_thread_info_get_tid (info));
4463 } else {
4465 * This will cause waits to be broken.
4466 * It will also prevent the thread from entering a wait, so if the thread returns
4467 * from the wait before it receives the abort signal, it will just spin in the wait
4468 * functions in the io-layer until the signal handler calls QueueUserAPC which will
4469 * make it return.
4471 InterlockedIncrement (&thread_interruption_requested);
4472 mono_thread_info_resume (mono_thread_info_get_tid (info));
4473 #ifndef HOST_WIN32
4474 wapi_interrupt_thread (thread->handle);
4475 #endif
4477 /*FIXME we need to wait for interruption to complete -- figure out how much into interruption we should wait for here*/
4480 static void
4481 transition_to_suspended (MonoInternalThread *thread)
4483 if ((thread->state & ThreadState_SuspendRequested) == 0) {
4484 g_assert (0); /*FIXME we should not reach this */
4485 /*Make sure we balance the suspend count.*/
4486 mono_thread_info_resume ((MonoNativeThreadId)(gpointer)(gsize)thread->tid);
4487 } else {
4488 thread->state &= ~ThreadState_SuspendRequested;
4489 thread->state |= ThreadState_Suspended;
4491 LeaveCriticalSection (thread->synch_cs);
4494 static void
4495 suspend_thread_internal (MonoInternalThread *thread, gboolean interrupt)
4497 if (!mono_thread_info_new_interrupt_enabled ()) {
4498 signal_thread_state_change (thread);
4499 return;
4502 EnterCriticalSection (thread->synch_cs);
4503 if (thread == mono_thread_internal_current ()) {
4504 transition_to_suspended (thread);
4505 mono_thread_info_self_suspend ();
4506 } else {
4507 MonoThreadInfo *info = mono_thread_info_safe_suspend_sync ((MonoNativeThreadId)(gsize)thread->tid, interrupt);
4508 MonoJitInfo *ji = mono_thread_info_get_last_managed (info);
4509 gboolean protected_wrapper = ji && mono_threads_is_critical_method (ji->method);
4510 gboolean running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
4512 if (running_managed && !protected_wrapper) {
4513 transition_to_suspended (thread);
4514 } else {
4515 if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 0)
4516 InterlockedIncrement (&thread_interruption_requested);
4517 #ifndef HOST_WIN32
4518 if (interrupt)
4519 wapi_interrupt_thread (thread->handle);
4520 #endif
4521 mono_thread_info_resume (mono_thread_info_get_tid (info));
4522 LeaveCriticalSection (thread->synch_cs);
4527 /*This is called with @thread synch_cs held and it must release it*/
4528 static void
4529 self_suspend_internal (MonoInternalThread *thread)
4531 if (!mono_thread_info_new_interrupt_enabled ()) {
4532 thread->state &= ~ThreadState_SuspendRequested;
4533 thread->state |= ThreadState_Suspended;
4534 thread->suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL);
4535 if (thread->suspend_event == NULL) {
4536 LeaveCriticalSection (thread->synch_cs);
4537 return;
4539 if (thread->suspended_event)
4540 SetEvent (thread->suspended_event);
4542 LeaveCriticalSection (thread->synch_cs);
4544 if (shutting_down) {
4545 /* After we left the lock, the runtime might shut down so everything becomes invalid */
4546 for (;;)
4547 Sleep (1000);
4550 WaitForSingleObject (thread->suspend_event, INFINITE);
4552 EnterCriticalSection (thread->synch_cs);
4554 CloseHandle (thread->suspend_event);
4555 thread->suspend_event = NULL;
4556 thread->state &= ~ThreadState_Suspended;
4558 /* The thread that requested the resume will have replaced this event
4559 * and will be waiting for it
4561 SetEvent (thread->resume_event);
4563 LeaveCriticalSection (thread->synch_cs);
4564 return;
4567 transition_to_suspended (thread);
4568 mono_thread_info_self_suspend ();
4571 /*This is called with @thread synch_cs held and it must release it*/
4572 static gboolean
4573 resume_thread_internal (MonoInternalThread *thread)
4575 if (!mono_thread_info_new_interrupt_enabled ()) {
4576 thread->resume_event = CreateEvent (NULL, TRUE, FALSE, NULL);
4577 if (thread->resume_event == NULL) {
4578 LeaveCriticalSection (thread->synch_cs);
4579 return FALSE;
4582 /* Awake the thread */
4583 SetEvent (thread->suspend_event);
4585 LeaveCriticalSection (thread->synch_cs);
4587 /* Wait for the thread to awake */
4588 WaitForSingleObject (thread->resume_event, INFINITE);
4589 CloseHandle (thread->resume_event);
4590 thread->resume_event = NULL;
4591 return TRUE;
4594 LeaveCriticalSection (thread->synch_cs);
4595 /* Awake the thread */
4596 if (!mono_thread_info_resume ((MonoNativeThreadId)(gpointer)(gsize)thread->tid))
4597 return FALSE;
4598 EnterCriticalSection (thread->synch_cs);
4599 thread->state &= ~ThreadState_Suspended;
4600 LeaveCriticalSection (thread->synch_cs);
4601 return TRUE;