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/threads.h>
15 #include <mono/metadata/threads-types.h>
16 #include <mono/io-layer/io-layer.h>
19 #undef THREAD_LOCK_DEBUG
20 #undef THREAD_WAIT_DEBUG
24 guint32 (*func)(void *);
28 /* Controls access to the 'threads' array */
29 static CRITICAL_SECTION threads_mutex;
31 /* Controls access to the sync field in MonoObjects, to avoid race
32 * conditions when adding sync data to an object for the first time.
34 static CRITICAL_SECTION monitor_mutex;
36 /* The array of existing threads that need joining before exit */
37 static GPtrArray *threads=NULL;
39 /* The MonoObject associated with the main thread */
40 static MonoObject *main_thread;
42 /* The TLS key that holds the MonoObject assigned to each thread */
43 static guint32 current_object_key;
45 /* The TLS key that holds the LocalDataStoreSlot hash in each thread */
46 static guint32 slothash_key;
48 static guint32 start_wrapper(void *data)
50 struct StartInfo *start_info=(struct StartInfo *)data;
51 guint32 (*start_func)(void *);
54 g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper");
57 /* FIXME: GC problem here with recorded object
60 * This is recorded so CurrentThread can return the
63 TlsSetValue(current_object_key, start_info->obj);
64 start_func=start_info->func;
71 g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper terminating");
77 static void handle_store(HANDLE thread)
79 EnterCriticalSection(&threads_mutex);
81 threads=g_ptr_array_new();
83 g_ptr_array_add(threads, thread);
84 LeaveCriticalSection(&threads_mutex);
87 static void handle_remove(HANDLE thread)
89 EnterCriticalSection(&threads_mutex);
90 g_ptr_array_remove_fast(threads, thread);
91 LeaveCriticalSection(&threads_mutex);
96 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoObject *this,
99 MonoClassField *field;
100 guint32 (*start_func)(void *);
101 struct StartInfo *start_info;
106 g_message(G_GNUC_PRETTY_FUNCTION
107 ": Trying to start a new thread: this (%p) start (%p)",
111 field=mono_class_get_field_from_name(mono_defaults.delegate_class, "method_ptr");
112 start_func= *(gpointer *)(((char *)start) + field->offset);
114 if(start_func==NULL) {
115 g_warning(G_GNUC_PRETTY_FUNCTION
116 ": Can't locate start method!");
119 /* This is freed in start_wrapper */
120 start_info=g_new0(struct StartInfo, 1);
121 start_info->func=start_func;
122 start_info->obj=this;
124 thread=CreateThread(NULL, 0, start_wrapper, start_info,
125 CREATE_SUSPENDED, &tid);
127 g_warning(G_GNUC_PRETTY_FUNCTION
128 ": CreateThread error 0x%x", GetLastError());
133 g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d",
137 /* Store handle for cleanup later */
138 handle_store(thread);
144 void ves_icall_System_Threading_Thread_Start_internal(MonoObject *this,
148 g_message(G_GNUC_PRETTY_FUNCTION ": Launching thread %p", this);
151 ResumeThread(thread);
154 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
157 g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
163 MonoObject *ves_icall_System_Threading_Thread_CurrentThread_internal(void)
167 /* Find the current thread object */
168 thread=TlsGetValue(current_object_key);
172 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoObject *this,
173 int ms, HANDLE thread)
177 /* FIXME: make sure .net's idea of INFINITE is the same as in C */
178 ret=WaitForSingleObject(thread, ms);
179 if(ret==WAIT_OBJECT_0) {
180 /* Clean up the handle */
181 handle_remove(thread);
188 void ves_icall_System_Threading_Thread_SlotHash_store(MonoObject *data)
191 g_message(G_GNUC_PRETTY_FUNCTION ": Storing key %p", data);
194 /* Object location stored here */
195 TlsSetValue(slothash_key, data);
198 MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void)
202 data=TlsGetValue(slothash_key);
205 g_message(G_GNUC_PRETTY_FUNCTION ": Retrieved key %p", data);
211 static MonoThreadsSync *mon_new(void)
213 MonoThreadsSync *new;
215 /* This should be freed when the object that owns it is
219 new=(MonoThreadsSync *)g_new0(MonoThreadsSync, 1);
220 new->monitor=CreateMutex(NULL, FALSE, NULL);
222 new->waiters_count=0;
223 new->was_broadcast=FALSE;
224 InitializeCriticalSection(&new->waiters_count_lock);
225 new->sema=CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
226 new->waiters_done=CreateEvent(NULL, FALSE, FALSE, NULL);
231 gboolean ves_icall_System_Threading_Monitor_Monitor_try_enter(MonoObject *obj,
234 MonoThreadsSync *mon;
237 #ifdef THREAD_LOCK_DEBUG
238 g_message(G_GNUC_PRETTY_FUNCTION
239 ": Trying to lock %p in thread %d", obj,
240 GetCurrentThreadId());
243 EnterCriticalSection(&monitor_mutex);
245 mon=obj->synchronisation;
248 obj->synchronisation=mon;
251 /* Don't hold the monitor lock while waiting to acquire the
254 LeaveCriticalSection(&monitor_mutex);
256 /* Acquire the mutex */
257 ret=WaitForSingleObject(mon->monitor, ms);
258 if(ret==WAIT_OBJECT_0) {
260 mon->tid=GetCurrentThreadId();
262 #ifdef THREAD_LOCK_DEBUG
263 g_message(G_GNUC_PRETTY_FUNCTION ": %p now locked %d times", obj,
273 void ves_icall_System_Threading_Monitor_Monitor_exit(MonoObject *obj)
275 MonoThreadsSync *mon;
277 #ifdef THREAD_LOCK_DEBUG
278 g_message(G_GNUC_PRETTY_FUNCTION ": Unlocking %p in thread %d", obj,
279 GetCurrentThreadId());
282 /* No need to lock monitor_mutex here because we only adjust
283 * the monitor state if this thread already owns the lock
285 mon=obj->synchronisation;
291 if(mon->tid!=GetCurrentThreadId()) {
297 #ifdef THREAD_LOCK_DEBUG
298 g_message(G_GNUC_PRETTY_FUNCTION ": %p now locked %d times", obj,
303 mon->tid=0; /* FIXME: check that 0 isnt a valid id */
306 ReleaseMutex(mon->monitor);
309 gboolean ves_icall_System_Threading_Monitor_Monitor_test_owner(MonoObject *obj)
311 MonoThreadsSync *mon;
314 #ifdef THREAD_LOCK_DEBUG
315 g_message(G_GNUC_PRETTY_FUNCTION
316 ": Testing if %p is owned by thread %d", obj,
317 GetCurrentThreadId());
320 EnterCriticalSection(&monitor_mutex);
322 mon=obj->synchronisation;
327 if(mon->tid!=GetCurrentThreadId()) {
334 LeaveCriticalSection(&monitor_mutex);
339 gboolean ves_icall_System_Threading_Monitor_Monitor_test_synchronised(MonoObject *obj)
341 MonoThreadsSync *mon;
344 #ifdef THREAD_LOCK_DEBUG
345 g_message(G_GNUC_PRETTY_FUNCTION
346 ": Testing if %p is owned by any thread", obj);
349 EnterCriticalSection(&monitor_mutex);
351 mon=obj->synchronisation;
360 g_assert(mon->count);
365 LeaveCriticalSection(&monitor_mutex);
371 void ves_icall_System_Threading_Monitor_Monitor_pulse(MonoObject *obj)
373 gboolean have_waiters;
374 MonoThreadsSync *mon;
376 #ifdef THREAD_LOCK_DEBUG
377 g_message("Pulsing %p in thread %d", obj, GetCurrentThreadId());
380 EnterCriticalSection(&monitor_mutex);
382 mon=obj->synchronisation;
384 LeaveCriticalSection(&monitor_mutex);
388 if(mon->tid!=GetCurrentThreadId()) {
389 LeaveCriticalSection(&monitor_mutex);
392 LeaveCriticalSection(&monitor_mutex);
394 EnterCriticalSection(&mon->waiters_count_lock);
395 have_waiters=(mon->waiters_count>0);
396 LeaveCriticalSection(&mon->waiters_count_lock);
398 if(have_waiters==TRUE) {
399 ReleaseSemaphore(mon->sema, 1, 0);
403 void ves_icall_System_Threading_Monitor_Monitor_pulse_all(MonoObject *obj)
405 gboolean have_waiters=FALSE;
406 MonoThreadsSync *mon;
408 #ifdef THREAD_LOCK_DEBUG
409 g_message("Pulsing all %p", obj);
412 EnterCriticalSection(&monitor_mutex);
414 mon=obj->synchronisation;
416 LeaveCriticalSection(&monitor_mutex);
420 if(mon->tid!=GetCurrentThreadId()) {
421 LeaveCriticalSection(&monitor_mutex);
424 LeaveCriticalSection(&monitor_mutex);
426 EnterCriticalSection(&mon->waiters_count_lock);
427 if(mon->waiters_count>0) {
428 mon->was_broadcast=TRUE;
432 if(have_waiters==TRUE) {
433 ReleaseSemaphore(mon->sema, mon->waiters_count, 0);
435 LeaveCriticalSection(&mon->waiters_count_lock);
437 WaitForSingleObject(mon->waiters_done, INFINITE);
438 mon->was_broadcast=FALSE;
440 LeaveCriticalSection(&mon->waiters_count_lock);
444 gboolean ves_icall_System_Threading_Monitor_Monitor_wait(MonoObject *obj,
447 gboolean last_waiter;
448 MonoThreadsSync *mon;
451 #ifdef THREAD_LOCK_DEBUG
452 g_message("Trying to wait for %p in thread %d with timeout %dms", obj,
453 GetCurrentThreadId(), ms);
456 EnterCriticalSection(&monitor_mutex);
458 mon=obj->synchronisation;
460 LeaveCriticalSection(&monitor_mutex);
464 if(mon->tid!=GetCurrentThreadId()) {
465 LeaveCriticalSection(&monitor_mutex);
468 LeaveCriticalSection(&monitor_mutex);
470 EnterCriticalSection(&mon->waiters_count_lock);
471 mon->waiters_count++;
472 LeaveCriticalSection(&mon->waiters_count_lock);
474 #ifdef THREAD_LOCK_DEBUG
475 g_message(G_GNUC_PRETTY_FUNCTION ": %p locked %d times", obj,
479 /* We need to put the lock count back afterwards */
480 save_count=mon->count;
482 while(mon->count>1) {
483 ReleaseMutex(mon->monitor);
487 /* We're releasing this mutex */
490 SignalObjectAndWait(mon->monitor, mon->sema, INFINITE, FALSE);
492 EnterCriticalSection(&mon->waiters_count_lock);
493 mon->waiters_count++;
494 last_waiter=mon->was_broadcast && mon->waiters_count==0;
495 LeaveCriticalSection(&mon->waiters_count_lock);
498 SignalObjectAndWait(mon->waiters_done, mon->monitor, INFINITE, FALSE);
500 WaitForSingleObject(mon->monitor, INFINITE);
503 /* We've reclaimed this mutex */
504 mon->count=save_count;
505 mon->tid=GetCurrentThreadId();
507 /* Lock the mutex the required number of times */
508 while(save_count>1) {
509 WaitForSingleObject(mon->monitor, INFINITE);
513 #ifdef THREAD_LOCK_DEBUG
514 g_message(G_GNUC_PRETTY_FUNCTION ": %p still locked %d times", obj,
521 /* FIXME: exitContext isnt documented */
522 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
524 WapiHandle **handles;
529 numhandles=mono_array_length(mono_handles);
530 handles=g_new0(WapiHandle *, numhandles);
531 for(i=0; i<numhandles; i++) {
532 handles[i]=mono_array_get(mono_handles, WapiHandle *, i);
535 ret=WaitForMultipleObjects(numhandles, handles, TRUE, ms);
536 if(ret==WAIT_FAILED) {
537 #ifdef THREAD_WAIT_DEBUG
538 g_message(G_GNUC_PRETTY_FUNCTION ": Wait failed");
541 } else if(ret==WAIT_TIMEOUT) {
542 #ifdef THREAD_WAIT_DEBUG
543 g_message(G_GNUC_PRETTY_FUNCTION ": Wait timed out");
551 /* FIXME: exitContext isnt documented */
552 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
554 WapiHandle **handles;
559 numhandles=mono_array_length(mono_handles);
560 handles=g_new0(WapiHandle *, numhandles);
561 for(i=0; i<numhandles; i++) {
562 handles[i]=mono_array_get(mono_handles, WapiHandle *, i);
565 ret=WaitForMultipleObjects(numhandles, handles, FALSE, ms);
567 #ifdef THREAD_WAIT_DEBUG
568 g_message(G_GNUC_PRETTY_FUNCTION ": returning %d", ret);
574 /* FIXME: exitContext isnt documented */
575 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, WapiHandle *handle, gint32 ms, gboolean exitContext)
579 ret=WaitForSingleObject(handle, ms);
580 if(ret==WAIT_FAILED) {
581 #ifdef THREAD_WAIT_DEBUG
582 g_message(G_GNUC_PRETTY_FUNCTION ": Wait failed");
585 } else if(ret==WAIT_TIMEOUT) {
586 #ifdef THREAD_WAIT_DEBUG
587 g_message(G_GNUC_PRETTY_FUNCTION ": Wait timed out");
596 void mono_thread_init(void)
598 MonoClass *thread_class;
600 /* Build a System.Threading.Thread object instance to return
601 * for the main line's Thread.CurrentThread property.
603 thread_class=mono_class_from_name(mono_defaults.corlib, "System.Threading", "Thread");
605 /* I wonder what happens if someone tries to destroy this
606 * object? In theory, I guess the whole program should act as
607 * though exit() were called :-)
609 main_thread = mono_object_new (thread_class);
611 InitializeCriticalSection(&threads_mutex);
612 InitializeCriticalSection(&monitor_mutex);
614 current_object_key=TlsAlloc();
615 TlsSetValue(current_object_key, main_thread);
617 slothash_key=TlsAlloc();
620 void mono_thread_cleanup(void)
622 HANDLE wait[MAXIMUM_WAIT_OBJECTS];
625 /* join each thread that's still running */
627 g_message("Joining each running thread...");
632 g_message("No threads");
637 /* This isnt the right way to do it.
639 * The first method call should be started in its own thread,
640 * and then the main thread should poll an event and wait for
641 * any terminated threads, until there are none left.
643 for(i=0; i<threads->len; i++) {
644 for(j=0; j<MAXIMUM_WAIT_OBJECTS && i+j<threads->len; j++) {
645 wait[j]=g_ptr_array_index(threads, i);
647 WaitForMultipleObjects(j, wait, TRUE, INFINITE);
650 g_ptr_array_free(threads, FALSE);