2002-07-03 Dick Porter <dick@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
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>
20
21 #if HAVE_BOEHM_GC
22 #include <gc/gc.h>
23 #endif
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         MonoObject *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 array of existing threads that need joining before exit */
51 static GArray *threads=NULL;
52
53 /* The MonoObject associated with the main thread */
54 static MonoObject *main_thread;
55
56 /* The TLS key that holds the MonoObject assigned to each thread */
57 static guint32 current_object_key;
58
59 /* function called at thread start */
60 static MonoThreadStartCB mono_thread_start_cb = NULL;
61
62 /* The TLS key that holds the LocalDataStoreSlot hash in each thread */
63 static guint32 slothash_key;
64
65 /* Spin lock for InterlockedXXX 64 bit functions */
66 static CRITICAL_SECTION interlocked_mutex;
67
68 /* handle_store() and handle_remove() manage the array of threads that
69  * still need to be waited for when the main thread exits.
70  */
71 static void handle_store(guint32 tid)
72 {
73         guint32 idx;
74         
75 #ifdef THREAD_DEBUG
76         g_message(G_GNUC_PRETTY_FUNCTION ": thread ID %d", tid);
77 #endif
78
79         EnterCriticalSection(&threads_mutex);
80         if(threads==NULL) {
81                 threads=g_array_new(FALSE, FALSE, sizeof(guint32));
82         }
83
84         /* Make sure there is only one instance in the array.  We
85          * store the thread both in the start_wrapper (in the
86          * subthread), and as soon as possible in the parent thread.
87          * This is to minimise the window in which the thread exists
88          * but we haven't recorded it.
89          */
90         for(idx=0; idx<threads->len; idx++) {
91                 if(g_array_index (threads, guint32, idx)==tid) {
92                         g_array_remove_index_fast (threads, idx);
93                 }
94         }
95         
96         g_array_append_val(threads, tid);
97         LeaveCriticalSection(&threads_mutex);
98 }
99
100 static void handle_remove(guint32 tid)
101 {
102         guint32 idx;
103         
104 #ifdef THREAD_DEBUG
105         g_message(G_GNUC_PRETTY_FUNCTION ": thread ID %d", tid);
106 #endif
107
108         EnterCriticalSection(&threads_mutex);
109
110         for(idx=0; idx<threads->len; idx++) {
111                 if(g_array_index (threads, guint32, idx)==tid) {
112                         g_array_remove_index_fast(threads, idx);
113                 }
114         }
115         
116         LeaveCriticalSection(&threads_mutex);
117
118         /* Don't close the handle here, wait for the object finalizer
119          * to do it. Otherwise, the following race condition applies:
120          *
121          * 1) Thread exits (and handle_remove() closes the handle)
122          *
123          * 2) Some other handle is reassigned the same slot
124          *
125          * 3) Another thread tries to join the first thread, and
126          * blocks waiting for the reassigned handle to be signalled
127          * (which might never happen).  This is possible, because the
128          * thread calling Join() still has a reference to the first
129          * thread's object.
130          */
131 }
132
133 static guint32 start_wrapper(void *data)
134 {
135         struct StartInfo *start_info=(struct StartInfo *)data;
136         guint32 (*start_func)(void *);
137         void *this;
138         guint32 tid;
139         
140 #ifdef THREAD_DEBUG
141         g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper");
142 #endif
143
144         /* FIXME: GC problem here with recorded object
145          * pointer!
146          *
147          * This is recorded so CurrentThread can return the
148          * Thread object.
149          */
150         TlsSetValue (current_object_key, start_info->obj);
151         start_func = start_info->func;
152         mono_domain_set (start_info->domain);
153         this = start_info->this;
154         g_free (start_info);
155
156         tid=GetCurrentThreadId ();
157         
158         handle_store(tid);
159         mono_profiler_thread_start (tid);
160
161         if (mono_thread_start_cb)
162                 mono_thread_start_cb (&tid);
163
164         start_func (this);
165
166 #ifdef THREAD_DEBUG
167         g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper terminating");
168 #endif
169         
170         mono_profiler_thread_end (tid);
171         handle_remove (tid);
172
173         return(0);
174 }
175
176 MonoObject *
177 mono_thread_create (MonoDomain *domain, gpointer func)
178 {
179         MonoClassField *field;
180         MonoObject *thread;
181         HANDLE thread_handle;
182         struct StartInfo *start_info;
183         guint32 tid;
184         
185         thread = mono_object_new (domain, mono_defaults.thread_class);
186
187         field=mono_class_get_field_from_name(mono_defaults.thread_class, "system_thread_handle");
188         g_assert (field);
189
190         start_info=g_new0 (struct StartInfo, 1);
191         start_info->func = func;
192         start_info->obj = thread;
193         start_info->domain = domain;
194         /* start_info->this needs to be set? */
195                 
196         thread_handle = CreateThread(NULL, 0, start_wrapper, start_info, 0, &tid);
197 #ifdef THREAD_DEBUG
198         g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d (handle %p)",
199                   tid, thread_handle);
200 #endif
201         g_assert (thread_handle);
202
203         handle_store(tid);
204
205         *(gpointer *)(((char *)thread) + field->offset) = thread_handle; 
206
207         return thread;
208 }
209
210 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoObject *this,
211                                                          MonoObject *start)
212 {
213         MonoMulticastDelegate *delegate = (MonoMulticastDelegate*)start;
214         guint32 (*start_func)(void *);
215         struct StartInfo *start_info;
216         HANDLE thread;
217         guint32 tid;
218         
219 #ifdef THREAD_DEBUG
220         g_message(G_GNUC_PRETTY_FUNCTION
221                   ": Trying to start a new thread: this (%p) start (%p)",
222                   this, start);
223 #endif
224         
225         start_func = delegate->delegate.method_ptr;
226         
227         if(start_func==NULL) {
228                 g_warning(G_GNUC_PRETTY_FUNCTION
229                           ": Can't locate start method!");
230                 return(NULL);
231         } else {
232                 /* This is freed in start_wrapper */
233                 start_info = g_new0 (struct StartInfo, 1);
234                 start_info->func = start_func;
235                 start_info->this = delegate->delegate.target;
236                 start_info->obj = this;
237                 start_info->domain = mono_domain_get ();
238                 
239                 thread=CreateThread(NULL, 0, start_wrapper, start_info,
240                                     CREATE_SUSPENDED, &tid);
241                 if(thread==NULL) {
242                         g_warning(G_GNUC_PRETTY_FUNCTION
243                                   ": CreateThread error 0x%x", GetLastError());
244                         return(NULL);
245                 }
246
247                 handle_store(tid);
248
249 #ifdef THREAD_DEBUG
250                 g_message(G_GNUC_PRETTY_FUNCTION
251                           ": Started thread ID %d (handle %p)", tid, thread);
252 #endif
253                 
254                 return(thread);
255         }
256 }
257
258 void ves_icall_System_Threading_Thread_Thread_free_internal (MonoObject *this,
259                                                              HANDLE thread)
260 {
261 #ifdef THREAD_DEBUG
262         g_message (G_GNUC_PRETTY_FUNCTION ": Closing thread %p, handle %p",
263                    this, thread);
264 #endif
265
266         CloseHandle (thread);
267 }
268
269 void ves_icall_System_Threading_Thread_Start_internal(MonoObject *this,
270                                                       HANDLE thread)
271 {
272 #ifdef THREAD_DEBUG
273         g_message(G_GNUC_PRETTY_FUNCTION ": Launching thread %p", this);
274 #endif
275
276         ResumeThread(thread);
277 }
278
279 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
280 {
281 #ifdef THREAD_DEBUG
282         g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
283 #endif
284
285         Sleep(ms);
286 }
287
288 MonoAppDomain *ves_icall_System_Threading_Thread_CurrentThreadDomain_internal(void) 
289 {
290         /* return the current app */
291         return mono_domain_get()->domain;
292 }
293
294 MonoObject *ves_icall_System_Threading_Thread_CurrentThread_internal(void)
295 {
296         MonoObject *thread;
297         
298         /* Find the current thread object */
299         thread=TlsGetValue(current_object_key);
300
301 #ifdef THREAD_DEBUG
302         g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", thread);
303 #endif
304
305         return(thread);
306 }
307
308 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoObject *this,
309                                                          int ms, HANDLE thread)
310 {
311         gboolean ret;
312         
313         if(ms== -1) {
314                 ms=INFINITE;
315         }
316 #ifdef THREAD_DEBUG
317         g_message (G_GNUC_PRETTY_FUNCTION ": joining thread handle %p, %d ms",
318                    thread, ms);
319 #endif
320         
321         ret=WaitForSingleObject(thread, ms);
322         if(ret==WAIT_OBJECT_0) {
323 #ifdef THREAD_DEBUG
324                 g_message (G_GNUC_PRETTY_FUNCTION ": join successful");
325 #endif
326
327                 return(TRUE);
328         }
329         
330 #ifdef THREAD_DEBUG
331                 g_message (G_GNUC_PRETTY_FUNCTION ": join failed");
332 #endif
333
334         return(FALSE);
335 }
336
337 void ves_icall_System_Threading_Thread_SlotHash_store(MonoObject *data)
338 {
339 #ifdef THREAD_DEBUG
340         g_message(G_GNUC_PRETTY_FUNCTION ": Storing key %p", data);
341 #endif
342
343         /* Object location stored here */
344         TlsSetValue(slothash_key, data);
345 }
346
347 MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void)
348 {
349         MonoObject *data;
350
351         data=TlsGetValue(slothash_key);
352         
353 #ifdef THREAD_DEBUG
354         g_message(G_GNUC_PRETTY_FUNCTION ": Retrieved key %p", data);
355 #endif
356         
357         return(data);
358 }
359
360 static void mon_finalize (void *o, void *unused)
361 {
362         MonoThreadsSync *mon=(MonoThreadsSync *)o;
363         
364 #ifdef THREAD_LOCK_DEBUG
365         g_message (G_GNUC_PRETTY_FUNCTION ": Finalizing sync");
366 #endif
367         
368         CloseHandle (mon->monitor);
369         CloseHandle (mon->sema);
370         CloseHandle (mon->waiters_done);
371 }
372
373 static MonoThreadsSync *mon_new(void)
374 {
375         MonoThreadsSync *new;
376         
377 #if HAVE_BOEHM_GC
378         new=(MonoThreadsSync *)GC_debug_malloc (sizeof(MonoThreadsSync), "sync", 1);
379         GC_debug_register_finalizer (new, mon_finalize, NULL, NULL, NULL);
380 #else
381         /* This should be freed when the object that owns it is
382          * deleted
383          */
384         new=(MonoThreadsSync *)g_new0 (MonoThreadsSync, 1);
385 #endif
386         
387         new->monitor=CreateMutex(NULL, FALSE, NULL);
388         if(new->monitor==NULL) {
389                 /* Throw some sort of system exception? (ditto for the
390                  * sem and event handles below)
391                  */
392         }
393
394         new->waiters_count=0;
395         new->was_broadcast=FALSE;
396         InitializeCriticalSection(&new->waiters_count_lock);
397         new->sema=CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
398         new->waiters_done=CreateEvent(NULL, FALSE, FALSE, NULL);
399         
400 #ifdef THREAD_LOCK_DEBUG
401         g_message(G_GNUC_PRETTY_FUNCTION
402                   ": ThreadsSync %p mutex created: %p, sem: %p, event: %p",
403                   new, new->monitor, new->sema, new->waiters_done);
404 #endif
405         
406         return(new);
407 }
408
409 gboolean ves_icall_System_Threading_Monitor_Monitor_try_enter(MonoObject *obj,
410                                                               int ms)
411 {
412         MonoThreadsSync *mon;
413         guint32 ret;
414         
415 #ifdef THREAD_LOCK_DEBUG
416         g_message(G_GNUC_PRETTY_FUNCTION
417                   ": Trying to lock object %p in thread %d", obj,
418                   GetCurrentThreadId());
419 #endif
420
421         EnterCriticalSection(&monitor_mutex);
422
423         mon=obj->synchronisation;
424         if(mon==NULL) {
425                 mon=mon_new();
426                 obj->synchronisation=mon;
427         }
428         
429         /* Don't hold the monitor lock while waiting to acquire the
430          * object lock
431          */
432         LeaveCriticalSection(&monitor_mutex);
433         
434         /* Acquire the mutex */
435 #ifdef THREAD_LOCK_DEBUG
436         g_message(G_GNUC_PRETTY_FUNCTION ": Acquiring monitor mutex %p",
437                   mon->monitor);
438 #endif
439         ret=WaitForSingleObject(mon->monitor, ms);
440         if(ret==WAIT_OBJECT_0) {
441                 mon->count++;
442                 mon->tid=GetCurrentThreadId();
443         
444 #ifdef THREAD_LOCK_DEBUG
445                 g_message(G_GNUC_PRETTY_FUNCTION
446                           ": object %p now locked %d times", obj, mon->count);
447 #endif
448
449                 return(TRUE);
450         }
451
452         return(FALSE);
453 }
454
455 void ves_icall_System_Threading_Monitor_Monitor_exit(MonoObject *obj)
456 {
457         MonoThreadsSync *mon;
458         
459 #ifdef THREAD_LOCK_DEBUG
460         g_message(G_GNUC_PRETTY_FUNCTION ": Unlocking %p in thread %d", obj,
461                   GetCurrentThreadId());
462 #endif
463
464         /* No need to lock monitor_mutex here because we only adjust
465          * the monitor state if this thread already owns the lock
466          */
467         mon=obj->synchronisation;
468
469         if(mon==NULL) {
470                 return;
471         }
472
473         if(mon->tid!=GetCurrentThreadId()) {
474                 return;
475         }
476         
477         mon->count--;
478         
479 #ifdef THREAD_LOCK_DEBUG
480         g_message(G_GNUC_PRETTY_FUNCTION ": %p now locked %d times", obj,
481                   mon->count);
482 #endif
483
484         if(mon->count==0) {
485                 mon->tid=0;     /* FIXME: check that 0 isnt a valid id */
486         }
487         
488 #ifdef THREAD_LOCK_DEBUG
489         g_message(G_GNUC_PRETTY_FUNCTION ": Releasing mutex %p", mon->monitor);
490 #endif
491
492         ReleaseMutex(mon->monitor);
493 }
494
495 gboolean ves_icall_System_Threading_Monitor_Monitor_test_owner(MonoObject *obj)
496 {
497         MonoThreadsSync *mon;
498         gboolean ret=FALSE;
499         
500 #ifdef THREAD_LOCK_DEBUG
501         g_message(G_GNUC_PRETTY_FUNCTION
502                   ": Testing if %p is owned by thread %d", obj,
503                   GetCurrentThreadId());
504 #endif
505
506         EnterCriticalSection(&monitor_mutex);
507         
508         mon=obj->synchronisation;
509         if(mon==NULL) {
510                 goto finished;
511         }
512
513         if(mon->tid!=GetCurrentThreadId()) {
514 #ifdef THREAD_LOCK_DEBUG
515                 g_message (G_GNUC_PRETTY_FUNCTION
516                            ": object %p is owned by thread %d", obj, mon->tid);
517 #endif
518
519                 goto finished;
520         }
521         
522         ret=TRUE;
523         
524 finished:
525         LeaveCriticalSection(&monitor_mutex);
526
527         return(ret);
528 }
529
530 gboolean ves_icall_System_Threading_Monitor_Monitor_test_synchronised(MonoObject *obj)
531 {
532         MonoThreadsSync *mon;
533         gboolean ret=FALSE;
534         
535 #ifdef THREAD_LOCK_DEBUG
536         g_message(G_GNUC_PRETTY_FUNCTION
537                   ": Testing if %p is owned by any thread", obj);
538 #endif
539
540         EnterCriticalSection(&monitor_mutex);
541         
542         mon=obj->synchronisation;
543         if(mon==NULL) {
544                 goto finished;
545         }
546
547         if(mon->tid==0) {
548                 goto finished;
549         }
550         
551         g_assert(mon->count);
552         
553         ret=TRUE;
554         
555 finished:
556         LeaveCriticalSection(&monitor_mutex);
557
558         return(ret);
559 }
560
561         
562 void ves_icall_System_Threading_Monitor_Monitor_pulse(MonoObject *obj)
563 {
564         gboolean have_waiters;
565         MonoThreadsSync *mon;
566         
567 #ifdef THREAD_LOCK_DEBUG
568         g_message("Pulsing %p in thread %d", obj, GetCurrentThreadId());
569 #endif
570
571         EnterCriticalSection(&monitor_mutex);
572         
573         mon=obj->synchronisation;
574         if(mon==NULL) {
575                 LeaveCriticalSection(&monitor_mutex);
576                 return;
577         }
578
579         if(mon->tid!=GetCurrentThreadId()) {
580                 LeaveCriticalSection(&monitor_mutex);
581                 return;
582         }
583         LeaveCriticalSection(&monitor_mutex);
584         
585         EnterCriticalSection(&mon->waiters_count_lock);
586         have_waiters=(mon->waiters_count>0);
587         LeaveCriticalSection(&mon->waiters_count_lock);
588         
589         if(have_waiters==TRUE) {
590                 ReleaseSemaphore(mon->sema, 1, 0);
591         }
592 }
593
594 void ves_icall_System_Threading_Monitor_Monitor_pulse_all(MonoObject *obj)
595 {
596         gboolean have_waiters=FALSE;
597         MonoThreadsSync *mon;
598         
599 #ifdef THREAD_LOCK_DEBUG
600         g_message("Pulsing all %p", obj);
601 #endif
602
603         EnterCriticalSection(&monitor_mutex);
604         
605         mon=obj->synchronisation;
606         if(mon==NULL) {
607                 LeaveCriticalSection(&monitor_mutex);
608                 return;
609         }
610
611         if(mon->tid!=GetCurrentThreadId()) {
612                 LeaveCriticalSection(&monitor_mutex);
613                 return;
614         }
615         LeaveCriticalSection(&monitor_mutex);
616         
617         EnterCriticalSection(&mon->waiters_count_lock);
618         if(mon->waiters_count>0) {
619                 mon->was_broadcast=TRUE;
620                 have_waiters=TRUE;
621         }
622         
623         if(have_waiters==TRUE) {
624                 ReleaseSemaphore(mon->sema, mon->waiters_count, 0);
625                 
626                 LeaveCriticalSection(&mon->waiters_count_lock);
627                 
628                 WaitForSingleObject(mon->waiters_done, INFINITE);
629                 mon->was_broadcast=FALSE;
630         } else {
631                 LeaveCriticalSection(&mon->waiters_count_lock);
632         }
633 }
634
635 gboolean ves_icall_System_Threading_Monitor_Monitor_wait(MonoObject *obj,
636                                                          int ms)
637 {
638         gboolean last_waiter;
639         MonoThreadsSync *mon;
640         guint32 save_count;
641         
642 #ifdef THREAD_LOCK_DEBUG
643         g_message("Trying to wait for %p in thread %d with timeout %dms", obj,
644                   GetCurrentThreadId(), ms);
645 #endif
646
647         EnterCriticalSection(&monitor_mutex);
648         
649         mon=obj->synchronisation;
650         if(mon==NULL) {
651                 LeaveCriticalSection(&monitor_mutex);
652                 return(FALSE);
653         }
654
655         if(mon->tid!=GetCurrentThreadId()) {
656                 LeaveCriticalSection(&monitor_mutex);
657                 return(FALSE);
658         }
659         LeaveCriticalSection(&monitor_mutex);
660         
661         EnterCriticalSection(&mon->waiters_count_lock);
662         mon->waiters_count++;
663         LeaveCriticalSection(&mon->waiters_count_lock);
664         
665 #ifdef THREAD_LOCK_DEBUG
666         g_message(G_GNUC_PRETTY_FUNCTION ": %p locked %d times", obj,
667                   mon->count);
668 #endif
669
670         /* We need to put the lock count back afterwards */
671         save_count=mon->count;
672         
673         while(mon->count>1) {
674 #ifdef THREAD_LOCK_DEBUG
675                 g_message(G_GNUC_PRETTY_FUNCTION ": Releasing mutex %p",
676                           mon->monitor);
677 #endif
678
679                 ReleaseMutex(mon->monitor);
680                 mon->count--;
681         }
682         
683         /* We're releasing this mutex */
684         mon->count=0;
685         mon->tid=0;
686 #ifdef THREAD_LOCK_DEBUG
687         g_message(G_GNUC_PRETTY_FUNCTION ": Signalling monitor mutex %p",
688                   mon->monitor);
689 #endif
690
691         SignalObjectAndWait(mon->monitor, mon->sema, INFINITE, FALSE);
692         
693         EnterCriticalSection(&mon->waiters_count_lock);
694         mon->waiters_count++;
695         last_waiter=mon->was_broadcast && mon->waiters_count==0;
696         LeaveCriticalSection(&mon->waiters_count_lock);
697         
698         if(last_waiter) {
699 #ifdef THREAD_LOCK_DEBUG
700         g_message(G_GNUC_PRETTY_FUNCTION ": Waiting for monitor mutex %p",
701                   mon->monitor);
702 #endif
703                 SignalObjectAndWait(mon->waiters_done, mon->monitor, INFINITE, FALSE);
704         } else {
705 #ifdef THREAD_LOCK_DEBUG
706         g_message(G_GNUC_PRETTY_FUNCTION ": Waiting for monitor mutex %p",
707                   mon->monitor);
708 #endif
709                 WaitForSingleObject(mon->monitor, INFINITE);
710         }
711
712         /* We've reclaimed this mutex */
713         mon->count=save_count;
714         mon->tid=GetCurrentThreadId();
715
716         /* Lock the mutex the required number of times */
717         while(save_count>1) {
718 #ifdef THREAD_LOCK_DEBUG
719                 g_message(G_GNUC_PRETTY_FUNCTION
720                           ": Waiting for monitor mutex %p", mon->monitor);
721 #endif
722                 WaitForSingleObject(mon->monitor, INFINITE);
723                 save_count--;
724         }
725         
726 #ifdef THREAD_LOCK_DEBUG
727         g_message(G_GNUC_PRETTY_FUNCTION ": %p still locked %d times", obj,
728                   mon->count);
729 #endif
730         
731         return(TRUE);
732 }
733
734 /* FIXME: exitContext isnt documented */
735 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
736 {
737         HANDLE *handles;
738         guint32 numhandles;
739         guint32 ret;
740         guint32 i;
741         
742         numhandles=mono_array_length(mono_handles);
743         handles=g_new0(HANDLE, numhandles);
744         for(i=0; i<numhandles; i++) {
745                 handles[i]=mono_array_get(mono_handles, HANDLE, i);
746         }
747         
748         if(ms== -1) {
749                 ms=INFINITE;
750         }
751         
752         ret=WaitForMultipleObjects(numhandles, handles, TRUE, ms);
753
754         g_free(handles);
755         
756         if(ret==WAIT_FAILED) {
757 #ifdef THREAD_WAIT_DEBUG
758                 g_message(G_GNUC_PRETTY_FUNCTION ": Wait failed");
759 #endif
760                 return(FALSE);
761         } else if(ret==WAIT_TIMEOUT) {
762 #ifdef THREAD_WAIT_DEBUG
763                 g_message(G_GNUC_PRETTY_FUNCTION ": Wait timed out");
764 #endif
765                 return(FALSE);
766         }
767         
768         return(TRUE);
769 }
770
771 /* FIXME: exitContext isnt documented */
772 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
773 {
774         HANDLE *handles;
775         guint32 numhandles;
776         guint32 ret;
777         guint32 i;
778         
779         numhandles=mono_array_length(mono_handles);
780         handles=g_new0(HANDLE, numhandles);
781         for(i=0; i<numhandles; i++) {
782                 handles[i]=mono_array_get(mono_handles, HANDLE, i);
783         }
784         
785         if(ms== -1) {
786                 ms=INFINITE;
787         }
788
789         ret=WaitForMultipleObjects(numhandles, handles, FALSE, ms);
790
791         g_free(handles);
792         
793 #ifdef THREAD_WAIT_DEBUG
794         g_message(G_GNUC_PRETTY_FUNCTION ": returning %d", ret);
795 #endif
796
797         return(ret);
798 }
799
800 /* FIXME: exitContext isnt documented */
801 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
802 {
803         guint32 ret;
804         
805 #ifdef THREAD_WAIT_DEBUG
806         g_message(G_GNUC_PRETTY_FUNCTION ": waiting for %p", handle);
807 #endif
808         
809         if(ms== -1) {
810                 ms=INFINITE;
811         }
812         
813         ret=WaitForSingleObject(handle, ms);
814         if(ret==WAIT_FAILED) {
815 #ifdef THREAD_WAIT_DEBUG
816                 g_message(G_GNUC_PRETTY_FUNCTION ": Wait failed");
817 #endif
818                 return(FALSE);
819         } else if(ret==WAIT_TIMEOUT) {
820 #ifdef THREAD_WAIT_DEBUG
821                 g_message(G_GNUC_PRETTY_FUNCTION ": Wait timed out");
822 #endif
823                 return(FALSE);
824         }
825         
826         return(TRUE);
827 }
828
829 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned,char *name) {    
830    return(CreateMutex(NULL,owned,name));                         
831 }                                                                   
832
833 void ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) { 
834         ReleaseMutex(handle);
835 }
836
837 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual,
838                                                                                                                           MonoBoolean initial,
839                                                                                                                           char *name) {
840         return (CreateEvent(NULL,manual,initial,name));
841 }
842
843 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
844         return (SetEvent(handle));
845 }
846
847 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
848         return (ResetEvent(handle));
849 }
850
851 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
852 {
853         return InterlockedIncrement (location);
854 }
855
856 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
857 {
858         gint32 lowret;
859         gint32 highret;
860
861         EnterCriticalSection(&interlocked_mutex);
862
863         lowret = InterlockedIncrement((gint32 *) location);
864         if (0 == lowret)
865                 highret = InterlockedIncrement((gint32 *) location + 1);
866         else
867                 highret = *((gint32 *) location + 1);
868
869         LeaveCriticalSection(&interlocked_mutex);
870
871         return (gint64) highret << 32 | (gint64) lowret;
872 }
873
874 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
875 {
876         return InterlockedDecrement(location);
877 }
878
879 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
880 {
881         gint32 lowret;
882         gint32 highret;
883
884         EnterCriticalSection(&interlocked_mutex);
885
886         lowret = InterlockedDecrement((gint32 *) location);
887         if (-1 == lowret)
888                 highret = InterlockedDecrement((gint32 *) location + 1);
889         else
890                 highret = *((gint32 *) location + 1);
891
892         LeaveCriticalSection(&interlocked_mutex);
893
894         return (gint64) highret << 32 | (gint64) lowret;
895 }
896
897 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location1, gint32 value)
898 {
899         return InterlockedExchange(location1, value);
900 }
901
902 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location1, MonoObject *value)
903 {
904         return (MonoObject *) InterlockedExchangePointer((gpointer *) location1, value);
905 }
906
907 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location1, gfloat value)
908 {
909         IntFloatUnion val, ret;
910
911         val.fval = value;
912         ret.ival = InterlockedExchange((gint32 *) location1, val.ival);
913
914         return ret.fval;
915 }
916
917 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location1, gint32 value, gint32 comparand)
918 {
919         return InterlockedCompareExchange(location1, value, comparand);
920 }
921
922 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location1, MonoObject *value, MonoObject *comparand)
923 {
924         return (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location1, value, comparand);
925 }
926
927 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location1, gfloat value, gfloat comparand)
928 {
929         IntFloatUnion val, ret, cmp;
930
931         val.fval = value;
932         cmp.fval = comparand;
933         ret.ival = InterlockedCompareExchange((gint32 *) location1, val.ival, cmp.ival);
934
935         return ret.fval;
936 }
937
938 void mono_thread_init(MonoDomain *domain, MonoThreadStartCB start_cb)
939 {
940         MonoClass *thread_class;
941         
942         /* Build a System.Threading.Thread object instance to return
943          * for the main line's Thread.CurrentThread property.
944          */
945         thread_class=mono_class_from_name(mono_defaults.corlib, "System.Threading", "Thread");
946         
947         /* I wonder what happens if someone tries to destroy this
948          * object? In theory, I guess the whole program should act as
949          * though exit() were called :-)
950          */
951 #ifdef THREAD_DEBUG
952         g_message(G_GNUC_PRETTY_FUNCTION
953                   ": Starting to build main Thread object");
954 #endif
955         main_thread = mono_object_new (domain, thread_class);
956 #ifdef THREAD_DEBUG
957         g_message(G_GNUC_PRETTY_FUNCTION
958                   ": Finished building main Thread object: %p", main_thread);
959 #endif
960
961         InitializeCriticalSection(&threads_mutex);
962         InitializeCriticalSection(&monitor_mutex);
963         InitializeCriticalSection(&interlocked_mutex);
964         
965         current_object_key=TlsAlloc();
966 #ifdef THREAD_DEBUG
967         g_message (G_GNUC_PRETTY_FUNCTION ": Allocated current_object_key %d",
968                    current_object_key);
969 #endif
970
971         TlsSetValue(current_object_key, main_thread);
972
973         mono_thread_start_cb = start_cb;
974
975         slothash_key=TlsAlloc();
976 }
977
978 void mono_thread_cleanup(void)
979 {
980         HANDLE wait[MAXIMUM_WAIT_OBJECTS];
981         guint32 i, j;
982         
983         /* join each thread that's still running */
984 #ifdef THREAD_DEBUG
985         g_message("Joining each running thread...");
986 #endif
987         
988         if(threads==NULL) {
989 #ifdef THREAD_DEBUG
990                 g_message("No threads");
991 #endif
992                 return;
993         }
994         
995         do{
996                 EnterCriticalSection (&threads_mutex);
997 #ifdef THREAD_DEBUG
998                 g_message("There are %d threads to join", threads->len);
999                 for(i=0; i<threads->len; i++) {
1000                         g_message("Waiting for: %d",
1001                                   g_array_index(threads, guint32, i));
1002                 }
1003 #endif
1004
1005                 for(i=0; i<MAXIMUM_WAIT_OBJECTS && i<threads->len; i++) {
1006                         wait[i]=OpenThread (THREAD_ALL_ACCESS, FALSE,
1007                                             g_array_index(threads, guint32, i));
1008                 }
1009                 
1010 #ifdef THREAD_DEBUG
1011                 g_message("%d threads to wait for in this batch", i);
1012 #endif
1013
1014                 LeaveCriticalSection (&threads_mutex);
1015
1016                 WaitForMultipleObjects(i, wait, TRUE, INFINITE);
1017
1018                 for(j=0; j<i; j++) {
1019                         CloseHandle (wait[j]);
1020                 }
1021         } while(i>0);
1022         
1023         g_array_free(threads, FALSE);
1024         threads=NULL;
1025 }