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.
14 #include <mono/metadata/object.h>
15 #include <mono/metadata/appdomain.h>
16 #include <mono/metadata/profiler-private.h>
17 #include <mono/metadata/threads.h>
18 #include <mono/metadata/threads-types.h>
19 #include <mono/io-layer/io-layer.h>
22 #undef THREAD_LOCK_DEBUG
23 #undef THREAD_WAIT_DEBUG
27 guint32 (*func)(void *);
33 /* Controls access to the 'threads' array */
34 static CRITICAL_SECTION threads_mutex;
36 /* Controls access to the sync field in MonoObjects, to avoid race
37 * conditions when adding sync data to an object for the first time.
39 static CRITICAL_SECTION monitor_mutex;
41 /* The array of existing threads that need joining before exit */
42 static GPtrArray *threads=NULL;
44 /* The MonoObject associated with the main thread */
45 static MonoObject *main_thread;
47 /* The TLS key that holds the MonoObject assigned to each thread */
48 static guint32 current_object_key;
50 /* The TLS key that holds the LocalDataStoreSlot hash in each thread */
51 static guint32 slothash_key;
53 /* Spin lock for InterlockedXXX 64 bit functions */
54 static CRITICAL_SECTION interlocked_mutex;
56 static guint32 start_wrapper(void *data)
58 struct StartInfo *start_info=(struct StartInfo *)data;
59 guint32 (*start_func)(void *);
63 g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper");
66 /* FIXME: GC problem here with recorded object
69 * This is recorded so CurrentThread can return the
72 TlsSetValue (current_object_key, start_info->obj);
73 start_func = start_info->func;
74 mono_domain_set (start_info->domain);
75 this = start_info->this;
81 g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper terminating");
87 static void handle_store(HANDLE thread)
90 g_message(G_GNUC_PRETTY_FUNCTION ": thread %p", thread);
93 EnterCriticalSection(&threads_mutex);
95 threads=g_ptr_array_new();
97 g_ptr_array_add(threads, thread);
98 LeaveCriticalSection(&threads_mutex);
101 static void handle_remove(HANDLE thread)
104 g_message(G_GNUC_PRETTY_FUNCTION ": thread %p", thread);
107 EnterCriticalSection(&threads_mutex);
108 g_ptr_array_remove_fast(threads, thread);
109 LeaveCriticalSection(&threads_mutex);
114 mono_thread_create (MonoDomain *domain, gpointer func)
116 MonoClassField *field;
118 HANDLE thread_handle;
119 struct StartInfo *start_info;
122 thread = mono_object_new (domain, mono_defaults.thread_class);
124 field=mono_class_get_field_from_name(mono_defaults.thread_class, "system_thread_handle");
127 start_info=g_new0 (struct StartInfo, 1);
128 start_info->func = func;
129 start_info->obj = thread;
130 start_info->domain = domain;
132 thread_handle = CreateThread(NULL, 0, start_wrapper, start_info, 0, &tid);
133 g_assert (thread_handle);
135 *(gpointer *)(((char *)thread) + field->offset) = thread_handle;
138 * This thread is not added to the threads array: why?
140 mono_profiler_thread_start (thread_handle);
144 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoObject *this,
147 MonoMulticastDelegate *delegate = (MonoMulticastDelegate*)start;
148 guint32 (*start_func)(void *);
149 struct StartInfo *start_info;
154 g_message(G_GNUC_PRETTY_FUNCTION
155 ": Trying to start a new thread: this (%p) start (%p)",
159 start_func = delegate->delegate.method_ptr;
161 if(start_func==NULL) {
162 g_warning(G_GNUC_PRETTY_FUNCTION
163 ": Can't locate start method!");
166 /* This is freed in start_wrapper */
167 start_info = g_new0 (struct StartInfo, 1);
168 start_info->func = start_func;
169 start_info->this = delegate->delegate.target;
170 start_info->obj = this;
171 start_info->domain = mono_domain_get ();
173 thread=CreateThread(NULL, 0, start_wrapper, start_info,
174 CREATE_SUSPENDED, &tid);
176 g_warning(G_GNUC_PRETTY_FUNCTION
177 ": CreateThread error 0x%x", GetLastError());
182 g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d",
186 /* Store handle for cleanup later */
187 handle_store(thread);
188 mono_profiler_thread_start (thread);
194 void ves_icall_System_Threading_Thread_Start_internal(MonoObject *this,
198 g_message(G_GNUC_PRETTY_FUNCTION ": Launching thread %p", this);
201 ResumeThread(thread);
204 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
207 g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
213 MonoAppDomain *ves_icall_System_Threading_Thread_CurrentThreadDomain_internal(void)
215 /* return the current app */
216 return mono_domain_get()->domain;
219 MonoObject *ves_icall_System_Threading_Thread_CurrentThread_internal(void)
223 /* Find the current thread object */
224 thread=TlsGetValue(current_object_key);
228 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoObject *this,
229 int ms, HANDLE thread)
237 ret=WaitForSingleObject(thread, ms);
238 if(ret==WAIT_OBJECT_0) {
239 /* is the handle still valid at this point? */
240 mono_profiler_thread_end (thread);
241 /* Clean up the handle */
242 handle_remove(thread);
249 void ves_icall_System_Threading_Thread_SlotHash_store(MonoObject *data)
252 g_message(G_GNUC_PRETTY_FUNCTION ": Storing key %p", data);
255 /* Object location stored here */
256 TlsSetValue(slothash_key, data);
259 MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void)
263 data=TlsGetValue(slothash_key);
266 g_message(G_GNUC_PRETTY_FUNCTION ": Retrieved key %p", data);
272 static MonoThreadsSync *mon_new(void)
274 MonoThreadsSync *new;
276 /* This should be freed when the object that owns it is
280 new=(MonoThreadsSync *)g_new0(MonoThreadsSync, 1);
281 new->monitor=CreateMutex(NULL, FALSE, NULL);
282 #ifdef THREAD_LOCK_DEBUG
283 g_message(G_GNUC_PRETTY_FUNCTION ": ThreadsSync mutex created: %p",
287 new->waiters_count=0;
288 new->was_broadcast=FALSE;
289 InitializeCriticalSection(&new->waiters_count_lock);
290 new->sema=CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
291 new->waiters_done=CreateEvent(NULL, FALSE, FALSE, NULL);
296 gboolean ves_icall_System_Threading_Monitor_Monitor_try_enter(MonoObject *obj,
299 MonoThreadsSync *mon;
302 #ifdef THREAD_LOCK_DEBUG
303 g_message(G_GNUC_PRETTY_FUNCTION
304 ": Trying to lock %p in thread %d", obj,
305 GetCurrentThreadId());
308 EnterCriticalSection(&monitor_mutex);
310 mon=obj->synchronisation;
313 obj->synchronisation=mon;
316 /* Don't hold the monitor lock while waiting to acquire the
319 LeaveCriticalSection(&monitor_mutex);
321 /* Acquire the mutex */
322 #ifdef THREAD_LOCK_DEBUG
323 g_message(G_GNUC_PRETTY_FUNCTION ": Acquiring monitor mutex %p",
326 ret=WaitForSingleObject(mon->monitor, ms);
327 if(ret==WAIT_OBJECT_0) {
329 mon->tid=GetCurrentThreadId();
331 #ifdef THREAD_LOCK_DEBUG
332 g_message(G_GNUC_PRETTY_FUNCTION ": %p now locked %d times", obj,
342 void ves_icall_System_Threading_Monitor_Monitor_exit(MonoObject *obj)
344 MonoThreadsSync *mon;
346 #ifdef THREAD_LOCK_DEBUG
347 g_message(G_GNUC_PRETTY_FUNCTION ": Unlocking %p in thread %d", obj,
348 GetCurrentThreadId());
351 /* No need to lock monitor_mutex here because we only adjust
352 * the monitor state if this thread already owns the lock
354 mon=obj->synchronisation;
360 if(mon->tid!=GetCurrentThreadId()) {
366 #ifdef THREAD_LOCK_DEBUG
367 g_message(G_GNUC_PRETTY_FUNCTION ": %p now locked %d times", obj,
372 mon->tid=0; /* FIXME: check that 0 isnt a valid id */
375 #ifdef THREAD_LOCK_DEBUG
376 g_message(G_GNUC_PRETTY_FUNCTION ": Releasing mutex %p", mon->monitor);
379 ReleaseMutex(mon->monitor);
382 gboolean ves_icall_System_Threading_Monitor_Monitor_test_owner(MonoObject *obj)
384 MonoThreadsSync *mon;
387 #ifdef THREAD_LOCK_DEBUG
388 g_message(G_GNUC_PRETTY_FUNCTION
389 ": Testing if %p is owned by thread %d", obj,
390 GetCurrentThreadId());
393 EnterCriticalSection(&monitor_mutex);
395 mon=obj->synchronisation;
400 if(mon->tid!=GetCurrentThreadId()) {
407 LeaveCriticalSection(&monitor_mutex);
412 gboolean ves_icall_System_Threading_Monitor_Monitor_test_synchronised(MonoObject *obj)
414 MonoThreadsSync *mon;
417 #ifdef THREAD_LOCK_DEBUG
418 g_message(G_GNUC_PRETTY_FUNCTION
419 ": Testing if %p is owned by any thread", obj);
422 EnterCriticalSection(&monitor_mutex);
424 mon=obj->synchronisation;
433 g_assert(mon->count);
438 LeaveCriticalSection(&monitor_mutex);
444 void ves_icall_System_Threading_Monitor_Monitor_pulse(MonoObject *obj)
446 gboolean have_waiters;
447 MonoThreadsSync *mon;
449 #ifdef THREAD_LOCK_DEBUG
450 g_message("Pulsing %p in thread %d", obj, GetCurrentThreadId());
453 EnterCriticalSection(&monitor_mutex);
455 mon=obj->synchronisation;
457 LeaveCriticalSection(&monitor_mutex);
461 if(mon->tid!=GetCurrentThreadId()) {
462 LeaveCriticalSection(&monitor_mutex);
465 LeaveCriticalSection(&monitor_mutex);
467 EnterCriticalSection(&mon->waiters_count_lock);
468 have_waiters=(mon->waiters_count>0);
469 LeaveCriticalSection(&mon->waiters_count_lock);
471 if(have_waiters==TRUE) {
472 ReleaseSemaphore(mon->sema, 1, 0);
476 void ves_icall_System_Threading_Monitor_Monitor_pulse_all(MonoObject *obj)
478 gboolean have_waiters=FALSE;
479 MonoThreadsSync *mon;
481 #ifdef THREAD_LOCK_DEBUG
482 g_message("Pulsing all %p", obj);
485 EnterCriticalSection(&monitor_mutex);
487 mon=obj->synchronisation;
489 LeaveCriticalSection(&monitor_mutex);
493 if(mon->tid!=GetCurrentThreadId()) {
494 LeaveCriticalSection(&monitor_mutex);
497 LeaveCriticalSection(&monitor_mutex);
499 EnterCriticalSection(&mon->waiters_count_lock);
500 if(mon->waiters_count>0) {
501 mon->was_broadcast=TRUE;
505 if(have_waiters==TRUE) {
506 ReleaseSemaphore(mon->sema, mon->waiters_count, 0);
508 LeaveCriticalSection(&mon->waiters_count_lock);
510 WaitForSingleObject(mon->waiters_done, INFINITE);
511 mon->was_broadcast=FALSE;
513 LeaveCriticalSection(&mon->waiters_count_lock);
517 gboolean ves_icall_System_Threading_Monitor_Monitor_wait(MonoObject *obj,
520 gboolean last_waiter;
521 MonoThreadsSync *mon;
524 #ifdef THREAD_LOCK_DEBUG
525 g_message("Trying to wait for %p in thread %d with timeout %dms", obj,
526 GetCurrentThreadId(), ms);
529 EnterCriticalSection(&monitor_mutex);
531 mon=obj->synchronisation;
533 LeaveCriticalSection(&monitor_mutex);
537 if(mon->tid!=GetCurrentThreadId()) {
538 LeaveCriticalSection(&monitor_mutex);
541 LeaveCriticalSection(&monitor_mutex);
543 EnterCriticalSection(&mon->waiters_count_lock);
544 mon->waiters_count++;
545 LeaveCriticalSection(&mon->waiters_count_lock);
547 #ifdef THREAD_LOCK_DEBUG
548 g_message(G_GNUC_PRETTY_FUNCTION ": %p locked %d times", obj,
552 /* We need to put the lock count back afterwards */
553 save_count=mon->count;
555 while(mon->count>1) {
556 #ifdef THREAD_LOCK_DEBUG
557 g_message(G_GNUC_PRETTY_FUNCTION ": Releasing mutex %p",
561 ReleaseMutex(mon->monitor);
565 /* We're releasing this mutex */
568 #ifdef THREAD_LOCK_DEBUG
569 g_message(G_GNUC_PRETTY_FUNCTION ": Signalling monitor mutex %p",
573 SignalObjectAndWait(mon->monitor, mon->sema, INFINITE, FALSE);
575 EnterCriticalSection(&mon->waiters_count_lock);
576 mon->waiters_count++;
577 last_waiter=mon->was_broadcast && mon->waiters_count==0;
578 LeaveCriticalSection(&mon->waiters_count_lock);
581 #ifdef THREAD_LOCK_DEBUG
582 g_message(G_GNUC_PRETTY_FUNCTION ": Waiting for monitor mutex %p",
585 SignalObjectAndWait(mon->waiters_done, mon->monitor, INFINITE, FALSE);
587 #ifdef THREAD_LOCK_DEBUG
588 g_message(G_GNUC_PRETTY_FUNCTION ": Waiting for monitor mutex %p",
591 WaitForSingleObject(mon->monitor, INFINITE);
594 /* We've reclaimed this mutex */
595 mon->count=save_count;
596 mon->tid=GetCurrentThreadId();
598 /* Lock the mutex the required number of times */
599 while(save_count>1) {
600 #ifdef THREAD_LOCK_DEBUG
601 g_message(G_GNUC_PRETTY_FUNCTION
602 ": Waiting for monitor mutex %p", mon->monitor);
604 WaitForSingleObject(mon->monitor, INFINITE);
608 #ifdef THREAD_LOCK_DEBUG
609 g_message(G_GNUC_PRETTY_FUNCTION ": %p still locked %d times", obj,
616 /* FIXME: exitContext isnt documented */
617 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
624 numhandles=mono_array_length(mono_handles);
625 handles=g_new0(HANDLE, numhandles);
626 for(i=0; i<numhandles; i++) {
627 handles[i]=mono_array_get(mono_handles, HANDLE, i);
634 ret=WaitForMultipleObjects(numhandles, handles, TRUE, ms);
638 if(ret==WAIT_FAILED) {
639 #ifdef THREAD_WAIT_DEBUG
640 g_message(G_GNUC_PRETTY_FUNCTION ": Wait failed");
643 } else if(ret==WAIT_TIMEOUT) {
644 #ifdef THREAD_WAIT_DEBUG
645 g_message(G_GNUC_PRETTY_FUNCTION ": Wait timed out");
653 /* FIXME: exitContext isnt documented */
654 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
661 numhandles=mono_array_length(mono_handles);
662 handles=g_new0(HANDLE, numhandles);
663 for(i=0; i<numhandles; i++) {
664 handles[i]=mono_array_get(mono_handles, HANDLE, i);
671 ret=WaitForMultipleObjects(numhandles, handles, FALSE, ms);
675 #ifdef THREAD_WAIT_DEBUG
676 g_message(G_GNUC_PRETTY_FUNCTION ": returning %d", ret);
682 /* FIXME: exitContext isnt documented */
683 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
687 #ifdef THREAD_WAIT_DEBUG
688 g_message(G_GNUC_PRETTY_FUNCTION ": waiting for %p", handle);
695 ret=WaitForSingleObject(handle, ms);
696 if(ret==WAIT_FAILED) {
697 #ifdef THREAD_WAIT_DEBUG
698 g_message(G_GNUC_PRETTY_FUNCTION ": Wait failed");
701 } else if(ret==WAIT_TIMEOUT) {
702 #ifdef THREAD_WAIT_DEBUG
703 g_message(G_GNUC_PRETTY_FUNCTION ": Wait timed out");
711 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned,char *name) {
712 return(CreateMutex(NULL,owned,name));
715 void ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) {
716 ReleaseMutex(handle);
719 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual,
722 return (CreateEvent(NULL,manual,initial,name));
725 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
726 return (SetEvent(handle));
729 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
730 return (ResetEvent(handle));
733 void mono_thread_init(MonoDomain *domain)
735 MonoClass *thread_class;
737 /* Build a System.Threading.Thread object instance to return
738 * for the main line's Thread.CurrentThread property.
740 thread_class=mono_class_from_name(mono_defaults.corlib, "System.Threading", "Thread");
742 /* I wonder what happens if someone tries to destroy this
743 * object? In theory, I guess the whole program should act as
744 * though exit() were called :-)
747 g_message(G_GNUC_PRETTY_FUNCTION
748 ": Starting to build main Thread object");
750 main_thread = mono_object_new (domain, thread_class);
752 g_message(G_GNUC_PRETTY_FUNCTION
753 ": Finished to building main Thread object");
756 InitializeCriticalSection(&threads_mutex);
757 InitializeCriticalSection(&monitor_mutex);
758 InitializeCriticalSection(&interlocked_mutex);
760 current_object_key=TlsAlloc();
761 TlsSetValue(current_object_key, main_thread);
763 slothash_key=TlsAlloc();
766 void mono_thread_cleanup(void)
768 HANDLE wait[MAXIMUM_WAIT_OBJECTS];
771 /* join each thread that's still running */
773 g_message("Joining each running thread...");
778 g_message("No threads");
783 /* This isnt the right way to do it.
785 * The first method call should be started in its own thread,
786 * and then the main thread should poll an event and wait for
787 * any terminated threads, until there are none left.
790 g_message("There are %d threads to join", threads->len);
791 for(i=0; i<threads->len; i++) {
792 g_message("Waiting for: %p", g_ptr_array_index(threads, i));
796 for(i=0; i<threads->len; i+=MAXIMUM_WAIT_OBJECTS) {
797 for(j=0; j<MAXIMUM_WAIT_OBJECTS && i+j<threads->len; j++) {
799 g_message("Waiting for threads %d in slot %d", i+j, j);
801 wait[j]=g_ptr_array_index(threads, i+j);
804 g_message("%d threads to wait for in this batch", j);
807 WaitForMultipleObjects(j, wait, TRUE, INFINITE);
810 g_ptr_array_free(threads, FALSE);
814 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
816 return InterlockedIncrement (location);
819 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
824 EnterCriticalSection(&interlocked_mutex);
826 lowret = InterlockedIncrement((gint32 *) location);
828 highret = InterlockedIncrement((gint32 *) location + 1);
830 highret = *((gint32 *) location + 1);
832 LeaveCriticalSection(&interlocked_mutex);
834 return (gint64) highret << 32 | (gint64) lowret;
837 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int(gint32 *location)
839 return InterlockedDecrement(location);
842 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long(gint64 * location)
847 EnterCriticalSection(&interlocked_mutex);
849 lowret = InterlockedDecrement((gint32 *) location);
851 highret = InterlockedDecrement((gint32 *) location + 1);
853 highret = *((gint32 *) location + 1);
855 LeaveCriticalSection(&interlocked_mutex);
857 return (gint64) highret << 32 | (gint64) lowret;
860 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int(gint32 *location1, gint32 value)
862 return InterlockedExchange(location1, value);
865 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object(MonoObject **location1, MonoObject *value)
867 return (MonoObject *) InterlockedExchangePointer((gpointer *) location1, value);
870 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single(gfloat *location1, gfloat value)
872 return (gfloat) InterlockedExchange((gint32 *) location1, (gint32) value);
875 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location1, gint32 value, gint32 comparand)
877 return InterlockedCompareExchange(location1, value, comparand);
880 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object(MonoObject **location1, MonoObject *value, MonoObject *comparand)
882 return (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location1, value, comparand);
885 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single(gfloat *location1, gfloat value, gfloat comparand)
887 return (gfloat) InterlockedCompareExchange((gint32 *) location1, (gint32) value, (gint32) comparand);