2 * threads.c: Thread support internal calls
5 * Dick Porter (dick@ximian.com)
6 * Paolo Molaro (lupus@ximian.com)
7 * Patrik Torstensson (patrik.torstensson@labs2.com)
9 * (C) 2001 Ximian, Inc.
17 #include <mono/metadata/object.h>
18 #include <mono/metadata/appdomain.h>
19 #include <mono/metadata/profiler-private.h>
20 #include <mono/metadata/threads.h>
21 #include <mono/metadata/threadpool.h>
22 #include <mono/metadata/threads-types.h>
23 #include <mono/metadata/exception.h>
24 #include <mono/metadata/environment.h>
25 #include <mono/metadata/monitor.h>
26 #include <mono/metadata/gc-internal.h>
27 #include <mono/metadata/marshal.h>
28 #include <mono/io-layer/io-layer.h>
30 #include <mono/os/gc_wrapper.h>
33 #undef THREAD_WAIT_DEBUG
37 guint32 (*func)(void *);
54 * The "os_handle" field of the WaitHandle class.
56 static MonoClassField *wait_handle_os_handle_field = NULL;
58 /* Controls access to the 'threads' hash table */
59 static CRITICAL_SECTION threads_mutex;
61 /* Controls access to context static data */
62 static CRITICAL_SECTION contexts_mutex;
64 /* Holds current status of static data heap */
65 static StaticDataInfo thread_static_info;
66 static StaticDataInfo context_static_info;
68 /* The hash of existing threads (key is thread ID) that need joining
71 static MonoGHashTable *threads=NULL;
73 /* The TLS key that holds the MonoObject assigned to each thread */
74 static guint32 current_object_key = -1;
76 /* function called at thread start */
77 static MonoThreadStartCB mono_thread_start_cb = NULL;
79 /* function called at thread attach */
80 static MonoThreadAttachCB mono_thread_attach_cb = NULL;
82 /* function called at thread cleanup */
83 static MonoThreadCleanupFunc mono_thread_cleanup = NULL;
85 /* function called when a new thread has been created */
86 static MonoThreadCallbacks *mono_thread_callbacks = NULL;
88 /* The TLS key that holds the LocalDataStoreSlot hash in each thread */
89 static guint32 slothash_key = -1;
91 /* The default stack size for each thread */
92 static guint32 default_stacksize = 0;
94 static void thread_adjust_static_data (MonoThread *thread);
95 static void mono_init_static_data_info (StaticDataInfo *static_data);
96 static guint32 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align);
98 /* Spin lock for InterlockedXXX 64 bit functions */
99 static CRITICAL_SECTION interlocked_mutex;
101 /* Controls access to interruption flag */
102 static CRITICAL_SECTION interruption_mutex;
104 /* global count of thread interruptions requested */
105 static gint32 thread_interruption_requested = 0;
108 /* handle_store() and handle_remove() manage the array of threads that
109 * still need to be waited for when the main thread exits.
111 static void handle_store(MonoThread *thread)
113 EnterCriticalSection(&threads_mutex);
116 g_message(G_GNUC_PRETTY_FUNCTION ": thread %p ID %d", thread,
121 threads=mono_g_hash_table_new(NULL, NULL);
124 /* We don't need to duplicate thread->handle, because it is
125 * only closed when the thread object is finalized by the GC.
127 mono_g_hash_table_insert(threads, GUINT_TO_POINTER(thread->tid), thread);
128 LeaveCriticalSection(&threads_mutex);
131 static void handle_remove(guint32 tid)
134 g_message(G_GNUC_PRETTY_FUNCTION ": thread ID %d", tid);
137 EnterCriticalSection(&threads_mutex);
140 mono_g_hash_table_remove (threads, GUINT_TO_POINTER(tid));
142 LeaveCriticalSection(&threads_mutex);
144 /* Don't close the handle here, wait for the object finalizer
145 * to do it. Otherwise, the following race condition applies:
147 * 1) Thread exits (and handle_remove() closes the handle)
149 * 2) Some other handle is reassigned the same slot
151 * 3) Another thread tries to join the first thread, and
152 * blocks waiting for the reassigned handle to be signalled
153 * (which might never happen). This is possible, because the
154 * thread calling Join() still has a reference to the first
159 static void thread_cleanup (MonoThread *thread)
161 if (!mono_monitor_try_enter ((MonoObject *)thread, INFINITE))
164 thread->state |= ThreadState_Stopped;
165 mono_monitor_exit ((MonoObject *)thread);
167 mono_profiler_thread_end (thread->tid);
168 handle_remove (thread->tid);
170 mono_thread_pop_appdomain_ref ();
172 if (mono_thread_cleanup)
173 mono_thread_cleanup (thread);
176 static guint32 start_wrapper(void *data)
178 struct StartInfo *start_info=(struct StartInfo *)data;
179 guint32 (*start_func)(void *);
182 MonoThread *thread=start_info->obj;
185 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper",
186 GetCurrentThreadId ());
189 /* We can be sure start_info->obj->tid and
190 * start_info->obj->handle have been set, because the thread
191 * was created suspended, and these values were set before the
197 TlsSetValue (current_object_key, thread);
199 if (!mono_domain_set (start_info->domain, FALSE)) {
200 /* No point in raising an appdomain_unloaded exception here */
201 /* FIXME: Cleanup here */
205 start_func = start_info->func;
206 this = start_info->this;
208 /* This MUST be called before any managed code can be
209 * executed, as it calls the callback function that (for the
210 * jit) sets the lmf marker.
212 mono_thread_new_init (tid, &tid, start_func);
213 thread->stack_ptr = &tid;
216 g_message (G_GNUC_PRETTY_FUNCTION
217 ": (%d,%d) Setting thread stack to %p",
218 GetCurrentThreadId (), getpid (), thread->stack_ptr);
222 g_message (G_GNUC_PRETTY_FUNCTION
223 ": (%d) Setting current_object_key to %p",
224 GetCurrentThreadId (), thread);
227 mono_profiler_thread_start (tid);
229 if(thread->start_notify!=NULL) {
230 /* Let the thread that called Start() know we're
233 ReleaseSemaphore (thread->start_notify, 1, NULL);
238 /* Every thread references the appdomain which created it */
239 mono_thread_push_appdomain_ref (mono_domain_get ());
241 thread_adjust_static_data (thread);
242 #ifndef PLATFORM_WIN32
244 g_message (G_GNUC_PRETTY_FUNCTION "start_wrapper for %d\n", thread->tid);
246 pthread_cleanup_push ((void (*) (void *)) mono_thread_detach, thread);
250 #ifdef PLATFORM_WIN32
251 /* If the thread calls ExitThread at all, this remaining code
252 * will not be executed, but the main thread will eventually
253 * call thread_cleanup() on this thread's behalf.
257 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper terminating",
258 GetCurrentThreadId ());
261 /* Remove the reference to the thread object in the TLS data,
262 * so the thread object can be finalized. This won't be
263 * reached if the thread threw an uncaught exception, so those
264 * thread handles will stay referenced :-( (This is due to
265 * missing support for scanning thread-specific data in the
266 * Boehm GC - the io-layer keeps a GC-visible hash of pointers
269 TlsSetValue (current_object_key, NULL);
271 thread_cleanup (thread);
273 pthread_cleanup_pop (1);
279 void mono_thread_new_init (guint32 tid, gpointer stack_start, gpointer func)
281 if (mono_thread_start_cb) {
282 mono_thread_start_cb (tid, stack_start, func);
285 if (mono_thread_callbacks)
286 (* mono_thread_callbacks->thread_created) (tid, stack_start, func);
289 void mono_threads_set_default_stacksize (guint32 stacksize)
291 default_stacksize = stacksize;
294 guint32 mono_threads_get_default_stacksize (void)
296 return default_stacksize;
299 void mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
302 HANDLE thread_handle;
303 struct StartInfo *start_info;
306 thread=(MonoThread *)mono_object_new (domain,
307 mono_defaults.thread_class);
309 start_info=g_new0 (struct StartInfo, 1);
310 start_info->func = func;
311 start_info->obj = thread;
312 start_info->domain = domain;
313 start_info->this = arg;
315 /* Create suspended, so we can do some housekeeping before the thread
318 thread_handle = CreateThread(NULL, default_stacksize, start_wrapper, start_info,
319 CREATE_SUSPENDED, &tid);
321 g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d (handle %p)",
324 g_assert (thread_handle);
326 thread->handle=thread_handle;
329 handle_store(thread);
331 ResumeThread (thread_handle);
335 mono_thread_attach (MonoDomain *domain)
338 HANDLE thread_handle;
341 if ((thread = mono_thread_current ())) {
342 /* Already attached */
346 thread = (MonoThread *)mono_object_new (domain,
347 mono_defaults.thread_class);
349 thread_handle = GetCurrentThread ();
350 g_assert (thread_handle);
352 tid=GetCurrentThreadId ();
354 thread->handle=thread_handle;
358 g_message(G_GNUC_PRETTY_FUNCTION ": Attached thread ID %d (handle %p)",
362 handle_store(thread);
365 g_message (G_GNUC_PRETTY_FUNCTION
366 ": (%d) Setting current_object_key to %p",
367 GetCurrentThreadId (), thread);
370 TlsSetValue (current_object_key, thread);
371 mono_domain_set (domain, TRUE);
373 thread_adjust_static_data (thread);
375 if (mono_thread_attach_cb) {
376 mono_thread_attach_cb (tid, &tid);
383 mono_thread_detach (MonoThread *thread)
385 g_return_if_fail (thread != NULL);
388 g_message (G_GNUC_PRETTY_FUNCTION "mono_thread_detach for %d\n", thread->tid);
390 TlsSetValue (current_object_key, NULL);
392 thread_cleanup (thread);
395 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this,
398 MonoMulticastDelegate *delegate = (MonoMulticastDelegate*)start;
399 guint32 (*start_func)(void *);
400 struct StartInfo *start_info;
408 g_message(G_GNUC_PRETTY_FUNCTION
409 ": Trying to start a new thread: this (%p) start (%p)",
413 im = mono_get_delegate_invoke (start->vtable->klass);
414 im = mono_marshal_get_delegate_invoke (im);
415 if (mono_thread_callbacks)
416 start_func = (* mono_thread_callbacks->thread_start_compile_func) (im);
418 start_func = mono_compile_method (im);
420 if(start_func==NULL) {
421 g_warning(G_GNUC_PRETTY_FUNCTION
422 ": Can't locate start method!");
425 /* This is freed in start_wrapper */
426 start_info = g_new0 (struct StartInfo, 1);
427 start_info->func = start_func;
428 start_info->this = delegate;
429 start_info->obj = this;
430 start_info->domain = mono_domain_get ();
432 this->start_notify=CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
433 if(this->start_notify==NULL) {
434 g_warning (G_GNUC_PRETTY_FUNCTION ": CreateSemaphore error 0x%x", GetLastError ());
438 thread=CreateThread(NULL, default_stacksize, start_wrapper, start_info,
439 CREATE_SUSPENDED, &tid);
441 g_warning(G_GNUC_PRETTY_FUNCTION
442 ": CreateThread error 0x%x", GetLastError());
449 /* Don't call handle_store() here, delay it to Start.
450 * We can't join a thread (trying to will just block
451 * forever) until it actually starts running, so don't
452 * store the handle till then.
456 g_message(G_GNUC_PRETTY_FUNCTION
457 ": Started thread ID %d (handle %p)", tid, thread);
464 void ves_icall_System_Threading_Thread_Thread_free_internal (MonoThread *this,
470 g_message (G_GNUC_PRETTY_FUNCTION ": Closing thread %p, handle %p",
474 CloseHandle (thread);
477 void ves_icall_System_Threading_Thread_Start_internal(MonoThread *this,
483 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Launching thread %p (%d)",
484 GetCurrentThreadId (), this, this->tid);
487 /* Only store the handle when the thread is about to be
488 * launched, to avoid the main thread deadlocking while trying
489 * to clean up a thread that will never be signalled.
493 if (mono_thread_callbacks)
494 (* mono_thread_callbacks->start_resume) (this->tid);
496 ResumeThread(thread);
498 if (mono_thread_callbacks)
499 (* mono_thread_callbacks->end_resume) (this->tid);
501 if(this->start_notify!=NULL) {
502 /* Wait for the thread to set up its TLS data etc, so
503 * theres no potential race condition if someone tries
504 * to look up the data believing the thread has
509 g_message(G_GNUC_PRETTY_FUNCTION
510 ": (%d) waiting for thread %p (%d) to start",
511 GetCurrentThreadId (), this, this->tid);
514 WaitForSingleObjectEx (this->start_notify, INFINITE, FALSE);
515 CloseHandle (this->start_notify);
516 this->start_notify=NULL;
520 g_message(G_GNUC_PRETTY_FUNCTION
521 ": (%d) Done launching thread %p (%d)",
522 GetCurrentThreadId (), this, this->tid);
526 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
531 g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
538 ves_icall_System_Threading_Thread_GetDomainID (void)
542 return mono_domain_get()->domain_id;
546 ves_icall_System_Threading_Thread_GetName_internal (MonoThread *this_obj)
551 return mono_string_new_utf16 (mono_domain_get (), this_obj->name, this_obj->name_len);
555 ves_icall_System_Threading_Thread_SetName_internal (MonoThread *this_obj, MonoString *name)
558 g_free (this_obj->name);
560 this_obj->name = g_new (gunichar2, mono_string_length (name));
561 memcpy (this_obj->name, mono_string_chars (name), mono_string_length (name) * 2);
562 this_obj->name_len = mono_string_length (name);
565 this_obj->name = NULL;
569 mono_thread_current (void)
575 /* Find the current thread object */
576 thread=TlsGetValue (current_object_key);
579 g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", thread);
585 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this,
586 int ms, HANDLE thread)
596 g_message (G_GNUC_PRETTY_FUNCTION ": joining thread handle %p, %d ms",
600 ret=WaitForSingleObjectEx (thread, ms, TRUE);
602 if(ret==WAIT_OBJECT_0) {
604 g_message (G_GNUC_PRETTY_FUNCTION ": join successful");
611 g_message (G_GNUC_PRETTY_FUNCTION ": join failed");
617 void ves_icall_System_Threading_Thread_SlotHash_store(MonoObject *data)
622 g_message(G_GNUC_PRETTY_FUNCTION ": Storing key %p", data);
625 /* Object location stored here */
626 TlsSetValue(slothash_key, data);
629 MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void)
635 data=TlsGetValue(slothash_key);
638 g_message(G_GNUC_PRETTY_FUNCTION ": Retrieved key %p", data);
644 /* FIXME: exitContext isnt documented */
645 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
651 MonoObject *waitHandle;
656 numhandles = mono_array_length(mono_handles);
657 handles = g_new0(HANDLE, numhandles);
659 if (wait_handle_os_handle_field == 0) {
660 /* Get the field os_handle which will contain the actual handle */
661 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");
662 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
665 for(i = 0; i < numhandles; i++) {
666 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
667 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
674 ret=WaitForMultipleObjectsEx(numhandles, handles, TRUE, ms, TRUE);
678 if(ret==WAIT_FAILED) {
679 #ifdef THREAD_WAIT_DEBUG
680 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
681 GetCurrentThreadId ());
684 } else if(ret==WAIT_TIMEOUT) {
685 #ifdef THREAD_WAIT_DEBUG
686 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
687 GetCurrentThreadId ());
695 /* FIXME: exitContext isnt documented */
696 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
702 MonoObject *waitHandle;
707 numhandles = mono_array_length(mono_handles);
708 handles = g_new0(HANDLE, numhandles);
710 if (wait_handle_os_handle_field == 0) {
711 /* Get the field os_handle which will contain the actual handle */
712 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");
713 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
716 for(i = 0; i < numhandles; i++) {
717 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
718 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
725 ret=WaitForMultipleObjectsEx(numhandles, handles, FALSE, ms, TRUE);
729 #ifdef THREAD_WAIT_DEBUG
730 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) returning %d",
731 GetCurrentThreadId (), ret);
735 * These need to be here. See MSDN dos on WaitForMultipleObjects.
737 if (ret >= WAIT_OBJECT_0 && ret <= WAIT_OBJECT_0 + numhandles - 1) {
738 return ret - WAIT_OBJECT_0;
740 else if (ret >= WAIT_ABANDONED_0 && ret <= WAIT_ABANDONED_0 + numhandles - 1) {
741 return ret - WAIT_ABANDONED_0;
748 /* FIXME: exitContext isnt documented */
749 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
755 #ifdef THREAD_WAIT_DEBUG
756 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) waiting for %p, %d ms",
757 GetCurrentThreadId (), handle, ms);
764 ret=WaitForSingleObjectEx (handle, ms, TRUE);
766 if(ret==WAIT_FAILED) {
767 #ifdef THREAD_WAIT_DEBUG
768 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
769 GetCurrentThreadId ());
772 } else if(ret==WAIT_TIMEOUT) {
773 #ifdef THREAD_WAIT_DEBUG
774 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
775 GetCurrentThreadId ());
783 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name)
787 return(CreateMutex (NULL, owned,
788 name==NULL?NULL:mono_string_chars (name)));
791 void ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) {
794 ReleaseMutex(handle);
797 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual, MonoBoolean initial, MonoString *name) {
800 return(CreateEvent (NULL, manual, initial,
801 name==NULL?NULL:mono_string_chars (name)));
804 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
807 return (SetEvent(handle));
810 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
813 return (ResetEvent(handle));
817 ves_icall_System_Threading_Events_CloseEvent_internal (HANDLE handle) {
820 CloseHandle (handle);
823 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
827 return InterlockedIncrement (location);
830 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
837 EnterCriticalSection(&interlocked_mutex);
839 lowret = InterlockedIncrement((gint32 *) location);
841 highret = InterlockedIncrement((gint32 *) location + 1);
843 highret = *((gint32 *) location + 1);
845 LeaveCriticalSection(&interlocked_mutex);
847 return (gint64) highret << 32 | (gint64) lowret;
850 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
854 return InterlockedDecrement(location);
857 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
864 EnterCriticalSection(&interlocked_mutex);
866 lowret = InterlockedDecrement((gint32 *) location);
868 highret = InterlockedDecrement((gint32 *) location + 1);
870 highret = *((gint32 *) location + 1);
872 LeaveCriticalSection(&interlocked_mutex);
874 return (gint64) highret << 32 | (gint64) lowret;
877 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location1, gint32 value)
881 return InterlockedExchange(location1, value);
884 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location1, MonoObject *value)
888 return (MonoObject *) InterlockedExchangePointer((gpointer *) location1, value);
891 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location1, gfloat value)
893 IntFloatUnion val, ret;
898 ret.ival = InterlockedExchange((gint32 *) location1, val.ival);
903 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location1, gint32 value, gint32 comparand)
907 return InterlockedCompareExchange(location1, value, comparand);
910 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location1, MonoObject *value, MonoObject *comparand)
914 return (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location1, value, comparand);
917 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location1, gfloat value, gfloat comparand)
919 IntFloatUnion val, ret, cmp;
924 cmp.fval = comparand;
925 ret.ival = InterlockedCompareExchange((gint32 *) location1, val.ival, cmp.ival);
931 mono_thread_get_abort_signal (void)
941 #endif /* __MINGW32__ */
945 static guint32 interruption_request_apc (gpointer param)
947 MonoException* exc = mono_thread_request_interruption (FALSE);
948 if (exc) mono_raise_exception (exc);
951 #endif /* __MINGW32__ */
954 * signal_thread_state_change
956 * Tells the thread that his state has changed and it has to enter the new
957 * state as soon as possible.
959 static void signal_thread_state_change (MonoThread *thread)
962 QueueUserAPC (interruption_request_apc, thread->handle, NULL);
964 /* fixme: store the state somewhere */
965 #ifdef PTHREAD_POINTER_ID
966 pthread_kill (GUINT_TO_POINTER(thread->tid), mono_thread_get_abort_signal ());
968 pthread_kill (thread->tid, mono_thread_get_abort_signal ());
970 #endif /* __MINGW32__ */
974 ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state)
978 if (!mono_monitor_try_enter ((MonoObject *)thread, INFINITE))
981 if ((thread->state & ThreadState_AbortRequested) != 0 ||
982 (thread->state & ThreadState_StopRequested) != 0)
984 mono_monitor_exit ((MonoObject *)thread);
988 /* Make sure the thread is awake */
989 ves_icall_System_Threading_Thread_Resume (thread);
991 thread->state |= ThreadState_AbortRequested;
992 thread->abort_state = state;
993 thread->abort_exc = NULL;
995 mono_monitor_exit ((MonoObject *)thread);
998 g_message (G_GNUC_PRETTY_FUNCTION
999 ": (%d) Abort requested for %p (%d)", GetCurrentThreadId (),
1000 thread, thread->tid);
1003 signal_thread_state_change (thread);
1007 ves_icall_System_Threading_Thread_ResetAbort (void)
1009 MonoThread *thread = mono_thread_current ();
1011 MONO_ARCH_SAVE_REGS;
1013 if (!mono_monitor_try_enter ((MonoObject *)thread, INFINITE))
1016 thread->state &= ~ThreadState_AbortRequested;
1018 if (!thread->abort_exc) {
1019 const char *msg = "Unable to reset abort because no abort was requested";
1020 mono_monitor_exit ((MonoObject *)thread);
1021 mono_raise_exception (mono_get_exception_thread_state (msg));
1023 thread->abort_exc = NULL;
1024 thread->abort_state = NULL;
1027 mono_monitor_exit ((MonoObject *)thread);
1031 ves_icall_System_Threading_Thread_Suspend (MonoThread *thread)
1033 MONO_ARCH_SAVE_REGS;
1035 if (!mono_monitor_try_enter ((MonoObject *)thread, INFINITE))
1038 if ((thread->state & ThreadState_Suspended) != 0 ||
1039 (thread->state & ThreadState_SuspendRequested) != 0 ||
1040 (thread->state & ThreadState_StopRequested) != 0)
1042 mono_monitor_exit ((MonoObject *)thread);
1046 thread->state |= ThreadState_SuspendRequested;
1047 mono_monitor_exit ((MonoObject *)thread);
1049 signal_thread_state_change (thread);
1053 ves_icall_System_Threading_Thread_Resume (MonoThread *thread)
1055 MONO_ARCH_SAVE_REGS;
1057 if (!mono_monitor_try_enter ((MonoObject *)thread, INFINITE))
1060 if ((thread->state & ThreadState_SuspendRequested) != 0) {
1061 thread->state &= ~ThreadState_SuspendRequested;
1062 mono_monitor_exit ((MonoObject *)thread);
1066 if ((thread->state & ThreadState_Suspended) == 0)
1068 mono_monitor_exit ((MonoObject *)thread);
1072 while (ResumeThread (thread->handle) == 0) {
1073 /* A rare case: we may try to resume the thread when it has not still
1074 called SuspendThread. This my happen since SuspendThread can't be
1075 called from inside the thread lock. In this case, keep trying until
1076 the thread is actually resumed */
1081 mono_monitor_exit ((MonoObject *)thread);
1084 void mono_thread_stop (MonoThread *thread)
1086 if (!mono_monitor_try_enter ((MonoObject *)thread, INFINITE))
1089 if ((thread->state & ThreadState_StopRequested) != 0 ||
1090 (thread->state & ThreadState_Stopped) != 0)
1092 mono_monitor_exit ((MonoObject *)thread);
1096 /* Make sure the thread is awake */
1097 ves_icall_System_Threading_Thread_Resume (thread);
1099 thread->state |= ThreadState_StopRequested;
1100 thread->state &= ~ThreadState_AbortRequested;
1102 mono_monitor_exit ((MonoObject *)thread);
1104 signal_thread_state_change (thread);
1108 ves_icall_System_Threading_Thread_VolatileRead1 (void *ptr)
1110 return *((volatile gint8 *) (ptr));
1114 ves_icall_System_Threading_Thread_VolatileRead2 (void *ptr)
1116 return *((volatile gint16 *) (ptr));
1120 ves_icall_System_Threading_Thread_VolatileRead4 (void *ptr)
1122 return *((volatile gint32 *) (ptr));
1126 ves_icall_System_Threading_Thread_VolatileRead8 (void *ptr)
1128 return *((volatile gint64 *) (ptr));
1132 ves_icall_System_Threading_Thread_VolatileReadIntPtr (void *ptr)
1134 return (void *) *((volatile void **) ptr);
1138 ves_icall_System_Threading_Thread_VolatileWrite1 (void *ptr, gint8 value)
1140 *((volatile gint8 *) ptr) = value;
1144 ves_icall_System_Threading_Thread_VolatileWrite2 (void *ptr, gint16 value)
1146 *((volatile gint16 *) ptr) = value;
1150 ves_icall_System_Threading_Thread_VolatileWrite4 (void *ptr, gint32 value)
1152 *((volatile gint32 *) ptr) = value;
1156 ves_icall_System_Threading_Thread_VolatileWrite8 (void *ptr, gint64 value)
1158 *((volatile gint64 *) ptr) = value;
1162 ves_icall_System_Threading_Thread_VolatileWriteIntPtr (void *ptr, void *value)
1164 *((volatile void **) ptr) = value;
1167 void mono_thread_init (MonoThreadStartCB start_cb,
1168 MonoThreadAttachCB attach_cb)
1170 InitializeCriticalSection(&threads_mutex);
1171 InitializeCriticalSection(&interlocked_mutex);
1172 InitializeCriticalSection(&contexts_mutex);
1173 InitializeCriticalSection(&interruption_mutex);
1175 mono_init_static_data_info (&thread_static_info);
1176 mono_init_static_data_info (&context_static_info);
1178 current_object_key=TlsAlloc();
1180 g_message (G_GNUC_PRETTY_FUNCTION ": Allocated current_object_key %d",
1181 current_object_key);
1184 mono_thread_start_cb = start_cb;
1185 mono_thread_attach_cb = attach_cb;
1187 slothash_key=TlsAlloc();
1189 /* Get a pseudo handle to the current process. This is just a
1190 * kludge so that wapi can build a process handle if needed.
1191 * As a pseudo handle is returned, we don't need to clean
1194 GetCurrentProcess ();
1198 mono_threads_install_cleanup (MonoThreadCleanupFunc func)
1200 mono_thread_cleanup = func;
1203 void mono_install_thread_callbacks (MonoThreadCallbacks *callbacks)
1205 mono_thread_callbacks = callbacks;
1209 static void print_tids (gpointer key, gpointer value, gpointer user)
1211 g_message ("Waiting for: %d", GPOINTER_TO_UINT(key));
1217 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
1218 MonoThread *threads[MAXIMUM_WAIT_OBJECTS];
1222 static void wait_for_tids (struct wait_data *wait, guint32 timeout)
1227 g_message(G_GNUC_PRETTY_FUNCTION
1228 ": %d threads to wait for in this batch", wait->num);
1231 ret=WaitForMultipleObjectsEx(wait->num, wait->handles, TRUE, timeout, FALSE);
1233 if(ret==WAIT_FAILED) {
1234 /* See the comment in build_wait_tids() */
1236 g_message (G_GNUC_PRETTY_FUNCTION ": Wait failed");
1242 for(i=0; i<wait->num; i++) {
1243 guint32 tid=wait->threads[i]->tid;
1245 if(mono_g_hash_table_lookup (threads, GUINT_TO_POINTER(tid))!=NULL) {
1246 /* This thread must have been killed, because
1247 * it hasn't cleaned itself up. (It's just
1248 * possible that the thread exited before the
1249 * parent thread had a chance to store the
1250 * handle, and now there is another pointer to
1251 * the already-exited thread stored. In this
1252 * case, we'll just get two
1253 * mono_profiler_thread_end() calls for the
1258 g_message (G_GNUC_PRETTY_FUNCTION
1259 ": cleaning up after thread %d", tid);
1261 thread_cleanup (wait->threads[i]);
1266 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
1268 struct wait_data *wait=(struct wait_data *)user;
1270 if(wait->num<MAXIMUM_WAIT_OBJECTS) {
1271 MonoThread *thread=(MonoThread *)value;
1273 /* Ignore background threads, we abort them later */
1274 if (thread->state & ThreadState_Background)
1275 return; /* just leave, ignore */
1277 if (mono_gc_is_finalizer_thread (thread))
1280 if (thread == mono_thread_current ())
1283 wait->handles[wait->num]=thread->handle;
1284 wait->threads[wait->num]=thread;
1287 /* Just ignore the rest, we can't do anything with
1294 remove_and_abort_threads (gpointer key, gpointer value, gpointer user)
1296 struct wait_data *wait=(struct wait_data *)user;
1297 guint32 self = GetCurrentThreadId ();
1298 MonoThread *thread = (MonoThread *) value;
1300 /* The finalizer thread is not a background thread */
1301 if (thread->tid != self && thread->state & ThreadState_Background) {
1303 wait->handles[wait->num]=thread->handle;
1304 wait->threads[wait->num]=thread;
1307 if(thread->state & ThreadState_AbortRequested ||
1308 thread->state & ThreadState_Aborted) {
1310 g_message (G_GNUC_PRETTY_FUNCTION ": Thread id %d already aborting", thread->tid);
1316 g_print (G_GNUC_PRETTY_FUNCTION ": Aborting id: %d\n", thread->tid);
1318 mono_thread_stop (thread);
1322 return (thread->tid != self && !mono_gc_is_finalizer_thread (thread));
1325 void mono_thread_manage (void)
1327 struct wait_data *wait=g_new0 (struct wait_data, 1);
1329 /* join each thread that's still running */
1331 g_message(G_GNUC_PRETTY_FUNCTION ": Joining each running thread...");
1334 EnterCriticalSection (&threads_mutex);
1337 g_message(G_GNUC_PRETTY_FUNCTION ": No threads");
1339 LeaveCriticalSection (&threads_mutex);
1342 LeaveCriticalSection (&threads_mutex);
1345 EnterCriticalSection (&threads_mutex);
1347 g_message(G_GNUC_PRETTY_FUNCTION
1348 ":There are %d threads to join",
1349 mono_g_hash_table_size (threads));
1350 mono_g_hash_table_foreach (threads, print_tids, NULL);
1354 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
1355 LeaveCriticalSection (&threads_mutex);
1357 /* Something to wait for */
1358 wait_for_tids (wait, INFINITE);
1360 } while(wait->num>0);
1362 mono_thread_pool_cleanup ();
1364 EnterCriticalSection(&threads_mutex);
1367 * Remove everything but the finalizer thread and self.
1368 * Also abort all the background threads
1371 mono_g_hash_table_foreach_remove (threads, remove_and_abort_threads, wait);
1373 LeaveCriticalSection(&threads_mutex);
1376 /* Something to wait for */
1377 wait_for_tids (wait, INFINITE);
1383 static void terminate_thread (gpointer key, gpointer value, gpointer user)
1385 MonoThread *thread=(MonoThread *)value;
1386 guint32 self=GPOINTER_TO_UINT (user);
1388 if(thread->tid!=self) {
1389 /*TerminateThread (thread->handle, -1);*/
1393 void mono_thread_abort_all_other_threads (void)
1395 guint32 self=GetCurrentThreadId ();
1397 EnterCriticalSection (&threads_mutex);
1399 g_message(G_GNUC_PRETTY_FUNCTION ":There are %d threads to abort",
1400 mono_g_hash_table_size (threads));
1401 mono_g_hash_table_foreach (threads, print_tids, NULL);
1404 mono_g_hash_table_foreach (threads, terminate_thread,
1405 GUINT_TO_POINTER (self));
1407 LeaveCriticalSection (&threads_mutex);
1411 * mono_thread_push_appdomain_ref:
1413 * Register that the current thread may have references to objects in domain
1414 * @domain on its stack. Each call to this function should be paired with a
1415 * call to pop_appdomain_ref.
1418 mono_thread_push_appdomain_ref (MonoDomain *domain)
1420 MonoThread *thread = mono_thread_current ();
1423 //printf ("PUSH REF: %p -> %s.\n", thread, domain->friendly_name);
1424 EnterCriticalSection (&threads_mutex);
1425 thread->appdomain_refs = g_slist_prepend (thread->appdomain_refs, domain);
1426 LeaveCriticalSection (&threads_mutex);
1431 mono_thread_pop_appdomain_ref (void)
1433 MonoThread *thread = mono_thread_current ();
1436 //printf ("POP REF: %p -> %s.\n", thread, ((MonoDomain*)(thread->appdomain_refs->data))->friendly_name);
1437 EnterCriticalSection (&threads_mutex);
1438 // FIXME: How can the list be empty ?
1439 if (thread->appdomain_refs)
1440 thread->appdomain_refs = g_slist_remove (thread->appdomain_refs, thread->appdomain_refs->data);
1441 LeaveCriticalSection (&threads_mutex);
1446 mono_thread_has_appdomain_ref (MonoThread *thread, MonoDomain *domain)
1449 EnterCriticalSection (&threads_mutex);
1450 res = g_slist_find (thread->appdomain_refs, domain) != NULL;
1451 LeaveCriticalSection (&threads_mutex);
1455 typedef struct abort_appdomain_data {
1456 struct wait_data wait;
1458 } abort_appdomain_data;
1461 abort_appdomain_thread (gpointer key, gpointer value, gpointer user_data)
1463 MonoThread *thread = (MonoThread*)value;
1464 abort_appdomain_data *data = (abort_appdomain_data*)user_data;
1465 MonoDomain *domain = data->domain;
1467 if (mono_thread_has_appdomain_ref (thread, domain)) {
1468 /* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread, domain->friendly_name); */
1469 ves_icall_System_Threading_Thread_Abort (thread, NULL);
1471 if(data->wait.num<MAXIMUM_WAIT_OBJECTS) {
1472 data->wait.handles [data->wait.num] = thread->handle;
1473 data->wait.threads [data->wait.num] = thread;
1476 /* Just ignore the rest, we can't do anything with
1484 * mono_threads_abort_appdomain_threads:
1486 * Abort threads which has references to the given appdomain.
1489 mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
1491 abort_appdomain_data user_data;
1494 /* printf ("ABORT BEGIN.\n"); */
1496 start_time = GetTickCount ();
1498 EnterCriticalSection (&threads_mutex);
1500 user_data.domain = domain;
1501 user_data.wait.num = 0;
1502 mono_g_hash_table_foreach (threads, abort_appdomain_thread, &user_data);
1503 LeaveCriticalSection (&threads_mutex);
1505 if (user_data.wait.num > 0)
1506 wait_for_tids (&user_data.wait, timeout);
1508 /* Update remaining time */
1509 timeout -= GetTickCount () - start_time;
1510 start_time = GetTickCount ();
1515 while (user_data.wait.num > 0);
1517 /* printf ("ABORT DONE.\n"); */
1523 * mono_thread_get_pending_exception:
1525 * Return an exception which needs to be raised when leaving a catch clause.
1526 * This is used for undeniable exception propagation.
1529 mono_thread_get_pending_exception (void)
1531 MonoThread *thread = mono_thread_current ();
1533 MONO_ARCH_SAVE_REGS;
1535 if (thread && thread->abort_exc) {
1537 * FIXME: Clear the abort exception and return an AppDomainUnloaded
1538 * exception if the thread no longer references a dying appdomain.
1540 thread->abort_exc->trace_ips = NULL;
1541 thread->abort_exc->stack_trace = NULL;
1542 return thread->abort_exc;
1548 #define NUM_STATIC_DATA_IDX 8
1549 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
1550 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
1555 * mono_alloc_static_data
1557 * Allocate memory blocks for storing threads or context static data
1560 mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset)
1562 guint idx = (offset >> 24) - 1;
1565 gpointer* static_data = *static_data_ptr;
1568 static_data = GC_MALLOC (static_data_size [0]);
1570 static_data = g_malloc0 (static_data_size [0]);
1572 *static_data_ptr = static_data;
1573 static_data [0] = static_data;
1576 for (i = 1; i < idx; ++i) {
1577 if (static_data [i])
1580 static_data [i] = GC_MALLOC (static_data_size [i]);
1582 static_data [i] = g_malloc0 (static_data_size [i]);
1588 * mono_init_static_data_info
1590 * Initializes static data counters
1592 static void mono_init_static_data_info (StaticDataInfo *static_data)
1594 static_data->idx = 0;
1595 static_data->offset = 0;
1599 * mono_alloc_static_data_slot
1601 * Generates an offset for static data. static_data contains the counters
1602 * used to generate it.
1605 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align)
1609 if (!static_data->idx && !static_data->offset) {
1611 * we use the first chunk of the first allocation also as
1612 * an array for the rest of the data
1614 static_data->offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX;
1616 static_data->offset += align - 1;
1617 static_data->offset &= ~(align - 1);
1618 if (static_data->offset + size >= static_data_size [static_data->idx]) {
1619 static_data->idx ++;
1620 g_assert (size <= static_data_size [static_data->idx]);
1622 * massive unloading and reloading of domains with thread-static
1623 * data may eventually exceed the allocated storage...
1624 * Need to check what the MS runtime does in that case.
1625 * Note that for each appdomain, we need to allocate a separate
1626 * thread data slot for security reasons. We could keep track
1627 * of the slots per-domain and when the domain is unloaded
1628 * out the slots on a sort of free list.
1630 g_assert (static_data->idx < NUM_STATIC_DATA_IDX);
1631 static_data->offset = 0;
1633 offset = static_data->offset | ((static_data->idx + 1) << 24);
1634 static_data->offset += size;
1639 * ensure thread static fields already allocated are valid for thread
1640 * This function is called when a thread is created or on thread attach.
1643 thread_adjust_static_data (MonoThread *thread)
1647 EnterCriticalSection (&threads_mutex);
1648 if (thread_static_info.offset || thread_static_info.idx > 0) {
1649 /* get the current allocated size */
1650 offset = thread_static_info.offset | ((thread_static_info.idx + 1) << 24);
1651 mono_alloc_static_data (&(thread->static_data), offset);
1653 LeaveCriticalSection (&threads_mutex);
1657 alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
1659 MonoThread *thread = value;
1660 guint32 offset = GPOINTER_TO_UINT (user);
1662 mono_alloc_static_data (&(thread->static_data), offset);
1666 * The offset for a special static variable is composed of three parts:
1667 * a bit that indicates the type of static data (0:thread, 1:context),
1668 * an index in the array of chunks of memory for the thread (thread->static_data)
1669 * and an offset in that chunk of mem. This allows allocating less memory in the
1674 mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align)
1677 if (static_type == SPECIAL_STATIC_THREAD)
1679 EnterCriticalSection (&threads_mutex);
1680 offset = mono_alloc_static_data_slot (&thread_static_info, size, align);
1681 /* This can be called during startup */
1682 if (threads != NULL)
1683 mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
1684 LeaveCriticalSection (&threads_mutex);
1688 g_assert (static_type == SPECIAL_STATIC_CONTEXT);
1689 EnterCriticalSection (&contexts_mutex);
1690 offset = mono_alloc_static_data_slot (&context_static_info, size, align);
1691 LeaveCriticalSection (&contexts_mutex);
1692 offset |= 0x80000000; // Set the high bit to indicate context static data
1698 mono_get_special_static_data (guint32 offset)
1700 // The high bit means either thread (0) or static (1) data.
1702 guint32 static_type = (offset & 0x80000000);
1705 offset &= 0x7fffffff;
1706 idx = (offset >> 24) - 1;
1708 if (static_type == 0)
1710 MonoThread *thread = mono_thread_current ();
1711 return ((char*) thread->static_data [idx]) + (offset & 0xffffff);
1715 // Allocate static data block under demand, since we don't have a list
1717 MonoAppContext *context = mono_context_get ();
1718 if (!context->static_data || !context->static_data [idx]) {
1719 EnterCriticalSection (&contexts_mutex);
1720 mono_alloc_static_data (&(context->static_data), offset);
1721 LeaveCriticalSection (&contexts_mutex);
1723 return ((char*) context->static_data [idx]) + (offset & 0xffffff);
1727 static void gc_stop_world (gpointer key, gpointer value, gpointer user)
1729 MonoThread *thread=(MonoThread *)value;
1730 guint32 self=GPOINTER_TO_UINT (user);
1733 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d", self, thread->tid);
1736 if(thread->tid==self)
1739 SuspendThread (thread->handle);
1742 void mono_gc_stop_world (void)
1744 guint32 self=GetCurrentThreadId ();
1747 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
1750 EnterCriticalSection (&threads_mutex);
1752 if (threads != NULL)
1753 mono_g_hash_table_foreach (threads, gc_stop_world, GUINT_TO_POINTER (self));
1755 LeaveCriticalSection (&threads_mutex);
1758 static void gc_start_world (gpointer key, gpointer value, gpointer user)
1760 MonoThread *thread=(MonoThread *)value;
1761 guint32 self=GPOINTER_TO_UINT (user);
1764 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d", self, thread->tid);
1767 if(thread->tid==self)
1770 ResumeThread (thread->handle);
1773 void mono_gc_start_world (void)
1775 guint32 self=GetCurrentThreadId ();
1778 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
1781 EnterCriticalSection (&threads_mutex);
1783 if (threads != NULL)
1784 mono_g_hash_table_foreach (threads, gc_start_world, GUINT_TO_POINTER (self));
1786 LeaveCriticalSection (&threads_mutex);
1790 static guint32 dummy_apc (gpointer param)
1796 * mono_thread_execute_interruption
1798 * Performs the operation that the requested thread state requires (abort,
1801 static MonoException* mono_thread_execute_interruption (MonoThread *thread)
1803 while (!mono_monitor_try_enter ((MonoObject *)thread, INFINITE))
1804 ; /* we really need to get in */
1806 if (thread->interruption_requested) {
1807 /* this will consume pending APC calls */
1808 WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
1809 EnterCriticalSection (&interruption_mutex);
1810 thread_interruption_requested--;
1811 LeaveCriticalSection (&interruption_mutex);
1812 thread->interruption_requested = FALSE;
1815 if ((thread->state & ThreadState_AbortRequested) != 0) {
1816 thread->abort_exc = mono_get_exception_thread_abort ();
1817 mono_monitor_exit ((MonoObject *)thread);
1818 return thread->abort_exc;
1820 else if ((thread->state & ThreadState_SuspendRequested) != 0) {
1821 thread->state &= ~ThreadState_SuspendRequested;
1822 thread->state |= ThreadState_Suspended;
1823 mono_monitor_exit ((MonoObject *)thread);
1825 SuspendThread (thread->handle);
1827 mono_monitor_try_enter ((MonoObject *)thread, INFINITE);
1828 thread->state &= ~ThreadState_Suspended;
1829 mono_monitor_exit ((MonoObject *)thread);
1832 else if ((thread->state & ThreadState_StopRequested) != 0) {
1833 /* FIXME: do this through the JIT? */
1834 mono_monitor_exit ((MonoObject *)thread);
1839 mono_monitor_exit ((MonoObject *)thread);
1844 * mono_thread_request_interruption
1846 * A signal handler can call this method to request the interruption of a
1847 * thread. The result of the interruption will depend on the current state of
1848 * the thread. If the result is an exception that needs to be throw, it is
1849 * provided as return value.
1851 MonoException* mono_thread_request_interruption (gboolean running_managed)
1853 MonoThread *thread = mono_thread_current ();
1855 /* The thread may already be stopping */
1859 if (!mono_monitor_try_enter ((MonoObject *)thread, INFINITE))
1862 if (thread->interruption_requested) {
1863 mono_monitor_exit ((MonoObject *)thread);
1867 if (!running_managed) {
1868 /* Can't stop while in unmanaged code. Increase the global interruption
1869 request count. When exiting the unmanaged method the count will be
1870 checked and the thread will be interrupted. */
1872 EnterCriticalSection (&interruption_mutex);
1873 thread_interruption_requested++;
1874 LeaveCriticalSection (&interruption_mutex);
1876 thread->interruption_requested = TRUE;
1877 mono_monitor_exit ((MonoObject *)thread);
1879 /* this will awake the thread if it is in WaitForSingleObject
1881 QueueUserAPC (dummy_apc, thread->handle, NULL);
1886 mono_monitor_exit ((MonoObject *)thread);
1887 return mono_thread_execute_interruption (thread);
1891 gboolean mono_thread_interruption_requested ()
1893 if (thread_interruption_requested) {
1894 MonoThread *thread = mono_thread_current ();
1895 /* The thread may already be stopping */
1897 return (thread->interruption_requested);
1903 * Performs the interruption of the current thread, if one has been requested.
1905 void mono_thread_interruption_checkpoint ()
1907 MonoThread *thread = mono_thread_current ();
1909 /* The thread may already be stopping */
1913 if (thread->interruption_requested) {
1914 MonoException* exc = mono_thread_execute_interruption (thread);
1915 if (exc) mono_raise_exception (exc);
1920 * Returns the address of a flag that will be non-zero if an interruption has
1921 * been requested for a thread. The thread to interrupt may not be the current
1922 * thread, so an additional call to mono_thread_interruption_requested() or
1923 * mono_thread_interruption_checkpoint() is allways needed if the flag is not
1926 gint32* mono_thread_interruption_request_flag ()
1928 return &thread_interruption_requested;
1931 #ifdef WITH_INCLUDED_LIBGC
1933 static void gc_push_all_stacks (gpointer key, gpointer value, gpointer user)
1935 MonoThread *thread=(MonoThread *)value;
1936 guint32 *selfp=(guint32 *)user, self = *selfp;
1939 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d - %p", self, thread->tid, thread->stack_ptr);
1942 if(thread->tid==self) {
1944 g_message (G_GNUC_PRETTY_FUNCTION ": %p - %p", selfp, thread->stack_ptr);
1946 GC_push_all_stack (selfp, thread->stack_ptr);
1950 #ifdef PLATFORM_WIN32
1951 GC_win32_push_thread_stack (thread->handle, thread->stack_ptr);
1953 mono_wapi_push_thread_stack (thread->handle, thread->stack_ptr);
1957 void mono_gc_push_all_stacks (void)
1959 guint32 self=GetCurrentThreadId ();
1962 g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
1965 EnterCriticalSection (&threads_mutex);
1967 if (threads != NULL)
1968 mono_g_hash_table_foreach (threads, gc_push_all_stacks, &self);
1970 LeaveCriticalSection (&threads_mutex);
1973 #endif /* WITH_INCLUDED_LIBGC */