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_LOCK_DEBUG
28 #undef THREAD_WAIT_DEBUG
32 guint32 (*func)(void *);
45 * The "os_handle" field of the WaitHandle class.
47 static MonoClassField *wait_handle_os_handle_field = NULL;
49 /* Controls access to the 'threads' array */
50 static CRITICAL_SECTION threads_mutex;
52 /* Controls access to the sync field in MonoObjects, to avoid race
53 * conditions when adding sync data to an object for the first time.
55 static CRITICAL_SECTION monitor_mutex;
57 /* The hash of existing threads (key is thread ID) that need joining
60 static MonoGHashTable *threads=NULL;
62 /* The TLS key that holds the MonoObject assigned to each thread */
63 static guint32 current_object_key;
65 /* function called at thread start */
66 static MonoThreadStartCB mono_thread_start_cb = NULL;
68 /* function called at thread attach */
69 static MonoThreadAttachCB mono_thread_attach_cb = NULL;
71 /* function called when a new thread has been created */
72 static MonoThreadCallbacks *mono_thread_callbacks = NULL;
74 /* The TLS key that holds the LocalDataStoreSlot hash in each thread */
75 static guint32 slothash_key;
77 /* Spin lock for InterlockedXXX 64 bit functions */
78 static CRITICAL_SECTION interlocked_mutex;
80 /* handle_store() and handle_remove() manage the array of threads that
81 * still need to be waited for when the main thread exits.
83 static void handle_store(MonoThread *thread)
85 EnterCriticalSection(&threads_mutex);
88 g_message(G_GNUC_PRETTY_FUNCTION ": thread %p ID %d", thread,
93 threads=mono_g_hash_table_new(g_int_hash, g_int_equal);
96 /* GHashTable will remove a previous entry if a duplicate key
97 * is stored, which is exactly what we want: we store the
98 * thread both in the start_wrapper (in the subthread), and as
99 * soon as possible in the parent thread. This is to minimise
100 * the window in which the thread exists but we haven't
103 mono_g_hash_table_remove (threads, &thread->tid);
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 *);
154 g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper");
157 /* We can be sure start_info->obj->tid and
158 * start_info->obj->handle have been set, because the thread
159 * was created suspended, and these values were set before the
163 tid=start_info->obj->tid;
165 mono_domain_set (start_info->domain);
167 /* This MUST be called before any managed code can be
168 * executed, as it calls the callback function that (for the
169 * jit) sets the lmf marker.
171 mono_new_thread_init (tid, &tid, start_func);
173 if(start_info->fake_thread) {
174 thread = (MonoThread *)mono_object_new (start_info->domain, mono_defaults.thread_class);
176 thread->handle=start_info->obj->handle;
179 thread=start_info->obj;
182 start_func = start_info->func;
183 this = start_info->this;
185 TlsSetValue (current_object_key, thread);
187 handle_store(thread);
189 if(start_info->fake_thread) {
190 /* This has to happen _after_ handle_store(), because
191 * the fake thread is still in the threads hash until
194 g_free (start_info->obj);
197 mono_profiler_thread_start (tid);
203 /* If the thread calls ExitThread at all, this remaining code
204 * will not be executed, but the main thread will eventually
205 * call thread_cleanup() on this thread's behalf.
209 g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper terminating");
212 thread_cleanup (tid);
217 void mono_new_thread_init (guint32 tid, gpointer stack_start, gpointer func)
219 if (mono_thread_start_cb) {
220 mono_thread_start_cb (tid, stack_start, func);
224 void mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
227 HANDLE thread_handle;
228 struct StartInfo *start_info;
231 /* This is just a temporary allocation. The object will be
232 * created properly with mono_object_new() inside
233 * start_wrapper(). (This is so the main thread can be
234 * created without needing to run any managed code.)
236 thread=g_new0 (MonoThread, 1);
238 start_info=g_new0 (struct StartInfo, 1);
239 start_info->func = func;
240 start_info->obj = thread;
241 start_info->fake_thread = TRUE;
242 start_info->domain = domain;
243 start_info->this = arg;
245 /* Create suspended, so we can do some housekeeping before the thread
248 thread_handle = CreateThread(NULL, 0, start_wrapper, start_info,
249 CREATE_SUSPENDED, &tid);
251 g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d (handle %p)",
254 g_assert (thread_handle);
256 thread->handle=thread_handle;
259 handle_store(thread);
261 ResumeThread (thread_handle);
265 mono_thread_attach (MonoDomain *domain)
268 HANDLE thread_handle;
271 if ((thread = mono_thread_current ())) {
272 g_warning ("mono_thread_attach called for an already attached thread");
273 if (mono_thread_attach_cb) {
274 mono_thread_attach_cb (tid, &tid);
279 thread = (MonoThread *)mono_object_new (domain,
280 mono_defaults.thread_class);
282 thread_handle = GetCurrentThread ();
283 g_assert (thread_handle);
285 tid=GetCurrentThreadId ();
287 thread->handle=thread_handle;
291 g_message(G_GNUC_PRETTY_FUNCTION ": Attached thread ID %d (handle %p)",
295 handle_store(thread);
297 TlsSetValue (current_object_key, thread);
298 mono_domain_set (domain);
300 if (mono_thread_attach_cb) {
301 mono_thread_attach_cb (tid, &tid);
307 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this,
310 MonoMulticastDelegate *delegate = (MonoMulticastDelegate*)start;
311 guint32 (*start_func)(void *);
312 struct StartInfo *start_info;
320 g_message(G_GNUC_PRETTY_FUNCTION
321 ": Trying to start a new thread: this (%p) start (%p)",
325 im = mono_get_delegate_invoke (start->vtable->klass);
326 if (mono_thread_callbacks)
327 start_func = (* mono_thread_callbacks->thread_start_compile_func) (im);
329 start_func = mono_compile_method (im);
331 if(start_func==NULL) {
332 g_warning(G_GNUC_PRETTY_FUNCTION
333 ": Can't locate start method!");
336 /* This is freed in start_wrapper */
337 start_info = g_new0 (struct StartInfo, 1);
338 start_info->func = start_func;
339 start_info->this = delegate;
340 start_info->obj = this;
341 start_info->fake_thread = FALSE;
342 start_info->domain = mono_domain_get ();
344 thread=CreateThread(NULL, 0, start_wrapper, start_info,
345 CREATE_SUSPENDED, &tid);
347 g_warning(G_GNUC_PRETTY_FUNCTION
348 ": CreateThread error 0x%x", GetLastError());
355 /* Don't call handle_store() here, delay it to Start.
356 * We can't join a thread (trying to will just block
357 * forever) until it actually starts running, so don't
358 * store the handle till then.
362 g_message(G_GNUC_PRETTY_FUNCTION
363 ": Started thread ID %d (handle %p)", tid, thread);
370 void ves_icall_System_Threading_Thread_Thread_free_internal (MonoThread *this,
376 g_message (G_GNUC_PRETTY_FUNCTION ": Closing thread %p, handle %p",
380 CloseHandle (thread);
383 void ves_icall_System_Threading_Thread_Start_internal(MonoThread *this,
389 g_message(G_GNUC_PRETTY_FUNCTION ": Launching thread %p", this);
392 /* Only store the handle when the thread is about to be
393 * launched, to avoid the main thread deadlocking while trying
394 * to clean up a thread that will never be signalled.
398 if (mono_thread_callbacks)
399 (* mono_thread_callbacks->start_resume) (this);
401 ResumeThread(thread);
403 if (mono_thread_callbacks)
404 (* mono_thread_callbacks->end_resume) (this);
407 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
412 g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
419 ves_icall_System_Threading_Thread_GetDomainID (void)
423 return mono_domain_get()->domain_id;
427 mono_thread_current (void)
433 /* Find the current thread object */
434 thread=TlsGetValue (current_object_key);
437 g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", thread);
443 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this,
444 int ms, HANDLE thread)
454 g_message (G_GNUC_PRETTY_FUNCTION ": joining thread handle %p, %d ms",
458 ret=WaitForSingleObject(thread, ms);
459 if(ret==WAIT_OBJECT_0) {
461 g_message (G_GNUC_PRETTY_FUNCTION ": join successful");
468 g_message (G_GNUC_PRETTY_FUNCTION ": join failed");
474 void ves_icall_System_Threading_Thread_SlotHash_store(MonoObject *data)
479 g_message(G_GNUC_PRETTY_FUNCTION ": Storing key %p", data);
482 /* Object location stored here */
483 TlsSetValue(slothash_key, data);
486 MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void)
492 data=TlsGetValue(slothash_key);
495 g_message(G_GNUC_PRETTY_FUNCTION ": Retrieved key %p", data);
501 static void mon_finalize (void *o, void *unused)
503 MonoThreadsSync *mon=(MonoThreadsSync *)o;
505 #ifdef THREAD_LOCK_DEBUG
506 g_message (G_GNUC_PRETTY_FUNCTION ": Finalizing sync");
509 CloseHandle (mon->monitor);
510 CloseHandle (mon->sema);
511 CloseHandle (mon->waiters_done);
512 DeleteCriticalSection (&mon->waiters_count_lock);
515 static MonoThreadsSync *mon_new(void)
517 MonoThreadsSync *new;
520 new=(MonoThreadsSync *)GC_MALLOC (sizeof(MonoThreadsSync));
521 GC_REGISTER_FINALIZER (new, mon_finalize, NULL, NULL, NULL);
523 /* This should be freed when the object that owns it is
526 new=(MonoThreadsSync *)g_new0 (MonoThreadsSync, 1);
529 new->monitor=CreateMutex(NULL, FALSE, NULL);
530 if(new->monitor==NULL) {
531 /* Throw some sort of system exception? (ditto for the
532 * sem and event handles below)
536 new->waiters_count=0;
537 new->was_broadcast=FALSE;
538 InitializeCriticalSection(&new->waiters_count_lock);
539 new->sema=CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
540 new->waiters_done=CreateEvent(NULL, FALSE, FALSE, NULL);
542 #ifdef THREAD_LOCK_DEBUG
543 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);
549 gboolean ves_icall_System_Threading_Monitor_Monitor_try_enter(MonoObject *obj,
552 MonoThreadsSync *mon;
557 #ifdef THREAD_LOCK_DEBUG
558 g_message(G_GNUC_PRETTY_FUNCTION
559 ": (%d) Trying to lock object %p", GetCurrentThreadId(),
563 EnterCriticalSection(&monitor_mutex);
565 mon=obj->synchronisation;
568 obj->synchronisation=mon;
571 /* Don't hold the monitor lock while waiting to acquire the
574 LeaveCriticalSection(&monitor_mutex);
576 /* Acquire the mutex */
577 #ifdef THREAD_LOCK_DEBUG
578 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Acquiring monitor mutex %p",
579 GetCurrentThreadId (), mon->monitor);
581 ret=WaitForSingleObject(mon->monitor, ms);
582 if(ret==WAIT_OBJECT_0) {
584 mon->tid=GetCurrentThreadId();
586 #ifdef THREAD_LOCK_DEBUG
587 g_message(G_GNUC_PRETTY_FUNCTION
588 ": (%d) object %p now locked %d times",
589 GetCurrentThreadId (), obj, mon->count);
598 void ves_icall_System_Threading_Monitor_Monitor_exit(MonoObject *obj)
600 MonoThreadsSync *mon;
604 #ifdef THREAD_LOCK_DEBUG
605 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Unlocking %p",
606 GetCurrentThreadId (), obj);
609 /* No need to lock monitor_mutex here because we only adjust
610 * the monitor state if this thread already owns the lock
612 mon=obj->synchronisation;
618 if(mon->tid!=GetCurrentThreadId()) {
624 #ifdef THREAD_LOCK_DEBUG
625 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %p now locked %d times",
626 GetCurrentThreadId (), obj, mon->count);
630 mon->tid=0; /* FIXME: check that 0 isnt a valid id */
633 #ifdef THREAD_LOCK_DEBUG
634 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Releasing mutex %p",
635 GetCurrentThreadId (), mon->monitor);
638 ReleaseMutex(mon->monitor);
641 gboolean ves_icall_System_Threading_Monitor_Monitor_test_owner(MonoObject *obj)
643 MonoThreadsSync *mon;
648 #ifdef THREAD_LOCK_DEBUG
649 g_message(G_GNUC_PRETTY_FUNCTION
650 ": Testing if %p is owned by thread %d", obj,
651 GetCurrentThreadId());
654 EnterCriticalSection(&monitor_mutex);
656 mon=obj->synchronisation;
661 if(mon->tid!=GetCurrentThreadId()) {
662 #ifdef THREAD_LOCK_DEBUG
663 g_message (G_GNUC_PRETTY_FUNCTION
664 ": (%d) object %p is owned by thread %d",
665 GetCurrentThreadId (), obj, mon->tid);
674 LeaveCriticalSection(&monitor_mutex);
679 gboolean ves_icall_System_Threading_Monitor_Monitor_test_synchronised(MonoObject *obj)
681 MonoThreadsSync *mon;
686 #ifdef THREAD_LOCK_DEBUG
687 g_message(G_GNUC_PRETTY_FUNCTION
688 ": (%d) Testing if %p is owned by any thread",
689 GetCurrentThreadId (), obj);
692 EnterCriticalSection(&monitor_mutex);
694 mon=obj->synchronisation;
703 g_assert(mon->count);
708 LeaveCriticalSection(&monitor_mutex);
714 void ves_icall_System_Threading_Monitor_Monitor_pulse(MonoObject *obj)
716 gboolean have_waiters;
717 MonoThreadsSync *mon;
721 #ifdef THREAD_LOCK_DEBUG
722 g_message(G_GNUC_PRETTY_FUNCTION "(%d) Pulsing %p",
723 GetCurrentThreadId (), obj);
726 EnterCriticalSection(&monitor_mutex);
728 mon=obj->synchronisation;
730 #ifdef THREAD_LOCK_DEBUG
731 g_message (G_GNUC_PRETTY_FUNCTION
732 "(%d) object %p not locked", GetCurrentThreadId (),
736 LeaveCriticalSection(&monitor_mutex);
740 if(mon->tid!=GetCurrentThreadId()) {
741 #ifdef THREAD_LOCK_DEBUG
742 g_message (G_GNUC_PRETTY_FUNCTION
743 "(%d) doesn't own lock (owned by %d)",
744 GetCurrentThreadId (), mon->tid);
747 LeaveCriticalSection(&monitor_mutex);
750 LeaveCriticalSection(&monitor_mutex);
752 EnterCriticalSection(&mon->waiters_count_lock);
753 have_waiters=(mon->waiters_count>0);
754 LeaveCriticalSection(&mon->waiters_count_lock);
756 if(have_waiters==TRUE) {
757 ReleaseSemaphore(mon->sema, 1, 0);
761 void ves_icall_System_Threading_Monitor_Monitor_pulse_all(MonoObject *obj)
763 gboolean have_waiters=FALSE;
764 MonoThreadsSync *mon;
768 #ifdef THREAD_LOCK_DEBUG
769 g_message("(%d) Pulsing all %p", GetCurrentThreadId (), obj);
772 EnterCriticalSection(&monitor_mutex);
774 mon=obj->synchronisation;
776 LeaveCriticalSection(&monitor_mutex);
780 if(mon->tid!=GetCurrentThreadId()) {
781 LeaveCriticalSection(&monitor_mutex);
784 LeaveCriticalSection(&monitor_mutex);
786 EnterCriticalSection(&mon->waiters_count_lock);
787 if(mon->waiters_count>0) {
788 mon->was_broadcast=TRUE;
792 if(have_waiters==TRUE) {
793 ReleaseSemaphore(mon->sema, mon->waiters_count, 0);
795 LeaveCriticalSection(&mon->waiters_count_lock);
797 WaitForSingleObject(mon->waiters_done, INFINITE);
798 mon->was_broadcast=FALSE;
800 LeaveCriticalSection(&mon->waiters_count_lock);
804 gboolean ves_icall_System_Threading_Monitor_Monitor_wait(MonoObject *obj,
807 gboolean last_waiter;
808 MonoThreadsSync *mon;
813 #ifdef THREAD_LOCK_DEBUG
814 g_message(G_GNUC_PRETTY_FUNCTION
815 "(%d) Trying to wait for %p with timeout %dms",
816 GetCurrentThreadId (), obj, ms);
819 EnterCriticalSection(&monitor_mutex);
821 mon=obj->synchronisation;
823 LeaveCriticalSection(&monitor_mutex);
827 if(mon->tid!=GetCurrentThreadId()) {
828 LeaveCriticalSection(&monitor_mutex);
831 LeaveCriticalSection(&monitor_mutex);
833 EnterCriticalSection(&mon->waiters_count_lock);
834 mon->waiters_count++;
835 LeaveCriticalSection(&mon->waiters_count_lock);
837 #ifdef THREAD_LOCK_DEBUG
838 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %p locked %d times",
839 GetCurrentThreadId (), obj, mon->count);
842 /* We need to put the lock count back afterwards */
843 save_count=mon->count;
845 while(mon->count>1) {
846 #ifdef THREAD_LOCK_DEBUG
847 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Releasing mutex %p",
848 GetCurrentThreadId (), mon->monitor);
851 ReleaseMutex(mon->monitor);
855 /* We're releasing this mutex */
858 #ifdef THREAD_LOCK_DEBUG
859 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Signalling monitor mutex %p",
860 GetCurrentThreadId (), mon->monitor);
863 SignalObjectAndWait(mon->monitor, mon->sema, INFINITE, FALSE);
865 EnterCriticalSection(&mon->waiters_count_lock);
866 mon->waiters_count++;
867 last_waiter=mon->was_broadcast && mon->waiters_count==0;
868 LeaveCriticalSection(&mon->waiters_count_lock);
871 #ifdef THREAD_LOCK_DEBUG
872 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Waiting for monitor mutex %p",
873 GetCurrentThreadId (), mon->monitor);
875 SignalObjectAndWait(mon->waiters_done, mon->monitor, INFINITE, FALSE);
877 #ifdef THREAD_LOCK_DEBUG
878 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Waiting for monitor mutex %p",
879 GetCurrentThreadId (), mon->monitor);
881 WaitForSingleObject(mon->monitor, INFINITE);
884 /* We've reclaimed this mutex */
885 mon->count=save_count;
886 mon->tid=GetCurrentThreadId();
888 /* Lock the mutex the required number of times */
889 while(save_count>1) {
890 #ifdef THREAD_LOCK_DEBUG
891 g_message(G_GNUC_PRETTY_FUNCTION
892 ": (%d) Waiting for monitor mutex %p",
893 GetCurrentThreadId (), mon->monitor);
895 WaitForSingleObject(mon->monitor, INFINITE);
899 #ifdef THREAD_LOCK_DEBUG
900 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %p still locked %d times",
901 GetCurrentThreadId (), obj, mon->count);
907 /* FIXME: exitContext isnt documented */
908 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
914 MonoObject *waitHandle;
919 numhandles = mono_array_length(mono_handles);
920 handles = g_new0(HANDLE, numhandles);
922 if (wait_handle_os_handle_field == 0) {
923 /* Get the field os_handle which will contain the actual handle */
924 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");
925 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
928 for(i = 0; i < numhandles; i++) {
929 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
930 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
937 ret=WaitForMultipleObjects(numhandles, handles, TRUE, ms);
941 if(ret==WAIT_FAILED) {
942 #ifdef THREAD_WAIT_DEBUG
943 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
944 GetCurrentThreadId ());
947 } else if(ret==WAIT_TIMEOUT) {
948 #ifdef THREAD_WAIT_DEBUG
949 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
950 GetCurrentThreadId ());
958 /* FIXME: exitContext isnt documented */
959 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
965 MonoObject *waitHandle;
970 numhandles = mono_array_length(mono_handles);
971 handles = g_new0(HANDLE, numhandles);
973 if (wait_handle_os_handle_field == 0) {
974 /* Get the field os_handle which will contain the actual handle */
975 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");
976 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
979 for(i = 0; i < numhandles; i++) {
980 waitHandle = mono_array_get(mono_handles, MonoObject*, i);
981 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
988 ret=WaitForMultipleObjects(numhandles, handles, FALSE, ms);
992 #ifdef THREAD_WAIT_DEBUG
993 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) returning %d",
994 GetCurrentThreadId (), ret);
998 * These need to be here. See MSDN dos on WaitForMultipleObjects.
1000 if (ret >= WAIT_OBJECT_0 && ret <= WAIT_OBJECT_0 + numhandles - 1) {
1001 return ret - WAIT_OBJECT_0;
1003 else if (ret >= WAIT_ABANDONED_0 && ret <= WAIT_ABANDONED_0 + numhandles - 1) {
1004 return ret - WAIT_ABANDONED_0;
1011 /* FIXME: exitContext isnt documented */
1012 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
1016 MONO_ARCH_SAVE_REGS;
1018 #ifdef THREAD_WAIT_DEBUG
1019 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) waiting for %p, %d ms",
1020 GetCurrentThreadId (), handle, ms);
1027 ret=WaitForSingleObject(handle, ms);
1028 if(ret==WAIT_FAILED) {
1029 #ifdef THREAD_WAIT_DEBUG
1030 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
1031 GetCurrentThreadId ());
1034 } else if(ret==WAIT_TIMEOUT) {
1035 #ifdef THREAD_WAIT_DEBUG
1036 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
1037 GetCurrentThreadId ());
1045 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned,char *name) {
1046 MONO_ARCH_SAVE_REGS;
1048 return(CreateMutex(NULL,owned,name));
1051 void ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) {
1052 MONO_ARCH_SAVE_REGS;
1054 ReleaseMutex(handle);
1057 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual,
1058 MonoBoolean initial,
1060 MONO_ARCH_SAVE_REGS;
1062 return (CreateEvent(NULL,manual,initial,name));
1065 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
1066 MONO_ARCH_SAVE_REGS;
1068 return (SetEvent(handle));
1071 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
1072 MONO_ARCH_SAVE_REGS;
1074 return (ResetEvent(handle));
1077 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
1079 MONO_ARCH_SAVE_REGS;
1081 return InterlockedIncrement (location);
1084 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
1089 MONO_ARCH_SAVE_REGS;
1091 EnterCriticalSection(&interlocked_mutex);
1093 lowret = InterlockedIncrement((gint32 *) location);
1095 highret = InterlockedIncrement((gint32 *) location + 1);
1097 highret = *((gint32 *) location + 1);
1099 LeaveCriticalSection(&interlocked_mutex);
1101 return (gint64) highret << 32 | (gint64) lowret;
1104 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
1106 MONO_ARCH_SAVE_REGS;
1108 return InterlockedDecrement(location);
1111 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
1116 MONO_ARCH_SAVE_REGS;
1118 EnterCriticalSection(&interlocked_mutex);
1120 lowret = InterlockedDecrement((gint32 *) location);
1122 highret = InterlockedDecrement((gint32 *) location + 1);
1124 highret = *((gint32 *) location + 1);
1126 LeaveCriticalSection(&interlocked_mutex);
1128 return (gint64) highret << 32 | (gint64) lowret;
1131 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location1, gint32 value)
1133 MONO_ARCH_SAVE_REGS;
1135 return InterlockedExchange(location1, value);
1138 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location1, MonoObject *value)
1140 MONO_ARCH_SAVE_REGS;
1142 return (MonoObject *) InterlockedExchangePointer((gpointer *) location1, value);
1145 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location1, gfloat value)
1147 IntFloatUnion val, ret;
1149 MONO_ARCH_SAVE_REGS;
1152 ret.ival = InterlockedExchange((gint32 *) location1, val.ival);
1157 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location1, gint32 value, gint32 comparand)
1159 MONO_ARCH_SAVE_REGS;
1161 return InterlockedCompareExchange(location1, value, comparand);
1164 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location1, MonoObject *value, MonoObject *comparand)
1166 MONO_ARCH_SAVE_REGS;
1168 return (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location1, value, comparand);
1171 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location1, gfloat value, gfloat comparand)
1173 IntFloatUnion val, ret, cmp;
1175 MONO_ARCH_SAVE_REGS;
1178 cmp.fval = comparand;
1179 ret.ival = InterlockedCompareExchange((gint32 *) location1, val.ival, cmp.ival);
1185 mono_thread_get_abort_signal (void)
1195 #endif /* __MINGW32__ */
1199 ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state)
1201 MONO_ARCH_SAVE_REGS;
1203 thread->abort_state = state;
1204 thread->abort_exc = mono_get_exception_thread_abort ();
1207 g_assert_not_reached ();
1209 /* fixme: store the state somewhere */
1210 #ifdef PTHREAD_POINTER_ID
1211 pthread_kill (GUINT_TO_POINTER(thread->tid), mono_thread_get_abort_signal ());
1213 pthread_kill (thread->tid, mono_thread_get_abort_signal ());
1215 #endif /* __MINGW32__ */
1219 ves_icall_System_Threading_Thread_ResetAbort (void)
1221 MonoThread *thread = mono_thread_current ();
1223 MONO_ARCH_SAVE_REGS;
1225 if (!thread->abort_exc) {
1226 const char *msg = "Unable to reset abort because no abort was requested";
1227 mono_raise_exception (mono_get_exception_thread_state (msg));
1229 thread->abort_exc = NULL;
1230 thread->abort_state = NULL;
1234 void mono_thread_init (MonoThreadStartCB start_cb,
1235 MonoThreadAttachCB attach_cb)
1237 InitializeCriticalSection(&threads_mutex);
1238 InitializeCriticalSection(&monitor_mutex);
1239 InitializeCriticalSection(&interlocked_mutex);
1241 current_object_key=TlsAlloc();
1243 g_message (G_GNUC_PRETTY_FUNCTION ": Allocated current_object_key %d",
1244 current_object_key);
1247 mono_thread_start_cb = start_cb;
1248 mono_thread_attach_cb = attach_cb;
1250 slothash_key=TlsAlloc();
1252 /* Get a pseudo handle to the current process. This is just a
1253 * kludge so that wapi can build a process handle if needed.
1254 * As a pseudo handle is returned, we don't need to clean
1257 GetCurrentProcess ();
1260 void mono_install_thread_callbacks (MonoThreadCallbacks *callbacks)
1262 mono_thread_callbacks = callbacks;
1266 static void print_tids (gpointer key, gpointer value, gpointer user)
1268 g_message ("Waiting for: %d", *(guint32 *)key);
1274 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
1275 MonoThread *threads[MAXIMUM_WAIT_OBJECTS];
1279 static void wait_for_tids (struct wait_data *wait)
1284 g_message(G_GNUC_PRETTY_FUNCTION
1285 ": %d threads to wait for in this batch", wait->num);
1288 ret=WaitForMultipleObjects(wait->num, wait->handles, TRUE, INFINITE);
1289 if(ret==WAIT_FAILED) {
1290 /* See the comment in build_wait_tids() */
1292 g_message (G_GNUC_PRETTY_FUNCTION ": Wait failed");
1298 for(i=0; i<wait->num; i++) {
1299 guint32 tid=wait->threads[i]->tid;
1301 if(mono_g_hash_table_lookup (threads, &tid)!=NULL) {
1302 /* This thread must have been killed, because
1303 * it hasn't cleaned itself up. (It's just
1304 * possible that the thread exited before the
1305 * parent thread had a chance to store the
1306 * handle, and now there is another pointer to
1307 * the already-exited thread stored. In this
1308 * case, we'll just get two
1309 * mono_profiler_thread_end() calls for the
1314 g_message (G_GNUC_PRETTY_FUNCTION
1315 ": cleaning up after thread %d", tid);
1317 thread_cleanup (tid);
1322 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
1324 struct wait_data *wait=(struct wait_data *)user;
1326 if(wait->num<MAXIMUM_WAIT_OBJECTS) {
1327 MonoThread *thread=(MonoThread *)value;
1329 /* Theres a theoretical chance that thread->handle
1330 * might be NULL if the child thread has called
1331 * handle_store() but the parent thread hasn't set the
1332 * handle pointer yet. WaitForMultipleObjects will
1333 * fail, and we'll just go round the loop again. By
1334 * that time the handle should be stored properly.
1336 wait->handles[wait->num]=thread->handle;
1337 wait->threads[wait->num]=thread;
1340 /* Just ignore the rest, we can't do anything with
1346 void mono_thread_manage (void)
1348 struct wait_data *wait=g_new0 (struct wait_data, 1);
1350 /* join each thread that's still running */
1352 g_message(G_GNUC_PRETTY_FUNCTION ": Joining each running thread...");
1357 g_message(G_GNUC_PRETTY_FUNCTION ": No threads");
1363 EnterCriticalSection (&threads_mutex);
1365 g_message(G_GNUC_PRETTY_FUNCTION
1366 ":There are %d threads to join",
1367 mono_g_hash_table_size (threads));
1368 mono_g_hash_table_foreach (threads, print_tids, NULL);
1372 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
1374 LeaveCriticalSection (&threads_mutex);
1376 /* Something to wait for */
1377 wait_for_tids (wait);
1379 } while(wait->num>0);
1383 mono_g_hash_table_destroy(threads);
1387 static void terminate_thread (gpointer key, gpointer value, gpointer user)
1389 MonoThread *thread=(MonoThread *)value;
1390 guint32 self=GPOINTER_TO_UINT (user);
1392 if(thread->tid!=self) {
1393 /*TerminateThread (thread->handle, -1);*/
1397 void mono_thread_abort_all_other_threads (void)
1399 guint32 self=GetCurrentThreadId ();
1401 EnterCriticalSection (&threads_mutex);
1403 g_message(G_GNUC_PRETTY_FUNCTION ":There are %d threads to abort",
1404 mono_g_hash_table_size (threads));
1405 mono_g_hash_table_foreach (threads, print_tids, NULL);
1408 mono_g_hash_table_foreach (threads, terminate_thread,
1409 GUINT_TO_POINTER (self));
1411 LeaveCriticalSection (&threads_mutex);