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/io-layer/io-layer.h>
23 #include <mono/os/gc_wrapper.h>
26 #undef THREAD_LOCK_DEBUG
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' array */
48 static CRITICAL_SECTION threads_mutex;
50 /* Controls access to the sync field in MonoObjects, to avoid race
51 * conditions when adding sync data to an object for the first time.
53 static CRITICAL_SECTION monitor_mutex;
55 /* The hash of existing threads (key is thread ID) that need joining
58 static MonoGHashTable *threads=NULL;
60 /* The MonoObject associated with the main thread */
61 static MonoThread *main_thread;
63 /* The TLS key that holds the MonoObject assigned to each thread */
64 static guint32 current_object_key;
66 /* function called at thread start */
67 static MonoThreadStartCB mono_thread_start_cb = NULL;
69 /* function called at thread attach */
70 static MonoThreadAttachCB mono_thread_attach_cb = NULL;
72 /* function called when a new thread has been created */
73 static MonoThreadCallbacks *mono_thread_callbacks = NULL;
75 /* The TLS key that holds the LocalDataStoreSlot hash in each thread */
76 static guint32 slothash_key;
78 /* Spin lock for InterlockedXXX 64 bit functions */
79 static CRITICAL_SECTION interlocked_mutex;
81 /* handle_store() and handle_remove() manage the array of threads that
82 * still need to be waited for when the main thread exits.
84 static void handle_store(MonoThread *thread)
86 EnterCriticalSection(&threads_mutex);
89 g_message(G_GNUC_PRETTY_FUNCTION ": thread %p ID %d", thread,
94 threads=mono_g_hash_table_new(g_int_hash, g_int_equal);
97 /* GHashTable will remove a previous entry if a duplicate key
98 * is stored, which is exactly what we want: we store the
99 * thread both in the start_wrapper (in the subthread), and as
100 * soon as possible in the parent thread. This is to minimise
101 * the window in which the thread exists but we haven't
105 /* We don't need to duplicate thread->handle, because it is
106 * only closed when the thread object is finalized by the GC.
108 mono_g_hash_table_insert(threads, &thread->tid, thread);
109 LeaveCriticalSection(&threads_mutex);
112 static void handle_remove(guint32 tid)
115 g_message(G_GNUC_PRETTY_FUNCTION ": thread ID %d", tid);
118 EnterCriticalSection(&threads_mutex);
120 mono_g_hash_table_remove (threads, &tid);
122 LeaveCriticalSection(&threads_mutex);
124 /* Don't close the handle here, wait for the object finalizer
125 * to do it. Otherwise, the following race condition applies:
127 * 1) Thread exits (and handle_remove() closes the handle)
129 * 2) Some other handle is reassigned the same slot
131 * 3) Another thread tries to join the first thread, and
132 * blocks waiting for the reassigned handle to be signalled
133 * (which might never happen). This is possible, because the
134 * thread calling Join() still has a reference to the first
139 static void thread_cleanup (guint32 tid)
141 mono_profiler_thread_end (tid);
145 static guint32 start_wrapper(void *data)
147 struct StartInfo *start_info=(struct StartInfo *)data;
148 guint32 (*start_func)(void *);
153 g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper");
156 start_func = start_info->func;
157 mono_domain_set (start_info->domain);
158 this = start_info->this;
160 tid=GetCurrentThreadId ();
161 /* Set the thread ID here as well as in the parent thread,
162 * because we don't know whether the thread object will
163 * already have its ID set before we get to it. This isn't a
164 * race condition, because if we're not guaranteed to get the
165 * same number in both the parent and child threads, then
166 * something else is seriously broken.
168 start_info->obj->tid=tid;
170 handle_store(start_info->obj);
172 mono_profiler_thread_start (tid);
174 mono_new_thread_init (start_info->obj, &tid, start_func);
180 /* If the thread calls ExitThread at all, this remaining code
181 * will not be executed, but the main thread will eventually
182 * call thread_cleanup() on this thread's behalf.
186 g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper terminating");
189 thread_cleanup (tid);
194 void mono_new_thread_init (MonoThread *thread_object, gpointer stack_start, gpointer func)
196 /* FIXME: GC problem here with recorded object
199 * This is recorded so CurrentThread can return the
202 TlsSetValue (current_object_key, thread_object);
204 if (mono_thread_start_cb) {
205 mono_thread_start_cb (thread_object, stack_start, func);
209 MonoThread *mono_thread_create (MonoDomain *domain, gpointer func,
213 HANDLE thread_handle;
214 struct StartInfo *start_info;
217 thread = (MonoThread *)mono_object_new (domain,
218 mono_defaults.thread_class);
220 start_info=g_new0 (struct StartInfo, 1);
221 start_info->func = func;
222 start_info->obj = thread;
223 start_info->domain = domain;
224 start_info->this = arg;
226 thread_handle = CreateThread(NULL, 0, start_wrapper, start_info, 0, &tid);
228 g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d (handle %p)",
231 g_assert (thread_handle);
233 thread->handle=thread_handle;
236 handle_store(thread);
242 mono_thread_attach (MonoDomain *domain)
245 HANDLE thread_handle;
248 if ((thread = mono_thread_current ())) {
249 g_warning ("mono_thread_attach called for an already attached thread");
250 if (mono_thread_attach_cb) {
251 mono_thread_attach_cb (thread, &tid);
256 thread = (MonoThread *)mono_object_new (domain,
257 mono_defaults.thread_class);
259 thread_handle = GetCurrentThread ();
260 g_assert (thread_handle);
262 tid=GetCurrentThreadId ();
264 thread->handle=thread_handle;
268 g_message(G_GNUC_PRETTY_FUNCTION ": Attached thread ID %d (handle %p)",
272 handle_store(thread);
274 TlsSetValue (current_object_key, thread);
275 mono_domain_set (domain);
277 if (mono_thread_attach_cb) {
278 mono_thread_attach_cb (thread, &tid);
284 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this,
287 MonoMulticastDelegate *delegate = (MonoMulticastDelegate*)start;
288 guint32 (*start_func)(void *);
289 struct StartInfo *start_info;
297 g_message(G_GNUC_PRETTY_FUNCTION
298 ": Trying to start a new thread: this (%p) start (%p)",
302 im = mono_get_delegate_invoke (start->vtable->klass);
303 if (mono_thread_callbacks)
304 start_func = (* mono_thread_callbacks->thread_start_compile_func) (im);
306 start_func = mono_compile_method (im);
308 if(start_func==NULL) {
309 g_warning(G_GNUC_PRETTY_FUNCTION
310 ": Can't locate start method!");
313 /* This is freed in start_wrapper */
314 start_info = g_new0 (struct StartInfo, 1);
315 start_info->func = start_func;
316 start_info->this = delegate;
317 start_info->obj = this;
318 start_info->domain = mono_domain_get ();
320 thread=CreateThread(NULL, 0, start_wrapper, start_info,
321 CREATE_SUSPENDED, &tid);
323 g_warning(G_GNUC_PRETTY_FUNCTION
324 ": CreateThread error 0x%x", GetLastError());
331 /* Don't call handle_store() here, delay it to Start.
332 * We can't join a thread (trying to will just block
333 * forever) until it actually starts running, so don't
334 * store the handle till then.
338 g_message(G_GNUC_PRETTY_FUNCTION
339 ": Started thread ID %d (handle %p)", tid, thread);
346 void ves_icall_System_Threading_Thread_Thread_free_internal (MonoThread *this,
352 g_message (G_GNUC_PRETTY_FUNCTION ": Closing thread %p, handle %p",
356 CloseHandle (thread);
359 void ves_icall_System_Threading_Thread_Start_internal(MonoThread *this,
365 g_message(G_GNUC_PRETTY_FUNCTION ": Launching thread %p", this);
368 /* Only store the handle when the thread is about to be
369 * launched, to avoid the main thread deadlocking while trying
370 * to clean up a thread that will never be signalled.
374 if (mono_thread_callbacks)
375 (* mono_thread_callbacks->start_resume) (this);
377 ResumeThread(thread);
379 if (mono_thread_callbacks)
380 (* mono_thread_callbacks->end_resume) (this);
383 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
388 g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
395 ves_icall_System_Threading_Thread_GetDomainID (void)
399 return mono_domain_get()->domain_id;
403 mono_thread_current (void)
409 /* Find the current thread object */
410 thread=TlsGetValue (current_object_key);
413 g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", thread);
419 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this,
420 int ms, HANDLE thread)
430 g_message (G_GNUC_PRETTY_FUNCTION ": joining thread handle %p, %d ms",
434 ret=WaitForSingleObject(thread, ms);
435 if(ret==WAIT_OBJECT_0) {
437 g_message (G_GNUC_PRETTY_FUNCTION ": join successful");
444 g_message (G_GNUC_PRETTY_FUNCTION ": join failed");
450 void ves_icall_System_Threading_Thread_SlotHash_store(MonoObject *data)
455 g_message(G_GNUC_PRETTY_FUNCTION ": Storing key %p", data);
458 /* Object location stored here */
459 TlsSetValue(slothash_key, data);
462 MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void)
468 data=TlsGetValue(slothash_key);
471 g_message(G_GNUC_PRETTY_FUNCTION ": Retrieved key %p", data);
477 static void mon_finalize (void *o, void *unused)
479 MonoThreadsSync *mon=(MonoThreadsSync *)o;
481 #ifdef THREAD_LOCK_DEBUG
482 g_message (G_GNUC_PRETTY_FUNCTION ": Finalizing sync");
485 CloseHandle (mon->monitor);
486 CloseHandle (mon->sema);
487 CloseHandle (mon->waiters_done);
488 DeleteCriticalSection (&mon->waiters_count_lock);
491 static MonoThreadsSync *mon_new(void)
493 MonoThreadsSync *new;
496 new=(MonoThreadsSync *)GC_MALLOC (sizeof(MonoThreadsSync));
497 GC_REGISTER_FINALIZER (new, mon_finalize, NULL, NULL, NULL);
499 /* This should be freed when the object that owns it is
502 new=(MonoThreadsSync *)g_new0 (MonoThreadsSync, 1);
505 new->monitor=CreateMutex(NULL, FALSE, NULL);
506 if(new->monitor==NULL) {
507 /* Throw some sort of system exception? (ditto for the
508 * sem and event handles below)
512 new->waiters_count=0;
513 new->was_broadcast=FALSE;
514 InitializeCriticalSection(&new->waiters_count_lock);
515 new->sema=CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
516 new->waiters_done=CreateEvent(NULL, FALSE, FALSE, NULL);
518 #ifdef THREAD_LOCK_DEBUG
519 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) ThreadsSync %p mutex created: %p, sem: %p, event: %p", GetCurrentThreadId (), new, new->monitor, new->sema, new->waiters_done);
525 gboolean ves_icall_System_Threading_Monitor_Monitor_try_enter(MonoObject *obj,
528 MonoThreadsSync *mon;
533 #ifdef THREAD_LOCK_DEBUG
534 g_message(G_GNUC_PRETTY_FUNCTION
535 ": (%d) Trying to lock object %p", GetCurrentThreadId(),
539 EnterCriticalSection(&monitor_mutex);
541 mon=obj->synchronisation;
544 obj->synchronisation=mon;
547 /* Don't hold the monitor lock while waiting to acquire the
550 LeaveCriticalSection(&monitor_mutex);
552 /* Acquire the mutex */
553 #ifdef THREAD_LOCK_DEBUG
554 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Acquiring monitor mutex %p",
555 GetCurrentThreadId (), mon->monitor);
557 ret=WaitForSingleObject(mon->monitor, ms);
558 if(ret==WAIT_OBJECT_0) {
560 mon->tid=GetCurrentThreadId();
562 #ifdef THREAD_LOCK_DEBUG
563 g_message(G_GNUC_PRETTY_FUNCTION
564 ": (%d) object %p now locked %d times",
565 GetCurrentThreadId (), obj, mon->count);
574 void ves_icall_System_Threading_Monitor_Monitor_exit(MonoObject *obj)
576 MonoThreadsSync *mon;
580 #ifdef THREAD_LOCK_DEBUG
581 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Unlocking %p",
582 GetCurrentThreadId (), obj);
585 /* No need to lock monitor_mutex here because we only adjust
586 * the monitor state if this thread already owns the lock
588 mon=obj->synchronisation;
594 if(mon->tid!=GetCurrentThreadId()) {
600 #ifdef THREAD_LOCK_DEBUG
601 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %p now locked %d times",
602 GetCurrentThreadId (), obj, mon->count);
606 mon->tid=0; /* FIXME: check that 0 isnt a valid id */
609 #ifdef THREAD_LOCK_DEBUG
610 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Releasing mutex %p",
611 GetCurrentThreadId (), mon->monitor);
614 ReleaseMutex(mon->monitor);
617 gboolean ves_icall_System_Threading_Monitor_Monitor_test_owner(MonoObject *obj)
619 MonoThreadsSync *mon;
624 #ifdef THREAD_LOCK_DEBUG
625 g_message(G_GNUC_PRETTY_FUNCTION
626 ": Testing if %p is owned by thread %d", obj,
627 GetCurrentThreadId());
630 EnterCriticalSection(&monitor_mutex);
632 mon=obj->synchronisation;
637 if(mon->tid!=GetCurrentThreadId()) {
638 #ifdef THREAD_LOCK_DEBUG
639 g_message (G_GNUC_PRETTY_FUNCTION
640 ": (%d) object %p is owned by thread %d",
641 GetCurrentThreadId (), obj, mon->tid);
650 LeaveCriticalSection(&monitor_mutex);
655 gboolean ves_icall_System_Threading_Monitor_Monitor_test_synchronised(MonoObject *obj)
657 MonoThreadsSync *mon;
662 #ifdef THREAD_LOCK_DEBUG
663 g_message(G_GNUC_PRETTY_FUNCTION
664 ": (%d) Testing if %p is owned by any thread",
665 GetCurrentThreadId (), obj);
668 EnterCriticalSection(&monitor_mutex);
670 mon=obj->synchronisation;
679 g_assert(mon->count);
684 LeaveCriticalSection(&monitor_mutex);
690 void ves_icall_System_Threading_Monitor_Monitor_pulse(MonoObject *obj)
692 gboolean have_waiters;
693 MonoThreadsSync *mon;
697 #ifdef THREAD_LOCK_DEBUG
698 g_message("(%d) Pulsing %p", GetCurrentThreadId (), obj);
701 EnterCriticalSection(&monitor_mutex);
703 mon=obj->synchronisation;
705 LeaveCriticalSection(&monitor_mutex);
709 if(mon->tid!=GetCurrentThreadId()) {
710 LeaveCriticalSection(&monitor_mutex);
713 LeaveCriticalSection(&monitor_mutex);
715 EnterCriticalSection(&mon->waiters_count_lock);
716 have_waiters=(mon->waiters_count>0);
717 LeaveCriticalSection(&mon->waiters_count_lock);
719 if(have_waiters==TRUE) {
720 ReleaseSemaphore(mon->sema, 1, 0);
724 void ves_icall_System_Threading_Monitor_Monitor_pulse_all(MonoObject *obj)
726 gboolean have_waiters=FALSE;
727 MonoThreadsSync *mon;
731 #ifdef THREAD_LOCK_DEBUG
732 g_message("(%d) Pulsing all %p", GetCurrentThreadId (), obj);
735 EnterCriticalSection(&monitor_mutex);
737 mon=obj->synchronisation;
739 LeaveCriticalSection(&monitor_mutex);
743 if(mon->tid!=GetCurrentThreadId()) {
744 LeaveCriticalSection(&monitor_mutex);
747 LeaveCriticalSection(&monitor_mutex);
749 EnterCriticalSection(&mon->waiters_count_lock);
750 if(mon->waiters_count>0) {
751 mon->was_broadcast=TRUE;
755 if(have_waiters==TRUE) {
756 ReleaseSemaphore(mon->sema, mon->waiters_count, 0);
758 LeaveCriticalSection(&mon->waiters_count_lock);
760 WaitForSingleObject(mon->waiters_done, INFINITE);
761 mon->was_broadcast=FALSE;
763 LeaveCriticalSection(&mon->waiters_count_lock);
767 gboolean ves_icall_System_Threading_Monitor_Monitor_wait(MonoObject *obj,
770 gboolean last_waiter;
771 MonoThreadsSync *mon;
776 #ifdef THREAD_LOCK_DEBUG
777 g_message("(%d) Trying to wait for %p with timeout %dms",
778 GetCurrentThreadId (), obj, ms);
781 EnterCriticalSection(&monitor_mutex);
783 mon=obj->synchronisation;
785 LeaveCriticalSection(&monitor_mutex);
789 if(mon->tid!=GetCurrentThreadId()) {
790 LeaveCriticalSection(&monitor_mutex);
793 LeaveCriticalSection(&monitor_mutex);
795 EnterCriticalSection(&mon->waiters_count_lock);
796 mon->waiters_count++;
797 LeaveCriticalSection(&mon->waiters_count_lock);
799 #ifdef THREAD_LOCK_DEBUG
800 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %p locked %d times",
801 GetCurrentThreadId (), obj, mon->count);
804 /* We need to put the lock count back afterwards */
805 save_count=mon->count;
807 while(mon->count>1) {
808 #ifdef THREAD_LOCK_DEBUG
809 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Releasing mutex %p",
810 GetCurrentThreadId (), mon->monitor);
813 ReleaseMutex(mon->monitor);
817 /* We're releasing this mutex */
820 #ifdef THREAD_LOCK_DEBUG
821 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Signalling monitor mutex %p",
822 GetCurrentThreadId (), mon->monitor);
825 SignalObjectAndWait(mon->monitor, mon->sema, INFINITE, FALSE);
827 EnterCriticalSection(&mon->waiters_count_lock);
828 mon->waiters_count++;
829 last_waiter=mon->was_broadcast && mon->waiters_count==0;
830 LeaveCriticalSection(&mon->waiters_count_lock);
833 #ifdef THREAD_LOCK_DEBUG
834 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Waiting for monitor mutex %p",
835 GetCurrentThreadId (), mon->monitor);
837 SignalObjectAndWait(mon->waiters_done, mon->monitor, INFINITE, FALSE);
839 #ifdef THREAD_LOCK_DEBUG
840 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Waiting for monitor mutex %p",
841 GetCurrentThreadId (), mon->monitor);
843 WaitForSingleObject(mon->monitor, INFINITE);
846 /* We've reclaimed this mutex */
847 mon->count=save_count;
848 mon->tid=GetCurrentThreadId();
850 /* Lock the mutex the required number of times */
851 while(save_count>1) {
852 #ifdef THREAD_LOCK_DEBUG
853 g_message(G_GNUC_PRETTY_FUNCTION
854 ": (%d) Waiting for monitor mutex %p",
855 GetCurrentThreadId (), mon->monitor);
857 WaitForSingleObject(mon->monitor, INFINITE);
861 #ifdef THREAD_LOCK_DEBUG
862 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %p still locked %d times",
863 GetCurrentThreadId (), obj, mon->count);
869 /* FIXME: exitContext isnt documented */
870 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
876 MonoObject *waitHandle;
881 numhandles = mono_array_length(mono_handles);
882 handles = g_new0(HANDLE, numhandles);
884 if (wait_handle_os_handle_field == 0) {
885 /* Get the field os_handle which will contain the actual handle */
886 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");
887 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
890 for(i = 0; i < numhandles; i++) {
891 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
892 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
899 ret=WaitForMultipleObjects(numhandles, handles, TRUE, ms);
903 if(ret==WAIT_FAILED) {
904 #ifdef THREAD_WAIT_DEBUG
905 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
906 GetCurrentThreadId ());
909 } else if(ret==WAIT_TIMEOUT) {
910 #ifdef THREAD_WAIT_DEBUG
911 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
912 GetCurrentThreadId ());
920 /* FIXME: exitContext isnt documented */
921 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
927 MonoObject *waitHandle;
932 numhandles = mono_array_length(mono_handles);
933 handles = g_new0(HANDLE, numhandles);
935 if (wait_handle_os_handle_field == 0) {
936 /* Get the field os_handle which will contain the actual handle */
937 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");
938 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
941 for(i = 0; i < numhandles; i++) {
942 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
943 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
950 ret=WaitForMultipleObjects(numhandles, handles, FALSE, ms);
954 #ifdef THREAD_WAIT_DEBUG
955 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) returning %d",
956 GetCurrentThreadId (), ret);
960 * These need to be here. See MSDN dos on WaitForMultipleObjects.
962 if (ret >= WAIT_OBJECT_0 && ret <= WAIT_OBJECT_0 + numhandles - 1) {
963 return ret - WAIT_OBJECT_0;
965 else if (ret >= WAIT_ABANDONED_0 && ret <= WAIT_ABANDONED_0 + numhandles - 1) {
966 return ret - WAIT_ABANDONED_0;
973 /* FIXME: exitContext isnt documented */
974 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
980 #ifdef THREAD_WAIT_DEBUG
981 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) waiting for %p, %d ms",
982 GetCurrentThreadId (), handle, ms);
989 ret=WaitForSingleObject(handle, ms);
990 if(ret==WAIT_FAILED) {
991 #ifdef THREAD_WAIT_DEBUG
992 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
993 GetCurrentThreadId ());
996 } else if(ret==WAIT_TIMEOUT) {
997 #ifdef THREAD_WAIT_DEBUG
998 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
999 GetCurrentThreadId ());
1007 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned,char *name) {
1008 MONO_ARCH_SAVE_REGS;
1010 return(CreateMutex(NULL,owned,name));
1013 void ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) {
1014 MONO_ARCH_SAVE_REGS;
1016 ReleaseMutex(handle);
1019 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual,
1020 MonoBoolean initial,
1022 MONO_ARCH_SAVE_REGS;
1024 return (CreateEvent(NULL,manual,initial,name));
1027 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
1028 MONO_ARCH_SAVE_REGS;
1030 return (SetEvent(handle));
1033 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
1034 MONO_ARCH_SAVE_REGS;
1036 return (ResetEvent(handle));
1039 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
1041 MONO_ARCH_SAVE_REGS;
1043 return InterlockedIncrement (location);
1046 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
1051 MONO_ARCH_SAVE_REGS;
1053 EnterCriticalSection(&interlocked_mutex);
1055 lowret = InterlockedIncrement((gint32 *) location);
1057 highret = InterlockedIncrement((gint32 *) location + 1);
1059 highret = *((gint32 *) location + 1);
1061 LeaveCriticalSection(&interlocked_mutex);
1063 return (gint64) highret << 32 | (gint64) lowret;
1066 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
1068 MONO_ARCH_SAVE_REGS;
1070 return InterlockedDecrement(location);
1073 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
1078 MONO_ARCH_SAVE_REGS;
1080 EnterCriticalSection(&interlocked_mutex);
1082 lowret = InterlockedDecrement((gint32 *) location);
1084 highret = InterlockedDecrement((gint32 *) location + 1);
1086 highret = *((gint32 *) location + 1);
1088 LeaveCriticalSection(&interlocked_mutex);
1090 return (gint64) highret << 32 | (gint64) lowret;
1093 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location1, gint32 value)
1095 MONO_ARCH_SAVE_REGS;
1097 return InterlockedExchange(location1, value);
1100 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location1, MonoObject *value)
1102 MONO_ARCH_SAVE_REGS;
1104 return (MonoObject *) InterlockedExchangePointer((gpointer *) location1, value);
1107 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location1, gfloat value)
1109 IntFloatUnion val, ret;
1111 MONO_ARCH_SAVE_REGS;
1114 ret.ival = InterlockedExchange((gint32 *) location1, val.ival);
1119 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location1, gint32 value, gint32 comparand)
1121 MONO_ARCH_SAVE_REGS;
1123 return InterlockedCompareExchange(location1, value, comparand);
1126 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location1, MonoObject *value, MonoObject *comparand)
1128 MONO_ARCH_SAVE_REGS;
1130 return (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location1, value, comparand);
1133 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location1, gfloat value, gfloat comparand)
1135 IntFloatUnion val, ret, cmp;
1137 MONO_ARCH_SAVE_REGS;
1140 cmp.fval = comparand;
1141 ret.ival = InterlockedCompareExchange((gint32 *) location1, val.ival, cmp.ival);
1147 mono_thread_get_abort_signal (void)
1157 #endif /* __MINGW32__ */
1161 ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state)
1163 MONO_ARCH_SAVE_REGS;
1165 thread->abort_state = state;
1166 thread->abort_exc = mono_get_exception_thread_abort ();
1169 g_assert_not_reached ();
1171 /* fixme: store the state somewhere */
1172 #ifdef PTHREAD_POINTER_ID
1173 pthread_kill (GUINT_TO_POINTER(thread->tid), mono_thread_get_abort_signal ());
1175 pthread_kill (thread->tid, mono_thread_get_abort_signal ());
1177 #endif /* __MINGW32__ */
1181 ves_icall_System_Threading_Thread_ResetAbort (void)
1183 MonoThread *thread = mono_thread_current ();
1185 MONO_ARCH_SAVE_REGS;
1187 if (!thread->abort_exc) {
1188 const char *msg = "Unable to reset abort because no abort was requested";
1189 mono_raise_exception (mono_get_exception_thread_state (msg));
1191 thread->abort_exc = NULL;
1192 thread->abort_state = NULL;
1196 void mono_thread_init (MonoDomain *domain, MonoThreadStartCB start_cb, MonoThreadAttachCB attach_cb)
1198 /* Build a System.Threading.Thread object instance to return
1199 * for the main line's Thread.CurrentThread property.
1202 /* I wonder what happens if someone tries to destroy this
1203 * object? In theory, I guess the whole program should act as
1204 * though exit() were called :-)
1207 g_message(G_GNUC_PRETTY_FUNCTION
1208 ": Starting to build main Thread object");
1210 main_thread = (MonoThread *)mono_object_new (domain, mono_defaults.thread_class);
1212 main_thread->handle = GetCurrentThread ();
1215 g_message(G_GNUC_PRETTY_FUNCTION
1216 ": Finished building main Thread object: %p", main_thread);
1219 InitializeCriticalSection(&threads_mutex);
1220 InitializeCriticalSection(&monitor_mutex);
1221 InitializeCriticalSection(&interlocked_mutex);
1223 current_object_key=TlsAlloc();
1225 g_message (G_GNUC_PRETTY_FUNCTION ": Allocated current_object_key %d",
1226 current_object_key);
1229 TlsSetValue(current_object_key, main_thread);
1231 mono_thread_start_cb = start_cb;
1232 mono_thread_attach_cb = attach_cb;
1234 slothash_key=TlsAlloc();
1236 /* Get a pseudo handle to the current process. This is just a
1237 * kludge so that wapi can build a process handle if needed.
1238 * As a pseudo handle is returned, we don't need to clean
1241 GetCurrentProcess ();
1244 void mono_install_thread_callbacks (MonoThreadCallbacks *callbacks)
1246 mono_thread_callbacks = callbacks;
1250 static void print_tids (gpointer key, gpointer value, gpointer user)
1252 g_message ("Waiting for: %d", *(guint32 *)key);
1258 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
1259 MonoThread *threads[MAXIMUM_WAIT_OBJECTS];
1263 static void wait_for_tids (struct wait_data *wait)
1268 g_message(G_GNUC_PRETTY_FUNCTION
1269 ": %d threads to wait for in this batch", wait->num);
1272 ret=WaitForMultipleObjects(wait->num, wait->handles, TRUE, INFINITE);
1273 if(ret==WAIT_FAILED) {
1274 /* See the comment in build_wait_tids() */
1276 g_message (G_GNUC_PRETTY_FUNCTION ": Wait failed");
1282 for(i=0; i<wait->num; i++) {
1283 guint32 tid=wait->threads[i]->tid;
1285 if(mono_g_hash_table_lookup (threads, &tid)!=NULL) {
1286 /* This thread must have been killed, because
1287 * it hasn't cleaned itself up. (It's just
1288 * possible that the thread exited before the
1289 * parent thread had a chance to store the
1290 * handle, and now there is another pointer to
1291 * the already-exited thread stored. In this
1292 * case, we'll just get two
1293 * mono_profiler_thread_end() calls for the
1298 g_message (G_GNUC_PRETTY_FUNCTION
1299 ": cleaning up after thread %d", tid);
1301 thread_cleanup (tid);
1306 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
1308 struct wait_data *wait=(struct wait_data *)user;
1310 if(wait->num<MAXIMUM_WAIT_OBJECTS) {
1311 MonoThread *thread=(MonoThread *)value;
1313 /* Theres a theoretical chance that thread->handle
1314 * might be NULL if the child thread has called
1315 * handle_store() but the parent thread hasn't set the
1316 * handle pointer yet. WaitForMultipleObjects will
1317 * fail, and we'll just go round the loop again. By
1318 * that time the handle should be stored properly.
1320 wait->handles[wait->num]=thread->handle;
1321 wait->threads[wait->num]=thread;
1324 /* Just ignore the rest, we can't do anything with
1330 void mono_thread_cleanup(void)
1332 struct wait_data *wait=g_new0 (struct wait_data, 1);
1334 /* join each thread that's still running */
1336 g_message(G_GNUC_PRETTY_FUNCTION ": Joining each running thread...");
1341 g_message(G_GNUC_PRETTY_FUNCTION ": No threads");
1347 EnterCriticalSection (&threads_mutex);
1349 g_message(G_GNUC_PRETTY_FUNCTION
1350 ":There are %d threads to join",
1351 mono_g_hash_table_size (threads));
1352 mono_g_hash_table_foreach (threads, print_tids, NULL);
1356 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
1358 LeaveCriticalSection (&threads_mutex);
1360 /* Something to wait for */
1361 wait_for_tids (wait);
1363 } while(wait->num>0);
1367 mono_g_hash_table_destroy(threads);