2002-11-28 Dietmar Maurer <dietmar@ximian.com>
[mono.git] / mono / metadata / threads.c
1 /*
2  * threads.c: Thread support internal calls
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *      Patrik Torstensson (patrik.torstensson@labs2.com)
7  *
8  * (C) 2001 Ximian, Inc.
9  */
10
11 #include <config.h>
12 #include <glib.h>
13 #include <signal.h>
14
15 #include <mono/metadata/object.h>
16 #include <mono/metadata/appdomain.h>
17 #include <mono/metadata/profiler-private.h>
18 #include <mono/metadata/threads.h>
19 #include <mono/metadata/threads-types.h>
20 #include <mono/metadata/exception.h>
21 #include <mono/io-layer/io-layer.h>
22
23 #include <mono/os/gc_wrapper.h>
24
25 #undef THREAD_DEBUG
26 #undef THREAD_LOCK_DEBUG
27 #undef THREAD_WAIT_DEBUG
28
29 struct StartInfo 
30 {
31         guint32 (*func)(void *);
32         MonoThread *obj;
33         void *this;
34         MonoDomain *domain;
35 };
36
37 typedef union {
38         gint32 ival;
39         gfloat fval;
40 } IntFloatUnion;
41  
42 /* Controls access to the 'threads' array */
43 static CRITICAL_SECTION threads_mutex;
44
45 /* Controls access to the sync field in MonoObjects, to avoid race
46  * conditions when adding sync data to an object for the first time.
47  */
48 static CRITICAL_SECTION monitor_mutex;
49
50 /* The hash of existing threads (key is thread ID) that need joining
51  * before exit
52  */
53 static MonoGHashTable *threads=NULL;
54
55 /* The MonoObject associated with the main thread */
56 static MonoThread *main_thread;
57
58 /* The TLS key that holds the MonoObject assigned to each thread */
59 static guint32 current_object_key;
60
61 /* function called at thread start */
62 static MonoThreadStartCB mono_thread_start_cb = NULL;
63
64 /* function called at thread attach */
65 static MonoThreadStartCB mono_thread_attach_cb = NULL;
66
67 /* The TLS key that holds the LocalDataStoreSlot hash in each thread */
68 static guint32 slothash_key;
69
70 /* Spin lock for InterlockedXXX 64 bit functions */
71 static CRITICAL_SECTION interlocked_mutex;
72
73 /* handle_store() and handle_remove() manage the array of threads that
74  * still need to be waited for when the main thread exits.
75  */
76 static void handle_store(MonoThread *thread)
77 {
78         EnterCriticalSection(&threads_mutex);
79
80 #ifdef THREAD_DEBUG
81         g_message(G_GNUC_PRETTY_FUNCTION ": thread %p ID %d", thread,
82                   thread->tid);
83 #endif
84
85         if(threads==NULL) {
86                 threads=mono_g_hash_table_new(g_int_hash, g_int_equal);
87         }
88
89         /* GHashTable will remove a previous entry if a duplicate key
90          * is stored, which is exactly what we want: we store the
91          * thread both in the start_wrapper (in the subthread), and as
92          * soon as possible in the parent thread.  This is to minimise
93          * the window in which the thread exists but we haven't
94          * recorded it.
95          */
96         
97         /* We don't need to duplicate thread->handle, because it is
98          * only closed when the thread object is finalized by the GC.
99          */
100         mono_g_hash_table_insert(threads, &thread->tid, thread);
101         LeaveCriticalSection(&threads_mutex);
102 }
103
104 static void handle_remove(guint32 tid)
105 {
106 #ifdef THREAD_DEBUG
107         g_message(G_GNUC_PRETTY_FUNCTION ": thread ID %d", tid);
108 #endif
109
110         EnterCriticalSection(&threads_mutex);
111
112         mono_g_hash_table_remove (threads, &tid);
113         
114         LeaveCriticalSection(&threads_mutex);
115
116         /* Don't close the handle here, wait for the object finalizer
117          * to do it. Otherwise, the following race condition applies:
118          *
119          * 1) Thread exits (and handle_remove() closes the handle)
120          *
121          * 2) Some other handle is reassigned the same slot
122          *
123          * 3) Another thread tries to join the first thread, and
124          * blocks waiting for the reassigned handle to be signalled
125          * (which might never happen).  This is possible, because the
126          * thread calling Join() still has a reference to the first
127          * thread's object.
128          */
129 }
130
131 static void thread_cleanup (guint32 tid)
132 {
133         mono_profiler_thread_end (tid);
134         handle_remove (tid);
135 }
136
137 static guint32 start_wrapper(void *data)
138 {
139         struct StartInfo *start_info=(struct StartInfo *)data;
140         guint32 (*start_func)(void *);
141         void *this;
142         guint32 tid;
143         
144 #ifdef THREAD_DEBUG
145         g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper");
146 #endif
147         
148         start_func = start_info->func;
149         mono_domain_set (start_info->domain);
150         this = start_info->this;
151
152         tid=GetCurrentThreadId ();
153         /* Set the thread ID here as well as in the parent thread,
154          * because we don't know whether the thread object will
155          * already have its ID set before we get to it.  This isn't a
156          * race condition, because if we're not guaranteed to get the
157          * same number in both the parent and child threads, then
158          * something else is seriously broken.
159          */
160         start_info->obj->tid=tid;
161         
162         handle_store(start_info->obj);
163
164         mono_profiler_thread_start (tid);
165
166         mono_new_thread_init (start_info->obj, &tid);
167
168         g_free (start_info);
169
170         start_func (this);
171
172         /* If the thread calls ExitThread at all, this remaining code
173          * will not be executed, but the main thread will eventually
174          * call thread_cleanup() on this thread's behalf.
175          */
176
177 #ifdef THREAD_DEBUG
178         g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper terminating");
179 #endif
180         
181         thread_cleanup (tid);
182         
183         return(0);
184 }
185
186 void mono_new_thread_init (MonoThread *thread_object, gpointer stack_start)
187 {
188         /* FIXME: GC problem here with recorded object
189          * pointer!
190          *
191          * This is recorded so CurrentThread can return the
192          * Thread object.
193          */
194         TlsSetValue (current_object_key, thread_object);
195
196         if (mono_thread_start_cb) {
197                 mono_thread_start_cb (stack_start);
198         }
199 }
200
201 MonoThread *mono_thread_create (MonoDomain *domain, gpointer func,
202                                 gpointer arg)
203 {
204         MonoThread *thread;
205         HANDLE thread_handle;
206         struct StartInfo *start_info;
207         guint32 tid;
208         
209         thread = (MonoThread *)mono_object_new (domain,
210                                                 mono_defaults.thread_class);
211
212         start_info=g_new0 (struct StartInfo, 1);
213         start_info->func = func;
214         start_info->obj = thread;
215         start_info->domain = domain;
216         start_info->this = arg;
217                 
218         thread_handle = CreateThread(NULL, 0, start_wrapper, start_info, 0, &tid);
219 #ifdef THREAD_DEBUG
220         g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d (handle %p)",
221                   tid, thread_handle);
222 #endif
223         g_assert (thread_handle);
224
225         thread->handle=thread_handle;
226         thread->tid=tid;
227
228         handle_store(thread);
229
230         return thread;
231 }
232
233 MonoThread *
234 mono_thread_attach (MonoDomain *domain)
235 {
236         MonoThread *thread;
237         HANDLE thread_handle;
238         guint32 tid;
239
240         thread = (MonoThread *)mono_object_new (domain,
241                                                 mono_defaults.thread_class);
242
243         thread_handle = GetCurrentThread ();
244         g_assert (thread_handle);
245
246         tid=GetCurrentThreadId ();
247
248         thread->handle=thread_handle;
249         thread->tid=tid;
250
251 #ifdef THREAD_DEBUG
252         g_message(G_GNUC_PRETTY_FUNCTION ": Attached thread ID %d (handle %p)",
253                   tid, thread_handle);
254 #endif
255
256         handle_store(thread);
257
258         TlsSetValue (current_object_key, thread);
259         mono_domain_set (domain);
260
261         if (mono_thread_attach_cb) {
262                 mono_thread_attach_cb (&tid);
263         }
264
265         return(thread);
266 }
267
268 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this,
269                                                          MonoObject *start)
270 {
271         MONO_ARCH_SAVE_REGS;
272
273         MonoMulticastDelegate *delegate = (MonoMulticastDelegate*)start;
274         guint32 (*start_func)(void *);
275         struct StartInfo *start_info;
276         MonoMethod *im;
277         HANDLE thread;
278         guint32 tid;
279         
280 #ifdef THREAD_DEBUG
281         g_message(G_GNUC_PRETTY_FUNCTION
282                   ": Trying to start a new thread: this (%p) start (%p)",
283                   this, start);
284 #endif
285         
286         im = mono_get_delegate_invoke (start->vtable->klass);
287         start_func = mono_compile_method (im);
288
289         if(start_func==NULL) {
290                 g_warning(G_GNUC_PRETTY_FUNCTION
291                           ": Can't locate start method!");
292                 return(NULL);
293         } else {
294                 /* This is freed in start_wrapper */
295                 start_info = g_new0 (struct StartInfo, 1);
296                 start_info->func = start_func;
297                 start_info->this = delegate;
298                 start_info->obj = this;
299                 start_info->domain = mono_domain_get ();
300
301                 thread=CreateThread(NULL, 0, start_wrapper, start_info,
302                                     CREATE_SUSPENDED, &tid);
303                 if(thread==NULL) {
304                         g_warning(G_GNUC_PRETTY_FUNCTION
305                                   ": CreateThread error 0x%x", GetLastError());
306                         return(NULL);
307                 }
308
309                 this->handle=thread;
310                 this->tid=tid;
311
312                 /* Don't call handle_store() here, delay it to Start.
313                  * We can't join a thread (trying to will just block
314                  * forever) until it actually starts running, so don't
315                  * store the handle till then.
316                  */
317
318 #ifdef THREAD_DEBUG
319                 g_message(G_GNUC_PRETTY_FUNCTION
320                           ": Started thread ID %d (handle %p)", tid, thread);
321 #endif
322                 
323                 return(thread);
324         }
325 }
326
327 void ves_icall_System_Threading_Thread_Thread_free_internal (MonoThread *this,
328                                                              HANDLE thread)
329 {
330         MONO_ARCH_SAVE_REGS;
331
332 #ifdef THREAD_DEBUG
333         g_message (G_GNUC_PRETTY_FUNCTION ": Closing thread %p, handle %p",
334                    this, thread);
335 #endif
336
337         CloseHandle (thread);
338 }
339
340 void ves_icall_System_Threading_Thread_Start_internal(MonoThread *this,
341                                                       HANDLE thread)
342 {
343         MONO_ARCH_SAVE_REGS;
344
345 #ifdef THREAD_DEBUG
346         g_message(G_GNUC_PRETTY_FUNCTION ": Launching thread %p", this);
347 #endif
348
349         /* Only store the handle when the thread is about to be
350          * launched, to avoid the main thread deadlocking while trying
351          * to clean up a thread that will never be signalled.
352          */
353         handle_store(this);
354
355         ResumeThread(thread);
356 }
357
358 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
359 {
360         MONO_ARCH_SAVE_REGS;
361
362 #ifdef THREAD_DEBUG
363         g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
364 #endif
365
366         Sleep(ms);
367 }
368
369 gint32
370 ves_icall_System_Threading_Thread_GetDomainID (void) 
371 {
372         MONO_ARCH_SAVE_REGS;
373
374         return mono_domain_get()->domain_id;
375 }
376
377 MonoThread *
378 mono_thread_current (void)
379 {
380         MONO_ARCH_SAVE_REGS;
381
382         MonoThread *thread;
383         
384         /* Find the current thread object */
385         thread=TlsGetValue (current_object_key);
386         
387 #ifdef THREAD_DEBUG
388         g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", thread);
389 #endif
390
391         return (thread);
392 }
393
394 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this,
395                                                          int ms, HANDLE thread)
396 {
397         MONO_ARCH_SAVE_REGS;
398
399         gboolean ret;
400         
401         if(ms== -1) {
402                 ms=INFINITE;
403         }
404 #ifdef THREAD_DEBUG
405         g_message (G_GNUC_PRETTY_FUNCTION ": joining thread handle %p, %d ms",
406                    thread, ms);
407 #endif
408         
409         ret=WaitForSingleObject(thread, ms);
410         if(ret==WAIT_OBJECT_0) {
411 #ifdef THREAD_DEBUG
412                 g_message (G_GNUC_PRETTY_FUNCTION ": join successful");
413 #endif
414
415                 return(TRUE);
416         }
417         
418 #ifdef THREAD_DEBUG
419                 g_message (G_GNUC_PRETTY_FUNCTION ": join failed");
420 #endif
421
422         return(FALSE);
423 }
424
425 void ves_icall_System_Threading_Thread_SlotHash_store(MonoObject *data)
426 {
427         MONO_ARCH_SAVE_REGS;
428
429 #ifdef THREAD_DEBUG
430         g_message(G_GNUC_PRETTY_FUNCTION ": Storing key %p", data);
431 #endif
432
433         /* Object location stored here */
434         TlsSetValue(slothash_key, data);
435 }
436
437 MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void)
438 {
439         MONO_ARCH_SAVE_REGS;
440
441         MonoObject *data;
442
443         data=TlsGetValue(slothash_key);
444         
445 #ifdef THREAD_DEBUG
446         g_message(G_GNUC_PRETTY_FUNCTION ": Retrieved key %p", data);
447 #endif
448         
449         return(data);
450 }
451
452 static void mon_finalize (void *o, void *unused)
453 {
454         MonoThreadsSync *mon=(MonoThreadsSync *)o;
455         
456 #ifdef THREAD_LOCK_DEBUG
457         g_message (G_GNUC_PRETTY_FUNCTION ": Finalizing sync");
458 #endif
459         
460         CloseHandle (mon->monitor);
461         CloseHandle (mon->sema);
462         CloseHandle (mon->waiters_done);
463 }
464
465 static MonoThreadsSync *mon_new(void)
466 {
467         MonoThreadsSync *new;
468         
469 #if HAVE_BOEHM_GC
470         new=(MonoThreadsSync *)GC_MALLOC (sizeof(MonoThreadsSync));
471         GC_REGISTER_FINALIZER (new, mon_finalize, NULL, NULL, NULL);
472 #else
473         /* This should be freed when the object that owns it is
474          * deleted
475          */
476         new=(MonoThreadsSync *)g_new0 (MonoThreadsSync, 1);
477 #endif
478         
479         new->monitor=CreateMutex(NULL, FALSE, NULL);
480         if(new->monitor==NULL) {
481                 /* Throw some sort of system exception? (ditto for the
482                  * sem and event handles below)
483                  */
484         }
485
486         new->waiters_count=0;
487         new->was_broadcast=FALSE;
488         InitializeCriticalSection(&new->waiters_count_lock);
489         new->sema=CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
490         new->waiters_done=CreateEvent(NULL, FALSE, FALSE, NULL);
491         
492 #ifdef THREAD_LOCK_DEBUG
493         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) ThreadsSync %p mutex created: %p, sem: %p, event: %p", GetCurrentThreadId (), new, new->monitor, new->sema, new->waiters_done);
494 #endif
495         
496         return(new);
497 }
498
499 gboolean ves_icall_System_Threading_Monitor_Monitor_try_enter(MonoObject *obj,
500                                                               int ms)
501 {
502         MONO_ARCH_SAVE_REGS;
503
504         MonoThreadsSync *mon;
505         guint32 ret;
506         
507 #ifdef THREAD_LOCK_DEBUG
508         g_message(G_GNUC_PRETTY_FUNCTION
509                   ": (%d) Trying to lock object %p", GetCurrentThreadId(),
510                   obj);
511 #endif
512
513         EnterCriticalSection(&monitor_mutex);
514
515         mon=obj->synchronisation;
516         if(mon==NULL) {
517                 mon=mon_new();
518                 obj->synchronisation=mon;
519         }
520         
521         /* Don't hold the monitor lock while waiting to acquire the
522          * object lock
523          */
524         LeaveCriticalSection(&monitor_mutex);
525         
526         /* Acquire the mutex */
527 #ifdef THREAD_LOCK_DEBUG
528         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Acquiring monitor mutex %p",
529                   GetCurrentThreadId (), mon->monitor);
530 #endif
531         ret=WaitForSingleObject(mon->monitor, ms);
532         if(ret==WAIT_OBJECT_0) {
533                 mon->count++;
534                 mon->tid=GetCurrentThreadId();
535         
536 #ifdef THREAD_LOCK_DEBUG
537                 g_message(G_GNUC_PRETTY_FUNCTION
538                           ": (%d) object %p now locked %d times",
539                           GetCurrentThreadId (), obj, mon->count);
540 #endif
541
542                 return(TRUE);
543         }
544
545         return(FALSE);
546 }
547
548 void ves_icall_System_Threading_Monitor_Monitor_exit(MonoObject *obj)
549 {
550         MONO_ARCH_SAVE_REGS;
551
552         MonoThreadsSync *mon;
553         
554 #ifdef THREAD_LOCK_DEBUG
555         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Unlocking %p",
556                   GetCurrentThreadId (), obj);
557 #endif
558
559         /* No need to lock monitor_mutex here because we only adjust
560          * the monitor state if this thread already owns the lock
561          */
562         mon=obj->synchronisation;
563
564         if(mon==NULL) {
565                 return;
566         }
567
568         if(mon->tid!=GetCurrentThreadId()) {
569                 return;
570         }
571         
572         mon->count--;
573         
574 #ifdef THREAD_LOCK_DEBUG
575         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %p now locked %d times",
576                   GetCurrentThreadId (), obj, mon->count);
577 #endif
578
579         if(mon->count==0) {
580                 mon->tid=0;     /* FIXME: check that 0 isnt a valid id */
581         }
582         
583 #ifdef THREAD_LOCK_DEBUG
584         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Releasing mutex %p",
585                   GetCurrentThreadId (), mon->monitor);
586 #endif
587
588         ReleaseMutex(mon->monitor);
589 }
590
591 gboolean ves_icall_System_Threading_Monitor_Monitor_test_owner(MonoObject *obj)
592 {
593         MONO_ARCH_SAVE_REGS;
594
595         MonoThreadsSync *mon;
596         gboolean ret=FALSE;
597         
598 #ifdef THREAD_LOCK_DEBUG
599         g_message(G_GNUC_PRETTY_FUNCTION
600                   ": Testing if %p is owned by thread %d", obj,
601                   GetCurrentThreadId());
602 #endif
603
604         EnterCriticalSection(&monitor_mutex);
605         
606         mon=obj->synchronisation;
607         if(mon==NULL) {
608                 goto finished;
609         }
610
611         if(mon->tid!=GetCurrentThreadId()) {
612 #ifdef THREAD_LOCK_DEBUG
613                 g_message (G_GNUC_PRETTY_FUNCTION
614                            ": (%d) object %p is owned by thread %d",
615                            GetCurrentThreadId (), obj, mon->tid);
616 #endif
617
618                 goto finished;
619         }
620         
621         ret=TRUE;
622         
623 finished:
624         LeaveCriticalSection(&monitor_mutex);
625
626         return(ret);
627 }
628
629 gboolean ves_icall_System_Threading_Monitor_Monitor_test_synchronised(MonoObject *obj)
630 {
631         MONO_ARCH_SAVE_REGS;
632
633         MonoThreadsSync *mon;
634         gboolean ret=FALSE;
635         
636 #ifdef THREAD_LOCK_DEBUG
637         g_message(G_GNUC_PRETTY_FUNCTION
638                   ": (%d) Testing if %p is owned by any thread",
639                   GetCurrentThreadId (), obj);
640 #endif
641
642         EnterCriticalSection(&monitor_mutex);
643         
644         mon=obj->synchronisation;
645         if(mon==NULL) {
646                 goto finished;
647         }
648
649         if(mon->tid==0) {
650                 goto finished;
651         }
652         
653         g_assert(mon->count);
654         
655         ret=TRUE;
656         
657 finished:
658         LeaveCriticalSection(&monitor_mutex);
659
660         return(ret);
661 }
662
663         
664 void ves_icall_System_Threading_Monitor_Monitor_pulse(MonoObject *obj)
665 {
666         MONO_ARCH_SAVE_REGS;
667
668         gboolean have_waiters;
669         MonoThreadsSync *mon;
670         
671 #ifdef THREAD_LOCK_DEBUG
672         g_message("(%d) Pulsing %p", GetCurrentThreadId (), obj);
673 #endif
674
675         EnterCriticalSection(&monitor_mutex);
676         
677         mon=obj->synchronisation;
678         if(mon==NULL) {
679                 LeaveCriticalSection(&monitor_mutex);
680                 return;
681         }
682
683         if(mon->tid!=GetCurrentThreadId()) {
684                 LeaveCriticalSection(&monitor_mutex);
685                 return;
686         }
687         LeaveCriticalSection(&monitor_mutex);
688         
689         EnterCriticalSection(&mon->waiters_count_lock);
690         have_waiters=(mon->waiters_count>0);
691         LeaveCriticalSection(&mon->waiters_count_lock);
692         
693         if(have_waiters==TRUE) {
694                 ReleaseSemaphore(mon->sema, 1, 0);
695         }
696 }
697
698 void ves_icall_System_Threading_Monitor_Monitor_pulse_all(MonoObject *obj)
699 {
700         MONO_ARCH_SAVE_REGS;
701
702         gboolean have_waiters=FALSE;
703         MonoThreadsSync *mon;
704         
705 #ifdef THREAD_LOCK_DEBUG
706         g_message("(%d) Pulsing all %p", GetCurrentThreadId (), obj);
707 #endif
708
709         EnterCriticalSection(&monitor_mutex);
710         
711         mon=obj->synchronisation;
712         if(mon==NULL) {
713                 LeaveCriticalSection(&monitor_mutex);
714                 return;
715         }
716
717         if(mon->tid!=GetCurrentThreadId()) {
718                 LeaveCriticalSection(&monitor_mutex);
719                 return;
720         }
721         LeaveCriticalSection(&monitor_mutex);
722         
723         EnterCriticalSection(&mon->waiters_count_lock);
724         if(mon->waiters_count>0) {
725                 mon->was_broadcast=TRUE;
726                 have_waiters=TRUE;
727         }
728         
729         if(have_waiters==TRUE) {
730                 ReleaseSemaphore(mon->sema, mon->waiters_count, 0);
731                 
732                 LeaveCriticalSection(&mon->waiters_count_lock);
733                 
734                 WaitForSingleObject(mon->waiters_done, INFINITE);
735                 mon->was_broadcast=FALSE;
736         } else {
737                 LeaveCriticalSection(&mon->waiters_count_lock);
738         }
739 }
740
741 gboolean ves_icall_System_Threading_Monitor_Monitor_wait(MonoObject *obj,
742                                                          int ms)
743 {
744         MONO_ARCH_SAVE_REGS;
745
746         gboolean last_waiter;
747         MonoThreadsSync *mon;
748         guint32 save_count;
749         
750 #ifdef THREAD_LOCK_DEBUG
751         g_message("(%d) Trying to wait for %p with timeout %dms",
752                   GetCurrentThreadId (), obj, ms);
753 #endif
754
755         EnterCriticalSection(&monitor_mutex);
756         
757         mon=obj->synchronisation;
758         if(mon==NULL) {
759                 LeaveCriticalSection(&monitor_mutex);
760                 return(FALSE);
761         }
762
763         if(mon->tid!=GetCurrentThreadId()) {
764                 LeaveCriticalSection(&monitor_mutex);
765                 return(FALSE);
766         }
767         LeaveCriticalSection(&monitor_mutex);
768         
769         EnterCriticalSection(&mon->waiters_count_lock);
770         mon->waiters_count++;
771         LeaveCriticalSection(&mon->waiters_count_lock);
772         
773 #ifdef THREAD_LOCK_DEBUG
774         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %p locked %d times",
775                   GetCurrentThreadId (), obj, mon->count);
776 #endif
777
778         /* We need to put the lock count back afterwards */
779         save_count=mon->count;
780         
781         while(mon->count>1) {
782 #ifdef THREAD_LOCK_DEBUG
783                 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Releasing mutex %p",
784                           GetCurrentThreadId (), mon->monitor);
785 #endif
786
787                 ReleaseMutex(mon->monitor);
788                 mon->count--;
789         }
790         
791         /* We're releasing this mutex */
792         mon->count=0;
793         mon->tid=0;
794 #ifdef THREAD_LOCK_DEBUG
795         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Signalling monitor mutex %p",
796                   GetCurrentThreadId (), mon->monitor);
797 #endif
798
799         SignalObjectAndWait(mon->monitor, mon->sema, INFINITE, FALSE);
800         
801         EnterCriticalSection(&mon->waiters_count_lock);
802         mon->waiters_count++;
803         last_waiter=mon->was_broadcast && mon->waiters_count==0;
804         LeaveCriticalSection(&mon->waiters_count_lock);
805         
806         if(last_waiter) {
807 #ifdef THREAD_LOCK_DEBUG
808         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Waiting for monitor mutex %p",
809                   GetCurrentThreadId (), mon->monitor);
810 #endif
811                 SignalObjectAndWait(mon->waiters_done, mon->monitor, INFINITE, FALSE);
812         } else {
813 #ifdef THREAD_LOCK_DEBUG
814         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Waiting for monitor mutex %p",
815                   GetCurrentThreadId (), mon->monitor);
816 #endif
817                 WaitForSingleObject(mon->monitor, INFINITE);
818         }
819
820         /* We've reclaimed this mutex */
821         mon->count=save_count;
822         mon->tid=GetCurrentThreadId();
823
824         /* Lock the mutex the required number of times */
825         while(save_count>1) {
826 #ifdef THREAD_LOCK_DEBUG
827                 g_message(G_GNUC_PRETTY_FUNCTION
828                           ": (%d) Waiting for monitor mutex %p",
829                           GetCurrentThreadId (), mon->monitor);
830 #endif
831                 WaitForSingleObject(mon->monitor, INFINITE);
832                 save_count--;
833         }
834         
835 #ifdef THREAD_LOCK_DEBUG
836         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %p still locked %d times",
837                   GetCurrentThreadId (), obj, mon->count);
838 #endif
839         
840         return(TRUE);
841 }
842
843 /* FIXME: exitContext isnt documented */
844 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
845 {
846         MONO_ARCH_SAVE_REGS;
847
848         HANDLE *handles;
849         guint32 numhandles;
850         guint32 ret;
851         guint32 i;
852         
853         numhandles=mono_array_length(mono_handles);
854         handles=g_new0(HANDLE, numhandles);
855         for(i=0; i<numhandles; i++) {
856                 handles[i]=mono_array_get(mono_handles, HANDLE, i);
857         }
858         
859         if(ms== -1) {
860                 ms=INFINITE;
861         }
862         
863         ret=WaitForMultipleObjects(numhandles, handles, TRUE, ms);
864
865         g_free(handles);
866         
867         if(ret==WAIT_FAILED) {
868 #ifdef THREAD_WAIT_DEBUG
869                 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
870                           GetCurrentThreadId ());
871 #endif
872                 return(FALSE);
873         } else if(ret==WAIT_TIMEOUT) {
874 #ifdef THREAD_WAIT_DEBUG
875                 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
876                           GetCurrentThreadId ());
877 #endif
878                 return(FALSE);
879         }
880         
881         return(TRUE);
882 }
883
884 /* FIXME: exitContext isnt documented */
885 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
886 {
887         MONO_ARCH_SAVE_REGS;
888
889         HANDLE *handles;
890         guint32 numhandles;
891         guint32 ret;
892         guint32 i;
893         
894         numhandles=mono_array_length(mono_handles);
895         handles=g_new0(HANDLE, numhandles);
896         for(i=0; i<numhandles; i++) {
897                 handles[i]=mono_array_get(mono_handles, HANDLE, i);
898         }
899         
900         if(ms== -1) {
901                 ms=INFINITE;
902         }
903
904         ret=WaitForMultipleObjects(numhandles, handles, FALSE, ms);
905
906         g_free(handles);
907         
908 #ifdef THREAD_WAIT_DEBUG
909         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) returning %d",
910                   GetCurrentThreadId (), ret);
911 #endif
912
913         return(ret);
914 }
915
916 /* FIXME: exitContext isnt documented */
917 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
918 {
919         MONO_ARCH_SAVE_REGS;
920
921         guint32 ret;
922         
923 #ifdef THREAD_WAIT_DEBUG
924         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) waiting for %p",
925                   GetCurrentThreadId (), handle);
926 #endif
927         
928         if(ms== -1) {
929                 ms=INFINITE;
930         }
931         
932         ret=WaitForSingleObject(handle, ms);
933         if(ret==WAIT_FAILED) {
934 #ifdef THREAD_WAIT_DEBUG
935                 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
936                           GetCurrentThreadId ());
937 #endif
938                 return(FALSE);
939         } else if(ret==WAIT_TIMEOUT) {
940 #ifdef THREAD_WAIT_DEBUG
941                 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
942                           GetCurrentThreadId ());
943 #endif
944                 return(FALSE);
945         }
946         
947         return(TRUE);
948 }
949
950 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned,char *name) { 
951         MONO_ARCH_SAVE_REGS;
952    
953         return(CreateMutex(NULL,owned,name));                    
954 }                                                                   
955
956 void ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) { 
957         MONO_ARCH_SAVE_REGS;
958
959         ReleaseMutex(handle);
960 }
961
962 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual,
963                                                                                                                           MonoBoolean initial,
964                                                                                                                           char *name) {
965         MONO_ARCH_SAVE_REGS;
966
967         return (CreateEvent(NULL,manual,initial,name));
968 }
969
970 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
971         MONO_ARCH_SAVE_REGS;
972
973         return (SetEvent(handle));
974 }
975
976 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
977         MONO_ARCH_SAVE_REGS;
978
979         return (ResetEvent(handle));
980 }
981
982 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
983 {
984         MONO_ARCH_SAVE_REGS;
985
986         return InterlockedIncrement (location);
987 }
988
989 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
990 {
991         MONO_ARCH_SAVE_REGS;
992
993         gint32 lowret;
994         gint32 highret;
995
996         EnterCriticalSection(&interlocked_mutex);
997
998         lowret = InterlockedIncrement((gint32 *) location);
999         if (0 == lowret)
1000                 highret = InterlockedIncrement((gint32 *) location + 1);
1001         else
1002                 highret = *((gint32 *) location + 1);
1003
1004         LeaveCriticalSection(&interlocked_mutex);
1005
1006         return (gint64) highret << 32 | (gint64) lowret;
1007 }
1008
1009 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
1010 {
1011         MONO_ARCH_SAVE_REGS;
1012
1013         return InterlockedDecrement(location);
1014 }
1015
1016 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
1017 {
1018         MONO_ARCH_SAVE_REGS;
1019
1020         gint32 lowret;
1021         gint32 highret;
1022
1023         EnterCriticalSection(&interlocked_mutex);
1024
1025         lowret = InterlockedDecrement((gint32 *) location);
1026         if (-1 == lowret)
1027                 highret = InterlockedDecrement((gint32 *) location + 1);
1028         else
1029                 highret = *((gint32 *) location + 1);
1030
1031         LeaveCriticalSection(&interlocked_mutex);
1032
1033         return (gint64) highret << 32 | (gint64) lowret;
1034 }
1035
1036 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location1, gint32 value)
1037 {
1038         MONO_ARCH_SAVE_REGS;
1039
1040         return InterlockedExchange(location1, value);
1041 }
1042
1043 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location1, MonoObject *value)
1044 {
1045         MONO_ARCH_SAVE_REGS;
1046
1047         return (MonoObject *) InterlockedExchangePointer((gpointer *) location1, value);
1048 }
1049
1050 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location1, gfloat value)
1051 {
1052         MONO_ARCH_SAVE_REGS;
1053
1054         IntFloatUnion val, ret;
1055
1056         val.fval = value;
1057         ret.ival = InterlockedExchange((gint32 *) location1, val.ival);
1058
1059         return ret.fval;
1060 }
1061
1062 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location1, gint32 value, gint32 comparand)
1063 {
1064         MONO_ARCH_SAVE_REGS;
1065
1066         return InterlockedCompareExchange(location1, value, comparand);
1067 }
1068
1069 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location1, MonoObject *value, MonoObject *comparand)
1070 {
1071         MONO_ARCH_SAVE_REGS;
1072
1073         return (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location1, value, comparand);
1074 }
1075
1076 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location1, gfloat value, gfloat comparand)
1077 {
1078         MONO_ARCH_SAVE_REGS;
1079
1080         IntFloatUnion val, ret, cmp;
1081
1082         val.fval = value;
1083         cmp.fval = comparand;
1084         ret.ival = InterlockedCompareExchange((gint32 *) location1, val.ival, cmp.ival);
1085
1086         return ret.fval;
1087 }
1088
1089 void
1090 ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state)
1091 {
1092         MONO_ARCH_SAVE_REGS;
1093
1094         thread->abort_state = state;
1095         thread->abort_exc = mono_get_exception_thread_abort ();
1096
1097
1098         /* fixme: store the state somewhere */
1099 #ifndef __MINGW32__
1100 #ifdef PTHREAD_POINTER_ID
1101         pthread_kill (GUINT_TO_POINTER(thread->tid), SIGUSR1);
1102 #else
1103         pthread_kill (thread->tid, SIGUSR1);
1104 #endif
1105 #else
1106         g_assert_not_reached ();
1107 #endif
1108 }
1109
1110 void
1111 ves_icall_System_Threading_Thread_ResetAbort (void)
1112 {
1113         MONO_ARCH_SAVE_REGS;
1114
1115         MonoThread *thread = mono_thread_current ();
1116         
1117         if (!thread->abort_exc) {
1118                 const char *msg = "Unable to reset abort because no abort was requested";
1119                 mono_raise_exception (mono_get_exception_thread_state (msg));
1120         } else {
1121                 thread->abort_exc = NULL;
1122                 thread->abort_state = NULL;
1123         }
1124 }
1125
1126 void mono_thread_init (MonoDomain *domain, MonoThreadStartCB start_cb,
1127                        MonoThreadStartCB attach_cb)
1128 {
1129         /* Build a System.Threading.Thread object instance to return
1130          * for the main line's Thread.CurrentThread property.
1131          */
1132         
1133         /* I wonder what happens if someone tries to destroy this
1134          * object? In theory, I guess the whole program should act as
1135          * though exit() were called :-)
1136          */
1137 #ifdef THREAD_DEBUG
1138         g_message(G_GNUC_PRETTY_FUNCTION
1139                   ": Starting to build main Thread object");
1140 #endif
1141         main_thread = (MonoThread *)mono_object_new (domain, mono_defaults.thread_class);
1142
1143         main_thread->handle = GetCurrentThread ();
1144
1145 #ifdef THREAD_DEBUG
1146         g_message(G_GNUC_PRETTY_FUNCTION
1147                   ": Finished building main Thread object: %p", main_thread);
1148 #endif
1149
1150         InitializeCriticalSection(&threads_mutex);
1151         InitializeCriticalSection(&monitor_mutex);
1152         InitializeCriticalSection(&interlocked_mutex);
1153         
1154         current_object_key=TlsAlloc();
1155 #ifdef THREAD_DEBUG
1156         g_message (G_GNUC_PRETTY_FUNCTION ": Allocated current_object_key %d",
1157                    current_object_key);
1158 #endif
1159
1160         TlsSetValue(current_object_key, main_thread);
1161
1162         mono_thread_start_cb = start_cb;
1163         mono_thread_attach_cb = attach_cb;
1164
1165         slothash_key=TlsAlloc();
1166
1167         /* Get a pseudo handle to the current process.  This is just a
1168          * kludge so that wapi can build a process handle if needed.
1169          * As a pseudo handle is returned, we don't need to clean
1170          * anything up.
1171          */
1172         GetCurrentProcess ();
1173 }
1174
1175 #ifdef THREAD_DEBUG
1176 static void print_tids (gpointer key, gpointer value, gpointer user)
1177 {
1178         g_message ("Waiting for: %d", *(guint32 *)key);
1179 }
1180 #endif
1181
1182 struct wait_data 
1183 {
1184         HANDLE handles[MAXIMUM_WAIT_OBJECTS];
1185         MonoThread *threads[MAXIMUM_WAIT_OBJECTS];
1186         guint32 num;
1187 };
1188
1189 static void wait_for_tids (struct wait_data *wait)
1190 {
1191         guint32 i, ret;
1192         
1193 #ifdef THREAD_DEBUG
1194         g_message(G_GNUC_PRETTY_FUNCTION
1195                   ": %d threads to wait for in this batch", wait->num);
1196 #endif
1197
1198         ret=WaitForMultipleObjects(wait->num, wait->handles, TRUE, INFINITE);
1199         if(ret==WAIT_FAILED) {
1200                 /* See the comment in build_wait_tids() */
1201 #ifdef THREAD_DEBUG
1202                 g_message (G_GNUC_PRETTY_FUNCTION ": Wait failed");
1203 #endif
1204                 return;
1205         }
1206         
1207
1208         for(i=0; i<wait->num; i++) {
1209                 guint32 tid=wait->threads[i]->tid;
1210                 
1211                 if(mono_g_hash_table_lookup (threads, &tid)!=NULL) {
1212                         /* This thread must have been killed, because
1213                          * it hasn't cleaned itself up. (It's just
1214                          * possible that the thread exited before the
1215                          * parent thread had a chance to store the
1216                          * handle, and now there is another pointer to
1217                          * the already-exited thread stored.  In this
1218                          * case, we'll just get two
1219                          * mono_profiler_thread_end() calls for the
1220                          * same thread.)
1221                          */
1222         
1223 #ifdef THREAD_DEBUG
1224                         g_message (G_GNUC_PRETTY_FUNCTION
1225                                    ": cleaning up after thread %d", tid);
1226 #endif
1227                         thread_cleanup (tid);
1228                 }
1229         }
1230 }
1231
1232 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
1233 {
1234         struct wait_data *wait=(struct wait_data *)user;
1235
1236         if(wait->num<MAXIMUM_WAIT_OBJECTS) {
1237                 MonoThread *thread=(MonoThread *)value;
1238                 
1239                 /* Theres a theoretical chance that thread->handle
1240                  * might be NULL if the child thread has called
1241                  * handle_store() but the parent thread hasn't set the
1242                  * handle pointer yet.  WaitForMultipleObjects will
1243                  * fail, and we'll just go round the loop again.  By
1244                  * that time the handle should be stored properly.
1245                  */
1246                 wait->handles[wait->num]=thread->handle;
1247                 wait->threads[wait->num]=thread;
1248                 wait->num++;
1249         } else {
1250                 /* Just ignore the rest, we can't do anything with
1251                  * them yet
1252                  */
1253         }
1254 }
1255
1256 void mono_thread_cleanup(void)
1257 {
1258         struct wait_data *wait=g_new0 (struct wait_data, 1);
1259         
1260         /* join each thread that's still running */
1261 #ifdef THREAD_DEBUG
1262         g_message(G_GNUC_PRETTY_FUNCTION ": Joining each running thread...");
1263 #endif
1264         
1265         if(threads==NULL) {
1266 #ifdef THREAD_DEBUG
1267                 g_message(G_GNUC_PRETTY_FUNCTION ": No threads");
1268 #endif
1269                 return;
1270         }
1271         
1272         do {
1273                 EnterCriticalSection (&threads_mutex);
1274 #ifdef THREAD_DEBUG
1275                 g_message(G_GNUC_PRETTY_FUNCTION
1276                           ":There are %d threads to join",
1277                           mono_g_hash_table_size (threads));
1278                 mono_g_hash_table_foreach (threads, print_tids, NULL);
1279 #endif
1280
1281                 wait->num=0;
1282                 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
1283         
1284                 LeaveCriticalSection (&threads_mutex);
1285                 if(wait->num>0) {
1286                         /* Something to wait for */
1287                         wait_for_tids (wait);
1288                 }
1289         } while(wait->num>0);
1290         
1291         g_free (wait);
1292         
1293         mono_g_hash_table_destroy(threads);
1294         threads=NULL;
1295 }