2009-05-15 Geoff Norton <gnorton@novell.com>
[mono-project.git] / mono / metadata / threads.c
blobf8d827983d7e41f6d145a2b6ef5e5bfbd0b79040
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 #include <mono/metadata/object.h>
20 #include <mono/metadata/domain-internals.h>
21 #include <mono/metadata/profiler-private.h>
22 #include <mono/metadata/threads.h>
23 #include <mono/metadata/threadpool.h>
24 #include <mono/metadata/threads-types.h>
25 #include <mono/metadata/exception.h>
26 #include <mono/metadata/environment.h>
27 #include <mono/metadata/monitor.h>
28 #include <mono/metadata/gc-internal.h>
29 #include <mono/metadata/marshal.h>
30 #include <mono/io-layer/io-layer.h>
31 #ifndef PLATFORM_WIN32
32 #include <mono/io-layer/threads.h>
33 #endif
34 #include <mono/metadata/object-internals.h>
35 #include <mono/metadata/mono-debug-debugger.h>
36 #include <mono/utils/mono-compiler.h>
37 #include <mono/utils/mono-mmap.h>
38 #include <mono/utils/mono-membar.h>
39 #include <mono/utils/mono-time.h>
41 #include <mono/metadata/gc-internal.h>
43 /*#define THREAD_DEBUG(a) do { a; } while (0)*/
44 #define THREAD_DEBUG(a)
45 /*#define THREAD_WAIT_DEBUG(a) do { a; } while (0)*/
46 #define THREAD_WAIT_DEBUG(a)
47 /*#define LIBGC_DEBUG(a) do { a; } while (0)*/
48 #define LIBGC_DEBUG(a)
50 /* Provide this for systems with glib < 2.6 */
51 #ifndef G_GSIZE_FORMAT
52 # if GLIB_SIZEOF_LONG == 8
53 # define G_GSIZE_FORMAT "lu"
54 # else
55 # define G_GSIZE_FORMAT "u"
56 # endif
57 #endif
59 struct StartInfo
61 guint32 (*func)(void *);
62 MonoThread *obj;
63 MonoObject *delegate;
64 void *start_arg;
65 MonoDomain *domain;
68 typedef union {
69 gint32 ival;
70 gfloat fval;
71 } IntFloatUnion;
73 typedef union {
74 gint64 ival;
75 gdouble fval;
76 } LongDoubleUnion;
78 typedef struct _MonoThreadDomainTls MonoThreadDomainTls;
79 struct _MonoThreadDomainTls {
80 MonoThreadDomainTls *next;
81 guint32 offset;
82 guint32 size;
85 typedef struct {
86 int idx;
87 int offset;
88 MonoThreadDomainTls *freelist;
89 } StaticDataInfo;
91 typedef struct {
92 gpointer p;
93 MonoHazardousFreeFunc free_func;
94 } DelayedFreeItem;
96 /* Number of cached culture objects in the MonoThread->cached_culture_info array
97 * (per-type): we use the first NUM entries for CultureInfo and the last for
98 * UICultureInfo. So the size of the array is really NUM_CACHED_CULTURES * 2.
100 #define NUM_CACHED_CULTURES 4
101 #define CULTURES_START_IDX 0
102 #define UICULTURES_START_IDX NUM_CACHED_CULTURES
104 /* Controls access to the 'threads' hash table */
105 #define mono_threads_lock() EnterCriticalSection (&threads_mutex)
106 #define mono_threads_unlock() LeaveCriticalSection (&threads_mutex)
107 static CRITICAL_SECTION threads_mutex;
109 /* Controls access to context static data */
110 #define mono_contexts_lock() EnterCriticalSection (&contexts_mutex)
111 #define mono_contexts_unlock() LeaveCriticalSection (&contexts_mutex)
112 static CRITICAL_SECTION contexts_mutex;
114 /* Holds current status of static data heap */
115 static StaticDataInfo thread_static_info;
116 static StaticDataInfo context_static_info;
118 /* The hash of existing threads (key is thread ID) that need joining
119 * before exit
121 static MonoGHashTable *threads=NULL;
124 * Threads which are starting up and they are not in the 'threads' hash yet.
125 * When handle_store is called for a thread, it will be removed from this hash table.
126 * Protected by mono_threads_lock ().
128 static MonoGHashTable *threads_starting_up = NULL;
130 /* The TLS key that holds the MonoObject assigned to each thread */
131 static guint32 current_object_key = -1;
133 #ifdef HAVE_KW_THREAD
134 /* we need to use both the Tls* functions and __thread because
135 * the gc needs to see all the threads
137 static __thread MonoThread * tls_current_object MONO_TLS_FAST;
138 #define SET_CURRENT_OBJECT(x) do { \
139 tls_current_object = x; \
140 TlsSetValue (current_object_key, x); \
141 } while (FALSE)
142 #define GET_CURRENT_OBJECT() tls_current_object
143 #else
144 #define SET_CURRENT_OBJECT(x) TlsSetValue (current_object_key, x);
145 #define GET_CURRENT_OBJECT() (MonoThread*) TlsGetValue (current_object_key);
146 #endif
148 /* function called at thread start */
149 static MonoThreadStartCB mono_thread_start_cb = NULL;
151 /* function called at thread attach */
152 static MonoThreadAttachCB mono_thread_attach_cb = NULL;
154 /* function called at thread cleanup */
155 static MonoThreadCleanupFunc mono_thread_cleanup_fn = NULL;
157 /* function called to notify the runtime about a pending exception on the current thread */
158 static MonoThreadNotifyPendingExcFunc mono_thread_notify_pending_exc_fn = NULL;
160 /* The default stack size for each thread */
161 static guint32 default_stacksize = 0;
162 #define default_stacksize_for_thread(thread) ((thread)->stack_size? (thread)->stack_size: default_stacksize)
164 static void thread_adjust_static_data (MonoThread *thread);
165 static void mono_init_static_data_info (StaticDataInfo *static_data);
166 static guint32 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align);
167 static gboolean mono_thread_resume (MonoThread* thread);
168 static void mono_thread_start (MonoThread *thread);
169 static void signal_thread_state_change (MonoThread *thread);
171 /* Spin lock for InterlockedXXX 64 bit functions */
172 #define mono_interlocked_lock() EnterCriticalSection (&interlocked_mutex)
173 #define mono_interlocked_unlock() LeaveCriticalSection (&interlocked_mutex)
174 static CRITICAL_SECTION interlocked_mutex;
176 /* global count of thread interruptions requested */
177 static gint32 thread_interruption_requested = 0;
179 /* Event signaled when a thread changes its background mode */
180 static HANDLE background_change_event;
182 /* The table for small ID assignment */
183 static CRITICAL_SECTION small_id_mutex;
184 static int small_id_table_size = 0;
185 static int small_id_next = 0;
186 static int highest_small_id = -1;
187 static MonoThread **small_id_table = NULL;
189 /* The hazard table */
190 #define HAZARD_TABLE_MAX_SIZE 16384 /* There cannot be more threads than this number. */
191 static volatile int hazard_table_size = 0;
192 static MonoThreadHazardPointers * volatile hazard_table = NULL;
194 /* The table where we keep pointers to blocks to be freed but that
195 have to wait because they're guarded by a hazard pointer. */
196 static CRITICAL_SECTION delayed_free_table_mutex;
197 static GArray *delayed_free_table = NULL;
199 static gboolean shutting_down = FALSE;
201 guint32
202 mono_thread_get_tls_key (void)
204 return current_object_key;
207 gint32
208 mono_thread_get_tls_offset (void)
210 int offset;
211 MONO_THREAD_VAR_OFFSET (tls_current_object,offset);
212 return offset;
215 /* handle_store() and handle_remove() manage the array of threads that
216 * still need to be waited for when the main thread exits.
218 * If handle_store() returns FALSE the thread must not be started
219 * because Mono is shutting down.
221 static gboolean handle_store(MonoThread *thread)
223 mono_threads_lock ();
225 THREAD_DEBUG (g_message ("%s: thread %p ID %"G_GSIZE_FORMAT, __func__, thread, (gsize)thread->tid));
227 if (threads_starting_up)
228 mono_g_hash_table_remove (threads_starting_up, thread);
230 if (shutting_down) {
231 mono_threads_unlock ();
232 return FALSE;
235 if(threads==NULL) {
236 MONO_GC_REGISTER_ROOT (threads);
237 threads=mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC);
240 /* We don't need to duplicate thread->handle, because it is
241 * only closed when the thread object is finalized by the GC.
243 mono_g_hash_table_insert(threads, (gpointer)(gsize)(thread->tid),
244 thread);
246 mono_threads_unlock ();
248 return TRUE;
251 static gboolean handle_remove(MonoThread *thread)
253 gboolean ret;
254 gsize tid = thread->tid;
256 THREAD_DEBUG (g_message ("%s: thread ID %"G_GSIZE_FORMAT, __func__, tid));
258 mono_threads_lock ();
260 if (threads) {
261 /* We have to check whether the thread object for the
262 * tid is still the same in the table because the
263 * thread might have been destroyed and the tid reused
264 * in the meantime, in which case the tid would be in
265 * the table, but with another thread object.
267 if (mono_g_hash_table_lookup (threads, (gpointer)tid) == thread) {
268 mono_g_hash_table_remove (threads, (gpointer)tid);
269 ret = TRUE;
270 } else {
271 ret = FALSE;
274 else
275 ret = FALSE;
277 mono_threads_unlock ();
279 /* Don't close the handle here, wait for the object finalizer
280 * to do it. Otherwise, the following race condition applies:
282 * 1) Thread exits (and handle_remove() closes the handle)
284 * 2) Some other handle is reassigned the same slot
286 * 3) Another thread tries to join the first thread, and
287 * blocks waiting for the reassigned handle to be signalled
288 * (which might never happen). This is possible, because the
289 * thread calling Join() still has a reference to the first
290 * thread's object.
292 return ret;
296 * Allocate a small thread id.
298 * FIXME: The biggest part of this function is very similar to
299 * domain_id_alloc() in domain.c and should be merged.
301 static int
302 small_id_alloc (MonoThread *thread)
304 int id = -1, i;
306 EnterCriticalSection (&small_id_mutex);
308 if (!small_id_table) {
309 small_id_table_size = 2;
310 small_id_table = mono_gc_alloc_fixed (small_id_table_size * sizeof (MonoThread*), NULL);
312 for (i = small_id_next; i < small_id_table_size; ++i) {
313 if (!small_id_table [i]) {
314 id = i;
315 break;
318 if (id == -1) {
319 for (i = 0; i < small_id_next; ++i) {
320 if (!small_id_table [i]) {
321 id = i;
322 break;
326 if (id == -1) {
327 MonoThread **new_table;
328 int new_size = small_id_table_size * 2;
329 if (new_size >= (1 << 16))
330 g_assert_not_reached ();
331 id = small_id_table_size;
332 new_table = mono_gc_alloc_fixed (new_size * sizeof (MonoThread*), NULL);
333 memcpy (new_table, small_id_table, small_id_table_size * sizeof (void*));
334 mono_gc_free_fixed (small_id_table);
335 small_id_table = new_table;
336 small_id_table_size = new_size;
338 thread->small_id = id;
339 g_assert (small_id_table [id] == NULL);
340 small_id_table [id] = thread;
341 small_id_next++;
342 if (small_id_next > small_id_table_size)
343 small_id_next = 0;
345 if (id >= hazard_table_size) {
346 gpointer page_addr;
347 int pagesize = mono_pagesize ();
348 int num_pages = (hazard_table_size * sizeof (MonoThreadHazardPointers) + pagesize - 1) / pagesize;
350 if (hazard_table == NULL) {
351 hazard_table = mono_valloc (NULL,
352 sizeof (MonoThreadHazardPointers) * HAZARD_TABLE_MAX_SIZE,
353 MONO_MMAP_NONE);
356 g_assert (hazard_table != NULL);
357 page_addr = (guint8*)hazard_table + num_pages * pagesize;
359 g_assert (id < HAZARD_TABLE_MAX_SIZE);
361 mono_mprotect (page_addr, pagesize, MONO_MMAP_READ | MONO_MMAP_WRITE);
363 ++num_pages;
364 hazard_table_size = num_pages * pagesize / sizeof (MonoThreadHazardPointers);
366 g_assert (id < hazard_table_size);
368 hazard_table [id].hazard_pointers [0] = NULL;
369 hazard_table [id].hazard_pointers [1] = NULL;
372 if (id > highest_small_id) {
373 highest_small_id = id;
374 mono_memory_write_barrier ();
377 LeaveCriticalSection (&small_id_mutex);
379 return id;
382 static void
383 small_id_free (int id)
385 g_assert (id >= 0 && id < small_id_table_size);
386 g_assert (small_id_table [id] != NULL);
388 small_id_table [id] = NULL;
391 static gboolean
392 is_pointer_hazardous (gpointer p)
394 int i;
395 int highest = highest_small_id;
397 g_assert (highest < hazard_table_size);
399 for (i = 0; i <= highest; ++i) {
400 if (hazard_table [i].hazard_pointers [0] == p
401 || hazard_table [i].hazard_pointers [1] == p)
402 return TRUE;
405 return FALSE;
408 MonoThreadHazardPointers*
409 mono_hazard_pointer_get (void)
411 MonoThread *current_thread = mono_thread_current ();
413 if (!(current_thread && current_thread->small_id >= 0)) {
414 static MonoThreadHazardPointers emerg_hazard_table;
415 g_warning ("Thread %p may have been prematurely finalized", current_thread);
416 return &emerg_hazard_table;
419 return &hazard_table [current_thread->small_id];
422 static void
423 try_free_delayed_free_item (int index)
425 if (delayed_free_table->len > index) {
426 DelayedFreeItem item = { NULL, NULL };
428 EnterCriticalSection (&delayed_free_table_mutex);
429 /* We have to check the length again because another
430 thread might have freed an item before we acquired
431 the lock. */
432 if (delayed_free_table->len > index) {
433 item = g_array_index (delayed_free_table, DelayedFreeItem, index);
435 if (!is_pointer_hazardous (item.p))
436 g_array_remove_index_fast (delayed_free_table, index);
437 else
438 item.p = NULL;
440 LeaveCriticalSection (&delayed_free_table_mutex);
442 if (item.p != NULL)
443 item.free_func (item.p);
447 void
448 mono_thread_hazardous_free_or_queue (gpointer p, MonoHazardousFreeFunc free_func)
450 int i;
452 /* First try to free a few entries in the delayed free
453 table. */
454 for (i = 2; i >= 0; --i)
455 try_free_delayed_free_item (i);
457 /* Now see if the pointer we're freeing is hazardous. If it
458 isn't, free it. Otherwise put it in the delay list. */
459 if (is_pointer_hazardous (p)) {
460 DelayedFreeItem item = { p, free_func };
462 ++mono_stats.hazardous_pointer_count;
464 EnterCriticalSection (&delayed_free_table_mutex);
465 g_array_append_val (delayed_free_table, item);
466 LeaveCriticalSection (&delayed_free_table_mutex);
467 } else
468 free_func (p);
471 void
472 mono_thread_hazardous_try_free_all (void)
474 int len;
475 int i;
477 if (!delayed_free_table)
478 return;
480 len = delayed_free_table->len;
482 for (i = len - 1; i >= 0; --i)
483 try_free_delayed_free_item (i);
486 static void ensure_synch_cs_set (MonoThread *thread)
488 CRITICAL_SECTION *synch_cs;
490 if (thread->synch_cs != NULL) {
491 return;
494 synch_cs = g_new0 (CRITICAL_SECTION, 1);
495 InitializeCriticalSection (synch_cs);
497 if (InterlockedCompareExchangePointer ((gpointer *)&thread->synch_cs,
498 synch_cs, NULL) != NULL) {
499 /* Another thread must have installed this CS */
500 DeleteCriticalSection (synch_cs);
501 g_free (synch_cs);
506 * NOTE: this function can be called also for threads different from the current one:
507 * make sure no code called from it will ever assume it is run on the thread that is
508 * getting cleaned up.
510 static void thread_cleanup (MonoThread *thread)
512 g_assert (thread != NULL);
514 if (thread->abort_state_handle) {
515 g_assert (thread->abort_exc);
516 mono_gchandle_free (thread->abort_state_handle);
517 thread->abort_state_handle = 0;
519 thread->abort_exc = NULL;
520 thread->current_appcontext = NULL;
522 /* if the thread is not in the hash it has been removed already */
523 if (!handle_remove (thread))
524 return;
525 mono_release_type_locks (thread);
527 EnterCriticalSection (thread->synch_cs);
529 thread->state |= ThreadState_Stopped;
530 thread->state &= ~ThreadState_Background;
532 LeaveCriticalSection (thread->synch_cs);
534 mono_profiler_thread_end (thread->tid);
536 if (thread == mono_thread_current ())
537 mono_thread_pop_appdomain_ref ();
539 if (thread->serialized_culture_info)
540 g_free (thread->serialized_culture_info);
542 g_free (thread->name);
544 thread->cached_culture_info = NULL;
546 mono_gc_free_fixed (thread->static_data);
547 thread->static_data = NULL;
549 if (mono_thread_cleanup_fn)
550 mono_thread_cleanup_fn (thread);
552 small_id_free (thread->small_id);
553 thread->small_id = -2;
556 static guint32 WINAPI start_wrapper(void *data)
558 struct StartInfo *start_info=(struct StartInfo *)data;
559 guint32 (*start_func)(void *);
560 void *start_arg;
561 gsize tid;
562 MonoThread *thread=start_info->obj;
563 MonoObject *start_delegate = start_info->delegate;
565 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper", __func__, GetCurrentThreadId ()));
567 /* We can be sure start_info->obj->tid and
568 * start_info->obj->handle have been set, because the thread
569 * was created suspended, and these values were set before the
570 * thread resumed
573 tid=thread->tid;
575 SET_CURRENT_OBJECT (thread);
577 mono_monitor_init_tls ();
579 /* Every thread references the appdomain which created it */
580 mono_thread_push_appdomain_ref (start_info->domain);
582 if (!mono_domain_set (start_info->domain, FALSE)) {
583 /* No point in raising an appdomain_unloaded exception here */
584 /* FIXME: Cleanup here */
585 mono_thread_pop_appdomain_ref ();
586 return 0;
589 start_func = start_info->func;
590 start_arg = start_info->start_arg;
592 /* This MUST be called before any managed code can be
593 * executed, as it calls the callback function that (for the
594 * jit) sets the lmf marker.
596 mono_thread_new_init (tid, &tid, start_func);
597 thread->stack_ptr = &tid;
599 LIBGC_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT",%d) Setting thread stack to %p", __func__, GetCurrentThreadId (), getpid (), thread->stack_ptr));
601 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, GetCurrentThreadId (), thread));
603 mono_profiler_thread_start (tid);
605 /* On 2.0 profile (and higher), set explicitly since state might have been
606 Unknown */
607 if (mono_framework_version () != 1) {
608 if (thread->apartment_state == ThreadApartmentState_Unknown)
609 thread->apartment_state = ThreadApartmentState_MTA;
612 mono_thread_init_apartment_state ();
614 if(thread->start_notify!=NULL) {
615 /* Let the thread that called Start() know we're
616 * ready
618 ReleaseSemaphore (thread->start_notify, 1, NULL);
621 MONO_GC_UNREGISTER_ROOT (start_info->start_arg);
622 g_free (start_info);
624 thread_adjust_static_data (thread);
625 #ifdef DEBUG
626 g_message ("%s: start_wrapper for %"G_GSIZE_FORMAT, __func__,
627 thread->tid);
628 #endif
630 /* start_func is set only for unmanaged start functions */
631 if (start_func) {
632 start_func (start_arg);
633 } else {
634 void *args [1];
635 g_assert (start_delegate != NULL);
636 args [0] = start_arg;
637 /* we may want to handle the exception here. See comment below on unhandled exceptions */
638 mono_runtime_delegate_invoke (start_delegate, args, NULL);
641 /* If the thread calls ExitThread at all, this remaining code
642 * will not be executed, but the main thread will eventually
643 * call thread_cleanup() on this thread's behalf.
646 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper terminating", __func__, GetCurrentThreadId ()));
648 thread_cleanup (thread);
650 /* Do any cleanup needed for apartment state. This
651 * cannot be done in thread_cleanup since thread_cleanup could be
652 * called for a thread other than the current thread.
653 * mono_thread_cleanup_apartment_state cleans up apartment
654 * for the current thead */
655 mono_thread_cleanup_apartment_state ();
657 /* Remove the reference to the thread object in the TLS data,
658 * so the thread object can be finalized. This won't be
659 * reached if the thread threw an uncaught exception, so those
660 * thread handles will stay referenced :-( (This is due to
661 * missing support for scanning thread-specific data in the
662 * Boehm GC - the io-layer keeps a GC-visible hash of pointers
663 * to TLS data.)
665 SET_CURRENT_OBJECT (NULL);
667 return(0);
670 void mono_thread_new_init (gsize tid, gpointer stack_start, gpointer func)
672 if (mono_thread_start_cb) {
673 mono_thread_start_cb (tid, stack_start, func);
677 void mono_threads_set_default_stacksize (guint32 stacksize)
679 default_stacksize = stacksize;
682 guint32 mono_threads_get_default_stacksize (void)
684 return default_stacksize;
688 * mono_create_thread:
690 * This is a wrapper around CreateThread which handles differences in the type of
691 * the the 'tid' argument.
693 gpointer mono_create_thread (WapiSecurityAttributes *security,
694 guint32 stacksize, WapiThreadStart start,
695 gpointer param, guint32 create, gsize *tid)
697 gpointer res;
699 #ifdef PLATFORM_WIN32
700 DWORD real_tid;
702 res = CreateThread (security, stacksize, start, param, create, &real_tid);
703 if (tid)
704 *tid = real_tid;
705 #else
706 res = CreateThread (security, stacksize, start, param, create, tid);
707 #endif
709 return res;
712 MonoThread* mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gboolean threadpool_thread)
714 MonoThread *thread;
715 HANDLE thread_handle;
716 struct StartInfo *start_info;
717 gsize tid;
719 thread=(MonoThread *)mono_object_new (domain,
720 mono_defaults.thread_class);
722 start_info=g_new0 (struct StartInfo, 1);
723 start_info->func = func;
724 start_info->obj = thread;
725 start_info->domain = domain;
726 start_info->start_arg = arg;
729 * The argument may be an object reference, and there is no ref to keep it alive
730 * when the new thread is started but not yet registered with the collector.
732 MONO_GC_REGISTER_ROOT (start_info->start_arg);
734 mono_threads_lock ();
735 if (shutting_down) {
736 mono_threads_unlock ();
737 return NULL;
739 if (threads_starting_up == NULL) {
740 MONO_GC_REGISTER_ROOT (threads_starting_up);
741 threads_starting_up = mono_g_hash_table_new (NULL, NULL);
743 mono_g_hash_table_insert (threads_starting_up, thread, thread);
744 mono_threads_unlock ();
746 /* Create suspended, so we can do some housekeeping before the thread
747 * starts
749 thread_handle = mono_create_thread (NULL, default_stacksize_for_thread (thread), (LPTHREAD_START_ROUTINE)start_wrapper, start_info,
750 CREATE_SUSPENDED, &tid);
751 THREAD_DEBUG (g_message ("%s: Started thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread_handle));
752 if (thread_handle == NULL) {
753 /* The thread couldn't be created, so throw an exception */
754 MONO_GC_UNREGISTER_ROOT (start_info->start_arg);
755 mono_threads_lock ();
756 mono_g_hash_table_remove (threads_starting_up, thread);
757 mono_threads_unlock ();
758 g_free (start_info);
759 mono_raise_exception (mono_get_exception_execution_engine ("Couldn't create thread"));
760 return NULL;
763 thread->handle=thread_handle;
764 thread->tid=tid;
765 thread->apartment_state=ThreadApartmentState_Unknown;
766 small_id_alloc (thread);
768 thread->synch_cs = g_new0 (CRITICAL_SECTION, 1);
769 InitializeCriticalSection (thread->synch_cs);
771 thread->threadpool_thread = threadpool_thread;
772 if (threadpool_thread)
773 mono_thread_set_state (thread, ThreadState_Background);
775 if (handle_store (thread))
776 ResumeThread (thread_handle);
778 return thread;
781 void
782 mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
784 mono_thread_create_internal (domain, func, arg, FALSE);
788 * mono_thread_get_stack_bounds:
790 * Return the address and size of the current threads stack. Return NULL as the
791 * stack address if the stack address cannot be determined.
793 void
794 mono_thread_get_stack_bounds (guint8 **staddr, size_t *stsize)
796 #if defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
797 *staddr = (guint8*)pthread_get_stackaddr_np (pthread_self ());
798 *stsize = pthread_get_stacksize_np (pthread_self ());
799 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
800 return;
801 /* FIXME: simplify the mess below */
802 #elif !defined(PLATFORM_WIN32)
803 pthread_attr_t attr;
804 guint8 *current = (guint8*)&attr;
806 pthread_attr_init (&attr);
807 #ifdef HAVE_PTHREAD_GETATTR_NP
808 pthread_getattr_np (pthread_self(), &attr);
809 #else
810 #ifdef HAVE_PTHREAD_ATTR_GET_NP
811 pthread_attr_get_np (pthread_self(), &attr);
812 #elif defined(sun)
813 *staddr = NULL;
814 pthread_attr_getstacksize (&attr, &stsize);
815 #else
816 *staddr = NULL;
817 *stsize = 0;
818 return;
819 #endif
820 #endif
822 #ifndef sun
823 pthread_attr_getstack (&attr, (void**)staddr, stsize);
824 if (*staddr)
825 g_assert ((current > *staddr) && (current < *staddr + *stsize));
826 #endif
828 pthread_attr_destroy (&attr);
829 #endif
831 /* When running under emacs, sometimes staddr is not aligned to a page size */
832 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
835 MonoThread *
836 mono_thread_attach (MonoDomain *domain)
838 MonoThread *thread;
839 HANDLE thread_handle;
840 gsize tid;
842 if ((thread = mono_thread_current ())) {
843 if (domain != mono_domain_get ())
844 mono_domain_set (domain, TRUE);
845 /* Already attached */
846 return thread;
849 if (!mono_gc_register_thread (&domain)) {
850 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 ());
853 thread = (MonoThread *)mono_object_new (domain,
854 mono_defaults.thread_class);
856 thread_handle = GetCurrentThread ();
857 g_assert (thread_handle);
859 tid=GetCurrentThreadId ();
862 * The handle returned by GetCurrentThread () is a pseudo handle, so it can't be used to
863 * refer to the thread from other threads for things like aborting.
865 DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle,
866 THREAD_ALL_ACCESS, TRUE, 0);
868 thread->handle=thread_handle;
869 thread->tid=tid;
870 thread->apartment_state=ThreadApartmentState_Unknown;
871 small_id_alloc (thread);
872 thread->stack_ptr = &tid;
874 thread->synch_cs = g_new0 (CRITICAL_SECTION, 1);
875 InitializeCriticalSection (thread->synch_cs);
877 THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread_handle));
879 if (!handle_store (thread)) {
880 /* Mono is shutting down, so just wait for the end */
881 for (;;)
882 Sleep (10000);
885 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, GetCurrentThreadId (), thread));
887 SET_CURRENT_OBJECT (thread);
888 mono_domain_set (domain, TRUE);
890 mono_monitor_init_tls ();
892 thread_adjust_static_data (thread);
894 if (mono_thread_attach_cb) {
895 guint8 *staddr;
896 size_t stsize;
898 mono_thread_get_stack_bounds (&staddr, &stsize);
900 if (staddr == NULL)
901 mono_thread_attach_cb (tid, &tid);
902 else
903 mono_thread_attach_cb (tid, staddr + stsize);
906 return(thread);
909 void
910 mono_thread_detach (MonoThread *thread)
912 g_return_if_fail (thread != NULL);
914 THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
916 thread_cleanup (thread);
918 SET_CURRENT_OBJECT (NULL);
920 /* Don't need to CloseHandle this thread, even though we took a
921 * reference in mono_thread_attach (), because the GC will do it
922 * when the Thread object is finalised.
926 void
927 mono_thread_exit ()
929 MonoThread *thread = mono_thread_current ();
931 THREAD_DEBUG (g_message ("%s: mono_thread_exit for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
933 thread_cleanup (thread);
934 SET_CURRENT_OBJECT (NULL);
936 /* we could add a callback here for embedders to use. */
937 if (thread == mono_thread_get_main ())
938 exit (mono_environment_exitcode_get ());
939 ExitThread (-1);
942 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this,
943 MonoObject *start)
945 guint32 (*start_func)(void *);
946 struct StartInfo *start_info;
947 HANDLE thread;
948 gsize tid;
950 MONO_ARCH_SAVE_REGS;
952 THREAD_DEBUG (g_message("%s: Trying to start a new thread: this (%p) start (%p)", __func__, this, start));
954 ensure_synch_cs_set (this);
956 EnterCriticalSection (this->synch_cs);
958 if ((this->state & ThreadState_Unstarted) == 0) {
959 LeaveCriticalSection (this->synch_cs);
960 mono_raise_exception (mono_get_exception_thread_state ("Thread has already been started."));
961 return NULL;
964 this->small_id = -1;
966 if ((this->state & ThreadState_Aborted) != 0) {
967 LeaveCriticalSection (this->synch_cs);
968 return this;
970 start_func = NULL;
972 /* This is freed in start_wrapper */
973 start_info = g_new0 (struct StartInfo, 1);
974 start_info->func = start_func;
975 start_info->start_arg = this->start_obj; /* FIXME: GC object stored in unmanaged memory */
976 start_info->delegate = start;
977 start_info->obj = this;
978 start_info->domain = mono_domain_get ();
980 this->start_notify=CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
981 if(this->start_notify==NULL) {
982 LeaveCriticalSection (this->synch_cs);
983 g_warning ("%s: CreateSemaphore error 0x%x", __func__, GetLastError ());
984 return(NULL);
987 mono_threads_lock ();
988 if (threads_starting_up == NULL) {
989 MONO_GC_REGISTER_ROOT (threads_starting_up);
990 threads_starting_up = mono_g_hash_table_new (NULL, NULL);
992 mono_g_hash_table_insert (threads_starting_up, this, this);
993 mono_threads_unlock ();
995 thread=mono_create_thread(NULL, default_stacksize_for_thread (this), (LPTHREAD_START_ROUTINE)start_wrapper, start_info,
996 CREATE_SUSPENDED, &tid);
997 if(thread==NULL) {
998 LeaveCriticalSection (this->synch_cs);
999 mono_threads_lock ();
1000 mono_g_hash_table_remove (threads_starting_up, this);
1001 mono_threads_unlock ();
1002 g_warning("%s: CreateThread error 0x%x", __func__, GetLastError());
1003 return(NULL);
1006 this->handle=thread;
1007 this->tid=tid;
1008 small_id_alloc (this);
1010 /* Don't call handle_store() here, delay it to Start.
1011 * We can't join a thread (trying to will just block
1012 * forever) until it actually starts running, so don't
1013 * store the handle till then.
1016 mono_thread_start (this);
1018 this->state &= ~ThreadState_Unstarted;
1020 THREAD_DEBUG (g_message ("%s: Started thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread));
1022 LeaveCriticalSection (this->synch_cs);
1023 return(thread);
1027 void ves_icall_System_Threading_Thread_Thread_init (MonoThread *this)
1029 MONO_ARCH_SAVE_REGS;
1031 ensure_synch_cs_set (this);
1034 void ves_icall_System_Threading_Thread_Thread_free_internal (MonoThread *this,
1035 HANDLE thread)
1037 MONO_ARCH_SAVE_REGS;
1039 THREAD_DEBUG (g_message ("%s: Closing thread %p, handle %p", __func__, this, thread));
1041 CloseHandle (thread);
1043 DeleteCriticalSection (this->synch_cs);
1044 g_free (this->synch_cs);
1045 this->synch_cs = NULL;
1047 g_assert (!this->abort_exc && !this->abort_state_handle);
1050 static void mono_thread_start (MonoThread *thread)
1052 MONO_ARCH_SAVE_REGS;
1054 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Launching thread %p (%"G_GSIZE_FORMAT")", __func__, GetCurrentThreadId (), thread, (gsize)thread->tid));
1056 /* Only store the handle when the thread is about to be
1057 * launched, to avoid the main thread deadlocking while trying
1058 * to clean up a thread that will never be signalled.
1060 if (!handle_store (thread))
1061 return;
1063 ResumeThread (thread->handle);
1065 if(thread->start_notify!=NULL) {
1066 /* Wait for the thread to set up its TLS data etc, so
1067 * theres no potential race condition if someone tries
1068 * to look up the data believing the thread has
1069 * started
1072 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") waiting for thread %p (%"G_GSIZE_FORMAT") to start", __func__, GetCurrentThreadId (), thread, (gsize)thread->tid));
1074 WaitForSingleObjectEx (thread->start_notify, INFINITE, FALSE);
1075 CloseHandle (thread->start_notify);
1076 thread->start_notify = NULL;
1079 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Done launching thread %p (%"G_GSIZE_FORMAT")", __func__, GetCurrentThreadId (), thread, (gsize)thread->tid));
1082 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
1084 MonoThread *thread = mono_thread_current ();
1086 MONO_ARCH_SAVE_REGS;
1088 THREAD_DEBUG (g_message ("%s: Sleeping for %d ms", __func__, ms));
1090 mono_thread_current_check_pending_interrupt ();
1092 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1094 SleepEx(ms,TRUE);
1096 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1099 void ves_icall_System_Threading_Thread_SpinWait_nop (void)
1103 gint32
1104 ves_icall_System_Threading_Thread_GetDomainID (void)
1106 MONO_ARCH_SAVE_REGS;
1108 return mono_domain_get()->domain_id;
1111 MonoString*
1112 ves_icall_System_Threading_Thread_GetName_internal (MonoThread *this_obj)
1114 MonoString* str;
1116 ensure_synch_cs_set (this_obj);
1118 EnterCriticalSection (this_obj->synch_cs);
1120 if (!this_obj->name)
1121 str = NULL;
1122 else
1123 str = mono_string_new_utf16 (mono_domain_get (), this_obj->name, this_obj->name_len);
1125 LeaveCriticalSection (this_obj->synch_cs);
1127 return str;
1130 void
1131 ves_icall_System_Threading_Thread_SetName_internal (MonoThread *this_obj, MonoString *name)
1133 ensure_synch_cs_set (this_obj);
1135 EnterCriticalSection (this_obj->synch_cs);
1137 if (this_obj->name) {
1138 LeaveCriticalSection (this_obj->synch_cs);
1140 mono_raise_exception (mono_get_exception_invalid_operation ("Thread.Name can only be set once."));
1141 return;
1143 if (name) {
1144 this_obj->name = g_new (gunichar2, mono_string_length (name));
1145 memcpy (this_obj->name, mono_string_chars (name), mono_string_length (name) * 2);
1146 this_obj->name_len = mono_string_length (name);
1148 else
1149 this_obj->name = NULL;
1151 LeaveCriticalSection (this_obj->synch_cs);
1154 static MonoObject*
1155 lookup_cached_culture (MonoThread *this, MonoDomain *domain, int start_idx)
1157 MonoObject *res;
1158 int i;
1160 if (this->cached_culture_info) {
1161 domain = mono_domain_get ();
1162 for (i = start_idx; i < start_idx + NUM_CACHED_CULTURES; ++i) {
1163 res = mono_array_get (this->cached_culture_info, MonoObject*, i);
1164 if (res && res->vtable->domain == domain)
1165 return res;
1169 return NULL;
1172 MonoObject*
1173 ves_icall_System_Threading_Thread_GetCachedCurrentCulture (MonoThread *this)
1175 return lookup_cached_culture (this, mono_domain_get (), CULTURES_START_IDX);
1178 MonoArray*
1179 ves_icall_System_Threading_Thread_GetSerializedCurrentCulture (MonoThread *this)
1181 MonoArray *res;
1183 ensure_synch_cs_set (this);
1185 EnterCriticalSection (this->synch_cs);
1187 if (this->serialized_culture_info) {
1188 res = mono_array_new (mono_domain_get (), mono_defaults.byte_class, this->serialized_culture_info_len);
1189 memcpy (mono_array_addr (res, guint8, 0), this->serialized_culture_info, this->serialized_culture_info_len);
1190 } else {
1191 res = NULL;
1194 LeaveCriticalSection (this->synch_cs);
1196 return res;
1199 static void
1200 cache_culture (MonoThread *this, MonoObject *culture, int start_idx)
1202 int i;
1203 MonoDomain *domain = mono_domain_get ();
1204 MonoObject *obj;
1205 int free_slot = -1;
1206 int same_domain_slot = -1;
1208 ensure_synch_cs_set (this);
1210 EnterCriticalSection (this->synch_cs);
1212 if (!this->cached_culture_info)
1213 MONO_OBJECT_SETREF (this, cached_culture_info, mono_array_new_cached (mono_object_domain (this), mono_defaults.object_class, NUM_CACHED_CULTURES * 2));
1215 for (i = start_idx; i < start_idx + NUM_CACHED_CULTURES; ++i) {
1216 obj = mono_array_get (this->cached_culture_info, MonoObject*, i);
1217 /* Free entry */
1218 if (!obj) {
1219 free_slot = i;
1220 /* we continue, because there may be a slot used with the same domain */
1221 continue;
1223 /* Replace */
1224 if (obj->vtable->domain == domain) {
1225 same_domain_slot = i;
1226 break;
1229 if (same_domain_slot >= 0)
1230 mono_array_setref (this->cached_culture_info, same_domain_slot, culture);
1231 else if (free_slot >= 0)
1232 mono_array_setref (this->cached_culture_info, free_slot, culture);
1233 /* we may want to replace an existing entry here, even when no suitable slot is found */
1235 LeaveCriticalSection (this->synch_cs);
1238 void
1239 ves_icall_System_Threading_Thread_SetCachedCurrentCulture (MonoThread *this, MonoObject *culture)
1241 cache_culture (this, culture, CULTURES_START_IDX);
1244 void
1245 ves_icall_System_Threading_Thread_SetSerializedCurrentCulture (MonoThread *this, MonoArray *arr)
1247 ensure_synch_cs_set (this);
1249 EnterCriticalSection (this->synch_cs);
1251 if (this->serialized_culture_info)
1252 g_free (this->serialized_culture_info);
1253 this->serialized_culture_info = g_new0 (guint8, mono_array_length (arr));
1254 this->serialized_culture_info_len = mono_array_length (arr);
1255 memcpy (this->serialized_culture_info, mono_array_addr (arr, guint8, 0), mono_array_length (arr));
1257 LeaveCriticalSection (this->synch_cs);
1261 MonoObject*
1262 ves_icall_System_Threading_Thread_GetCachedCurrentUICulture (MonoThread *this)
1264 return lookup_cached_culture (this, mono_domain_get (), UICULTURES_START_IDX);
1267 MonoArray*
1268 ves_icall_System_Threading_Thread_GetSerializedCurrentUICulture (MonoThread *this)
1270 MonoArray *res;
1272 ensure_synch_cs_set (this);
1274 EnterCriticalSection (this->synch_cs);
1276 if (this->serialized_ui_culture_info) {
1277 res = mono_array_new (mono_domain_get (), mono_defaults.byte_class, this->serialized_ui_culture_info_len);
1278 memcpy (mono_array_addr (res, guint8, 0), this->serialized_ui_culture_info, this->serialized_ui_culture_info_len);
1279 } else {
1280 res = NULL;
1283 LeaveCriticalSection (this->synch_cs);
1285 return res;
1288 void
1289 ves_icall_System_Threading_Thread_SetCachedCurrentUICulture (MonoThread *this, MonoObject *culture)
1291 cache_culture (this, culture, UICULTURES_START_IDX);
1294 void
1295 ves_icall_System_Threading_Thread_SetSerializedCurrentUICulture (MonoThread *this, MonoArray *arr)
1297 ensure_synch_cs_set (this);
1299 EnterCriticalSection (this->synch_cs);
1301 if (this->serialized_ui_culture_info)
1302 g_free (this->serialized_ui_culture_info);
1303 this->serialized_ui_culture_info = g_new0 (guint8, mono_array_length (arr));
1304 this->serialized_ui_culture_info_len = mono_array_length (arr);
1305 memcpy (this->serialized_ui_culture_info, mono_array_addr (arr, guint8, 0), mono_array_length (arr));
1307 LeaveCriticalSection (this->synch_cs);
1310 /* the jit may read the compiled code of this function */
1311 MonoThread *
1312 mono_thread_current (void)
1314 MonoThread *res = GET_CURRENT_OBJECT ()
1315 THREAD_DEBUG (g_message ("%s: returning %p", __func__, res));
1316 return res;
1319 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this,
1320 int ms, HANDLE thread)
1322 MonoThread *cur_thread = mono_thread_current ();
1323 gboolean ret;
1325 MONO_ARCH_SAVE_REGS;
1327 mono_thread_current_check_pending_interrupt ();
1329 ensure_synch_cs_set (this);
1331 EnterCriticalSection (this->synch_cs);
1333 if ((this->state & ThreadState_Unstarted) != 0) {
1334 LeaveCriticalSection (this->synch_cs);
1336 mono_raise_exception (mono_get_exception_thread_state ("Thread has not been started."));
1337 return FALSE;
1340 LeaveCriticalSection (this->synch_cs);
1342 if(ms== -1) {
1343 ms=INFINITE;
1345 THREAD_DEBUG (g_message ("%s: joining thread handle %p, %d ms", __func__, thread, ms));
1347 mono_thread_set_state (cur_thread, ThreadState_WaitSleepJoin);
1349 ret=WaitForSingleObjectEx (thread, ms, TRUE);
1351 mono_thread_clr_state (cur_thread, ThreadState_WaitSleepJoin);
1353 if(ret==WAIT_OBJECT_0) {
1354 THREAD_DEBUG (g_message ("%s: join successful", __func__));
1356 return(TRUE);
1359 THREAD_DEBUG (g_message ("%s: join failed", __func__));
1361 return(FALSE);
1364 /* FIXME: exitContext isnt documented */
1365 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
1367 HANDLE *handles;
1368 guint32 numhandles;
1369 guint32 ret;
1370 guint32 i;
1371 MonoObject *waitHandle;
1372 MonoThread *thread = mono_thread_current ();
1374 MONO_ARCH_SAVE_REGS;
1376 /* Do this WaitSleepJoin check before creating objects */
1377 mono_thread_current_check_pending_interrupt ();
1379 numhandles = mono_array_length(mono_handles);
1380 handles = g_new0(HANDLE, numhandles);
1382 for(i = 0; i < numhandles; i++) {
1383 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
1384 handles [i] = mono_wait_handle_get_handle ((MonoWaitHandle *) waitHandle);
1387 if(ms== -1) {
1388 ms=INFINITE;
1391 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1393 ret=WaitForMultipleObjectsEx(numhandles, handles, TRUE, ms, TRUE);
1395 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1397 g_free(handles);
1399 if(ret==WAIT_FAILED) {
1400 THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Wait failed", __func__, GetCurrentThreadId ()));
1401 return(FALSE);
1402 } else if(ret==WAIT_TIMEOUT || ret == WAIT_IO_COMPLETION) {
1403 /* Do we want to try again if we get
1404 * WAIT_IO_COMPLETION? The documentation for
1405 * WaitHandle doesn't give any clues. (We'd have to
1406 * fiddle with the timeout if we retry.)
1408 THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Wait timed out", __func__, GetCurrentThreadId ()));
1409 return(FALSE);
1412 return(TRUE);
1415 /* FIXME: exitContext isnt documented */
1416 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
1418 HANDLE *handles;
1419 guint32 numhandles;
1420 guint32 ret;
1421 guint32 i;
1422 MonoObject *waitHandle;
1423 MonoThread *thread = mono_thread_current ();
1425 MONO_ARCH_SAVE_REGS;
1427 /* Do this WaitSleepJoin check before creating objects */
1428 mono_thread_current_check_pending_interrupt ();
1430 numhandles = mono_array_length(mono_handles);
1431 handles = g_new0(HANDLE, numhandles);
1433 for(i = 0; i < numhandles; i++) {
1434 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
1435 handles [i] = mono_wait_handle_get_handle ((MonoWaitHandle *) waitHandle);
1438 if(ms== -1) {
1439 ms=INFINITE;
1442 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1444 ret=WaitForMultipleObjectsEx(numhandles, handles, FALSE, ms, TRUE);
1446 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1448 g_free(handles);
1450 THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") returning %d", __func__, GetCurrentThreadId (), ret));
1453 * These need to be here. See MSDN dos on WaitForMultipleObjects.
1455 if (ret >= WAIT_OBJECT_0 && ret <= WAIT_OBJECT_0 + numhandles - 1) {
1456 return ret - WAIT_OBJECT_0;
1458 else if (ret >= WAIT_ABANDONED_0 && ret <= WAIT_ABANDONED_0 + numhandles - 1) {
1459 return ret - WAIT_ABANDONED_0;
1461 else {
1462 return ret;
1466 /* FIXME: exitContext isnt documented */
1467 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
1469 guint32 ret;
1470 MonoThread *thread = mono_thread_current ();
1472 MONO_ARCH_SAVE_REGS;
1474 THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") waiting for %p, %d ms", __func__, GetCurrentThreadId (), handle, ms));
1476 if(ms== -1) {
1477 ms=INFINITE;
1480 mono_thread_current_check_pending_interrupt ();
1482 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1484 ret=WaitForSingleObjectEx (handle, ms, TRUE);
1486 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1488 if(ret==WAIT_FAILED) {
1489 THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Wait failed", __func__, GetCurrentThreadId ()));
1490 return(FALSE);
1491 } else if(ret==WAIT_TIMEOUT || ret == WAIT_IO_COMPLETION) {
1492 /* Do we want to try again if we get
1493 * WAIT_IO_COMPLETION? The documentation for
1494 * WaitHandle doesn't give any clues. (We'd have to
1495 * fiddle with the timeout if we retry.)
1497 THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Wait timed out", __func__, GetCurrentThreadId ()));
1498 return(FALSE);
1501 return(TRUE);
1504 gboolean
1505 ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (HANDLE toSignal, HANDLE toWait, gint32 ms, gboolean exitContext)
1507 guint32 ret;
1508 MonoThread *thread = mono_thread_current ();
1510 MONO_ARCH_SAVE_REGS;
1512 if (ms == -1)
1513 ms = INFINITE;
1515 mono_thread_current_check_pending_interrupt ();
1517 mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1519 ret = SignalObjectAndWait (toSignal, toWait, ms, TRUE);
1521 mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1523 return (!(ret == WAIT_TIMEOUT || ret == WAIT_IO_COMPLETION || ret == WAIT_FAILED));
1526 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created)
1528 HANDLE mutex;
1530 MONO_ARCH_SAVE_REGS;
1532 *created = TRUE;
1534 if (name == NULL) {
1535 mutex = CreateMutex (NULL, owned, NULL);
1536 } else {
1537 mutex = CreateMutex (NULL, owned, mono_string_chars (name));
1539 if (GetLastError () == ERROR_ALREADY_EXISTS) {
1540 *created = FALSE;
1544 return(mutex);
1547 MonoBoolean ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) {
1548 MONO_ARCH_SAVE_REGS;
1550 return(ReleaseMutex (handle));
1553 HANDLE ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name,
1554 gint32 rights,
1555 gint32 *error)
1557 HANDLE ret;
1559 MONO_ARCH_SAVE_REGS;
1561 *error = ERROR_SUCCESS;
1563 ret = OpenMutex (rights, FALSE, mono_string_chars (name));
1564 if (ret == NULL) {
1565 *error = GetLastError ();
1568 return(ret);
1572 HANDLE ves_icall_System_Threading_Semaphore_CreateSemaphore_internal (gint32 initialCount, gint32 maximumCount, MonoString *name, MonoBoolean *created)
1574 HANDLE sem;
1576 MONO_ARCH_SAVE_REGS;
1578 *created = TRUE;
1580 if (name == NULL) {
1581 sem = CreateSemaphore (NULL, initialCount, maximumCount, NULL);
1582 } else {
1583 sem = CreateSemaphore (NULL, initialCount, maximumCount,
1584 mono_string_chars (name));
1586 if (GetLastError () == ERROR_ALREADY_EXISTS) {
1587 *created = FALSE;
1591 return(sem);
1594 gint32 ves_icall_System_Threading_Semaphore_ReleaseSemaphore_internal (HANDLE handle, gint32 releaseCount, MonoBoolean *fail)
1596 gint32 prevcount;
1598 MONO_ARCH_SAVE_REGS;
1600 *fail = !ReleaseSemaphore (handle, releaseCount, &prevcount);
1602 return (prevcount);
1605 HANDLE ves_icall_System_Threading_Semaphore_OpenSemaphore_internal (MonoString *name, gint32 rights, gint32 *error)
1607 HANDLE ret;
1609 MONO_ARCH_SAVE_REGS;
1611 *error = ERROR_SUCCESS;
1613 ret = OpenSemaphore (rights, FALSE, mono_string_chars (name));
1614 if (ret == NULL) {
1615 *error = GetLastError ();
1618 return(ret);
1621 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual, MonoBoolean initial, MonoString *name, MonoBoolean *created)
1623 HANDLE event;
1625 MONO_ARCH_SAVE_REGS;
1627 *created = TRUE;
1629 if (name == NULL) {
1630 event = CreateEvent (NULL, manual, initial, NULL);
1631 } else {
1632 event = CreateEvent (NULL, manual, initial,
1633 mono_string_chars (name));
1635 if (GetLastError () == ERROR_ALREADY_EXISTS) {
1636 *created = FALSE;
1640 return(event);
1643 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
1644 MONO_ARCH_SAVE_REGS;
1646 return (SetEvent(handle));
1649 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
1650 MONO_ARCH_SAVE_REGS;
1652 return (ResetEvent(handle));
1655 void
1656 ves_icall_System_Threading_Events_CloseEvent_internal (HANDLE handle) {
1657 MONO_ARCH_SAVE_REGS;
1659 CloseHandle (handle);
1662 HANDLE ves_icall_System_Threading_Events_OpenEvent_internal (MonoString *name,
1663 gint32 rights,
1664 gint32 *error)
1666 HANDLE ret;
1668 MONO_ARCH_SAVE_REGS;
1670 *error = ERROR_SUCCESS;
1672 ret = OpenEvent (rights, FALSE, mono_string_chars (name));
1673 if (ret == NULL) {
1674 *error = GetLastError ();
1677 return(ret);
1680 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
1682 MONO_ARCH_SAVE_REGS;
1684 return InterlockedIncrement (location);
1687 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
1689 gint64 ret;
1691 MONO_ARCH_SAVE_REGS;
1693 mono_interlocked_lock ();
1695 ret = ++ *location;
1697 mono_interlocked_unlock ();
1700 return ret;
1703 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
1705 MONO_ARCH_SAVE_REGS;
1707 return InterlockedDecrement(location);
1710 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
1712 gint64 ret;
1714 MONO_ARCH_SAVE_REGS;
1716 mono_interlocked_lock ();
1718 ret = -- *location;
1720 mono_interlocked_unlock ();
1722 return ret;
1725 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location, gint32 value)
1727 MONO_ARCH_SAVE_REGS;
1729 return InterlockedExchange(location, value);
1732 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location, MonoObject *value)
1734 MONO_ARCH_SAVE_REGS;
1736 return (MonoObject *) InterlockedExchangePointer((gpointer *) location, value);
1739 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location, gfloat value)
1741 IntFloatUnion val, ret;
1743 MONO_ARCH_SAVE_REGS;
1745 val.fval = value;
1746 ret.ival = InterlockedExchange((gint32 *) location, val.ival);
1748 return ret.fval;
1751 gint64
1752 ves_icall_System_Threading_Interlocked_Exchange_Long (gint64 *location, gint64 value)
1754 #if SIZEOF_VOID_P == 8
1755 return (gint64) InterlockedExchangePointer((gpointer *) location, (gpointer)value);
1756 #else
1757 gint64 res;
1760 * According to MSDN, this function is only atomic with regards to the
1761 * other Interlocked functions on 32 bit platforms.
1763 mono_interlocked_lock ();
1764 res = *location;
1765 *location = value;
1766 mono_interlocked_unlock ();
1768 return res;
1769 #endif
1772 gdouble
1773 ves_icall_System_Threading_Interlocked_Exchange_Double (gdouble *location, gdouble value)
1775 #if SIZEOF_VOID_P == 8
1776 LongDoubleUnion val, ret;
1778 val.fval = value;
1779 ret.ival = (gint64)InterlockedExchangePointer((gpointer *) location, (gpointer)val.ival);
1781 return ret.fval;
1782 #else
1783 gdouble res;
1786 * According to MSDN, this function is only atomic with regards to the
1787 * other Interlocked functions on 32 bit platforms.
1789 mono_interlocked_lock ();
1790 res = *location;
1791 *location = value;
1792 mono_interlocked_unlock ();
1794 return res;
1795 #endif
1798 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location, gint32 value, gint32 comparand)
1800 MONO_ARCH_SAVE_REGS;
1802 return InterlockedCompareExchange(location, value, comparand);
1805 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location, MonoObject *value, MonoObject *comparand)
1807 MONO_ARCH_SAVE_REGS;
1809 return (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location, value, comparand);
1812 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location, gfloat value, gfloat comparand)
1814 IntFloatUnion val, ret, cmp;
1816 MONO_ARCH_SAVE_REGS;
1818 val.fval = value;
1819 cmp.fval = comparand;
1820 ret.ival = InterlockedCompareExchange((gint32 *) location, val.ival, cmp.ival);
1822 return ret.fval;
1825 gdouble
1826 ves_icall_System_Threading_Interlocked_CompareExchange_Double (gdouble *location, gdouble value, gdouble comparand)
1828 #if SIZEOF_VOID_P == 8
1829 LongDoubleUnion val, comp, ret;
1831 val.fval = value;
1832 comp.fval = comparand;
1833 ret.ival = (gint64)InterlockedCompareExchangePointer((gpointer *) location, (gpointer)val.ival, (gpointer)comp.ival);
1835 return ret.fval;
1836 #else
1837 gdouble old;
1839 mono_interlocked_lock ();
1840 old = *location;
1841 if (old == comparand)
1842 *location = value;
1843 mono_interlocked_unlock ();
1845 return old;
1846 #endif
1849 gint64
1850 ves_icall_System_Threading_Interlocked_CompareExchange_Long (gint64 *location, gint64 value, gint64 comparand)
1852 #if SIZEOF_VOID_P == 8
1853 return (gint64)InterlockedCompareExchangePointer((gpointer *) location, (gpointer)value, (gpointer)comparand);
1854 #else
1855 gint64 old;
1857 mono_interlocked_lock ();
1858 old = *location;
1859 if (old == comparand)
1860 *location = value;
1861 mono_interlocked_unlock ();
1863 return old;
1864 #endif
1867 MonoObject*
1868 ves_icall_System_Threading_Interlocked_CompareExchange_T (MonoObject **location, MonoObject *value, MonoObject *comparand)
1870 MONO_ARCH_SAVE_REGS;
1872 return InterlockedCompareExchangePointer ((gpointer *)location, value, comparand);
1875 MonoObject*
1876 ves_icall_System_Threading_Interlocked_Exchange_T (MonoObject **location, MonoObject *value)
1878 MONO_ARCH_SAVE_REGS;
1880 return InterlockedExchangePointer ((gpointer *)location, value);
1883 gint32
1884 ves_icall_System_Threading_Interlocked_Add_Int (gint32 *location, gint32 value)
1886 #if SIZEOF_VOID_P == 8
1887 /* Should be implemented as a JIT intrinsic */
1888 mono_raise_exception (mono_get_exception_not_implemented (NULL));
1889 return 0;
1890 #else
1891 gint32 orig;
1893 mono_interlocked_lock ();
1894 orig = *location;
1895 *location = orig + value;
1896 mono_interlocked_unlock ();
1898 return orig + value;
1899 #endif
1902 gint64
1903 ves_icall_System_Threading_Interlocked_Add_Long (gint64 *location, gint64 value)
1905 #if SIZEOF_VOID_P == 8
1906 /* Should be implemented as a JIT intrinsic */
1907 mono_raise_exception (mono_get_exception_not_implemented (NULL));
1908 return 0;
1909 #else
1910 gint64 orig;
1912 mono_interlocked_lock ();
1913 orig = *location;
1914 *location = orig + value;
1915 mono_interlocked_unlock ();
1917 return orig + value;
1918 #endif
1921 gint64
1922 ves_icall_System_Threading_Interlocked_Read_Long (gint64 *location)
1924 #if SIZEOF_VOID_P == 8
1925 /* 64 bit reads are already atomic */
1926 return *location;
1927 #else
1928 gint64 res;
1930 mono_interlocked_lock ();
1931 res = *location;
1932 mono_interlocked_unlock ();
1934 return res;
1935 #endif
1938 void
1939 ves_icall_System_Threading_Thread_MemoryBarrier (void)
1941 mono_threads_lock ();
1942 mono_threads_unlock ();
1945 void
1946 ves_icall_System_Threading_Thread_ClrState (MonoThread* this, guint32 state)
1948 mono_thread_clr_state (this, state);
1950 if (state & ThreadState_Background) {
1951 /* If the thread changes the background mode, the main thread has to
1952 * be notified, since it has to rebuild the list of threads to
1953 * wait for.
1955 SetEvent (background_change_event);
1959 void
1960 ves_icall_System_Threading_Thread_SetState (MonoThread* this, guint32 state)
1962 mono_thread_set_state (this, state);
1964 if (state & ThreadState_Background) {
1965 /* If the thread changes the background mode, the main thread has to
1966 * be notified, since it has to rebuild the list of threads to
1967 * wait for.
1969 SetEvent (background_change_event);
1973 guint32
1974 ves_icall_System_Threading_Thread_GetState (MonoThread* this)
1976 guint32 state;
1978 ensure_synch_cs_set (this);
1980 EnterCriticalSection (this->synch_cs);
1982 state = this->state;
1984 LeaveCriticalSection (this->synch_cs);
1986 return state;
1989 void ves_icall_System_Threading_Thread_Interrupt_internal (MonoThread *this)
1991 gboolean throw = FALSE;
1993 ensure_synch_cs_set (this);
1995 if (this == mono_thread_current ())
1996 return;
1998 EnterCriticalSection (this->synch_cs);
2000 this->thread_interrupt_requested = TRUE;
2002 if (this->state & ThreadState_WaitSleepJoin) {
2003 throw = TRUE;
2006 LeaveCriticalSection (this->synch_cs);
2008 if (throw) {
2009 signal_thread_state_change (this);
2013 void mono_thread_current_check_pending_interrupt ()
2015 MonoThread *thread = mono_thread_current ();
2016 gboolean throw = FALSE;
2018 mono_debugger_check_interruption ();
2020 ensure_synch_cs_set (thread);
2022 EnterCriticalSection (thread->synch_cs);
2024 if (thread->thread_interrupt_requested) {
2025 throw = TRUE;
2026 thread->thread_interrupt_requested = FALSE;
2029 LeaveCriticalSection (thread->synch_cs);
2031 if (throw) {
2032 mono_raise_exception (mono_get_exception_thread_interrupted ());
2036 int
2037 mono_thread_get_abort_signal (void)
2039 #ifdef PLATFORM_WIN32
2040 return -1;
2041 #else
2042 #ifndef SIGRTMIN
2043 #ifdef SIGUSR1
2044 return SIGUSR1;
2045 #else
2046 return -1;
2047 #endif
2048 #else
2049 static int abort_signum = -1;
2050 int i;
2051 if (abort_signum != -1)
2052 return abort_signum;
2053 /* we try to avoid SIGRTMIN and any one that might have been set already, see bug #75387 */
2054 for (i = SIGRTMIN + 1; i < SIGRTMAX; ++i) {
2055 struct sigaction sinfo;
2056 sigaction (i, NULL, &sinfo);
2057 if (sinfo.sa_handler == SIG_DFL && (void*)sinfo.sa_sigaction == (void*)SIG_DFL) {
2058 abort_signum = i;
2059 return i;
2062 /* fallback to the old way */
2063 return SIGRTMIN;
2064 #endif
2065 #endif /* PLATFORM_WIN32 */
2068 #ifdef PLATFORM_WIN32
2069 static void CALLBACK interruption_request_apc (ULONG_PTR param)
2071 MonoException* exc = mono_thread_request_interruption (FALSE);
2072 if (exc) mono_raise_exception (exc);
2074 #endif /* PLATFORM_WIN32 */
2077 * signal_thread_state_change
2079 * Tells the thread that his state has changed and it has to enter the new
2080 * state as soon as possible.
2082 static void signal_thread_state_change (MonoThread *thread)
2084 if (thread == mono_thread_current ()) {
2085 /* Do it synchronously */
2086 MonoException *exc = mono_thread_request_interruption (FALSE);
2087 if (exc)
2088 mono_raise_exception (exc);
2091 #ifdef PLATFORM_WIN32
2092 QueueUserAPC ((PAPCFUNC)interruption_request_apc, thread->handle, NULL);
2093 #else
2094 /* fixme: store the state somewhere */
2095 #ifdef PTHREAD_POINTER_ID
2096 pthread_kill ((gpointer)(gsize)(thread->tid), mono_thread_get_abort_signal ());
2097 #else
2098 pthread_kill (thread->tid, mono_thread_get_abort_signal ());
2099 #endif
2102 * This will cause waits to be broken.
2103 * It will also prevent the thread from entering a wait, so if the thread returns
2104 * from the wait before it receives the abort signal, it will just spin in the wait
2105 * functions in the io-layer until the signal handler calls QueueUserAPC which will
2106 * make it return.
2108 wapi_interrupt_thread (thread->handle);
2109 #endif /* PLATFORM_WIN32 */
2112 void
2113 ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state)
2115 MONO_ARCH_SAVE_REGS;
2117 ensure_synch_cs_set (thread);
2119 EnterCriticalSection (thread->synch_cs);
2121 if ((thread->state & ThreadState_AbortRequested) != 0 ||
2122 (thread->state & ThreadState_StopRequested) != 0 ||
2123 (thread->state & ThreadState_Stopped) != 0)
2125 LeaveCriticalSection (thread->synch_cs);
2126 return;
2129 if ((thread->state & ThreadState_Unstarted) != 0) {
2130 thread->state |= ThreadState_Aborted;
2131 LeaveCriticalSection (thread->synch_cs);
2132 return;
2135 thread->state |= ThreadState_AbortRequested;
2136 if (thread->abort_state_handle)
2137 mono_gchandle_free (thread->abort_state_handle);
2138 if (state) {
2139 thread->abort_state_handle = mono_gchandle_new (state, FALSE);
2140 g_assert (thread->abort_state_handle);
2141 } else {
2142 thread->abort_state_handle = 0;
2144 thread->abort_exc = NULL;
2146 LeaveCriticalSection (thread->synch_cs);
2148 THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Abort requested for %p (%"G_GSIZE_FORMAT")", __func__, GetCurrentThreadId (), thread, (gsize)thread->tid));
2150 /* During shutdown, we can't wait for other threads */
2151 if (!shutting_down)
2152 /* Make sure the thread is awake */
2153 mono_thread_resume (thread);
2155 signal_thread_state_change (thread);
2158 void
2159 ves_icall_System_Threading_Thread_ResetAbort (void)
2161 MonoThread *thread = mono_thread_current ();
2163 MONO_ARCH_SAVE_REGS;
2165 ensure_synch_cs_set (thread);
2167 EnterCriticalSection (thread->synch_cs);
2169 thread->state &= ~ThreadState_AbortRequested;
2171 if (!thread->abort_exc) {
2172 const char *msg = "Unable to reset abort because no abort was requested";
2173 LeaveCriticalSection (thread->synch_cs);
2174 mono_raise_exception (mono_get_exception_thread_state (msg));
2175 } else {
2176 thread->abort_exc = NULL;
2177 if (thread->abort_state_handle) {
2178 mono_gchandle_free (thread->abort_state_handle);
2179 /* This is actually not necessary - the handle
2180 only counts if the exception is set */
2181 thread->abort_state_handle = 0;
2185 LeaveCriticalSection (thread->synch_cs);
2188 static MonoObject*
2189 serialize_object (MonoObject *obj, gboolean *failure, MonoObject **exc)
2191 static MonoMethod *serialize_method;
2193 void *params [1];
2194 MonoObject *array;
2196 if (!serialize_method) {
2197 MonoClass *klass = mono_class_from_name (mono_defaults.corlib, "System.Runtime.Remoting", "RemotingServices");
2198 serialize_method = mono_class_get_method_from_name (klass, "SerializeCallData", -1);
2201 if (!serialize_method) {
2202 *failure = TRUE;
2203 return NULL;
2206 g_assert (!obj->vtable->klass->marshalbyref);
2208 params [0] = obj;
2209 *exc = NULL;
2210 array = mono_runtime_invoke (serialize_method, NULL, params, exc);
2211 if (*exc)
2212 *failure = TRUE;
2214 return array;
2217 static MonoObject*
2218 deserialize_object (MonoObject *obj, gboolean *failure, MonoObject **exc)
2220 static MonoMethod *deserialize_method;
2222 void *params [1];
2223 MonoObject *result;
2225 if (!deserialize_method) {
2226 MonoClass *klass = mono_class_from_name (mono_defaults.corlib, "System.Runtime.Remoting", "RemotingServices");
2227 deserialize_method = mono_class_get_method_from_name (klass, "DeserializeCallData", -1);
2229 if (!deserialize_method) {
2230 *failure = TRUE;
2231 return NULL;
2234 params [0] = obj;
2235 *exc = NULL;
2236 result = mono_runtime_invoke (deserialize_method, NULL, params, exc);
2237 if (*exc)
2238 *failure = TRUE;
2240 return result;
2243 static MonoObject*
2244 make_transparent_proxy (MonoObject *obj, gboolean *failure, MonoObject **exc)
2246 static MonoMethod *get_proxy_method;
2248 MonoDomain *domain = mono_domain_get ();
2249 MonoRealProxy *real_proxy;
2250 MonoReflectionType *reflection_type;
2251 MonoTransparentProxy *transparent_proxy;
2253 if (!get_proxy_method)
2254 get_proxy_method = mono_class_get_method_from_name (mono_defaults.real_proxy_class, "GetTransparentProxy", 0);
2256 g_assert (obj->vtable->klass->marshalbyref);
2258 real_proxy = (MonoRealProxy*) mono_object_new (domain, mono_defaults.real_proxy_class);
2259 reflection_type = mono_type_get_object (domain, &obj->vtable->klass->byval_arg);
2261 real_proxy->class_to_proxy = reflection_type;
2262 real_proxy->unwrapped_server = obj;
2264 *exc = NULL;
2265 transparent_proxy = (MonoTransparentProxy*) mono_runtime_invoke (get_proxy_method, real_proxy, NULL, exc);
2266 if (*exc)
2267 *failure = TRUE;
2269 return (MonoObject*) transparent_proxy;
2272 MonoObject*
2273 ves_icall_System_Threading_Thread_GetAbortExceptionState (MonoThread *thread)
2275 MonoObject *state, *serialized, *deserialized, *exc;
2276 MonoDomain *domain;
2277 gboolean failure = FALSE;
2279 if (!thread->abort_state_handle)
2280 return NULL;
2282 state = mono_gchandle_get_target (thread->abort_state_handle);
2283 g_assert (state);
2285 domain = mono_domain_get ();
2286 if (state->vtable->domain == domain)
2287 return state;
2289 if (state->vtable->klass->marshalbyref) {
2290 deserialized = make_transparent_proxy (state, &failure, &exc);
2291 } else {
2292 mono_domain_set_internal_with_options (state->vtable->domain, FALSE);
2293 serialized = serialize_object (state, &failure, &exc);
2294 mono_domain_set_internal_with_options (domain, FALSE);
2295 if (!failure)
2296 deserialized = deserialize_object (serialized, &failure, &exc);
2299 if (failure) {
2300 MonoException *invalid_op_exc = mono_get_exception_invalid_operation ("Thread.ExceptionState cannot access an ExceptionState from a different AppDomain");
2301 if (exc)
2302 MONO_OBJECT_SETREF (invalid_op_exc, inner_ex, exc);
2303 mono_raise_exception (invalid_op_exc);
2306 return deserialized;
2309 static gboolean
2310 mono_thread_suspend (MonoThread *thread)
2312 MONO_ARCH_SAVE_REGS;
2314 ensure_synch_cs_set (thread);
2316 EnterCriticalSection (thread->synch_cs);
2318 if ((thread->state & ThreadState_Unstarted) != 0 ||
2319 (thread->state & ThreadState_Aborted) != 0 ||
2320 (thread->state & ThreadState_Stopped) != 0)
2322 LeaveCriticalSection (thread->synch_cs);
2323 return FALSE;
2326 if ((thread->state & ThreadState_Suspended) != 0 ||
2327 (thread->state & ThreadState_SuspendRequested) != 0 ||
2328 (thread->state & ThreadState_StopRequested) != 0)
2330 LeaveCriticalSection (thread->synch_cs);
2331 return TRUE;
2334 thread->state |= ThreadState_SuspendRequested;
2336 LeaveCriticalSection (thread->synch_cs);
2338 signal_thread_state_change (thread);
2339 return TRUE;
2342 void
2343 ves_icall_System_Threading_Thread_Suspend (MonoThread *thread)
2345 if (!mono_thread_suspend (thread))
2346 mono_raise_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
2349 static gboolean
2350 mono_thread_resume (MonoThread *thread)
2352 MONO_ARCH_SAVE_REGS;
2354 ensure_synch_cs_set (thread);
2356 EnterCriticalSection (thread->synch_cs);
2358 if ((thread->state & ThreadState_SuspendRequested) != 0) {
2359 thread->state &= ~ThreadState_SuspendRequested;
2360 LeaveCriticalSection (thread->synch_cs);
2361 return TRUE;
2364 if ((thread->state & ThreadState_Suspended) == 0 ||
2365 (thread->state & ThreadState_Unstarted) != 0 ||
2366 (thread->state & ThreadState_Aborted) != 0 ||
2367 (thread->state & ThreadState_Stopped) != 0)
2369 LeaveCriticalSection (thread->synch_cs);
2370 return FALSE;
2373 thread->resume_event = CreateEvent (NULL, TRUE, FALSE, NULL);
2374 if (thread->resume_event == NULL) {
2375 LeaveCriticalSection (thread->synch_cs);
2376 return(FALSE);
2379 /* Awake the thread */
2380 SetEvent (thread->suspend_event);
2382 LeaveCriticalSection (thread->synch_cs);
2384 /* Wait for the thread to awake */
2385 WaitForSingleObject (thread->resume_event, INFINITE);
2386 CloseHandle (thread->resume_event);
2387 thread->resume_event = NULL;
2389 return TRUE;
2392 void
2393 ves_icall_System_Threading_Thread_Resume (MonoThread *thread)
2395 if (!mono_thread_resume (thread))
2396 mono_raise_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
2399 static gboolean
2400 find_wrapper (MonoMethod *m, gint no, gint ilo, gboolean managed, gpointer data)
2402 if (managed)
2403 return TRUE;
2405 if (m->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE ||
2406 m->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE ||
2407 m->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH)
2409 *((gboolean*)data) = TRUE;
2410 return TRUE;
2412 return FALSE;
2415 static gboolean
2416 is_running_protected_wrapper (void)
2418 gboolean found = FALSE;
2419 mono_stack_walk (find_wrapper, &found);
2420 return found;
2423 void mono_thread_stop (MonoThread *thread)
2425 ensure_synch_cs_set (thread);
2427 EnterCriticalSection (thread->synch_cs);
2429 if ((thread->state & ThreadState_StopRequested) != 0 ||
2430 (thread->state & ThreadState_Stopped) != 0)
2432 LeaveCriticalSection (thread->synch_cs);
2433 return;
2436 /* Make sure the thread is awake */
2437 mono_thread_resume (thread);
2439 thread->state |= ThreadState_StopRequested;
2440 thread->state &= ~ThreadState_AbortRequested;
2442 LeaveCriticalSection (thread->synch_cs);
2444 signal_thread_state_change (thread);
2447 gint8
2448 ves_icall_System_Threading_Thread_VolatileRead1 (void *ptr)
2450 return *((volatile gint8 *) (ptr));
2453 gint16
2454 ves_icall_System_Threading_Thread_VolatileRead2 (void *ptr)
2456 return *((volatile gint16 *) (ptr));
2459 gint32
2460 ves_icall_System_Threading_Thread_VolatileRead4 (void *ptr)
2462 return *((volatile gint32 *) (ptr));
2465 gint64
2466 ves_icall_System_Threading_Thread_VolatileRead8 (void *ptr)
2468 return *((volatile gint64 *) (ptr));
2471 void *
2472 ves_icall_System_Threading_Thread_VolatileReadIntPtr (void *ptr)
2474 return (void *) *((volatile void **) ptr);
2477 void
2478 ves_icall_System_Threading_Thread_VolatileWrite1 (void *ptr, gint8 value)
2480 *((volatile gint8 *) ptr) = value;
2483 void
2484 ves_icall_System_Threading_Thread_VolatileWrite2 (void *ptr, gint16 value)
2486 *((volatile gint16 *) ptr) = value;
2489 void
2490 ves_icall_System_Threading_Thread_VolatileWrite4 (void *ptr, gint32 value)
2492 *((volatile gint32 *) ptr) = value;
2495 void
2496 ves_icall_System_Threading_Thread_VolatileWrite8 (void *ptr, gint64 value)
2498 *((volatile gint64 *) ptr) = value;
2501 void
2502 ves_icall_System_Threading_Thread_VolatileWriteIntPtr (void *ptr, void *value)
2504 *((volatile void **) ptr) = value;
2507 void mono_thread_init (MonoThreadStartCB start_cb,
2508 MonoThreadAttachCB attach_cb)
2510 MONO_GC_REGISTER_ROOT (small_id_table);
2511 InitializeCriticalSection(&threads_mutex);
2512 InitializeCriticalSection(&interlocked_mutex);
2513 InitializeCriticalSection(&contexts_mutex);
2514 InitializeCriticalSection(&delayed_free_table_mutex);
2515 InitializeCriticalSection(&small_id_mutex);
2517 background_change_event = CreateEvent (NULL, TRUE, FALSE, NULL);
2518 g_assert(background_change_event != NULL);
2520 mono_init_static_data_info (&thread_static_info);
2521 mono_init_static_data_info (&context_static_info);
2523 current_object_key=TlsAlloc();
2524 THREAD_DEBUG (g_message ("%s: Allocated current_object_key %d", __func__, current_object_key));
2526 mono_thread_start_cb = start_cb;
2527 mono_thread_attach_cb = attach_cb;
2529 delayed_free_table = g_array_new (FALSE, FALSE, sizeof (DelayedFreeItem));
2531 /* Get a pseudo handle to the current process. This is just a
2532 * kludge so that wapi can build a process handle if needed.
2533 * As a pseudo handle is returned, we don't need to clean
2534 * anything up.
2536 GetCurrentProcess ();
2539 void mono_thread_cleanup (void)
2541 mono_thread_hazardous_try_free_all ();
2543 #if !defined(PLATFORM_WIN32) && !defined(RUN_IN_SUBTHREAD)
2544 /* The main thread must abandon any held mutexes (particularly
2545 * important for named mutexes as they are shared across
2546 * processes, see bug 74680.) This will happen when the
2547 * thread exits, but if it's not running in a subthread it
2548 * won't exit in time.
2550 /* Using non-w32 API is a nasty kludge, but I couldn't find
2551 * anything in the documentation that would let me do this
2552 * here yet still be safe to call on windows.
2554 _wapi_thread_signal_self (mono_environment_exitcode_get ());
2555 #endif
2557 #if 0
2558 /* This stuff needs more testing, it seems one of these
2559 * critical sections can be locked when mono_thread_cleanup is
2560 * called.
2562 DeleteCriticalSection (&threads_mutex);
2563 DeleteCriticalSection (&interlocked_mutex);
2564 DeleteCriticalSection (&contexts_mutex);
2565 DeleteCriticalSection (&delayed_free_table_mutex);
2566 DeleteCriticalSection (&small_id_mutex);
2567 CloseHandle (background_change_event);
2568 #endif
2570 g_array_free (delayed_free_table, TRUE);
2571 delayed_free_table = NULL;
2573 TlsFree (current_object_key);
2576 void
2577 mono_threads_install_cleanup (MonoThreadCleanupFunc func)
2579 mono_thread_cleanup_fn = func;
2582 void
2583 mono_thread_set_manage_callback (MonoThread *thread, MonoThreadManageCallback func)
2585 thread->manage_callback = func;
2588 void mono_threads_install_notify_pending_exc (MonoThreadNotifyPendingExcFunc func)
2590 mono_thread_notify_pending_exc_fn = func;
2593 G_GNUC_UNUSED
2594 static void print_tids (gpointer key, gpointer value, gpointer user)
2596 /* GPOINTER_TO_UINT breaks horribly if sizeof(void *) >
2597 * sizeof(uint) and a cast to uint would overflow
2599 /* Older versions of glib don't have G_GSIZE_FORMAT, so just
2600 * print this as a pointer.
2602 g_message ("Waiting for: %p", key);
2605 struct wait_data
2607 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
2608 MonoThread *threads[MAXIMUM_WAIT_OBJECTS];
2609 guint32 num;
2612 static void wait_for_tids (struct wait_data *wait, guint32 timeout)
2614 guint32 i, ret;
2616 THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num));
2618 ret=WaitForMultipleObjectsEx(wait->num, wait->handles, TRUE, timeout, FALSE);
2620 if(ret==WAIT_FAILED) {
2621 /* See the comment in build_wait_tids() */
2622 THREAD_DEBUG (g_message ("%s: Wait failed", __func__));
2623 return;
2626 for(i=0; i<wait->num; i++)
2627 CloseHandle (wait->handles[i]);
2629 if (ret == WAIT_TIMEOUT)
2630 return;
2632 for(i=0; i<wait->num; i++) {
2633 gsize tid = wait->threads[i]->tid;
2635 mono_threads_lock ();
2636 if(mono_g_hash_table_lookup (threads, (gpointer)tid)!=NULL) {
2637 /* This thread must have been killed, because
2638 * it hasn't cleaned itself up. (It's just
2639 * possible that the thread exited before the
2640 * parent thread had a chance to store the
2641 * handle, and now there is another pointer to
2642 * the already-exited thread stored. In this
2643 * case, we'll just get two
2644 * mono_profiler_thread_end() calls for the
2645 * same thread.)
2648 mono_threads_unlock ();
2649 THREAD_DEBUG (g_message ("%s: cleaning up after thread %p (%"G_GSIZE_FORMAT")", __func__, wait->threads[i], tid));
2650 thread_cleanup (wait->threads[i]);
2651 } else {
2652 mono_threads_unlock ();
2657 static void wait_for_tids_or_state_change (struct wait_data *wait, guint32 timeout)
2659 guint32 i, ret, count;
2661 THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num));
2663 /* Add the thread state change event, so it wakes up if a thread changes
2664 * to background mode.
2666 count = wait->num;
2667 if (count < MAXIMUM_WAIT_OBJECTS) {
2668 wait->handles [count] = background_change_event;
2669 count++;
2672 ret=WaitForMultipleObjectsEx (count, wait->handles, FALSE, timeout, FALSE);
2674 if(ret==WAIT_FAILED) {
2675 /* See the comment in build_wait_tids() */
2676 THREAD_DEBUG (g_message ("%s: Wait failed", __func__));
2677 return;
2680 for(i=0; i<wait->num; i++)
2681 CloseHandle (wait->handles[i]);
2683 if (ret == WAIT_TIMEOUT)
2684 return;
2686 if (ret < wait->num) {
2687 gsize tid = wait->threads[ret]->tid;
2688 mono_threads_lock ();
2689 if (mono_g_hash_table_lookup (threads, (gpointer)tid)!=NULL) {
2690 /* See comment in wait_for_tids about thread cleanup */
2691 mono_threads_unlock ();
2692 THREAD_DEBUG (g_message ("%s: cleaning up after thread %"G_GSIZE_FORMAT, __func__, tid));
2693 thread_cleanup (wait->threads [ret]);
2694 } else
2695 mono_threads_unlock ();
2699 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
2701 struct wait_data *wait=(struct wait_data *)user;
2703 if(wait->num<MAXIMUM_WAIT_OBJECTS) {
2704 HANDLE handle;
2705 MonoThread *thread=(MonoThread *)value;
2707 /* Ignore background threads, we abort them later */
2708 /* Do not lock here since it is not needed and the caller holds threads_lock */
2709 if (thread->state & ThreadState_Background) {
2710 THREAD_DEBUG (g_message ("%s: ignoring background thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
2711 return; /* just leave, ignore */
2714 if (mono_gc_is_finalizer_thread (thread)) {
2715 THREAD_DEBUG (g_message ("%s: ignoring finalizer thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
2716 return;
2719 if (thread == mono_thread_current ()) {
2720 THREAD_DEBUG (g_message ("%s: ignoring current thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
2721 return;
2724 if (thread == mono_thread_get_main ()) {
2725 THREAD_DEBUG (g_message ("%s: ignoring main thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
2726 return;
2729 handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
2730 if (handle == NULL) {
2731 THREAD_DEBUG (g_message ("%s: ignoring unopenable thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
2732 return;
2735 THREAD_DEBUG (g_message ("%s: Invoking mono_thread_manage callback on thread %p", __func__, thread));
2736 if ((thread->manage_callback == NULL) || (thread->manage_callback (thread) == TRUE)) {
2737 wait->handles[wait->num]=handle;
2738 wait->threads[wait->num]=thread;
2739 wait->num++;
2741 THREAD_DEBUG (g_message ("%s: adding thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
2742 } else {
2743 THREAD_DEBUG (g_message ("%s: ignoring (because of callback) thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
2747 } else {
2748 /* Just ignore the rest, we can't do anything with
2749 * them yet
2754 static gboolean
2755 remove_and_abort_threads (gpointer key, gpointer value, gpointer user)
2757 struct wait_data *wait=(struct wait_data *)user;
2758 gsize self = GetCurrentThreadId ();
2759 MonoThread *thread = (MonoThread *) value;
2760 HANDLE handle;
2762 if (wait->num >= MAXIMUM_WAIT_OBJECTS)
2763 return FALSE;
2765 /* The finalizer thread is not a background thread */
2766 if (thread->tid != self && (thread->state & ThreadState_Background) != 0) {
2768 handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
2769 if (handle == NULL)
2770 return FALSE;
2772 /* printf ("A: %d\n", wait->num); */
2773 wait->handles[wait->num]=thread->handle;
2774 wait->threads[wait->num]=thread;
2775 wait->num++;
2777 THREAD_DEBUG (g_print ("%s: Aborting id: %"G_GSIZE_FORMAT"\n", __func__, (gsize)thread->tid));
2778 mono_thread_stop (thread);
2779 return TRUE;
2782 return (thread->tid != self && !mono_gc_is_finalizer_thread (thread));
2785 static MonoException* mono_thread_execute_interruption (MonoThread *thread);
2787 /**
2788 * mono_threads_set_shutting_down:
2790 * Is called by a thread that wants to shut down Mono. If the runtime is already
2791 * shutting down, the calling thread is suspended/stopped, and this function never
2792 * returns.
2794 void
2795 mono_threads_set_shutting_down (void)
2797 MonoThread *current_thread = mono_thread_current ();
2799 mono_threads_lock ();
2801 if (shutting_down) {
2802 mono_threads_unlock ();
2804 /* Make sure we're properly suspended/stopped */
2806 EnterCriticalSection (current_thread->synch_cs);
2808 if ((current_thread->state & ThreadState_SuspendRequested) ||
2809 (current_thread->state & ThreadState_AbortRequested) ||
2810 (current_thread->state & ThreadState_StopRequested)) {
2811 LeaveCriticalSection (current_thread->synch_cs);
2812 mono_thread_execute_interruption (current_thread);
2813 } else {
2814 current_thread->state |= ThreadState_Stopped;
2815 LeaveCriticalSection (current_thread->synch_cs);
2818 /* Wake up other threads potentially waiting for us */
2819 ExitThread (0);
2820 } else {
2821 shutting_down = TRUE;
2823 /* Not really a background state change, but this will
2824 * interrupt the main thread if it is waiting for all
2825 * the other threads.
2827 SetEvent (background_change_event);
2829 mono_threads_unlock ();
2833 /**
2834 * mono_threads_is_shutting_down:
2836 * Returns whether a thread has commenced shutdown of Mono. Note that
2837 * if the function returns FALSE the caller must not assume that
2838 * shutdown is not in progress, because the situation might have
2839 * changed since the function returned. For that reason this function
2840 * is of very limited utility.
2842 gboolean
2843 mono_threads_is_shutting_down (void)
2845 return shutting_down;
2848 void mono_thread_manage (void)
2850 struct wait_data *wait=g_new0 (struct wait_data, 1);
2852 /* join each thread that's still running */
2853 THREAD_DEBUG (g_message ("%s: Joining each running thread...", __func__));
2855 mono_threads_lock ();
2856 if(threads==NULL) {
2857 THREAD_DEBUG (g_message("%s: No threads", __func__));
2858 mono_threads_unlock ();
2859 return;
2861 mono_threads_unlock ();
2863 do {
2864 mono_threads_lock ();
2865 if (shutting_down) {
2866 /* somebody else is shutting down */
2867 mono_threads_unlock ();
2868 break;
2870 THREAD_DEBUG (g_message ("%s: There are %d threads to join", __func__, mono_g_hash_table_size (threads));
2871 mono_g_hash_table_foreach (threads, print_tids, NULL));
2873 ResetEvent (background_change_event);
2874 wait->num=0;
2875 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
2876 mono_threads_unlock ();
2877 if(wait->num>0) {
2878 /* Something to wait for */
2879 wait_for_tids_or_state_change (wait, INFINITE);
2881 THREAD_DEBUG (g_message ("%s: I have %d threads after waiting.", __func__, wait->num));
2882 } while(wait->num>0);
2884 mono_threads_set_shutting_down ();
2886 /* No new threads will be created after this point */
2888 mono_runtime_set_shutting_down ();
2890 THREAD_DEBUG (g_message ("%s: threadpool cleanup", __func__));
2891 mono_thread_pool_cleanup ();
2894 * Remove everything but the finalizer thread and self.
2895 * Also abort all the background threads
2896 * */
2897 do {
2898 mono_threads_lock ();
2900 wait->num = 0;
2901 mono_g_hash_table_foreach_remove (threads, remove_and_abort_threads, wait);
2903 mono_threads_unlock ();
2905 THREAD_DEBUG (g_message ("%s: wait->num is now %d", __func__, wait->num));
2906 if(wait->num>0) {
2907 /* Something to wait for */
2908 wait_for_tids (wait, INFINITE);
2910 } while (wait->num > 0);
2913 * give the subthreads a chance to really quit (this is mainly needed
2914 * to get correct user and system times from getrusage/wait/time(1)).
2915 * This could be removed if we avoid pthread_detach() and use pthread_join().
2917 #ifndef PLATFORM_WIN32
2918 sched_yield ();
2919 #endif
2921 g_free (wait);
2924 static void terminate_thread (gpointer key, gpointer value, gpointer user)
2926 MonoThread *thread=(MonoThread *)value;
2928 if(thread->tid != (gsize)user) {
2929 /*TerminateThread (thread->handle, -1);*/
2933 void mono_thread_abort_all_other_threads (void)
2935 gsize self = GetCurrentThreadId ();
2937 mono_threads_lock ();
2938 THREAD_DEBUG (g_message ("%s: There are %d threads to abort", __func__,
2939 mono_g_hash_table_size (threads));
2940 mono_g_hash_table_foreach (threads, print_tids, NULL));
2942 mono_g_hash_table_foreach (threads, terminate_thread, (gpointer)self);
2944 mono_threads_unlock ();
2947 static void
2948 collect_threads_for_suspend (gpointer key, gpointer value, gpointer user_data)
2950 MonoThread *thread = (MonoThread*)value;
2951 struct wait_data *wait = (struct wait_data*)user_data;
2952 HANDLE handle;
2955 * We try to exclude threads early, to avoid running into the MAXIMUM_WAIT_OBJECTS
2956 * limitation.
2957 * This needs no locking.
2959 if ((thread->state & ThreadState_Suspended) != 0 ||
2960 (thread->state & ThreadState_Stopped) != 0)
2961 return;
2963 if (wait->num<MAXIMUM_WAIT_OBJECTS) {
2964 handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
2965 if (handle == NULL)
2966 return;
2968 wait->handles [wait->num] = handle;
2969 wait->threads [wait->num] = thread;
2970 wait->num++;
2975 * mono_thread_suspend_all_other_threads:
2977 * Suspend all managed threads except the finalizer thread and this thread. It is
2978 * not possible to resume them later.
2980 void mono_thread_suspend_all_other_threads (void)
2982 struct wait_data *wait = g_new0 (struct wait_data, 1);
2983 int i;
2984 gsize self = GetCurrentThreadId ();
2985 gpointer *events;
2986 guint32 eventidx = 0;
2987 gboolean starting, finished;
2990 * The other threads could be in an arbitrary state at this point, i.e.
2991 * they could be starting up, shutting down etc. This means that there could be
2992 * threads which are not even in the threads hash table yet.
2996 * First we set a barrier which will be checked by all threads before they
2997 * are added to the threads hash table, and they will exit if the flag is set.
2998 * This ensures that no threads could be added to the hash later.
2999 * We will use shutting_down as the barrier for now.
3001 g_assert (shutting_down);
3004 * We make multiple calls to WaitForMultipleObjects since:
3005 * - we can only wait for MAXIMUM_WAIT_OBJECTS threads
3006 * - some threads could exit without becoming suspended
3008 finished = FALSE;
3009 while (!finished) {
3011 * Make a copy of the hashtable since we can't do anything with
3012 * threads while threads_mutex is held.
3014 wait->num = 0;
3015 mono_threads_lock ();
3016 mono_g_hash_table_foreach (threads, collect_threads_for_suspend, wait);
3017 mono_threads_unlock ();
3019 events = g_new0 (gpointer, wait->num);
3020 eventidx = 0;
3021 /* Get the suspended events that we'll be waiting for */
3022 for (i = 0; i < wait->num; ++i) {
3023 MonoThread *thread = wait->threads [i];
3024 gboolean signal_suspend = FALSE;
3026 if ((thread->tid == self) || mono_gc_is_finalizer_thread (thread)) {
3027 //CloseHandle (wait->handles [i]);
3028 wait->threads [i] = NULL; /* ignore this thread in next loop */
3029 continue;
3032 ensure_synch_cs_set (thread);
3034 EnterCriticalSection (thread->synch_cs);
3036 if (thread->suspended_event == NULL) {
3037 thread->suspended_event = CreateEvent (NULL, TRUE, FALSE, NULL);
3038 if (thread->suspended_event == NULL) {
3039 /* Forget this one and go on to the next */
3040 LeaveCriticalSection (thread->synch_cs);
3041 continue;
3045 if ((thread->state & ThreadState_Suspended) != 0 ||
3046 (thread->state & ThreadState_StopRequested) != 0 ||
3047 (thread->state & ThreadState_Stopped) != 0) {
3048 LeaveCriticalSection (thread->synch_cs);
3049 CloseHandle (wait->handles [i]);
3050 wait->threads [i] = NULL; /* ignore this thread in next loop */
3051 continue;
3054 if ((thread->state & ThreadState_SuspendRequested) == 0)
3055 signal_suspend = TRUE;
3057 events [eventidx++] = thread->suspended_event;
3059 /* Convert abort requests into suspend requests */
3060 if ((thread->state & ThreadState_AbortRequested) != 0)
3061 thread->state &= ~ThreadState_AbortRequested;
3063 thread->state |= ThreadState_SuspendRequested;
3065 LeaveCriticalSection (thread->synch_cs);
3067 /* Signal the thread to suspend */
3068 if (signal_suspend)
3069 signal_thread_state_change (thread);
3072 if (eventidx > 0) {
3073 WaitForMultipleObjectsEx (eventidx, events, TRUE, 100, FALSE);
3074 for (i = 0; i < wait->num; ++i) {
3075 MonoThread *thread = wait->threads [i];
3077 if (thread == NULL)
3078 continue;
3080 EnterCriticalSection (thread->synch_cs);
3081 if ((thread->state & ThreadState_Suspended) != 0) {
3082 CloseHandle (thread->suspended_event);
3083 thread->suspended_event = NULL;
3085 LeaveCriticalSection (thread->synch_cs);
3087 } else {
3089 * If there are threads which are starting up, we wait until they
3090 * are suspended when they try to register in the threads hash.
3091 * This is guaranteed to finish, since the threads which can create new
3092 * threads get suspended after a while.
3093 * FIXME: The finalizer thread can still create new threads.
3095 mono_threads_lock ();
3096 starting = mono_g_hash_table_size (threads_starting_up) > 0;
3097 mono_threads_unlock ();
3098 if (starting)
3099 Sleep (100);
3100 else
3101 finished = TRUE;
3104 g_free (events);
3107 g_free (wait);
3110 static void
3111 collect_threads (gpointer key, gpointer value, gpointer user_data)
3113 MonoThread *thread = (MonoThread*)value;
3114 struct wait_data *wait = (struct wait_data*)user_data;
3115 HANDLE handle;
3117 if (wait->num<MAXIMUM_WAIT_OBJECTS) {
3118 handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
3119 if (handle == NULL)
3120 return;
3122 wait->handles [wait->num] = handle;
3123 wait->threads [wait->num] = thread;
3124 wait->num++;
3129 * mono_threads_request_thread_dump:
3131 * Ask all threads except the current to print their stacktrace to stdout.
3133 void
3134 mono_threads_request_thread_dump (void)
3136 struct wait_data *wait = g_new0 (struct wait_data, 1);
3137 int i;
3140 * Make a copy of the hashtable since we can't do anything with
3141 * threads while threads_mutex is held.
3143 mono_threads_lock ();
3144 mono_g_hash_table_foreach (threads, collect_threads, wait);
3145 mono_threads_unlock ();
3147 for (i = 0; i < wait->num; ++i) {
3148 MonoThread *thread = wait->threads [i];
3150 if (!mono_gc_is_finalizer_thread (thread) && (thread != mono_thread_current ()) && !thread->thread_dump_requested) {
3151 thread->thread_dump_requested = TRUE;
3153 signal_thread_state_change (thread);
3156 CloseHandle (wait->handles [i]);
3161 * mono_thread_push_appdomain_ref:
3163 * Register that the current thread may have references to objects in domain
3164 * @domain on its stack. Each call to this function should be paired with a
3165 * call to pop_appdomain_ref.
3167 void
3168 mono_thread_push_appdomain_ref (MonoDomain *domain)
3170 MonoThread *thread = mono_thread_current ();
3172 if (thread) {
3173 /* printf ("PUSH REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, domain->friendly_name); */
3174 mono_threads_lock ();
3175 thread->appdomain_refs = g_slist_prepend (thread->appdomain_refs, domain);
3176 mono_threads_unlock ();
3180 void
3181 mono_thread_pop_appdomain_ref (void)
3183 MonoThread *thread = mono_thread_current ();
3185 if (thread) {
3186 /* printf ("POP REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, ((MonoDomain*)(thread->appdomain_refs->data))->friendly_name); */
3187 mono_threads_lock ();
3188 /* FIXME: How can the list be empty ? */
3189 if (thread->appdomain_refs)
3190 thread->appdomain_refs = g_slist_remove (thread->appdomain_refs, thread->appdomain_refs->data);
3191 mono_threads_unlock ();
3195 gboolean
3196 mono_thread_has_appdomain_ref (MonoThread *thread, MonoDomain *domain)
3198 gboolean res;
3199 mono_threads_lock ();
3200 res = g_slist_find (thread->appdomain_refs, domain) != NULL;
3201 mono_threads_unlock ();
3202 return res;
3205 typedef struct abort_appdomain_data {
3206 struct wait_data wait;
3207 MonoDomain *domain;
3208 } abort_appdomain_data;
3210 static void
3211 collect_appdomain_thread (gpointer key, gpointer value, gpointer user_data)
3213 MonoThread *thread = (MonoThread*)value;
3214 abort_appdomain_data *data = (abort_appdomain_data*)user_data;
3215 MonoDomain *domain = data->domain;
3217 if (mono_thread_has_appdomain_ref (thread, domain)) {
3218 /* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread->tid, domain->friendly_name); */
3220 if(data->wait.num<MAXIMUM_WAIT_OBJECTS) {
3221 HANDLE handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
3222 if (handle == NULL)
3223 return;
3224 data->wait.handles [data->wait.num] = handle;
3225 data->wait.threads [data->wait.num] = thread;
3226 data->wait.num++;
3227 } else {
3228 /* Just ignore the rest, we can't do anything with
3229 * them yet
3236 * mono_threads_abort_appdomain_threads:
3238 * Abort threads which has references to the given appdomain.
3240 gboolean
3241 mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
3243 abort_appdomain_data user_data;
3244 guint32 start_time;
3245 int orig_timeout = timeout;
3246 int i;
3248 THREAD_DEBUG (g_message ("%s: starting abort", __func__));
3250 start_time = mono_msec_ticks ();
3251 do {
3252 mono_threads_lock ();
3254 user_data.domain = domain;
3255 user_data.wait.num = 0;
3256 /* This shouldn't take any locks */
3257 mono_g_hash_table_foreach (threads, collect_appdomain_thread, &user_data);
3258 mono_threads_unlock ();
3260 if (user_data.wait.num > 0) {
3261 /* Abort the threads outside the threads lock */
3262 for (i = 0; i < user_data.wait.num; ++i)
3263 ves_icall_System_Threading_Thread_Abort (user_data.wait.threads [i], NULL);
3266 * We should wait for the threads either to abort, or to leave the
3267 * domain. We can't do the latter, so we wait with a timeout.
3269 wait_for_tids (&user_data.wait, 100);
3272 /* Update remaining time */
3273 timeout -= mono_msec_ticks () - start_time;
3274 start_time = mono_msec_ticks ();
3276 if (orig_timeout != -1 && timeout < 0)
3277 return FALSE;
3279 while (user_data.wait.num > 0);
3281 THREAD_DEBUG (g_message ("%s: abort done", __func__));
3283 return TRUE;
3286 static void
3287 clear_cached_culture (gpointer key, gpointer value, gpointer user_data)
3289 MonoThread *thread = (MonoThread*)value;
3290 MonoDomain *domain = (MonoDomain*)user_data;
3291 int i;
3293 /* No locking needed here */
3294 /* FIXME: why no locking? writes to the cache are protected with synch_cs above */
3296 if (thread->cached_culture_info) {
3297 for (i = 0; i < NUM_CACHED_CULTURES * 2; ++i) {
3298 MonoObject *obj = mono_array_get (thread->cached_culture_info, MonoObject*, i);
3299 if (obj && obj->vtable->domain == domain)
3300 mono_array_set (thread->cached_culture_info, MonoObject*, i, NULL);
3306 * mono_threads_clear_cached_culture:
3308 * Clear the cached_current_culture from all threads if it is in the
3309 * given appdomain.
3311 void
3312 mono_threads_clear_cached_culture (MonoDomain *domain)
3314 mono_threads_lock ();
3315 mono_g_hash_table_foreach (threads, clear_cached_culture, domain);
3316 mono_threads_unlock ();
3320 * mono_thread_get_undeniable_exception:
3322 * Return an exception which needs to be raised when leaving a catch clause.
3323 * This is used for undeniable exception propagation.
3325 MonoException*
3326 mono_thread_get_undeniable_exception (void)
3328 MonoThread *thread = mono_thread_current ();
3330 MONO_ARCH_SAVE_REGS;
3332 if (thread && thread->abort_exc && !is_running_protected_wrapper ()) {
3334 * FIXME: Clear the abort exception and return an AppDomainUnloaded
3335 * exception if the thread no longer references a dying appdomain.
3337 thread->abort_exc->trace_ips = NULL;
3338 thread->abort_exc->stack_trace = NULL;
3339 return thread->abort_exc;
3342 return NULL;
3345 #define NUM_STATIC_DATA_IDX 8
3346 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
3347 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
3352 * mono_alloc_static_data
3354 * Allocate memory blocks for storing threads or context static data
3356 static void
3357 mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset)
3359 guint idx = (offset >> 24) - 1;
3360 int i;
3362 gpointer* static_data = *static_data_ptr;
3363 if (!static_data) {
3364 static_data = mono_gc_alloc_fixed (static_data_size [0], NULL);
3365 *static_data_ptr = static_data;
3366 static_data [0] = static_data;
3369 for (i = 1; i <= idx; ++i) {
3370 if (static_data [i])
3371 continue;
3372 static_data [i] = mono_gc_alloc_fixed (static_data_size [i], NULL);
3377 * mono_init_static_data_info
3379 * Initializes static data counters
3381 static void mono_init_static_data_info (StaticDataInfo *static_data)
3383 static_data->idx = 0;
3384 static_data->offset = 0;
3385 static_data->freelist = NULL;
3389 * mono_alloc_static_data_slot
3391 * Generates an offset for static data. static_data contains the counters
3392 * used to generate it.
3394 static guint32
3395 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align)
3397 guint32 offset;
3399 if (!static_data->idx && !static_data->offset) {
3401 * we use the first chunk of the first allocation also as
3402 * an array for the rest of the data
3404 static_data->offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX;
3406 static_data->offset += align - 1;
3407 static_data->offset &= ~(align - 1);
3408 if (static_data->offset + size >= static_data_size [static_data->idx]) {
3409 static_data->idx ++;
3410 g_assert (size <= static_data_size [static_data->idx]);
3411 g_assert (static_data->idx < NUM_STATIC_DATA_IDX);
3412 static_data->offset = 0;
3414 offset = static_data->offset | ((static_data->idx + 1) << 24);
3415 static_data->offset += size;
3416 return offset;
3420 * ensure thread static fields already allocated are valid for thread
3421 * This function is called when a thread is created or on thread attach.
3423 static void
3424 thread_adjust_static_data (MonoThread *thread)
3426 guint32 offset;
3428 mono_threads_lock ();
3429 if (thread_static_info.offset || thread_static_info.idx > 0) {
3430 /* get the current allocated size */
3431 offset = thread_static_info.offset | ((thread_static_info.idx + 1) << 24);
3432 mono_alloc_static_data (&(thread->static_data), offset);
3434 mono_threads_unlock ();
3437 static void
3438 alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
3440 MonoThread *thread = value;
3441 guint32 offset = GPOINTER_TO_UINT (user);
3443 mono_alloc_static_data (&(thread->static_data), offset);
3446 static MonoThreadDomainTls*
3447 search_tls_slot_in_freelist (StaticDataInfo *static_data, guint32 size, guint32 align)
3449 MonoThreadDomainTls* prev = NULL;
3450 MonoThreadDomainTls* tmp = static_data->freelist;
3451 while (tmp) {
3452 if (tmp->size == size) {
3453 if (prev)
3454 prev->next = tmp->next;
3455 else
3456 static_data->freelist = tmp->next;
3457 return tmp;
3459 tmp = tmp->next;
3461 return NULL;
3465 * The offset for a special static variable is composed of three parts:
3466 * a bit that indicates the type of static data (0:thread, 1:context),
3467 * an index in the array of chunks of memory for the thread (thread->static_data)
3468 * and an offset in that chunk of mem. This allows allocating less memory in the
3469 * common case.
3472 guint32
3473 mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align)
3475 guint32 offset;
3476 if (static_type == SPECIAL_STATIC_THREAD)
3478 MonoThreadDomainTls *item;
3479 mono_threads_lock ();
3480 item = search_tls_slot_in_freelist (&thread_static_info, size, align);
3481 /*g_print ("TLS alloc: %d in domain %p (total: %d), cached: %p\n", size, mono_domain_get (), thread_static_info.offset, item);*/
3482 if (item) {
3483 offset = item->offset;
3484 g_free (item);
3485 } else {
3486 offset = mono_alloc_static_data_slot (&thread_static_info, size, align);
3488 /* This can be called during startup */
3489 if (threads != NULL)
3490 mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
3491 mono_threads_unlock ();
3493 else
3495 g_assert (static_type == SPECIAL_STATIC_CONTEXT);
3496 mono_contexts_lock ();
3497 offset = mono_alloc_static_data_slot (&context_static_info, size, align);
3498 mono_contexts_unlock ();
3499 offset |= 0x80000000; /* Set the high bit to indicate context static data */
3501 return offset;
3504 gpointer
3505 mono_get_special_static_data (guint32 offset)
3507 /* The high bit means either thread (0) or static (1) data. */
3509 guint32 static_type = (offset & 0x80000000);
3510 int idx;
3512 offset &= 0x7fffffff;
3513 idx = (offset >> 24) - 1;
3515 if (static_type == 0)
3517 MonoThread *thread = mono_thread_current ();
3518 return ((char*) thread->static_data [idx]) + (offset & 0xffffff);
3520 else
3522 /* Allocate static data block under demand, since we don't have a list
3523 // of contexts
3525 MonoAppContext *context = mono_context_get ();
3526 if (!context->static_data || !context->static_data [idx]) {
3527 mono_contexts_lock ();
3528 mono_alloc_static_data (&(context->static_data), offset);
3529 mono_contexts_unlock ();
3531 return ((char*) context->static_data [idx]) + (offset & 0xffffff);
3535 typedef struct {
3536 guint32 offset;
3537 guint32 size;
3538 } TlsOffsetSize;
3540 static void
3541 free_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
3543 MonoThread *thread = value;
3544 TlsOffsetSize *data = user;
3545 int idx = (data->offset >> 24) - 1;
3546 char *ptr;
3548 if (!thread->static_data || !thread->static_data [idx])
3549 return;
3550 ptr = ((char*) thread->static_data [idx]) + (data->offset & 0xffffff);
3551 memset (ptr, 0, data->size);
3554 static void
3555 do_free_special (gpointer key, gpointer value, gpointer data)
3557 MonoClassField *field = key;
3558 guint32 offset = GPOINTER_TO_UINT (value);
3559 guint32 static_type = (offset & 0x80000000);
3560 gint32 align;
3561 guint32 size;
3562 size = mono_type_size (field->type, &align);
3563 /*g_print ("free %s , size: %d, offset: %x\n", field->name, size, offset);*/
3564 if (static_type == 0) {
3565 TlsOffsetSize data;
3566 MonoThreadDomainTls *item = g_new0 (MonoThreadDomainTls, 1);
3567 data.offset = offset & 0x7fffffff;
3568 data.size = size;
3569 if (threads != NULL)
3570 mono_g_hash_table_foreach (threads, free_thread_static_data_helper, &data);
3571 item->offset = offset;
3572 item->size = size;
3573 item->next = thread_static_info.freelist;
3574 thread_static_info.freelist = item;
3575 } else {
3576 /* FIXME: free context static data as well */
3580 void
3581 mono_alloc_special_static_data_free (GHashTable *special_static_fields)
3583 mono_threads_lock ();
3584 g_hash_table_foreach (special_static_fields, do_free_special, NULL);
3585 mono_threads_unlock ();
3588 static MonoClassField *local_slots = NULL;
3590 typedef struct {
3591 /* local tls data to get locals_slot from a thread */
3592 guint32 offset;
3593 int idx;
3594 /* index in the locals_slot array */
3595 int slot;
3596 } LocalSlotID;
3598 static void
3599 clear_local_slot (gpointer key, gpointer value, gpointer user_data)
3601 LocalSlotID *sid = user_data;
3602 MonoThread *thread = (MonoThread*)value;
3603 MonoArray *slots_array;
3605 * the static field is stored at: ((char*) thread->static_data [idx]) + (offset & 0xffffff);
3606 * it is for the right domain, so we need to check if it is allocated an initialized
3607 * for the current thread.
3609 /*g_print ("handling thread %p\n", thread);*/
3610 if (!thread->static_data || !thread->static_data [sid->idx])
3611 return;
3612 slots_array = *(MonoArray **)(((char*) thread->static_data [sid->idx]) + (sid->offset & 0xffffff));
3613 if (!slots_array || sid->slot >= mono_array_length (slots_array))
3614 return;
3615 mono_array_set (slots_array, MonoObject*, sid->slot, NULL);
3618 void
3619 mono_thread_free_local_slot_values (int slot, MonoBoolean thread_local)
3621 MonoDomain *domain;
3622 LocalSlotID sid;
3623 sid.slot = slot;
3624 if (thread_local) {
3625 void *addr = NULL;
3626 if (!local_slots) {
3627 local_slots = mono_class_get_field_from_name (mono_defaults.thread_class, "local_slots");
3628 if (!local_slots) {
3629 g_warning ("local_slots field not found in Thread class");
3630 return;
3633 domain = mono_domain_get ();
3634 mono_domain_lock (domain);
3635 if (domain->special_static_fields)
3636 addr = g_hash_table_lookup (domain->special_static_fields, local_slots);
3637 mono_domain_unlock (domain);
3638 if (!addr)
3639 return;
3640 /*g_print ("freeing slot %d at %p\n", slot, addr);*/
3641 sid.offset = GPOINTER_TO_UINT (addr);
3642 sid.offset &= 0x7fffffff;
3643 sid.idx = (sid.offset >> 24) - 1;
3644 mono_threads_lock ();
3645 mono_g_hash_table_foreach (threads, clear_local_slot, &sid);
3646 mono_threads_unlock ();
3647 } else {
3648 /* FIXME: clear the slot for MonoAppContexts, too */
3652 #ifdef PLATFORM_WIN32
3653 static void CALLBACK dummy_apc (ULONG_PTR param)
3656 #else
3657 static guint32 dummy_apc (gpointer param)
3659 return 0;
3661 #endif
3664 * mono_thread_execute_interruption
3666 * Performs the operation that the requested thread state requires (abort,
3667 * suspend or stop)
3669 static MonoException* mono_thread_execute_interruption (MonoThread *thread)
3671 ensure_synch_cs_set (thread);
3673 EnterCriticalSection (thread->synch_cs);
3675 if (thread->interruption_requested) {
3676 /* this will consume pending APC calls */
3677 WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
3678 InterlockedDecrement (&thread_interruption_requested);
3679 thread->interruption_requested = FALSE;
3680 #ifndef PLATFORM_WIN32
3681 /* Clear the interrupted flag of the thread so it can wait again */
3682 wapi_clear_interruption ();
3683 #endif
3686 if ((thread->state & ThreadState_AbortRequested) != 0) {
3687 if (thread->abort_exc == NULL)
3688 MONO_OBJECT_SETREF (thread, abort_exc, mono_get_exception_thread_abort ());
3689 LeaveCriticalSection (thread->synch_cs);
3690 return thread->abort_exc;
3692 else if ((thread->state & ThreadState_SuspendRequested) != 0) {
3693 thread->state &= ~ThreadState_SuspendRequested;
3694 thread->state |= ThreadState_Suspended;
3695 thread->suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL);
3696 if (thread->suspend_event == NULL) {
3697 LeaveCriticalSection (thread->synch_cs);
3698 return(NULL);
3700 if (thread->suspended_event)
3701 SetEvent (thread->suspended_event);
3703 LeaveCriticalSection (thread->synch_cs);
3705 if (shutting_down) {
3706 /* After we left the lock, the runtime might shut down so everything becomes invalid */
3707 for (;;)
3708 Sleep (1000);
3711 WaitForSingleObject (thread->suspend_event, INFINITE);
3713 EnterCriticalSection (thread->synch_cs);
3715 CloseHandle (thread->suspend_event);
3716 thread->suspend_event = NULL;
3717 thread->state &= ~ThreadState_Suspended;
3719 /* The thread that requested the resume will have replaced this event
3720 * and will be waiting for it
3722 SetEvent (thread->resume_event);
3724 LeaveCriticalSection (thread->synch_cs);
3726 return NULL;
3728 else if ((thread->state & ThreadState_StopRequested) != 0) {
3729 /* FIXME: do this through the JIT? */
3731 LeaveCriticalSection (thread->synch_cs);
3733 mono_thread_exit ();
3734 return NULL;
3735 } else if (thread->thread_interrupt_requested) {
3737 thread->thread_interrupt_requested = FALSE;
3738 LeaveCriticalSection (thread->synch_cs);
3740 return(mono_get_exception_thread_interrupted ());
3743 LeaveCriticalSection (thread->synch_cs);
3745 return NULL;
3749 * mono_thread_request_interruption
3751 * A signal handler can call this method to request the interruption of a
3752 * thread. The result of the interruption will depend on the current state of
3753 * the thread. If the result is an exception that needs to be throw, it is
3754 * provided as return value.
3756 MonoException*
3757 mono_thread_request_interruption (gboolean running_managed)
3759 MonoThread *thread = mono_thread_current ();
3761 /* The thread may already be stopping */
3762 if (thread == NULL)
3763 return NULL;
3765 if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
3766 return NULL;
3768 if (!running_managed || is_running_protected_wrapper ()) {
3769 /* Can't stop while in unmanaged code. Increase the global interruption
3770 request count. When exiting the unmanaged method the count will be
3771 checked and the thread will be interrupted. */
3773 InterlockedIncrement (&thread_interruption_requested);
3775 if (mono_thread_notify_pending_exc_fn && !running_managed)
3776 /* The JIT will notify the thread about the interruption */
3777 /* This shouldn't take any locks */
3778 mono_thread_notify_pending_exc_fn ();
3780 /* this will awake the thread if it is in WaitForSingleObject
3781 or similar */
3782 /* Our implementation of this function ignores the func argument */
3783 QueueUserAPC ((PAPCFUNC)dummy_apc, thread->handle, NULL);
3784 return NULL;
3786 else {
3787 return mono_thread_execute_interruption (thread);
3791 gboolean mono_thread_interruption_requested ()
3793 if (thread_interruption_requested) {
3794 MonoThread *thread = mono_thread_current ();
3795 /* The thread may already be stopping */
3796 if (thread != NULL)
3797 return (thread->interruption_requested);
3799 return FALSE;
3802 static void mono_thread_interruption_checkpoint_request (gboolean bypass_abort_protection)
3804 MonoThread *thread = mono_thread_current ();
3806 /* The thread may already be stopping */
3807 if (thread == NULL)
3808 return;
3810 mono_debugger_check_interruption ();
3812 if (thread->interruption_requested && (bypass_abort_protection || !is_running_protected_wrapper ())) {
3813 MonoException* exc = mono_thread_execute_interruption (thread);
3814 if (exc) mono_raise_exception (exc);
3819 * Performs the interruption of the current thread, if one has been requested,
3820 * and the thread is not running a protected wrapper.
3822 void mono_thread_interruption_checkpoint ()
3824 mono_thread_interruption_checkpoint_request (FALSE);
3828 * Performs the interruption of the current thread, if one has been requested.
3830 void mono_thread_force_interruption_checkpoint ()
3832 mono_thread_interruption_checkpoint_request (TRUE);
3836 * mono_thread_get_and_clear_pending_exception:
3838 * Return any pending exceptions for the current thread and clear it as a side effect.
3840 MonoException*
3841 mono_thread_get_and_clear_pending_exception (void)
3843 MonoThread *thread = mono_thread_current ();
3845 /* The thread may already be stopping */
3846 if (thread == NULL)
3847 return NULL;
3849 if (thread->interruption_requested && !is_running_protected_wrapper ()) {
3850 return mono_thread_execute_interruption (thread);
3853 if (thread->pending_exception) {
3854 MonoException *exc = thread->pending_exception;
3856 thread->pending_exception = NULL;
3857 return exc;
3860 return NULL;
3864 * mono_set_pending_exception:
3866 * Set the pending exception of the current thread to EXC. On platforms which
3867 * support it, the exception will be thrown when execution returns to managed code.
3868 * On other platforms, this function is equivalent to mono_raise_exception ().
3869 * Internal calls which report exceptions using this function instead of
3870 * raise_exception () might be called by JITted code using a more efficient calling
3871 * convention.
3873 void
3874 mono_set_pending_exception (MonoException *exc)
3876 MonoThread *thread = mono_thread_current ();
3878 /* The thread may already be stopping */
3879 if (thread == NULL)
3880 return;
3882 if (mono_thread_notify_pending_exc_fn) {
3883 MONO_OBJECT_SETREF (thread, pending_exception, exc);
3885 mono_thread_notify_pending_exc_fn ();
3886 } else {
3887 /* No way to notify the JIT about the exception, have to throw it now */
3888 mono_raise_exception (exc);
3893 * mono_thread_interruption_request_flag:
3895 * Returns the address of a flag that will be non-zero if an interruption has
3896 * been requested for a thread. The thread to interrupt may not be the current
3897 * thread, so an additional call to mono_thread_interruption_requested() or
3898 * mono_thread_interruption_checkpoint() is allways needed if the flag is not
3899 * zero.
3901 gint32* mono_thread_interruption_request_flag ()
3903 return &thread_interruption_requested;
3906 void
3907 mono_thread_init_apartment_state (void)
3909 MonoThread* thread;
3910 thread = mono_thread_current ();
3912 #ifdef PLATFORM_WIN32
3913 /* Positive return value indicates success, either
3914 * S_OK if this is first CoInitialize call, or
3915 * S_FALSE if CoInitialize already called, but with same
3916 * threading model. A negative value indicates failure,
3917 * probably due to trying to change the threading model.
3919 if (CoInitializeEx(NULL, (thread->apartment_state == ThreadApartmentState_STA)
3920 ? COINIT_APARTMENTTHREADED
3921 : COINIT_MULTITHREADED) < 0) {
3922 thread->apartment_state = ThreadApartmentState_Unknown;
3924 #endif
3927 void
3928 mono_thread_cleanup_apartment_state (void)
3930 #ifdef PLATFORM_WIN32
3931 MonoThread* thread;
3932 thread = mono_thread_current ();
3934 if (thread && thread->apartment_state != ThreadApartmentState_Unknown) {
3935 CoUninitialize ();
3937 #endif
3940 void
3941 mono_thread_set_state (MonoThread *thread, MonoThreadState state)
3943 ensure_synch_cs_set (thread);
3945 EnterCriticalSection (thread->synch_cs);
3946 thread->state |= state;
3947 LeaveCriticalSection (thread->synch_cs);
3950 void
3951 mono_thread_clr_state (MonoThread *thread, MonoThreadState state)
3953 ensure_synch_cs_set (thread);
3955 EnterCriticalSection (thread->synch_cs);
3956 thread->state &= ~state;
3957 LeaveCriticalSection (thread->synch_cs);
3960 gboolean
3961 mono_thread_test_state (MonoThread *thread, MonoThreadState test)
3963 gboolean ret = FALSE;
3965 ensure_synch_cs_set (thread);
3967 EnterCriticalSection (thread->synch_cs);
3969 if ((thread->state & test) != 0) {
3970 ret = TRUE;
3973 LeaveCriticalSection (thread->synch_cs);
3975 return ret;
3978 static MonoClassField *execution_context_field;
3980 static MonoObject**
3981 get_execution_context_addr (void)
3983 MonoDomain *domain = mono_domain_get ();
3984 guint32 offset;
3986 if (!execution_context_field) {
3987 execution_context_field = mono_class_get_field_from_name (mono_defaults.thread_class,
3988 "_ec");
3989 g_assert (execution_context_field);
3992 g_assert (mono_class_try_get_vtable (domain, mono_defaults.appdomain_class));
3994 mono_domain_lock (domain);
3995 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, execution_context_field));
3996 mono_domain_unlock (domain);
3997 g_assert (offset);
3999 return (MonoObject**) mono_get_special_static_data (offset);
4002 MonoObject*
4003 mono_thread_get_execution_context (void)
4005 return *get_execution_context_addr ();
4008 void
4009 mono_thread_set_execution_context (MonoObject *ec)
4011 *get_execution_context_addr () = ec;