2 * threads.c: Thread support internal calls
5 * Dick Porter (dick@ximian.com)
6 * Patrik Torstensson (patrik.torstensson@labs2.com)
8 * (C) 2001 Ximian, Inc.
15 #include <mono/metadata/object.h>
16 #include <mono/metadata/appdomain.h>
17 #include <mono/metadata/profiler-private.h>
18 #include <mono/metadata/threads.h>
19 #include <mono/metadata/threads-types.h>
20 #include <mono/metadata/exception.h>
21 #include <mono/metadata/environment.h>
22 #include <mono/io-layer/io-layer.h>
24 #include <mono/os/gc_wrapper.h>
27 #undef THREAD_WAIT_DEBUG
31 guint32 (*func)(void *);
43 * The "os_handle" field of the WaitHandle class.
45 static MonoClassField *wait_handle_os_handle_field = NULL;
47 /* Controls access to the 'threads' hash table */
48 static CRITICAL_SECTION threads_mutex;
50 /* The hash of existing threads (key is thread ID) that need joining
53 static MonoGHashTable *threads=NULL;
55 /* The TLS key that holds the MonoObject assigned to each thread */
56 static guint32 current_object_key;
58 /* function called at thread start */
59 static MonoThreadStartCB mono_thread_start_cb = NULL;
61 /* function called at thread attach */
62 static MonoThreadAttachCB mono_thread_attach_cb = NULL;
64 /* function called when a new thread has been created */
65 static MonoThreadCallbacks *mono_thread_callbacks = NULL;
67 /* The TLS key that holds the LocalDataStoreSlot hash in each thread */
68 static guint32 slothash_key;
70 /* Spin lock for InterlockedXXX 64 bit functions */
71 static CRITICAL_SECTION interlocked_mutex;
73 /* handle_store() and handle_remove() manage the array of threads that
74 * still need to be waited for when the main thread exits.
76 static void handle_store(MonoThread *thread)
78 EnterCriticalSection(&threads_mutex);
81 g_message(G_GNUC_PRETTY_FUNCTION ": thread %p ID %d", thread,
86 threads=mono_g_hash_table_new(g_direct_hash, g_direct_equal);
89 /* We don't need to duplicate thread->handle, because it is
90 * only closed when the thread object is finalized by the GC.
92 mono_g_hash_table_insert(threads, GUINT_TO_POINTER(thread->tid), thread);
93 LeaveCriticalSection(&threads_mutex);
96 static void handle_remove(guint32 tid)
99 g_message(G_GNUC_PRETTY_FUNCTION ": thread ID %d", tid);
102 EnterCriticalSection(&threads_mutex);
104 mono_g_hash_table_remove (threads, GUINT_TO_POINTER(tid));
106 LeaveCriticalSection(&threads_mutex);
108 /* Don't close the handle here, wait for the object finalizer
109 * to do it. Otherwise, the following race condition applies:
111 * 1) Thread exits (and handle_remove() closes the handle)
113 * 2) Some other handle is reassigned the same slot
115 * 3) Another thread tries to join the first thread, and
116 * blocks waiting for the reassigned handle to be signalled
117 * (which might never happen). This is possible, because the
118 * thread calling Join() still has a reference to the first
123 static void thread_cleanup (guint32 tid)
125 mono_profiler_thread_end (tid);
129 static guint32 start_wrapper(void *data)
131 struct StartInfo *start_info=(struct StartInfo *)data;
132 guint32 (*start_func)(void *);
135 MonoThread *thread=start_info->obj;
138 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper",
139 GetCurrentThreadId ());
142 /* We can be sure start_info->obj->tid and
143 * start_info->obj->handle have been set, because the thread
144 * was created suspended, and these values were set before the
150 mono_domain_set (start_info->domain);
152 start_func = start_info->func;
153 this = start_info->this;
155 /* This MUST be called before any managed code can be
156 * executed, as it calls the callback function that (for the
157 * jit) sets the lmf marker.
159 mono_thread_new_init (tid, &tid, start_func);
162 g_message (G_GNUC_PRETTY_FUNCTION
163 ": (%d) Setting current_object_key to %p",
164 GetCurrentThreadId (), thread);
167 TlsSetValue (current_object_key, thread);
169 mono_profiler_thread_start (tid);
171 if(thread->start_notify!=NULL) {
172 /* Let the thread that called Start() know we're
175 ReleaseSemaphore (thread->start_notify, 1, NULL);
182 /* If the thread calls ExitThread at all, this remaining code
183 * will not be executed, but the main thread will eventually
184 * call thread_cleanup() on this thread's behalf.
188 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper terminating",
189 GetCurrentThreadId ());
192 thread_cleanup (tid);
197 void mono_thread_new_init (guint32 tid, gpointer stack_start, gpointer func)
199 if (mono_thread_start_cb) {
200 mono_thread_start_cb (tid, stack_start, func);
204 void mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
207 HANDLE thread_handle;
208 struct StartInfo *start_info;
211 thread=(MonoThread *)mono_object_new (domain,
212 mono_defaults.thread_class);
214 start_info=g_new0 (struct StartInfo, 1);
215 start_info->func = func;
216 start_info->obj = thread;
217 start_info->domain = domain;
218 start_info->this = arg;
220 /* Create suspended, so we can do some housekeeping before the thread
223 thread_handle = CreateThread(NULL, 0, start_wrapper, start_info,
224 CREATE_SUSPENDED, &tid);
226 g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d (handle %p)",
229 g_assert (thread_handle);
231 thread->handle=thread_handle;
234 handle_store(thread);
236 ResumeThread (thread_handle);
240 mono_thread_attach (MonoDomain *domain)
243 HANDLE thread_handle;
246 if ((thread = mono_thread_current ())) {
247 g_warning ("mono_thread_attach called for an already attached thread");
248 if (mono_thread_attach_cb) {
249 mono_thread_attach_cb (tid, &tid);
254 thread = (MonoThread *)mono_object_new (domain,
255 mono_defaults.thread_class);
257 thread_handle = GetCurrentThread ();
258 g_assert (thread_handle);
260 tid=GetCurrentThreadId ();
262 thread->handle=thread_handle;
266 g_message(G_GNUC_PRETTY_FUNCTION ": Attached thread ID %d (handle %p)",
270 handle_store(thread);
273 g_message (G_GNUC_PRETTY_FUNCTION
274 ": (%d) Setting current_object_key to %p",
275 GetCurrentThreadId (), thread);
278 TlsSetValue (current_object_key, thread);
279 mono_domain_set (domain);
281 if (mono_thread_attach_cb) {
282 mono_thread_attach_cb (tid, &tid);
288 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this,
291 MonoMulticastDelegate *delegate = (MonoMulticastDelegate*)start;
292 guint32 (*start_func)(void *);
293 struct StartInfo *start_info;
301 g_message(G_GNUC_PRETTY_FUNCTION
302 ": Trying to start a new thread: this (%p) start (%p)",
306 im = mono_get_delegate_invoke (start->vtable->klass);
307 if (mono_thread_callbacks)
308 start_func = (* mono_thread_callbacks->thread_start_compile_func) (im);
310 start_func = mono_compile_method (im);
312 if(start_func==NULL) {
313 g_warning(G_GNUC_PRETTY_FUNCTION
314 ": Can't locate start method!");
317 /* This is freed in start_wrapper */
318 start_info = g_new0 (struct StartInfo, 1);
319 start_info->func = start_func;
320 start_info->this = delegate;
321 start_info->obj = this;
322 start_info->domain = mono_domain_get ();
324 this->start_notify=CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
325 if(this->start_notify==NULL) {
326 g_warning (G_GNUC_PRETTY_FUNCTION ": CreateSemaphore error 0x%x", GetLastError ());
330 thread=CreateThread(NULL, 0, start_wrapper, start_info,
331 CREATE_SUSPENDED, &tid);
333 g_warning(G_GNUC_PRETTY_FUNCTION
334 ": CreateThread error 0x%x", GetLastError());
341 /* Don't call handle_store() here, delay it to Start.
342 * We can't join a thread (trying to will just block
343 * forever) until it actually starts running, so don't
344 * store the handle till then.
348 g_message(G_GNUC_PRETTY_FUNCTION
349 ": Started thread ID %d (handle %p)", tid, thread);
356 void ves_icall_System_Threading_Thread_Thread_free_internal (MonoThread *this,
362 g_message (G_GNUC_PRETTY_FUNCTION ": Closing thread %p, handle %p",
366 CloseHandle (thread);
369 void ves_icall_System_Threading_Thread_Start_internal(MonoThread *this,
375 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Launching thread %p (%d)",
376 GetCurrentThreadId (), this, this->tid);
379 /* Only store the handle when the thread is about to be
380 * launched, to avoid the main thread deadlocking while trying
381 * to clean up a thread that will never be signalled.
385 if (mono_thread_callbacks)
386 (* mono_thread_callbacks->start_resume) (this->tid);
388 ResumeThread(thread);
390 if (mono_thread_callbacks)
391 (* mono_thread_callbacks->end_resume) (this->tid);
393 if(this->start_notify!=NULL) {
394 /* Wait for the thread to set up its TLS data etc, so
395 * theres no potential race condition if someone tries
396 * to look up the data believing the thread has
401 g_message(G_GNUC_PRETTY_FUNCTION
402 ": (%d) waiting for thread %p (%d) to start",
403 GetCurrentThreadId (), this, this->tid);
406 WaitForSingleObject (this->start_notify, INFINITE);
407 CloseHandle (this->start_notify);
408 this->start_notify=NULL;
412 g_message(G_GNUC_PRETTY_FUNCTION
413 ": (%d) Done launching thread %p (%d)",
414 GetCurrentThreadId (), this, this->tid);
418 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
423 g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
430 ves_icall_System_Threading_Thread_GetDomainID (void)
434 return mono_domain_get()->domain_id;
438 mono_thread_current (void)
444 /* Find the current thread object */
445 thread=TlsGetValue (current_object_key);
448 g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", thread);
454 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this,
455 int ms, HANDLE thread)
465 g_message (G_GNUC_PRETTY_FUNCTION ": joining thread handle %p, %d ms",
469 ret=WaitForSingleObject(thread, ms);
470 if(ret==WAIT_OBJECT_0) {
472 g_message (G_GNUC_PRETTY_FUNCTION ": join successful");
479 g_message (G_GNUC_PRETTY_FUNCTION ": join failed");
485 void ves_icall_System_Threading_Thread_SlotHash_store(MonoObject *data)
490 g_message(G_GNUC_PRETTY_FUNCTION ": Storing key %p", data);
493 /* Object location stored here */
494 TlsSetValue(slothash_key, data);
497 MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void)
503 data=TlsGetValue(slothash_key);
506 g_message(G_GNUC_PRETTY_FUNCTION ": Retrieved key %p", data);
512 /* FIXME: exitContext isnt documented */
513 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
519 MonoObject *waitHandle;
524 numhandles = mono_array_length(mono_handles);
525 handles = g_new0(HANDLE, numhandles);
527 if (wait_handle_os_handle_field == 0) {
528 /* Get the field os_handle which will contain the actual handle */
529 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");
530 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
533 for(i = 0; i < numhandles; i++) {
534 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
535 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
542 ret=WaitForMultipleObjects(numhandles, handles, TRUE, ms);
546 if(ret==WAIT_FAILED) {
547 #ifdef THREAD_WAIT_DEBUG
548 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
549 GetCurrentThreadId ());
552 } else if(ret==WAIT_TIMEOUT) {
553 #ifdef THREAD_WAIT_DEBUG
554 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
555 GetCurrentThreadId ());
563 /* FIXME: exitContext isnt documented */
564 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
570 MonoObject *waitHandle;
575 numhandles = mono_array_length(mono_handles);
576 handles = g_new0(HANDLE, numhandles);
578 if (wait_handle_os_handle_field == 0) {
579 /* Get the field os_handle which will contain the actual handle */
580 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");
581 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
584 for(i = 0; i < numhandles; i++) {
585 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
586 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
593 ret=WaitForMultipleObjects(numhandles, handles, FALSE, ms);
597 #ifdef THREAD_WAIT_DEBUG
598 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) returning %d",
599 GetCurrentThreadId (), ret);
603 * These need to be here. See MSDN dos on WaitForMultipleObjects.
605 if (ret >= WAIT_OBJECT_0 && ret <= WAIT_OBJECT_0 + numhandles - 1) {
606 return ret - WAIT_OBJECT_0;
608 else if (ret >= WAIT_ABANDONED_0 && ret <= WAIT_ABANDONED_0 + numhandles - 1) {
609 return ret - WAIT_ABANDONED_0;
616 /* FIXME: exitContext isnt documented */
617 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
623 #ifdef THREAD_WAIT_DEBUG
624 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) waiting for %p, %d ms",
625 GetCurrentThreadId (), handle, ms);
632 ret=WaitForSingleObject(handle, ms);
633 if(ret==WAIT_FAILED) {
634 #ifdef THREAD_WAIT_DEBUG
635 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
636 GetCurrentThreadId ());
639 } else if(ret==WAIT_TIMEOUT) {
640 #ifdef THREAD_WAIT_DEBUG
641 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
642 GetCurrentThreadId ());
650 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned,char *name) {
653 return(CreateMutex(NULL,owned,name));
656 void ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) {
659 ReleaseMutex(handle);
662 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual,
667 return (CreateEvent(NULL,manual,initial,name));
670 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
673 return (SetEvent(handle));
676 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
679 return (ResetEvent(handle));
682 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
686 return InterlockedIncrement (location);
689 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
696 EnterCriticalSection(&interlocked_mutex);
698 lowret = InterlockedIncrement((gint32 *) location);
700 highret = InterlockedIncrement((gint32 *) location + 1);
702 highret = *((gint32 *) location + 1);
704 LeaveCriticalSection(&interlocked_mutex);
706 return (gint64) highret << 32 | (gint64) lowret;
709 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
713 return InterlockedDecrement(location);
716 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
723 EnterCriticalSection(&interlocked_mutex);
725 lowret = InterlockedDecrement((gint32 *) location);
727 highret = InterlockedDecrement((gint32 *) location + 1);
729 highret = *((gint32 *) location + 1);
731 LeaveCriticalSection(&interlocked_mutex);
733 return (gint64) highret << 32 | (gint64) lowret;
736 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location1, gint32 value)
740 return InterlockedExchange(location1, value);
743 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location1, MonoObject *value)
747 return (MonoObject *) InterlockedExchangePointer((gpointer *) location1, value);
750 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location1, gfloat value)
752 IntFloatUnion val, ret;
757 ret.ival = InterlockedExchange((gint32 *) location1, val.ival);
762 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location1, gint32 value, gint32 comparand)
766 return InterlockedCompareExchange(location1, value, comparand);
769 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location1, MonoObject *value, MonoObject *comparand)
773 return (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location1, value, comparand);
776 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location1, gfloat value, gfloat comparand)
778 IntFloatUnion val, ret, cmp;
783 cmp.fval = comparand;
784 ret.ival = InterlockedCompareExchange((gint32 *) location1, val.ival, cmp.ival);
790 mono_thread_get_abort_signal (void)
800 #endif /* __MINGW32__ */
804 ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state)
808 thread->abort_state = state;
809 thread->abort_exc = mono_get_exception_thread_abort ();
812 g_message (G_GNUC_PRETTY_FUNCTION
813 ": (%d) Abort requested for %p (%d)", GetCurrentThreadId (),
814 thread, thread->tid);
818 g_assert_not_reached ();
820 /* fixme: store the state somewhere */
821 #ifdef PTHREAD_POINTER_ID
822 pthread_kill (GUINT_TO_POINTER(thread->tid), mono_thread_get_abort_signal ());
824 pthread_kill (thread->tid, mono_thread_get_abort_signal ());
826 #endif /* __MINGW32__ */
830 ves_icall_System_Threading_Thread_ResetAbort (void)
832 MonoThread *thread = mono_thread_current ();
836 if (!thread->abort_exc) {
837 const char *msg = "Unable to reset abort because no abort was requested";
838 mono_raise_exception (mono_get_exception_thread_state (msg));
840 thread->abort_exc = NULL;
841 thread->abort_state = NULL;
845 void mono_thread_init (MonoThreadStartCB start_cb,
846 MonoThreadAttachCB attach_cb)
848 InitializeCriticalSection(&threads_mutex);
849 InitializeCriticalSection(&interlocked_mutex);
851 current_object_key=TlsAlloc();
853 g_message (G_GNUC_PRETTY_FUNCTION ": Allocated current_object_key %d",
857 mono_thread_start_cb = start_cb;
858 mono_thread_attach_cb = attach_cb;
860 slothash_key=TlsAlloc();
862 /* Get a pseudo handle to the current process. This is just a
863 * kludge so that wapi can build a process handle if needed.
864 * As a pseudo handle is returned, we don't need to clean
867 GetCurrentProcess ();
870 void mono_install_thread_callbacks (MonoThreadCallbacks *callbacks)
872 mono_thread_callbacks = callbacks;
876 static void print_tids (gpointer key, gpointer value, gpointer user)
878 g_message ("Waiting for: %d", GPOINTER_TO_UINT(key));
884 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
885 MonoThread *threads[MAXIMUM_WAIT_OBJECTS];
889 static void wait_for_tids (struct wait_data *wait)
894 g_message(G_GNUC_PRETTY_FUNCTION
895 ": %d threads to wait for in this batch", wait->num);
898 ret=WaitForMultipleObjects(wait->num, wait->handles, TRUE, INFINITE);
899 if(ret==WAIT_FAILED) {
900 /* See the comment in build_wait_tids() */
902 g_message (G_GNUC_PRETTY_FUNCTION ": Wait failed");
908 for(i=0; i<wait->num; i++) {
909 guint32 tid=wait->threads[i]->tid;
911 if(mono_g_hash_table_lookup (threads, GUINT_TO_POINTER(tid))!=NULL) {
912 /* This thread must have been killed, because
913 * it hasn't cleaned itself up. (It's just
914 * possible that the thread exited before the
915 * parent thread had a chance to store the
916 * handle, and now there is another pointer to
917 * the already-exited thread stored. In this
918 * case, we'll just get two
919 * mono_profiler_thread_end() calls for the
924 g_message (G_GNUC_PRETTY_FUNCTION
925 ": cleaning up after thread %d", tid);
927 thread_cleanup (tid);
932 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
934 struct wait_data *wait=(struct wait_data *)user;
936 if(wait->num<MAXIMUM_WAIT_OBJECTS) {
937 MonoThread *thread=(MonoThread *)value;
939 wait->handles[wait->num]=thread->handle;
940 wait->threads[wait->num]=thread;
943 /* Just ignore the rest, we can't do anything with
949 void mono_thread_manage (void)
951 struct wait_data *wait=g_new0 (struct wait_data, 1);
953 /* join each thread that's still running */
955 g_message(G_GNUC_PRETTY_FUNCTION ": Joining each running thread...");
960 g_message(G_GNUC_PRETTY_FUNCTION ": No threads");
966 EnterCriticalSection (&threads_mutex);
968 g_message(G_GNUC_PRETTY_FUNCTION
969 ":There are %d threads to join",
970 mono_g_hash_table_size (threads));
971 mono_g_hash_table_foreach (threads, print_tids, NULL);
975 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
977 LeaveCriticalSection (&threads_mutex);
979 /* Something to wait for */
980 wait_for_tids (wait);
982 } while(wait->num>0);
986 mono_g_hash_table_destroy(threads);
990 static void terminate_thread (gpointer key, gpointer value, gpointer user)
992 MonoThread *thread=(MonoThread *)value;
993 guint32 self=GPOINTER_TO_UINT (user);
995 if(thread->tid!=self) {
996 /*TerminateThread (thread->handle, -1);*/
1000 void mono_thread_abort_all_other_threads (void)
1002 guint32 self=GetCurrentThreadId ();
1004 EnterCriticalSection (&threads_mutex);
1006 g_message(G_GNUC_PRETTY_FUNCTION ":There are %d threads to abort",
1007 mono_g_hash_table_size (threads));
1008 mono_g_hash_table_foreach (threads, print_tids, NULL);
1011 mono_g_hash_table_foreach (threads, terminate_thread,
1012 GUINT_TO_POINTER (self));
1014 LeaveCriticalSection (&threads_mutex);