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 *);
42 /* Controls access to the 'threads' array */
43 static CRITICAL_SECTION threads_mutex;
45 /* Controls access to the sync field in MonoObjects, to avoid race
46 * conditions when adding sync data to an object for the first time.
48 static CRITICAL_SECTION monitor_mutex;
50 /* The hash of existing threads (key is thread ID) that need joining
53 static MonoGHashTable *threads=NULL;
55 /* The MonoObject associated with the main thread */
56 static MonoThread *main_thread;
58 /* The TLS key that holds the MonoObject assigned to each thread */
59 static guint32 current_object_key;
61 /* function called at thread start */
62 static MonoThreadStartCB mono_thread_start_cb = NULL;
64 /* function called at thread attach */
65 static MonoThreadStartCB mono_thread_attach_cb = 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_int_hash, g_int_equal);
89 /* GHashTable will remove a previous entry if a duplicate key
90 * is stored, which is exactly what we want: we store the
91 * thread both in the start_wrapper (in the subthread), and as
92 * soon as possible in the parent thread. This is to minimise
93 * the window in which the thread exists but we haven't
97 /* We don't need to duplicate thread->handle, because it is
98 * only closed when the thread object is finalized by the GC.
100 mono_g_hash_table_insert(threads, &thread->tid, thread);
101 LeaveCriticalSection(&threads_mutex);
104 static void handle_remove(guint32 tid)
107 g_message(G_GNUC_PRETTY_FUNCTION ": thread ID %d", tid);
110 EnterCriticalSection(&threads_mutex);
112 mono_g_hash_table_remove (threads, &tid);
114 LeaveCriticalSection(&threads_mutex);
116 /* Don't close the handle here, wait for the object finalizer
117 * to do it. Otherwise, the following race condition applies:
119 * 1) Thread exits (and handle_remove() closes the handle)
121 * 2) Some other handle is reassigned the same slot
123 * 3) Another thread tries to join the first thread, and
124 * blocks waiting for the reassigned handle to be signalled
125 * (which might never happen). This is possible, because the
126 * thread calling Join() still has a reference to the first
131 static void thread_cleanup (guint32 tid)
133 mono_profiler_thread_end (tid);
137 static guint32 start_wrapper(void *data)
139 struct StartInfo *start_info=(struct StartInfo *)data;
140 guint32 (*start_func)(void *);
145 g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper");
148 start_func = start_info->func;
149 mono_domain_set (start_info->domain);
150 this = start_info->this;
152 tid=GetCurrentThreadId ();
153 /* Set the thread ID here as well as in the parent thread,
154 * because we don't know whether the thread object will
155 * already have its ID set before we get to it. This isn't a
156 * race condition, because if we're not guaranteed to get the
157 * same number in both the parent and child threads, then
158 * something else is seriously broken.
160 start_info->obj->tid=tid;
162 handle_store(start_info->obj);
164 mono_profiler_thread_start (tid);
166 mono_new_thread_init (start_info->obj, &tid);
172 /* If the thread calls ExitThread at all, this remaining code
173 * will not be executed, but the main thread will eventually
174 * call thread_cleanup() on this thread's behalf.
178 g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper terminating");
181 thread_cleanup (tid);
186 void mono_new_thread_init (MonoThread *thread_object, gpointer stack_start)
188 /* FIXME: GC problem here with recorded object
191 * This is recorded so CurrentThread can return the
194 TlsSetValue (current_object_key, thread_object);
196 if (mono_thread_start_cb) {
197 mono_thread_start_cb (stack_start);
201 MonoThread *mono_thread_create (MonoDomain *domain, gpointer func,
205 HANDLE thread_handle;
206 struct StartInfo *start_info;
209 thread = (MonoThread *)mono_object_new (domain,
210 mono_defaults.thread_class);
212 start_info=g_new0 (struct StartInfo, 1);
213 start_info->func = func;
214 start_info->obj = thread;
215 start_info->domain = domain;
216 start_info->this = arg;
218 thread_handle = CreateThread(NULL, 0, start_wrapper, start_info, 0, &tid);
220 g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d (handle %p)",
223 g_assert (thread_handle);
225 thread->handle=thread_handle;
228 handle_store(thread);
234 mono_thread_attach (MonoDomain *domain)
237 HANDLE thread_handle;
240 thread = (MonoThread *)mono_object_new (domain,
241 mono_defaults.thread_class);
243 thread_handle = GetCurrentThread ();
244 g_assert (thread_handle);
246 tid=GetCurrentThreadId ();
248 thread->handle=thread_handle;
252 g_message(G_GNUC_PRETTY_FUNCTION ": Attached thread ID %d (handle %p)",
256 handle_store(thread);
258 TlsSetValue (current_object_key, thread);
259 mono_domain_set (domain);
261 if (mono_thread_attach_cb) {
262 mono_thread_attach_cb (&tid);
268 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this,
273 MonoMulticastDelegate *delegate = (MonoMulticastDelegate*)start;
274 guint32 (*start_func)(void *);
275 struct StartInfo *start_info;
281 g_message(G_GNUC_PRETTY_FUNCTION
282 ": Trying to start a new thread: this (%p) start (%p)",
286 im = mono_get_delegate_invoke (start->vtable->klass);
287 start_func = mono_compile_method (im);
289 if(start_func==NULL) {
290 g_warning(G_GNUC_PRETTY_FUNCTION
291 ": Can't locate start method!");
294 /* This is freed in start_wrapper */
295 start_info = g_new0 (struct StartInfo, 1);
296 start_info->func = start_func;
297 start_info->this = delegate;
298 start_info->obj = this;
299 start_info->domain = mono_domain_get ();
301 thread=CreateThread(NULL, 0, start_wrapper, start_info,
302 CREATE_SUSPENDED, &tid);
304 g_warning(G_GNUC_PRETTY_FUNCTION
305 ": CreateThread error 0x%x", GetLastError());
312 /* Don't call handle_store() here, delay it to Start.
313 * We can't join a thread (trying to will just block
314 * forever) until it actually starts running, so don't
315 * store the handle till then.
319 g_message(G_GNUC_PRETTY_FUNCTION
320 ": Started thread ID %d (handle %p)", tid, thread);
327 void ves_icall_System_Threading_Thread_Thread_free_internal (MonoThread *this,
333 g_message (G_GNUC_PRETTY_FUNCTION ": Closing thread %p, handle %p",
337 CloseHandle (thread);
340 void ves_icall_System_Threading_Thread_Start_internal(MonoThread *this,
346 g_message(G_GNUC_PRETTY_FUNCTION ": Launching thread %p", this);
349 /* Only store the handle when the thread is about to be
350 * launched, to avoid the main thread deadlocking while trying
351 * to clean up a thread that will never be signalled.
355 ResumeThread(thread);
358 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
363 g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
370 ves_icall_System_Threading_Thread_GetDomainID (void)
374 return mono_domain_get()->domain_id;
378 mono_thread_current (void)
384 /* Find the current thread object */
385 thread=TlsGetValue (current_object_key);
388 g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", thread);
394 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this,
395 int ms, HANDLE thread)
405 g_message (G_GNUC_PRETTY_FUNCTION ": joining thread handle %p, %d ms",
409 ret=WaitForSingleObject(thread, ms);
410 if(ret==WAIT_OBJECT_0) {
412 g_message (G_GNUC_PRETTY_FUNCTION ": join successful");
419 g_message (G_GNUC_PRETTY_FUNCTION ": join failed");
425 void ves_icall_System_Threading_Thread_SlotHash_store(MonoObject *data)
430 g_message(G_GNUC_PRETTY_FUNCTION ": Storing key %p", data);
433 /* Object location stored here */
434 TlsSetValue(slothash_key, data);
437 MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void)
443 data=TlsGetValue(slothash_key);
446 g_message(G_GNUC_PRETTY_FUNCTION ": Retrieved key %p", data);
452 static void mon_finalize (void *o, void *unused)
454 MonoThreadsSync *mon=(MonoThreadsSync *)o;
456 #ifdef THREAD_LOCK_DEBUG
457 g_message (G_GNUC_PRETTY_FUNCTION ": Finalizing sync");
460 CloseHandle (mon->monitor);
461 CloseHandle (mon->sema);
462 CloseHandle (mon->waiters_done);
465 static MonoThreadsSync *mon_new(void)
467 MonoThreadsSync *new;
470 new=(MonoThreadsSync *)GC_MALLOC (sizeof(MonoThreadsSync));
471 GC_REGISTER_FINALIZER (new, mon_finalize, NULL, NULL, NULL);
473 /* This should be freed when the object that owns it is
476 new=(MonoThreadsSync *)g_new0 (MonoThreadsSync, 1);
479 new->monitor=CreateMutex(NULL, FALSE, NULL);
480 if(new->monitor==NULL) {
481 /* Throw some sort of system exception? (ditto for the
482 * sem and event handles below)
486 new->waiters_count=0;
487 new->was_broadcast=FALSE;
488 InitializeCriticalSection(&new->waiters_count_lock);
489 new->sema=CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
490 new->waiters_done=CreateEvent(NULL, FALSE, FALSE, NULL);
492 #ifdef THREAD_LOCK_DEBUG
493 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);
499 gboolean ves_icall_System_Threading_Monitor_Monitor_try_enter(MonoObject *obj,
504 MonoThreadsSync *mon;
507 #ifdef THREAD_LOCK_DEBUG
508 g_message(G_GNUC_PRETTY_FUNCTION
509 ": (%d) Trying to lock object %p", GetCurrentThreadId(),
513 EnterCriticalSection(&monitor_mutex);
515 mon=obj->synchronisation;
518 obj->synchronisation=mon;
521 /* Don't hold the monitor lock while waiting to acquire the
524 LeaveCriticalSection(&monitor_mutex);
526 /* Acquire the mutex */
527 #ifdef THREAD_LOCK_DEBUG
528 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Acquiring monitor mutex %p",
529 GetCurrentThreadId (), mon->monitor);
531 ret=WaitForSingleObject(mon->monitor, ms);
532 if(ret==WAIT_OBJECT_0) {
534 mon->tid=GetCurrentThreadId();
536 #ifdef THREAD_LOCK_DEBUG
537 g_message(G_GNUC_PRETTY_FUNCTION
538 ": (%d) object %p now locked %d times",
539 GetCurrentThreadId (), obj, mon->count);
548 void ves_icall_System_Threading_Monitor_Monitor_exit(MonoObject *obj)
552 MonoThreadsSync *mon;
554 #ifdef THREAD_LOCK_DEBUG
555 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Unlocking %p",
556 GetCurrentThreadId (), obj);
559 /* No need to lock monitor_mutex here because we only adjust
560 * the monitor state if this thread already owns the lock
562 mon=obj->synchronisation;
568 if(mon->tid!=GetCurrentThreadId()) {
574 #ifdef THREAD_LOCK_DEBUG
575 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %p now locked %d times",
576 GetCurrentThreadId (), obj, mon->count);
580 mon->tid=0; /* FIXME: check that 0 isnt a valid id */
583 #ifdef THREAD_LOCK_DEBUG
584 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Releasing mutex %p",
585 GetCurrentThreadId (), mon->monitor);
588 ReleaseMutex(mon->monitor);
591 gboolean ves_icall_System_Threading_Monitor_Monitor_test_owner(MonoObject *obj)
595 MonoThreadsSync *mon;
598 #ifdef THREAD_LOCK_DEBUG
599 g_message(G_GNUC_PRETTY_FUNCTION
600 ": Testing if %p is owned by thread %d", obj,
601 GetCurrentThreadId());
604 EnterCriticalSection(&monitor_mutex);
606 mon=obj->synchronisation;
611 if(mon->tid!=GetCurrentThreadId()) {
612 #ifdef THREAD_LOCK_DEBUG
613 g_message (G_GNUC_PRETTY_FUNCTION
614 ": (%d) object %p is owned by thread %d",
615 GetCurrentThreadId (), obj, mon->tid);
624 LeaveCriticalSection(&monitor_mutex);
629 gboolean ves_icall_System_Threading_Monitor_Monitor_test_synchronised(MonoObject *obj)
633 MonoThreadsSync *mon;
636 #ifdef THREAD_LOCK_DEBUG
637 g_message(G_GNUC_PRETTY_FUNCTION
638 ": (%d) Testing if %p is owned by any thread",
639 GetCurrentThreadId (), obj);
642 EnterCriticalSection(&monitor_mutex);
644 mon=obj->synchronisation;
653 g_assert(mon->count);
658 LeaveCriticalSection(&monitor_mutex);
664 void ves_icall_System_Threading_Monitor_Monitor_pulse(MonoObject *obj)
668 gboolean have_waiters;
669 MonoThreadsSync *mon;
671 #ifdef THREAD_LOCK_DEBUG
672 g_message("(%d) Pulsing %p", GetCurrentThreadId (), obj);
675 EnterCriticalSection(&monitor_mutex);
677 mon=obj->synchronisation;
679 LeaveCriticalSection(&monitor_mutex);
683 if(mon->tid!=GetCurrentThreadId()) {
684 LeaveCriticalSection(&monitor_mutex);
687 LeaveCriticalSection(&monitor_mutex);
689 EnterCriticalSection(&mon->waiters_count_lock);
690 have_waiters=(mon->waiters_count>0);
691 LeaveCriticalSection(&mon->waiters_count_lock);
693 if(have_waiters==TRUE) {
694 ReleaseSemaphore(mon->sema, 1, 0);
698 void ves_icall_System_Threading_Monitor_Monitor_pulse_all(MonoObject *obj)
702 gboolean have_waiters=FALSE;
703 MonoThreadsSync *mon;
705 #ifdef THREAD_LOCK_DEBUG
706 g_message("(%d) Pulsing all %p", GetCurrentThreadId (), obj);
709 EnterCriticalSection(&monitor_mutex);
711 mon=obj->synchronisation;
713 LeaveCriticalSection(&monitor_mutex);
717 if(mon->tid!=GetCurrentThreadId()) {
718 LeaveCriticalSection(&monitor_mutex);
721 LeaveCriticalSection(&monitor_mutex);
723 EnterCriticalSection(&mon->waiters_count_lock);
724 if(mon->waiters_count>0) {
725 mon->was_broadcast=TRUE;
729 if(have_waiters==TRUE) {
730 ReleaseSemaphore(mon->sema, mon->waiters_count, 0);
732 LeaveCriticalSection(&mon->waiters_count_lock);
734 WaitForSingleObject(mon->waiters_done, INFINITE);
735 mon->was_broadcast=FALSE;
737 LeaveCriticalSection(&mon->waiters_count_lock);
741 gboolean ves_icall_System_Threading_Monitor_Monitor_wait(MonoObject *obj,
746 gboolean last_waiter;
747 MonoThreadsSync *mon;
750 #ifdef THREAD_LOCK_DEBUG
751 g_message("(%d) Trying to wait for %p with timeout %dms",
752 GetCurrentThreadId (), obj, ms);
755 EnterCriticalSection(&monitor_mutex);
757 mon=obj->synchronisation;
759 LeaveCriticalSection(&monitor_mutex);
763 if(mon->tid!=GetCurrentThreadId()) {
764 LeaveCriticalSection(&monitor_mutex);
767 LeaveCriticalSection(&monitor_mutex);
769 EnterCriticalSection(&mon->waiters_count_lock);
770 mon->waiters_count++;
771 LeaveCriticalSection(&mon->waiters_count_lock);
773 #ifdef THREAD_LOCK_DEBUG
774 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %p locked %d times",
775 GetCurrentThreadId (), obj, mon->count);
778 /* We need to put the lock count back afterwards */
779 save_count=mon->count;
781 while(mon->count>1) {
782 #ifdef THREAD_LOCK_DEBUG
783 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Releasing mutex %p",
784 GetCurrentThreadId (), mon->monitor);
787 ReleaseMutex(mon->monitor);
791 /* We're releasing this mutex */
794 #ifdef THREAD_LOCK_DEBUG
795 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Signalling monitor mutex %p",
796 GetCurrentThreadId (), mon->monitor);
799 SignalObjectAndWait(mon->monitor, mon->sema, INFINITE, FALSE);
801 EnterCriticalSection(&mon->waiters_count_lock);
802 mon->waiters_count++;
803 last_waiter=mon->was_broadcast && mon->waiters_count==0;
804 LeaveCriticalSection(&mon->waiters_count_lock);
807 #ifdef THREAD_LOCK_DEBUG
808 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Waiting for monitor mutex %p",
809 GetCurrentThreadId (), mon->monitor);
811 SignalObjectAndWait(mon->waiters_done, mon->monitor, INFINITE, FALSE);
813 #ifdef THREAD_LOCK_DEBUG
814 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Waiting for monitor mutex %p",
815 GetCurrentThreadId (), mon->monitor);
817 WaitForSingleObject(mon->monitor, INFINITE);
820 /* We've reclaimed this mutex */
821 mon->count=save_count;
822 mon->tid=GetCurrentThreadId();
824 /* Lock the mutex the required number of times */
825 while(save_count>1) {
826 #ifdef THREAD_LOCK_DEBUG
827 g_message(G_GNUC_PRETTY_FUNCTION
828 ": (%d) Waiting for monitor mutex %p",
829 GetCurrentThreadId (), mon->monitor);
831 WaitForSingleObject(mon->monitor, INFINITE);
835 #ifdef THREAD_LOCK_DEBUG
836 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %p still locked %d times",
837 GetCurrentThreadId (), obj, mon->count);
843 /* FIXME: exitContext isnt documented */
844 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
853 numhandles=mono_array_length(mono_handles);
854 handles=g_new0(HANDLE, numhandles);
855 for(i=0; i<numhandles; i++) {
856 handles[i]=mono_array_get(mono_handles, HANDLE, i);
863 ret=WaitForMultipleObjects(numhandles, handles, TRUE, ms);
867 if(ret==WAIT_FAILED) {
868 #ifdef THREAD_WAIT_DEBUG
869 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
870 GetCurrentThreadId ());
873 } else if(ret==WAIT_TIMEOUT) {
874 #ifdef THREAD_WAIT_DEBUG
875 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
876 GetCurrentThreadId ());
884 /* FIXME: exitContext isnt documented */
885 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
894 numhandles=mono_array_length(mono_handles);
895 handles=g_new0(HANDLE, numhandles);
896 for(i=0; i<numhandles; i++) {
897 handles[i]=mono_array_get(mono_handles, HANDLE, i);
904 ret=WaitForMultipleObjects(numhandles, handles, FALSE, ms);
908 #ifdef THREAD_WAIT_DEBUG
909 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) returning %d",
910 GetCurrentThreadId (), ret);
916 /* FIXME: exitContext isnt documented */
917 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
923 #ifdef THREAD_WAIT_DEBUG
924 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) waiting for %p",
925 GetCurrentThreadId (), handle);
932 ret=WaitForSingleObject(handle, ms);
933 if(ret==WAIT_FAILED) {
934 #ifdef THREAD_WAIT_DEBUG
935 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
936 GetCurrentThreadId ());
939 } else if(ret==WAIT_TIMEOUT) {
940 #ifdef THREAD_WAIT_DEBUG
941 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
942 GetCurrentThreadId ());
950 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned,char *name) {
953 return(CreateMutex(NULL,owned,name));
956 void ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) {
959 ReleaseMutex(handle);
962 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual,
967 return (CreateEvent(NULL,manual,initial,name));
970 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
973 return (SetEvent(handle));
976 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
979 return (ResetEvent(handle));
982 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
986 return InterlockedIncrement (location);
989 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
996 EnterCriticalSection(&interlocked_mutex);
998 lowret = InterlockedIncrement((gint32 *) location);
1000 highret = InterlockedIncrement((gint32 *) location + 1);
1002 highret = *((gint32 *) location + 1);
1004 LeaveCriticalSection(&interlocked_mutex);
1006 return (gint64) highret << 32 | (gint64) lowret;
1009 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
1011 MONO_ARCH_SAVE_REGS;
1013 return InterlockedDecrement(location);
1016 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
1018 MONO_ARCH_SAVE_REGS;
1023 EnterCriticalSection(&interlocked_mutex);
1025 lowret = InterlockedDecrement((gint32 *) location);
1027 highret = InterlockedDecrement((gint32 *) location + 1);
1029 highret = *((gint32 *) location + 1);
1031 LeaveCriticalSection(&interlocked_mutex);
1033 return (gint64) highret << 32 | (gint64) lowret;
1036 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location1, gint32 value)
1038 MONO_ARCH_SAVE_REGS;
1040 return InterlockedExchange(location1, value);
1043 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location1, MonoObject *value)
1045 MONO_ARCH_SAVE_REGS;
1047 return (MonoObject *) InterlockedExchangePointer((gpointer *) location1, value);
1050 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location1, gfloat value)
1052 MONO_ARCH_SAVE_REGS;
1054 IntFloatUnion val, ret;
1057 ret.ival = InterlockedExchange((gint32 *) location1, val.ival);
1062 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location1, gint32 value, gint32 comparand)
1064 MONO_ARCH_SAVE_REGS;
1066 return InterlockedCompareExchange(location1, value, comparand);
1069 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location1, MonoObject *value, MonoObject *comparand)
1071 MONO_ARCH_SAVE_REGS;
1073 return (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location1, value, comparand);
1076 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location1, gfloat value, gfloat comparand)
1078 MONO_ARCH_SAVE_REGS;
1080 IntFloatUnion val, ret, cmp;
1083 cmp.fval = comparand;
1084 ret.ival = InterlockedCompareExchange((gint32 *) location1, val.ival, cmp.ival);
1090 ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state)
1092 MONO_ARCH_SAVE_REGS;
1094 thread->abort_state = state;
1095 thread->abort_exc = mono_get_exception_thread_abort ();
1098 /* fixme: store the state somewhere */
1100 #ifdef PTHREAD_POINTER_ID
1101 pthread_kill (GUINT_TO_POINTER(thread->tid), SIGUSR1);
1103 pthread_kill (thread->tid, SIGUSR1);
1106 g_assert_not_reached ();
1111 ves_icall_System_Threading_Thread_ResetAbort (void)
1113 MONO_ARCH_SAVE_REGS;
1115 MonoThread *thread = mono_thread_current ();
1117 if (!thread->abort_exc) {
1118 const char *msg = "Unable to reset abort because no abort was requested";
1119 mono_raise_exception (mono_get_exception_thread_state (msg));
1121 thread->abort_exc = NULL;
1122 thread->abort_state = NULL;
1126 void mono_thread_init (MonoDomain *domain, MonoThreadStartCB start_cb,
1127 MonoThreadStartCB attach_cb)
1129 /* Build a System.Threading.Thread object instance to return
1130 * for the main line's Thread.CurrentThread property.
1133 /* I wonder what happens if someone tries to destroy this
1134 * object? In theory, I guess the whole program should act as
1135 * though exit() were called :-)
1138 g_message(G_GNUC_PRETTY_FUNCTION
1139 ": Starting to build main Thread object");
1141 main_thread = (MonoThread *)mono_object_new (domain, mono_defaults.thread_class);
1143 main_thread->handle = GetCurrentThread ();
1146 g_message(G_GNUC_PRETTY_FUNCTION
1147 ": Finished building main Thread object: %p", main_thread);
1150 InitializeCriticalSection(&threads_mutex);
1151 InitializeCriticalSection(&monitor_mutex);
1152 InitializeCriticalSection(&interlocked_mutex);
1154 current_object_key=TlsAlloc();
1156 g_message (G_GNUC_PRETTY_FUNCTION ": Allocated current_object_key %d",
1157 current_object_key);
1160 TlsSetValue(current_object_key, main_thread);
1162 mono_thread_start_cb = start_cb;
1163 mono_thread_attach_cb = attach_cb;
1165 slothash_key=TlsAlloc();
1167 /* Get a pseudo handle to the current process. This is just a
1168 * kludge so that wapi can build a process handle if needed.
1169 * As a pseudo handle is returned, we don't need to clean
1172 GetCurrentProcess ();
1176 static void print_tids (gpointer key, gpointer value, gpointer user)
1178 g_message ("Waiting for: %d", *(guint32 *)key);
1184 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
1185 MonoThread *threads[MAXIMUM_WAIT_OBJECTS];
1189 static void wait_for_tids (struct wait_data *wait)
1194 g_message(G_GNUC_PRETTY_FUNCTION
1195 ": %d threads to wait for in this batch", wait->num);
1198 ret=WaitForMultipleObjects(wait->num, wait->handles, TRUE, INFINITE);
1199 if(ret==WAIT_FAILED) {
1200 /* See the comment in build_wait_tids() */
1202 g_message (G_GNUC_PRETTY_FUNCTION ": Wait failed");
1208 for(i=0; i<wait->num; i++) {
1209 guint32 tid=wait->threads[i]->tid;
1211 if(mono_g_hash_table_lookup (threads, &tid)!=NULL) {
1212 /* This thread must have been killed, because
1213 * it hasn't cleaned itself up. (It's just
1214 * possible that the thread exited before the
1215 * parent thread had a chance to store the
1216 * handle, and now there is another pointer to
1217 * the already-exited thread stored. In this
1218 * case, we'll just get two
1219 * mono_profiler_thread_end() calls for the
1224 g_message (G_GNUC_PRETTY_FUNCTION
1225 ": cleaning up after thread %d", tid);
1227 thread_cleanup (tid);
1232 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
1234 struct wait_data *wait=(struct wait_data *)user;
1236 if(wait->num<MAXIMUM_WAIT_OBJECTS) {
1237 MonoThread *thread=(MonoThread *)value;
1239 /* Theres a theoretical chance that thread->handle
1240 * might be NULL if the child thread has called
1241 * handle_store() but the parent thread hasn't set the
1242 * handle pointer yet. WaitForMultipleObjects will
1243 * fail, and we'll just go round the loop again. By
1244 * that time the handle should be stored properly.
1246 wait->handles[wait->num]=thread->handle;
1247 wait->threads[wait->num]=thread;
1250 /* Just ignore the rest, we can't do anything with
1256 void mono_thread_cleanup(void)
1258 struct wait_data *wait=g_new0 (struct wait_data, 1);
1260 /* join each thread that's still running */
1262 g_message(G_GNUC_PRETTY_FUNCTION ": Joining each running thread...");
1267 g_message(G_GNUC_PRETTY_FUNCTION ": No threads");
1273 EnterCriticalSection (&threads_mutex);
1275 g_message(G_GNUC_PRETTY_FUNCTION
1276 ":There are %d threads to join",
1277 mono_g_hash_table_size (threads));
1278 mono_g_hash_table_foreach (threads, print_tids, NULL);
1282 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
1284 LeaveCriticalSection (&threads_mutex);
1286 /* Something to wait for */
1287 wait_for_tids (wait);
1289 } while(wait->num>0);
1293 mono_g_hash_table_destroy(threads);