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
23 guint32 (*func)(void *);
27 /* Controls access to the 'threads' array */
28 static CRITICAL_SECTION threads_mutex;
30 /* Controls access to the sync field in MonoObjects, to avoid race
31 * conditions when adding sync data to an object for the first time.
33 static CRITICAL_SECTION monitor_mutex;
35 /* The array of existing threads that need joining before exit */
36 static GPtrArray *threads=NULL;
38 /* The MonoObject associated with the main thread */
39 static MonoObject *main_thread;
41 /* The TLS key that holds the MonoObject assigned to each thread */
42 static guint32 current_object_key;
44 /* The TLS key that holds the LocalDataStoreSlot hash in each thread */
45 static guint32 slothash_key;
47 static guint32 start_wrapper(void *data)
49 struct StartInfo *start_info=(struct StartInfo *)data;
50 guint32 (*start_func)(void *);
53 g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper");
56 /* FIXME: GC problem here with recorded object
59 * This is recorded so CurrentThread can return the
62 TlsSetValue(current_object_key, start_info->obj);
63 start_func=start_info->func;
70 g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper terminating");
76 static void handle_store(HANDLE thread)
78 EnterCriticalSection(&threads_mutex);
80 threads=g_ptr_array_new();
82 g_ptr_array_add(threads, thread);
83 LeaveCriticalSection(&threads_mutex);
86 static void handle_remove(HANDLE thread)
88 EnterCriticalSection(&threads_mutex);
89 g_ptr_array_remove_fast(threads, thread);
90 LeaveCriticalSection(&threads_mutex);
95 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoObject *this,
98 MonoClassField *field;
99 guint32 (*start_func)(void *);
100 struct StartInfo *start_info;
105 g_message(G_GNUC_PRETTY_FUNCTION
106 ": Trying to start a new thread: this (%p) start (%p)",
110 field=mono_class_get_field_from_name(mono_defaults.delegate_class, "method_ptr");
111 start_func= *(gpointer *)(((char *)start) + field->offset);
113 if(start_func==NULL) {
114 g_warning(G_GNUC_PRETTY_FUNCTION
115 ": Can't locate start method!");
118 /* This is freed in start_wrapper */
119 start_info=g_new0(struct StartInfo, 1);
120 start_info->func=start_func;
121 start_info->obj=this;
123 thread=CreateThread(NULL, 0, start_wrapper, start_info,
124 CREATE_SUSPENDED, &tid);
126 g_warning(G_GNUC_PRETTY_FUNCTION
127 ": CreateThread error 0x%x", GetLastError());
132 g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d",
136 /* Store handle for cleanup later */
137 handle_store(thread);
143 void ves_icall_System_Threading_Thread_Start_internal(MonoObject *this,
147 g_message(G_GNUC_PRETTY_FUNCTION ": Launching thread %p", this);
150 ResumeThread(thread);
153 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
156 g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
162 MonoObject *ves_icall_System_Threading_Thread_CurrentThread_internal(void)
166 /* Find the current thread object */
167 thread=TlsGetValue(current_object_key);
171 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoObject *this,
172 int ms, HANDLE thread)
176 /* FIXME: make sure .net's idea of INFINITE is the same as in C */
177 ret=WaitForSingleObject(thread, ms);
178 if(ret==WAIT_OBJECT_0) {
179 /* Clean up the handle */
180 handle_remove(thread);
187 void ves_icall_System_Threading_Thread_SlotHash_store(MonoObject *data)
190 g_message(G_GNUC_PRETTY_FUNCTION ": Storing key %p", data);
193 /* Object location stored here */
194 TlsSetValue(slothash_key, data);
197 MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void)
201 data=TlsGetValue(slothash_key);
204 g_message(G_GNUC_PRETTY_FUNCTION ": Retrieved key %p", data);
210 static MonoThreadsSync *mon_new(void)
212 MonoThreadsSync *new;
214 /* This should be freed when the object that owns it is
218 new=(MonoThreadsSync *)g_new0(MonoThreadsSync, 1);
219 new->monitor=CreateMutex(NULL, FALSE, NULL);
221 new->waiters_count=0;
222 new->was_broadcast=FALSE;
223 InitializeCriticalSection(&new->waiters_count_lock);
224 new->sema=CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
225 new->waiters_done=CreateEvent(NULL, FALSE, FALSE, NULL);
230 gboolean ves_icall_System_Threading_Monitor_Monitor_try_enter(MonoObject *obj,
233 MonoThreadsSync *mon;
236 #ifdef THREAD_LOCK_DEBUG
237 g_message(G_GNUC_PRETTY_FUNCTION
238 ": Trying to lock %p in thread %d", obj,
239 GetCurrentThreadId());
242 EnterCriticalSection(&monitor_mutex);
244 mon=obj->synchronisation;
247 obj->synchronisation=mon;
250 /* Don't hold the monitor lock while waiting to acquire the
253 LeaveCriticalSection(&monitor_mutex);
255 /* Acquire the mutex */
256 ret=WaitForSingleObject(mon->monitor, ms);
257 if(ret==WAIT_OBJECT_0) {
259 mon->tid=GetCurrentThreadId();
261 #ifdef THREAD_LOCK_DEBUG
262 g_message(G_GNUC_PRETTY_FUNCTION ": %p now locked %d times", obj,
272 void ves_icall_System_Threading_Monitor_Monitor_exit(MonoObject *obj)
274 MonoThreadsSync *mon;
276 #ifdef THREAD_LOCK_DEBUG
277 g_message(G_GNUC_PRETTY_FUNCTION ": Unlocking %p in thread %d", obj,
278 GetCurrentThreadId());
281 /* No need to lock monitor_mutex here because we only adjust
282 * the monitor state if this thread already owns the lock
284 mon=obj->synchronisation;
290 if(mon->tid!=GetCurrentThreadId()) {
296 #ifdef THREAD_LOCK_DEBUG
297 g_message(G_GNUC_PRETTY_FUNCTION ": %p now locked %d times", obj,
302 mon->tid=0; /* FIXME: check that 0 isnt a valid id */
305 ReleaseMutex(mon->monitor);
308 gboolean ves_icall_System_Threading_Monitor_Monitor_test_owner(MonoObject *obj)
310 MonoThreadsSync *mon;
313 #ifdef THREAD_LOCK_DEBUG
314 g_message(G_GNUC_PRETTY_FUNCTION
315 ": Testing if %p is owned by thread %d", obj,
316 GetCurrentThreadId());
319 EnterCriticalSection(&monitor_mutex);
321 mon=obj->synchronisation;
326 if(mon->tid!=GetCurrentThreadId()) {
333 LeaveCriticalSection(&monitor_mutex);
338 gboolean ves_icall_System_Threading_Monitor_Monitor_test_synchronised(MonoObject *obj)
340 MonoThreadsSync *mon;
343 #ifdef THREAD_LOCK_DEBUG
344 g_message(G_GNUC_PRETTY_FUNCTION
345 ": Testing if %p is owned by any thread", obj);
348 EnterCriticalSection(&monitor_mutex);
350 mon=obj->synchronisation;
359 g_assert(mon->count);
364 LeaveCriticalSection(&monitor_mutex);
370 void ves_icall_System_Threading_Monitor_Monitor_pulse(MonoObject *obj)
372 gboolean have_waiters;
373 MonoThreadsSync *mon;
375 #ifdef THREAD_LOCK_DEBUG
376 g_message("Pulsing %p in thread %d", obj, GetCurrentThreadId());
379 EnterCriticalSection(&monitor_mutex);
381 mon=obj->synchronisation;
383 LeaveCriticalSection(&monitor_mutex);
387 if(mon->tid!=GetCurrentThreadId()) {
388 LeaveCriticalSection(&monitor_mutex);
391 LeaveCriticalSection(&monitor_mutex);
393 EnterCriticalSection(&mon->waiters_count_lock);
394 have_waiters=(mon->waiters_count>0);
395 LeaveCriticalSection(&mon->waiters_count_lock);
397 if(have_waiters==TRUE) {
398 ReleaseSemaphore(mon->sema, 1, 0);
402 void ves_icall_System_Threading_Monitor_Monitor_pulse_all(MonoObject *obj)
404 gboolean have_waiters=FALSE;
405 MonoThreadsSync *mon;
407 #ifdef THREAD_LOCK_DEBUG
408 g_message("Pulsing all %p", obj);
411 EnterCriticalSection(&monitor_mutex);
413 mon=obj->synchronisation;
415 LeaveCriticalSection(&monitor_mutex);
419 if(mon->tid!=GetCurrentThreadId()) {
420 LeaveCriticalSection(&monitor_mutex);
423 LeaveCriticalSection(&monitor_mutex);
425 EnterCriticalSection(&mon->waiters_count_lock);
426 if(mon->waiters_count>0) {
427 mon->was_broadcast=TRUE;
431 if(have_waiters==TRUE) {
432 ReleaseSemaphore(mon->sema, mon->waiters_count, 0);
434 LeaveCriticalSection(&mon->waiters_count_lock);
436 WaitForSingleObject(mon->waiters_done, INFINITE);
437 mon->was_broadcast=FALSE;
439 LeaveCriticalSection(&mon->waiters_count_lock);
443 gboolean ves_icall_System_Threading_Monitor_Monitor_wait(MonoObject *obj,
446 gboolean last_waiter;
447 MonoThreadsSync *mon;
450 #ifdef THREAD_LOCK_DEBUG
451 g_message("Trying to wait for %p in thread %d with timeout %dms", obj,
452 GetCurrentThreadId(), ms);
455 EnterCriticalSection(&monitor_mutex);
457 mon=obj->synchronisation;
459 LeaveCriticalSection(&monitor_mutex);
463 if(mon->tid!=GetCurrentThreadId()) {
464 LeaveCriticalSection(&monitor_mutex);
467 LeaveCriticalSection(&monitor_mutex);
469 EnterCriticalSection(&mon->waiters_count_lock);
470 mon->waiters_count++;
471 LeaveCriticalSection(&mon->waiters_count_lock);
473 #ifdef THREAD_LOCK_DEBUG
474 g_message(G_GNUC_PRETTY_FUNCTION ": %p locked %d times", obj,
478 /* We need to put the lock count back afterwards */
479 save_count=mon->count;
481 while(mon->count>1) {
482 ReleaseMutex(mon->monitor);
486 /* We're releasing this mutex */
489 SignalObjectAndWait(mon->monitor, mon->sema, INFINITE, FALSE);
491 EnterCriticalSection(&mon->waiters_count_lock);
492 mon->waiters_count++;
493 last_waiter=mon->was_broadcast && mon->waiters_count==0;
494 LeaveCriticalSection(&mon->waiters_count_lock);
497 SignalObjectAndWait(mon->waiters_done, mon->monitor, INFINITE, FALSE);
499 WaitForSingleObject(mon->monitor, INFINITE);
502 /* We've reclaimed this mutex */
503 mon->count=save_count;
504 mon->tid=GetCurrentThreadId();
506 /* Lock the mutex the required number of times */
507 while(save_count>1) {
508 WaitForSingleObject(mon->monitor, INFINITE);
512 #ifdef THREAD_LOCK_DEBUG
513 g_message(G_GNUC_PRETTY_FUNCTION ": %p still locked %d times", obj,
520 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *handles, gint32 ms, gboolean exitContext)
525 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *handles, gint32 ms, gboolean exitContext)
530 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, WapiHandle *handle, gint32 ms, gboolean exitContext)
536 void mono_thread_init(void)
538 MonoClass *thread_class;
540 /* Build a System.Threading.Thread object instance to return
541 * for the main line's Thread.CurrentThread property.
543 thread_class=mono_class_from_name(mono_defaults.corlib, "System.Threading", "Thread");
545 /* I wonder what happens if someone tries to destroy this
546 * object? In theory, I guess the whole program should act as
547 * though exit() were called :-)
549 main_thread = mono_object_new (thread_class);
551 InitializeCriticalSection(&threads_mutex);
552 InitializeCriticalSection(&monitor_mutex);
554 current_object_key=TlsAlloc();
555 TlsSetValue(current_object_key, main_thread);
557 slothash_key=TlsAlloc();
560 void mono_thread_cleanup(void)
562 HANDLE wait[MAXIMUM_WAIT_OBJECTS];
565 /* join each thread that's still running */
567 g_message("Joining each running thread...");
572 g_message("No threads");
577 /* This isnt the right way to do it.
579 * The first method call should be started in its own thread,
580 * and then the main thread should poll an event and wait for
581 * any terminated threads, until there are none left.
583 for(i=0; i<threads->len; i++) {
584 for(j=0; j<MAXIMUM_WAIT_OBJECTS && i+j<threads->len; j++) {
585 wait[j]=g_ptr_array_index(threads, i);
587 WaitForMultipleObjects(j, wait, TRUE, INFINITE);
590 g_ptr_array_free(threads, FALSE);