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 static guint32 start_wrapper(void *data)
55 struct StartInfo *start_info=(struct StartInfo *)data;
56 guint32 (*start_func)(void *);
60 g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper");
63 /* FIXME: GC problem here with recorded object
66 * This is recorded so CurrentThread can return the
69 TlsSetValue (current_object_key, start_info->obj);
70 start_func = start_info->func;
71 mono_domain_set (start_info->domain);
72 this = start_info->this;
78 g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper terminating");
84 static void handle_store(HANDLE thread)
87 g_message(G_GNUC_PRETTY_FUNCTION ": thread %p", thread);
90 EnterCriticalSection(&threads_mutex);
92 threads=g_ptr_array_new();
94 g_ptr_array_add(threads, thread);
95 LeaveCriticalSection(&threads_mutex);
98 static void handle_remove(HANDLE thread)
101 g_message(G_GNUC_PRETTY_FUNCTION ": thread %p", thread);
104 EnterCriticalSection(&threads_mutex);
105 g_ptr_array_remove_fast(threads, thread);
106 LeaveCriticalSection(&threads_mutex);
111 mono_thread_create (MonoDomain *domain, gpointer func)
113 MonoClassField *field;
115 HANDLE thread_handle;
116 struct StartInfo *start_info;
119 thread = mono_object_new (domain, mono_defaults.thread_class);
121 field=mono_class_get_field_from_name(mono_defaults.thread_class, "system_thread_handle");
124 start_info=g_new0 (struct StartInfo, 1);
125 start_info->func = func;
126 start_info->obj = thread;
127 start_info->domain = domain;
129 thread_handle = CreateThread(NULL, 0, start_wrapper, start_info, 0, &tid);
130 g_assert (thread_handle);
132 *(gpointer *)(((char *)thread) + field->offset) = thread_handle;
135 * This thread is not added to the threads array: why?
137 mono_profiler_thread_start (thread_handle);
141 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoObject *this,
144 MonoMulticastDelegate *delegate = (MonoMulticastDelegate*)start;
145 guint32 (*start_func)(void *);
146 struct StartInfo *start_info;
151 g_message(G_GNUC_PRETTY_FUNCTION
152 ": Trying to start a new thread: this (%p) start (%p)",
156 start_func = delegate->delegate.method_ptr;
158 if(start_func==NULL) {
159 g_warning(G_GNUC_PRETTY_FUNCTION
160 ": Can't locate start method!");
163 /* This is freed in start_wrapper */
164 start_info = g_new0 (struct StartInfo, 1);
165 start_info->func = start_func;
166 start_info->this = delegate->delegate.target;
167 start_info->obj = this;
168 start_info->domain = mono_domain_get ();
170 thread=CreateThread(NULL, 0, start_wrapper, start_info,
171 CREATE_SUSPENDED, &tid);
173 g_warning(G_GNUC_PRETTY_FUNCTION
174 ": CreateThread error 0x%x", GetLastError());
179 g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d",
183 /* Store handle for cleanup later */
184 handle_store(thread);
185 mono_profiler_thread_start (thread);
191 void ves_icall_System_Threading_Thread_Start_internal(MonoObject *this,
195 g_message(G_GNUC_PRETTY_FUNCTION ": Launching thread %p", this);
198 ResumeThread(thread);
201 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
204 g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
210 MonoAppDomain *ves_icall_System_Threading_Thread_CurrentThreadDomain_internal(void)
212 /* return the current app */
213 return mono_domain_get()->domain;
216 MonoObject *ves_icall_System_Threading_Thread_CurrentThread_internal(void)
220 /* Find the current thread object */
221 thread=TlsGetValue(current_object_key);
225 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoObject *this,
226 int ms, HANDLE thread)
234 ret=WaitForSingleObject(thread, ms);
235 if(ret==WAIT_OBJECT_0) {
236 /* is the handle still valid at this point? */
237 mono_profiler_thread_end (thread);
238 /* Clean up the handle */
239 handle_remove(thread);
246 void ves_icall_System_Threading_Thread_SlotHash_store(MonoObject *data)
249 g_message(G_GNUC_PRETTY_FUNCTION ": Storing key %p", data);
252 /* Object location stored here */
253 TlsSetValue(slothash_key, data);
256 MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void)
260 data=TlsGetValue(slothash_key);
263 g_message(G_GNUC_PRETTY_FUNCTION ": Retrieved key %p", data);
269 static MonoThreadsSync *mon_new(void)
271 MonoThreadsSync *new;
273 /* This should be freed when the object that owns it is
277 new=(MonoThreadsSync *)g_new0(MonoThreadsSync, 1);
278 new->monitor=CreateMutex(NULL, FALSE, NULL);
279 #ifdef THREAD_LOCK_DEBUG
280 g_message(G_GNUC_PRETTY_FUNCTION ": ThreadsSync mutex created: %p",
284 new->waiters_count=0;
285 new->was_broadcast=FALSE;
286 InitializeCriticalSection(&new->waiters_count_lock);
287 new->sema=CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
288 new->waiters_done=CreateEvent(NULL, FALSE, FALSE, NULL);
293 gboolean ves_icall_System_Threading_Monitor_Monitor_try_enter(MonoObject *obj,
296 MonoThreadsSync *mon;
299 #ifdef THREAD_LOCK_DEBUG
300 g_message(G_GNUC_PRETTY_FUNCTION
301 ": Trying to lock %p in thread %d", obj,
302 GetCurrentThreadId());
305 EnterCriticalSection(&monitor_mutex);
307 mon=obj->synchronisation;
310 obj->synchronisation=mon;
313 /* Don't hold the monitor lock while waiting to acquire the
316 LeaveCriticalSection(&monitor_mutex);
318 /* Acquire the mutex */
319 #ifdef THREAD_LOCK_DEBUG
320 g_message(G_GNUC_PRETTY_FUNCTION ": Acquiring monitor mutex %p",
323 ret=WaitForSingleObject(mon->monitor, ms);
324 if(ret==WAIT_OBJECT_0) {
326 mon->tid=GetCurrentThreadId();
328 #ifdef THREAD_LOCK_DEBUG
329 g_message(G_GNUC_PRETTY_FUNCTION ": %p now locked %d times", obj,
339 void ves_icall_System_Threading_Monitor_Monitor_exit(MonoObject *obj)
341 MonoThreadsSync *mon;
343 #ifdef THREAD_LOCK_DEBUG
344 g_message(G_GNUC_PRETTY_FUNCTION ": Unlocking %p in thread %d", obj,
345 GetCurrentThreadId());
348 /* No need to lock monitor_mutex here because we only adjust
349 * the monitor state if this thread already owns the lock
351 mon=obj->synchronisation;
357 if(mon->tid!=GetCurrentThreadId()) {
363 #ifdef THREAD_LOCK_DEBUG
364 g_message(G_GNUC_PRETTY_FUNCTION ": %p now locked %d times", obj,
369 mon->tid=0; /* FIXME: check that 0 isnt a valid id */
372 #ifdef THREAD_LOCK_DEBUG
373 g_message(G_GNUC_PRETTY_FUNCTION ": Releasing mutex %p", mon->monitor);
376 ReleaseMutex(mon->monitor);
379 gboolean ves_icall_System_Threading_Monitor_Monitor_test_owner(MonoObject *obj)
381 MonoThreadsSync *mon;
384 #ifdef THREAD_LOCK_DEBUG
385 g_message(G_GNUC_PRETTY_FUNCTION
386 ": Testing if %p is owned by thread %d", obj,
387 GetCurrentThreadId());
390 EnterCriticalSection(&monitor_mutex);
392 mon=obj->synchronisation;
397 if(mon->tid!=GetCurrentThreadId()) {
404 LeaveCriticalSection(&monitor_mutex);
409 gboolean ves_icall_System_Threading_Monitor_Monitor_test_synchronised(MonoObject *obj)
411 MonoThreadsSync *mon;
414 #ifdef THREAD_LOCK_DEBUG
415 g_message(G_GNUC_PRETTY_FUNCTION
416 ": Testing if %p is owned by any thread", obj);
419 EnterCriticalSection(&monitor_mutex);
421 mon=obj->synchronisation;
430 g_assert(mon->count);
435 LeaveCriticalSection(&monitor_mutex);
441 void ves_icall_System_Threading_Monitor_Monitor_pulse(MonoObject *obj)
443 gboolean have_waiters;
444 MonoThreadsSync *mon;
446 #ifdef THREAD_LOCK_DEBUG
447 g_message("Pulsing %p in thread %d", obj, GetCurrentThreadId());
450 EnterCriticalSection(&monitor_mutex);
452 mon=obj->synchronisation;
454 LeaveCriticalSection(&monitor_mutex);
458 if(mon->tid!=GetCurrentThreadId()) {
459 LeaveCriticalSection(&monitor_mutex);
462 LeaveCriticalSection(&monitor_mutex);
464 EnterCriticalSection(&mon->waiters_count_lock);
465 have_waiters=(mon->waiters_count>0);
466 LeaveCriticalSection(&mon->waiters_count_lock);
468 if(have_waiters==TRUE) {
469 ReleaseSemaphore(mon->sema, 1, 0);
473 void ves_icall_System_Threading_Monitor_Monitor_pulse_all(MonoObject *obj)
475 gboolean have_waiters=FALSE;
476 MonoThreadsSync *mon;
478 #ifdef THREAD_LOCK_DEBUG
479 g_message("Pulsing all %p", obj);
482 EnterCriticalSection(&monitor_mutex);
484 mon=obj->synchronisation;
486 LeaveCriticalSection(&monitor_mutex);
490 if(mon->tid!=GetCurrentThreadId()) {
491 LeaveCriticalSection(&monitor_mutex);
494 LeaveCriticalSection(&monitor_mutex);
496 EnterCriticalSection(&mon->waiters_count_lock);
497 if(mon->waiters_count>0) {
498 mon->was_broadcast=TRUE;
502 if(have_waiters==TRUE) {
503 ReleaseSemaphore(mon->sema, mon->waiters_count, 0);
505 LeaveCriticalSection(&mon->waiters_count_lock);
507 WaitForSingleObject(mon->waiters_done, INFINITE);
508 mon->was_broadcast=FALSE;
510 LeaveCriticalSection(&mon->waiters_count_lock);
514 gboolean ves_icall_System_Threading_Monitor_Monitor_wait(MonoObject *obj,
517 gboolean last_waiter;
518 MonoThreadsSync *mon;
521 #ifdef THREAD_LOCK_DEBUG
522 g_message("Trying to wait for %p in thread %d with timeout %dms", obj,
523 GetCurrentThreadId(), ms);
526 EnterCriticalSection(&monitor_mutex);
528 mon=obj->synchronisation;
530 LeaveCriticalSection(&monitor_mutex);
534 if(mon->tid!=GetCurrentThreadId()) {
535 LeaveCriticalSection(&monitor_mutex);
538 LeaveCriticalSection(&monitor_mutex);
540 EnterCriticalSection(&mon->waiters_count_lock);
541 mon->waiters_count++;
542 LeaveCriticalSection(&mon->waiters_count_lock);
544 #ifdef THREAD_LOCK_DEBUG
545 g_message(G_GNUC_PRETTY_FUNCTION ": %p locked %d times", obj,
549 /* We need to put the lock count back afterwards */
550 save_count=mon->count;
552 while(mon->count>1) {
553 #ifdef THREAD_LOCK_DEBUG
554 g_message(G_GNUC_PRETTY_FUNCTION ": Releasing mutex %p",
558 ReleaseMutex(mon->monitor);
562 /* We're releasing this mutex */
565 #ifdef THREAD_LOCK_DEBUG
566 g_message(G_GNUC_PRETTY_FUNCTION ": Signalling monitor mutex %p",
570 SignalObjectAndWait(mon->monitor, mon->sema, INFINITE, FALSE);
572 EnterCriticalSection(&mon->waiters_count_lock);
573 mon->waiters_count++;
574 last_waiter=mon->was_broadcast && mon->waiters_count==0;
575 LeaveCriticalSection(&mon->waiters_count_lock);
578 #ifdef THREAD_LOCK_DEBUG
579 g_message(G_GNUC_PRETTY_FUNCTION ": Waiting for monitor mutex %p",
582 SignalObjectAndWait(mon->waiters_done, mon->monitor, INFINITE, FALSE);
584 #ifdef THREAD_LOCK_DEBUG
585 g_message(G_GNUC_PRETTY_FUNCTION ": Waiting for monitor mutex %p",
588 WaitForSingleObject(mon->monitor, INFINITE);
591 /* We've reclaimed this mutex */
592 mon->count=save_count;
593 mon->tid=GetCurrentThreadId();
595 /* Lock the mutex the required number of times */
596 while(save_count>1) {
597 #ifdef THREAD_LOCK_DEBUG
598 g_message(G_GNUC_PRETTY_FUNCTION
599 ": Waiting for monitor mutex %p", mon->monitor);
601 WaitForSingleObject(mon->monitor, INFINITE);
605 #ifdef THREAD_LOCK_DEBUG
606 g_message(G_GNUC_PRETTY_FUNCTION ": %p still locked %d times", obj,
613 /* FIXME: exitContext isnt documented */
614 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
621 numhandles=mono_array_length(mono_handles);
622 handles=g_new0(HANDLE, numhandles);
623 for(i=0; i<numhandles; i++) {
624 handles[i]=mono_array_get(mono_handles, HANDLE, i);
631 ret=WaitForMultipleObjects(numhandles, handles, TRUE, ms);
635 if(ret==WAIT_FAILED) {
636 #ifdef THREAD_WAIT_DEBUG
637 g_message(G_GNUC_PRETTY_FUNCTION ": Wait failed");
640 } else if(ret==WAIT_TIMEOUT) {
641 #ifdef THREAD_WAIT_DEBUG
642 g_message(G_GNUC_PRETTY_FUNCTION ": Wait timed out");
650 /* FIXME: exitContext isnt documented */
651 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
658 numhandles=mono_array_length(mono_handles);
659 handles=g_new0(HANDLE, numhandles);
660 for(i=0; i<numhandles; i++) {
661 handles[i]=mono_array_get(mono_handles, HANDLE, i);
668 ret=WaitForMultipleObjects(numhandles, handles, FALSE, ms);
672 #ifdef THREAD_WAIT_DEBUG
673 g_message(G_GNUC_PRETTY_FUNCTION ": returning %d", ret);
679 /* FIXME: exitContext isnt documented */
680 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
684 #ifdef THREAD_WAIT_DEBUG
685 g_message(G_GNUC_PRETTY_FUNCTION ": waiting for %p", handle);
692 ret=WaitForSingleObject(handle, ms);
693 if(ret==WAIT_FAILED) {
694 #ifdef THREAD_WAIT_DEBUG
695 g_message(G_GNUC_PRETTY_FUNCTION ": Wait failed");
698 } else if(ret==WAIT_TIMEOUT) {
699 #ifdef THREAD_WAIT_DEBUG
700 g_message(G_GNUC_PRETTY_FUNCTION ": Wait timed out");
708 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned,char *name) {
709 return(CreateMutex(NULL,owned,name));
712 void ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) {
713 ReleaseMutex(handle);
716 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual,
719 return (CreateEvent(NULL,manual,initial,name));
722 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
723 return (SetEvent(handle));
726 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
727 return (ResetEvent(handle));
730 void mono_thread_init(MonoDomain *domain)
732 MonoClass *thread_class;
734 /* Build a System.Threading.Thread object instance to return
735 * for the main line's Thread.CurrentThread property.
737 thread_class=mono_class_from_name(mono_defaults.corlib, "System.Threading", "Thread");
739 /* I wonder what happens if someone tries to destroy this
740 * object? In theory, I guess the whole program should act as
741 * though exit() were called :-)
744 g_message(G_GNUC_PRETTY_FUNCTION
745 ": Starting to build main Thread object");
747 main_thread = mono_object_new (domain, thread_class);
749 g_message(G_GNUC_PRETTY_FUNCTION
750 ": Finished to building main Thread object");
753 InitializeCriticalSection(&threads_mutex);
754 InitializeCriticalSection(&monitor_mutex);
756 current_object_key=TlsAlloc();
757 TlsSetValue(current_object_key, main_thread);
759 slothash_key=TlsAlloc();
762 void mono_thread_cleanup(void)
764 HANDLE wait[MAXIMUM_WAIT_OBJECTS];
767 /* join each thread that's still running */
769 g_message("Joining each running thread...");
774 g_message("No threads");
779 /* This isnt the right way to do it.
781 * The first method call should be started in its own thread,
782 * and then the main thread should poll an event and wait for
783 * any terminated threads, until there are none left.
786 g_message("There are %d threads to join", threads->len);
787 for(i=0; i<threads->len; i++) {
788 g_message("Waiting for: %p", g_ptr_array_index(threads, i));
792 for(i=0; i<threads->len; i+=MAXIMUM_WAIT_OBJECTS) {
793 for(j=0; j<MAXIMUM_WAIT_OBJECTS && i+j<threads->len; j++) {
795 g_message("Waiting for threads %d in slot %d", i+j, j);
797 wait[j]=g_ptr_array_index(threads, i+j);
800 g_message("%d threads to wait for in this batch", j);
803 WaitForMultipleObjects(j, wait, TRUE, INFINITE);
806 g_ptr_array_free(threads, FALSE);