2 * threads.c: Thread support internal calls
5 * Dick Porter (dick@ximian.com)
7 * (C) 2001 Ximian, Inc.
13 #include <mono/metadata/object.h>
14 #include <mono/metadata/appdomain.h>
15 #include <mono/metadata/threads.h>
16 #include <mono/metadata/threads-types.h>
17 #include <mono/io-layer/io-layer.h>
20 #undef THREAD_LOCK_DEBUG
21 #undef THREAD_WAIT_DEBUG
25 guint32 (*func)(void *);
30 /* Controls access to the 'threads' array */
31 static CRITICAL_SECTION threads_mutex;
33 /* Controls access to the sync field in MonoObjects, to avoid race
34 * conditions when adding sync data to an object for the first time.
36 static CRITICAL_SECTION monitor_mutex;
38 /* The array of existing threads that need joining before exit */
39 static GPtrArray *threads=NULL;
41 /* The MonoObject associated with the main thread */
42 static MonoObject *main_thread;
44 /* The TLS key that holds the MonoObject assigned to each thread */
45 static guint32 current_object_key;
47 /* The TLS key that holds the LocalDataStoreSlot hash in each thread */
48 static guint32 slothash_key;
50 static guint32 start_wrapper(void *data)
52 struct StartInfo *start_info=(struct StartInfo *)data;
53 guint32 (*start_func)(void *);
56 g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper");
59 /* FIXME: GC problem here with recorded object
62 * This is recorded so CurrentThread can return the
65 TlsSetValue(current_object_key, start_info->obj);
66 start_func=start_info->func;
67 mono_domain_set (start_info->domain);
74 g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper terminating");
80 static void handle_store(HANDLE thread)
83 g_message(G_GNUC_PRETTY_FUNCTION ": thread %p", thread);
86 EnterCriticalSection(&threads_mutex);
88 threads=g_ptr_array_new();
90 g_ptr_array_add(threads, thread);
91 LeaveCriticalSection(&threads_mutex);
94 static void handle_remove(HANDLE thread)
97 g_message(G_GNUC_PRETTY_FUNCTION ": thread %p", thread);
100 EnterCriticalSection(&threads_mutex);
101 g_ptr_array_remove_fast(threads, thread);
102 LeaveCriticalSection(&threads_mutex);
107 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoObject *this,
110 MonoClassField *field;
111 guint32 (*start_func)(void *);
112 struct StartInfo *start_info;
117 g_message(G_GNUC_PRETTY_FUNCTION
118 ": Trying to start a new thread: this (%p) start (%p)",
122 field=mono_class_get_field_from_name(mono_defaults.delegate_class, "method_ptr");
123 start_func= *(gpointer *)(((char *)start) + field->offset);
125 if(start_func==NULL) {
126 g_warning(G_GNUC_PRETTY_FUNCTION
127 ": Can't locate start method!");
130 /* This is freed in start_wrapper */
131 start_info=g_new0(struct StartInfo, 1);
132 start_info->func=start_func;
133 start_info->obj=this;
134 start_info->domain = mono_domain_get ();
136 thread=CreateThread(NULL, 0, start_wrapper, start_info,
137 CREATE_SUSPENDED, &tid);
139 g_warning(G_GNUC_PRETTY_FUNCTION
140 ": CreateThread error 0x%x", GetLastError());
145 g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d",
149 /* Store handle for cleanup later */
150 handle_store(thread);
156 void ves_icall_System_Threading_Thread_Start_internal(MonoObject *this,
160 g_message(G_GNUC_PRETTY_FUNCTION ": Launching thread %p", this);
163 ResumeThread(thread);
166 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
169 g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
175 MonoObject *ves_icall_System_Threading_Thread_CurrentThread_internal(void)
179 /* Find the current thread object */
180 thread=TlsGetValue(current_object_key);
184 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoObject *this,
185 int ms, HANDLE thread)
193 ret=WaitForSingleObject(thread, ms);
194 if(ret==WAIT_OBJECT_0) {
195 /* Clean up the handle */
196 handle_remove(thread);
203 void ves_icall_System_Threading_Thread_SlotHash_store(MonoObject *data)
206 g_message(G_GNUC_PRETTY_FUNCTION ": Storing key %p", data);
209 /* Object location stored here */
210 TlsSetValue(slothash_key, data);
213 MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void)
217 data=TlsGetValue(slothash_key);
220 g_message(G_GNUC_PRETTY_FUNCTION ": Retrieved key %p", data);
226 static MonoThreadsSync *mon_new(void)
228 MonoThreadsSync *new;
230 /* This should be freed when the object that owns it is
234 new=(MonoThreadsSync *)g_new0(MonoThreadsSync, 1);
235 new->monitor=CreateMutex(NULL, FALSE, NULL);
236 #ifdef THREAD_LOCK_DEBUG
237 g_message(G_GNUC_PRETTY_FUNCTION ": ThreadsSync mutex created: %p",
241 new->waiters_count=0;
242 new->was_broadcast=FALSE;
243 InitializeCriticalSection(&new->waiters_count_lock);
244 new->sema=CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
245 new->waiters_done=CreateEvent(NULL, FALSE, FALSE, NULL);
250 gboolean ves_icall_System_Threading_Monitor_Monitor_try_enter(MonoObject *obj,
253 MonoThreadsSync *mon;
256 #ifdef THREAD_LOCK_DEBUG
257 g_message(G_GNUC_PRETTY_FUNCTION
258 ": Trying to lock %p in thread %d", obj,
259 GetCurrentThreadId());
262 EnterCriticalSection(&monitor_mutex);
264 mon=obj->synchronisation;
267 obj->synchronisation=mon;
270 /* Don't hold the monitor lock while waiting to acquire the
273 LeaveCriticalSection(&monitor_mutex);
275 /* Acquire the mutex */
276 #ifdef THREAD_LOCK_DEBUG
277 g_message(G_GNUC_PRETTY_FUNCTION ": Acquiring monitor mutex %p",
280 ret=WaitForSingleObject(mon->monitor, ms);
281 if(ret==WAIT_OBJECT_0) {
283 mon->tid=GetCurrentThreadId();
285 #ifdef THREAD_LOCK_DEBUG
286 g_message(G_GNUC_PRETTY_FUNCTION ": %p now locked %d times", obj,
296 void ves_icall_System_Threading_Monitor_Monitor_exit(MonoObject *obj)
298 MonoThreadsSync *mon;
300 #ifdef THREAD_LOCK_DEBUG
301 g_message(G_GNUC_PRETTY_FUNCTION ": Unlocking %p in thread %d", obj,
302 GetCurrentThreadId());
305 /* No need to lock monitor_mutex here because we only adjust
306 * the monitor state if this thread already owns the lock
308 mon=obj->synchronisation;
314 if(mon->tid!=GetCurrentThreadId()) {
320 #ifdef THREAD_LOCK_DEBUG
321 g_message(G_GNUC_PRETTY_FUNCTION ": %p now locked %d times", obj,
326 mon->tid=0; /* FIXME: check that 0 isnt a valid id */
329 #ifdef THREAD_LOCK_DEBUG
330 g_message(G_GNUC_PRETTY_FUNCTION ": Releasing mutex %p", mon->monitor);
333 ReleaseMutex(mon->monitor);
336 gboolean ves_icall_System_Threading_Monitor_Monitor_test_owner(MonoObject *obj)
338 MonoThreadsSync *mon;
341 #ifdef THREAD_LOCK_DEBUG
342 g_message(G_GNUC_PRETTY_FUNCTION
343 ": Testing if %p is owned by thread %d", obj,
344 GetCurrentThreadId());
347 EnterCriticalSection(&monitor_mutex);
349 mon=obj->synchronisation;
354 if(mon->tid!=GetCurrentThreadId()) {
361 LeaveCriticalSection(&monitor_mutex);
366 gboolean ves_icall_System_Threading_Monitor_Monitor_test_synchronised(MonoObject *obj)
368 MonoThreadsSync *mon;
371 #ifdef THREAD_LOCK_DEBUG
372 g_message(G_GNUC_PRETTY_FUNCTION
373 ": Testing if %p is owned by any thread", obj);
376 EnterCriticalSection(&monitor_mutex);
378 mon=obj->synchronisation;
387 g_assert(mon->count);
392 LeaveCriticalSection(&monitor_mutex);
398 void ves_icall_System_Threading_Monitor_Monitor_pulse(MonoObject *obj)
400 gboolean have_waiters;
401 MonoThreadsSync *mon;
403 #ifdef THREAD_LOCK_DEBUG
404 g_message("Pulsing %p in thread %d", obj, GetCurrentThreadId());
407 EnterCriticalSection(&monitor_mutex);
409 mon=obj->synchronisation;
411 LeaveCriticalSection(&monitor_mutex);
415 if(mon->tid!=GetCurrentThreadId()) {
416 LeaveCriticalSection(&monitor_mutex);
419 LeaveCriticalSection(&monitor_mutex);
421 EnterCriticalSection(&mon->waiters_count_lock);
422 have_waiters=(mon->waiters_count>0);
423 LeaveCriticalSection(&mon->waiters_count_lock);
425 if(have_waiters==TRUE) {
426 ReleaseSemaphore(mon->sema, 1, 0);
430 void ves_icall_System_Threading_Monitor_Monitor_pulse_all(MonoObject *obj)
432 gboolean have_waiters=FALSE;
433 MonoThreadsSync *mon;
435 #ifdef THREAD_LOCK_DEBUG
436 g_message("Pulsing all %p", obj);
439 EnterCriticalSection(&monitor_mutex);
441 mon=obj->synchronisation;
443 LeaveCriticalSection(&monitor_mutex);
447 if(mon->tid!=GetCurrentThreadId()) {
448 LeaveCriticalSection(&monitor_mutex);
451 LeaveCriticalSection(&monitor_mutex);
453 EnterCriticalSection(&mon->waiters_count_lock);
454 if(mon->waiters_count>0) {
455 mon->was_broadcast=TRUE;
459 if(have_waiters==TRUE) {
460 ReleaseSemaphore(mon->sema, mon->waiters_count, 0);
462 LeaveCriticalSection(&mon->waiters_count_lock);
464 WaitForSingleObject(mon->waiters_done, INFINITE);
465 mon->was_broadcast=FALSE;
467 LeaveCriticalSection(&mon->waiters_count_lock);
471 gboolean ves_icall_System_Threading_Monitor_Monitor_wait(MonoObject *obj,
474 gboolean last_waiter;
475 MonoThreadsSync *mon;
478 #ifdef THREAD_LOCK_DEBUG
479 g_message("Trying to wait for %p in thread %d with timeout %dms", obj,
480 GetCurrentThreadId(), ms);
483 EnterCriticalSection(&monitor_mutex);
485 mon=obj->synchronisation;
487 LeaveCriticalSection(&monitor_mutex);
491 if(mon->tid!=GetCurrentThreadId()) {
492 LeaveCriticalSection(&monitor_mutex);
495 LeaveCriticalSection(&monitor_mutex);
497 EnterCriticalSection(&mon->waiters_count_lock);
498 mon->waiters_count++;
499 LeaveCriticalSection(&mon->waiters_count_lock);
501 #ifdef THREAD_LOCK_DEBUG
502 g_message(G_GNUC_PRETTY_FUNCTION ": %p locked %d times", obj,
506 /* We need to put the lock count back afterwards */
507 save_count=mon->count;
509 while(mon->count>1) {
510 #ifdef THREAD_LOCK_DEBUG
511 g_message(G_GNUC_PRETTY_FUNCTION ": Releasing mutex %p",
515 ReleaseMutex(mon->monitor);
519 /* We're releasing this mutex */
522 #ifdef THREAD_LOCK_DEBUG
523 g_message(G_GNUC_PRETTY_FUNCTION ": Signalling monitor mutex %p",
527 SignalObjectAndWait(mon->monitor, mon->sema, INFINITE, FALSE);
529 EnterCriticalSection(&mon->waiters_count_lock);
530 mon->waiters_count++;
531 last_waiter=mon->was_broadcast && mon->waiters_count==0;
532 LeaveCriticalSection(&mon->waiters_count_lock);
535 #ifdef THREAD_LOCK_DEBUG
536 g_message(G_GNUC_PRETTY_FUNCTION ": Waiting for monitor mutex %p",
539 SignalObjectAndWait(mon->waiters_done, mon->monitor, INFINITE, FALSE);
541 #ifdef THREAD_LOCK_DEBUG
542 g_message(G_GNUC_PRETTY_FUNCTION ": Waiting for monitor mutex %p",
545 WaitForSingleObject(mon->monitor, INFINITE);
548 /* We've reclaimed this mutex */
549 mon->count=save_count;
550 mon->tid=GetCurrentThreadId();
552 /* Lock the mutex the required number of times */
553 while(save_count>1) {
554 #ifdef THREAD_LOCK_DEBUG
555 g_message(G_GNUC_PRETTY_FUNCTION
556 ": Waiting for monitor mutex %p", mon->monitor);
558 WaitForSingleObject(mon->monitor, INFINITE);
562 #ifdef THREAD_LOCK_DEBUG
563 g_message(G_GNUC_PRETTY_FUNCTION ": %p still locked %d times", obj,
570 /* FIXME: exitContext isnt documented */
571 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
578 numhandles=mono_array_length(mono_handles);
579 handles=g_new0(HANDLE, numhandles);
580 for(i=0; i<numhandles; i++) {
581 handles[i]=mono_array_get(mono_handles, HANDLE, i);
588 ret=WaitForMultipleObjects(numhandles, handles, TRUE, ms);
592 if(ret==WAIT_FAILED) {
593 #ifdef THREAD_WAIT_DEBUG
594 g_message(G_GNUC_PRETTY_FUNCTION ": Wait failed");
597 } else if(ret==WAIT_TIMEOUT) {
598 #ifdef THREAD_WAIT_DEBUG
599 g_message(G_GNUC_PRETTY_FUNCTION ": Wait timed out");
607 /* FIXME: exitContext isnt documented */
608 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
615 numhandles=mono_array_length(mono_handles);
616 handles=g_new0(HANDLE, numhandles);
617 for(i=0; i<numhandles; i++) {
618 handles[i]=mono_array_get(mono_handles, HANDLE, i);
625 ret=WaitForMultipleObjects(numhandles, handles, FALSE, ms);
629 #ifdef THREAD_WAIT_DEBUG
630 g_message(G_GNUC_PRETTY_FUNCTION ": returning %d", ret);
636 /* FIXME: exitContext isnt documented */
637 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
641 #ifdef THREAD_WAIT_DEBUG
642 g_message(G_GNUC_PRETTY_FUNCTION ": waiting for %p", handle);
649 ret=WaitForSingleObject(handle, ms);
650 if(ret==WAIT_FAILED) {
651 #ifdef THREAD_WAIT_DEBUG
652 g_message(G_GNUC_PRETTY_FUNCTION ": Wait failed");
655 } else if(ret==WAIT_TIMEOUT) {
656 #ifdef THREAD_WAIT_DEBUG
657 g_message(G_GNUC_PRETTY_FUNCTION ": Wait timed out");
665 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned,char *name) {
666 return(CreateMutex(NULL,owned,name));
669 void ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) {
670 ReleaseMutex(handle);
673 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual,
\r
674 MonoBoolean initial,
\r
676 return (CreateEvent(NULL,manual,initial,name));
\r
679 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
\r
680 return (SetEvent(handle));
\r
683 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
\r
684 return (ResetEvent(handle));
\r
687 void mono_thread_init(MonoDomain *domain)
689 MonoClass *thread_class;
691 /* Build a System.Threading.Thread object instance to return
692 * for the main line's Thread.CurrentThread property.
694 thread_class=mono_class_from_name(mono_defaults.corlib, "System.Threading", "Thread");
696 /* I wonder what happens if someone tries to destroy this
697 * object? In theory, I guess the whole program should act as
698 * though exit() were called :-)
700 main_thread = mono_object_new (domain, thread_class);
702 InitializeCriticalSection(&threads_mutex);
703 InitializeCriticalSection(&monitor_mutex);
705 current_object_key=TlsAlloc();
706 TlsSetValue(current_object_key, main_thread);
708 slothash_key=TlsAlloc();
711 void mono_thread_cleanup(void)
713 HANDLE wait[MAXIMUM_WAIT_OBJECTS];
716 /* join each thread that's still running */
718 g_message("Joining each running thread...");
723 g_message("No threads");
728 /* This isnt the right way to do it.
730 * The first method call should be started in its own thread,
731 * and then the main thread should poll an event and wait for
732 * any terminated threads, until there are none left.
735 g_message("There are %d threads to join", threads->len);
738 for(i=0; i<threads->len; i+=MAXIMUM_WAIT_OBJECTS) {
739 for(j=0; j<MAXIMUM_WAIT_OBJECTS && i+j<threads->len; j++) {
741 g_message("Waiting for threads %d in slot %d", i+j, j);
743 wait[j]=g_ptr_array_index(threads, i+j);
746 g_message("%d threads to wait for in this batch", j);
749 WaitForMultipleObjects(j, wait, TRUE, INFINITE);
752 g_ptr_array_free(threads, FALSE);