2003-02-17 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 #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/metadata/environment.h>
22 #include <mono/io-layer/io-layer.h>
23
24 #include <mono/os/gc_wrapper.h>
25
26 #undef THREAD_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 /*
43  * The "os_handle" field of the WaitHandle class.
44  */
45 static MonoClassField *wait_handle_os_handle_field = NULL;
46
47 /* Controls access to the 'threads' hash table */
48 static CRITICAL_SECTION threads_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 TLS key that holds the MonoObject assigned to each thread */
56 static guint32 current_object_key;
57
58 /* function called at thread start */
59 static MonoThreadStartCB mono_thread_start_cb = NULL;
60
61 /* function called at thread attach */
62 static MonoThreadAttachCB mono_thread_attach_cb = NULL;
63
64 /* function called when a new thread has been created */
65 static MonoThreadCallbacks *mono_thread_callbacks = 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_direct_hash, g_direct_equal);
87         }
88
89         /* We don't need to duplicate thread->handle, because it is
90          * only closed when the thread object is finalized by the GC.
91          */
92         mono_g_hash_table_insert(threads, GUINT_TO_POINTER(thread->tid), thread);
93         LeaveCriticalSection(&threads_mutex);
94 }
95
96 static void handle_remove(guint32 tid)
97 {
98 #ifdef THREAD_DEBUG
99         g_message(G_GNUC_PRETTY_FUNCTION ": thread ID %d", tid);
100 #endif
101
102         EnterCriticalSection(&threads_mutex);
103
104         mono_g_hash_table_remove (threads, GUINT_TO_POINTER(tid));
105         
106         LeaveCriticalSection(&threads_mutex);
107
108         /* Don't close the handle here, wait for the object finalizer
109          * to do it. Otherwise, the following race condition applies:
110          *
111          * 1) Thread exits (and handle_remove() closes the handle)
112          *
113          * 2) Some other handle is reassigned the same slot
114          *
115          * 3) Another thread tries to join the first thread, and
116          * blocks waiting for the reassigned handle to be signalled
117          * (which might never happen).  This is possible, because the
118          * thread calling Join() still has a reference to the first
119          * thread's object.
120          */
121 }
122
123 static void thread_cleanup (guint32 tid)
124 {
125         mono_profiler_thread_end (tid);
126         handle_remove (tid);
127 }
128
129 static guint32 start_wrapper(void *data)
130 {
131         struct StartInfo *start_info=(struct StartInfo *)data;
132         guint32 (*start_func)(void *);
133         void *this;
134         guint32 tid;
135         MonoThread *thread=start_info->obj;
136         
137 #ifdef THREAD_DEBUG
138         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper",
139                   GetCurrentThreadId ());
140 #endif
141         
142         /* We can be sure start_info->obj->tid and
143          * start_info->obj->handle have been set, because the thread
144          * was created suspended, and these values were set before the
145          * thread resumed
146          */
147
148         tid=thread->tid;
149         
150         mono_domain_set (start_info->domain);
151
152         start_func = start_info->func;
153         this = start_info->this;
154
155         /* This MUST be called before any managed code can be
156          * executed, as it calls the callback function that (for the
157          * jit) sets the lmf marker.
158          */
159         mono_thread_new_init (tid, &tid, start_func);
160
161 #ifdef THREAD_DEBUG
162         g_message (G_GNUC_PRETTY_FUNCTION
163                    ": (%d) Setting current_object_key to %p",
164                    GetCurrentThreadId (), thread);
165 #endif
166
167         TlsSetValue (current_object_key, thread);
168
169         mono_profiler_thread_start (tid);
170
171         if(thread->start_notify!=NULL) {
172                 /* Let the thread that called Start() know we're
173                  * ready
174                  */
175                 ReleaseSemaphore (thread->start_notify, 1, NULL);
176         }
177         
178         g_free (start_info);
179
180         start_func (this);
181
182         /* If the thread calls ExitThread at all, this remaining code
183          * will not be executed, but the main thread will eventually
184          * call thread_cleanup() on this thread's behalf.
185          */
186
187 #ifdef THREAD_DEBUG
188         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper terminating",
189                   GetCurrentThreadId ());
190 #endif
191         
192         thread_cleanup (tid);
193         
194         return(0);
195 }
196
197 void mono_thread_new_init (guint32 tid, gpointer stack_start, gpointer func)
198 {
199         if (mono_thread_start_cb) {
200                 mono_thread_start_cb (tid, stack_start, func);
201         }
202 }
203
204 void mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
205 {
206         MonoThread *thread;
207         HANDLE thread_handle;
208         struct StartInfo *start_info;
209         guint32 tid;
210         
211         thread=(MonoThread *)mono_object_new (domain,
212                                               mono_defaults.thread_class);
213
214         start_info=g_new0 (struct StartInfo, 1);
215         start_info->func = func;
216         start_info->obj = thread;
217         start_info->domain = domain;
218         start_info->this = arg;
219                 
220         /* Create suspended, so we can do some housekeeping before the thread
221          * starts
222          */
223         thread_handle = CreateThread(NULL, 0, start_wrapper, start_info,
224                                      CREATE_SUSPENDED, &tid);
225 #ifdef THREAD_DEBUG
226         g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d (handle %p)",
227                   tid, thread_handle);
228 #endif
229         g_assert (thread_handle);
230
231         thread->handle=thread_handle;
232         thread->tid=tid;
233
234         handle_store(thread);
235
236         ResumeThread (thread_handle);
237 }
238
239 MonoThread *
240 mono_thread_attach (MonoDomain *domain)
241 {
242         MonoThread *thread;
243         HANDLE thread_handle;
244         guint32 tid;
245
246         if ((thread = mono_thread_current ())) {
247                 g_warning ("mono_thread_attach called for an already attached thread");
248                 if (mono_thread_attach_cb) {
249                         mono_thread_attach_cb (tid, &tid);
250                 }
251                 return thread;
252         }
253
254         thread = (MonoThread *)mono_object_new (domain,
255                                                 mono_defaults.thread_class);
256
257         thread_handle = GetCurrentThread ();
258         g_assert (thread_handle);
259
260         tid=GetCurrentThreadId ();
261
262         thread->handle=thread_handle;
263         thread->tid=tid;
264
265 #ifdef THREAD_DEBUG
266         g_message(G_GNUC_PRETTY_FUNCTION ": Attached thread ID %d (handle %p)",
267                   tid, thread_handle);
268 #endif
269
270         handle_store(thread);
271
272 #ifdef THREAD_DEBUG
273         g_message (G_GNUC_PRETTY_FUNCTION
274                    ": (%d) Setting current_object_key to %p",
275                    GetCurrentThreadId (), thread);
276 #endif
277
278         TlsSetValue (current_object_key, thread);
279         mono_domain_set (domain);
280
281         if (mono_thread_attach_cb) {
282                 mono_thread_attach_cb (tid, &tid);
283         }
284
285         return(thread);
286 }
287
288 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this,
289                                                          MonoObject *start)
290 {
291         MonoMulticastDelegate *delegate = (MonoMulticastDelegate*)start;
292         guint32 (*start_func)(void *);
293         struct StartInfo *start_info;
294         MonoMethod *im;
295         HANDLE thread;
296         guint32 tid;
297         
298         MONO_ARCH_SAVE_REGS;
299
300 #ifdef THREAD_DEBUG
301         g_message(G_GNUC_PRETTY_FUNCTION
302                   ": Trying to start a new thread: this (%p) start (%p)",
303                   this, start);
304 #endif
305         
306         im = mono_get_delegate_invoke (start->vtable->klass);
307         if (mono_thread_callbacks)
308                 start_func = (* mono_thread_callbacks->thread_start_compile_func) (im);
309         else
310                 start_func = mono_compile_method (im);
311
312         if(start_func==NULL) {
313                 g_warning(G_GNUC_PRETTY_FUNCTION
314                           ": Can't locate start method!");
315                 return(NULL);
316         } else {
317                 /* This is freed in start_wrapper */
318                 start_info = g_new0 (struct StartInfo, 1);
319                 start_info->func = start_func;
320                 start_info->this = delegate;
321                 start_info->obj = this;
322                 start_info->domain = mono_domain_get ();
323
324                 this->start_notify=CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
325                 if(this->start_notify==NULL) {
326                         g_warning (G_GNUC_PRETTY_FUNCTION ": CreateSemaphore error 0x%x", GetLastError ());
327                         return(NULL);
328                 }
329
330                 thread=CreateThread(NULL, 0, start_wrapper, start_info,
331                                     CREATE_SUSPENDED, &tid);
332                 if(thread==NULL) {
333                         g_warning(G_GNUC_PRETTY_FUNCTION
334                                   ": CreateThread error 0x%x", GetLastError());
335                         return(NULL);
336                 }
337                 
338                 this->handle=thread;
339                 this->tid=tid;
340
341                 /* Don't call handle_store() here, delay it to Start.
342                  * We can't join a thread (trying to will just block
343                  * forever) until it actually starts running, so don't
344                  * store the handle till then.
345                  */
346
347 #ifdef THREAD_DEBUG
348                 g_message(G_GNUC_PRETTY_FUNCTION
349                           ": Started thread ID %d (handle %p)", tid, thread);
350 #endif
351
352                 return(thread);
353         }
354 }
355
356 void ves_icall_System_Threading_Thread_Thread_free_internal (MonoThread *this,
357                                                              HANDLE thread)
358 {
359         MONO_ARCH_SAVE_REGS;
360
361 #ifdef THREAD_DEBUG
362         g_message (G_GNUC_PRETTY_FUNCTION ": Closing thread %p, handle %p",
363                    this, thread);
364 #endif
365
366         CloseHandle (thread);
367 }
368
369 void ves_icall_System_Threading_Thread_Start_internal(MonoThread *this,
370                                                       HANDLE thread)
371 {
372         MONO_ARCH_SAVE_REGS;
373
374 #ifdef THREAD_DEBUG
375         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Launching thread %p (%d)",
376                   GetCurrentThreadId (), this, this->tid);
377 #endif
378
379         /* Only store the handle when the thread is about to be
380          * launched, to avoid the main thread deadlocking while trying
381          * to clean up a thread that will never be signalled.
382          */
383         handle_store(this);
384
385         if (mono_thread_callbacks)
386                 (* mono_thread_callbacks->start_resume) (this->tid);
387
388         ResumeThread(thread);
389
390         if (mono_thread_callbacks)
391                 (* mono_thread_callbacks->end_resume) (this->tid);
392
393         if(this->start_notify!=NULL) {
394                 /* Wait for the thread to set up its TLS data etc, so
395                  * theres no potential race condition if someone tries
396                  * to look up the data believing the thread has
397                  * started
398                  */
399
400 #ifdef THREAD_DEBUG
401                 g_message(G_GNUC_PRETTY_FUNCTION
402                           ": (%d) waiting for thread %p (%d) to start",
403                           GetCurrentThreadId (), this, this->tid);
404 #endif
405
406                 WaitForSingleObject (this->start_notify, INFINITE);
407                 CloseHandle (this->start_notify);
408                 this->start_notify=NULL;
409         }
410
411 #ifdef THREAD_DEBUG
412         g_message(G_GNUC_PRETTY_FUNCTION
413                   ": (%d) Done launching thread %p (%d)",
414                   GetCurrentThreadId (), this, this->tid);
415 #endif
416 }
417
418 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
419 {
420         MONO_ARCH_SAVE_REGS;
421
422 #ifdef THREAD_DEBUG
423         g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
424 #endif
425
426         Sleep(ms);
427 }
428
429 gint32
430 ves_icall_System_Threading_Thread_GetDomainID (void) 
431 {
432         MONO_ARCH_SAVE_REGS;
433
434         return mono_domain_get()->domain_id;
435 }
436
437 MonoThread *
438 mono_thread_current (void)
439 {
440         MonoThread *thread;
441         
442         MONO_ARCH_SAVE_REGS;
443
444         /* Find the current thread object */
445         thread=TlsGetValue (current_object_key);
446         
447 #ifdef THREAD_DEBUG
448         g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", thread);
449 #endif
450
451         return (thread);
452 }
453
454 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this,
455                                                          int ms, HANDLE thread)
456 {
457         gboolean ret;
458         
459         MONO_ARCH_SAVE_REGS;
460
461         if(ms== -1) {
462                 ms=INFINITE;
463         }
464 #ifdef THREAD_DEBUG
465         g_message (G_GNUC_PRETTY_FUNCTION ": joining thread handle %p, %d ms",
466                    thread, ms);
467 #endif
468         
469         ret=WaitForSingleObject(thread, ms);
470         if(ret==WAIT_OBJECT_0) {
471 #ifdef THREAD_DEBUG
472                 g_message (G_GNUC_PRETTY_FUNCTION ": join successful");
473 #endif
474
475                 return(TRUE);
476         }
477         
478 #ifdef THREAD_DEBUG
479                 g_message (G_GNUC_PRETTY_FUNCTION ": join failed");
480 #endif
481
482         return(FALSE);
483 }
484
485 void ves_icall_System_Threading_Thread_SlotHash_store(MonoObject *data)
486 {
487         MONO_ARCH_SAVE_REGS;
488
489 #ifdef THREAD_DEBUG
490         g_message(G_GNUC_PRETTY_FUNCTION ": Storing key %p", data);
491 #endif
492
493         /* Object location stored here */
494         TlsSetValue(slothash_key, data);
495 }
496
497 MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void)
498 {
499         MonoObject *data;
500
501         MONO_ARCH_SAVE_REGS;
502
503         data=TlsGetValue(slothash_key);
504         
505 #ifdef THREAD_DEBUG
506         g_message(G_GNUC_PRETTY_FUNCTION ": Retrieved key %p", data);
507 #endif
508         
509         return(data);
510 }
511
512 /* FIXME: exitContext isnt documented */
513 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
514 {
515         HANDLE *handles;
516         guint32 numhandles;
517         guint32 ret;
518         guint32 i;
519         MonoObject *waitHandle;
520         MonoClass *klass;
521                 
522         MONO_ARCH_SAVE_REGS;
523
524         numhandles = mono_array_length(mono_handles);
525         handles = g_new0(HANDLE, numhandles);
526
527         if (wait_handle_os_handle_field == 0) {
528                 /* Get the field os_handle which will contain the actual handle */
529                 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");   
530                 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
531         }
532                 
533         for(i = 0; i < numhandles; i++) {       
534                 waitHandle = mono_array_get(mono_handles, MonoObject*, i);              
535                 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
536         }
537         
538         if(ms== -1) {
539                 ms=INFINITE;
540         }
541         
542         ret=WaitForMultipleObjects(numhandles, handles, TRUE, ms);
543
544         g_free(handles);
545         
546         if(ret==WAIT_FAILED) {
547 #ifdef THREAD_WAIT_DEBUG
548                 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
549                           GetCurrentThreadId ());
550 #endif
551                 return(FALSE);
552         } else if(ret==WAIT_TIMEOUT) {
553 #ifdef THREAD_WAIT_DEBUG
554                 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
555                           GetCurrentThreadId ());
556 #endif
557                 return(FALSE);
558         }
559         
560         return(TRUE);
561 }
562
563 /* FIXME: exitContext isnt documented */
564 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
565 {
566         HANDLE *handles;
567         guint32 numhandles;
568         guint32 ret;
569         guint32 i;
570         MonoObject *waitHandle;
571         MonoClass *klass;
572                 
573         MONO_ARCH_SAVE_REGS;
574
575         numhandles = mono_array_length(mono_handles);
576         handles = g_new0(HANDLE, numhandles);
577
578         if (wait_handle_os_handle_field == 0) {
579                 /* Get the field os_handle which will contain the actual handle */
580                 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");   
581                 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
582         }
583                 
584         for(i = 0; i < numhandles; i++) {       
585                 waitHandle = mono_array_get(mono_handles, MonoObject*, i);              
586                 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
587         }
588         
589         if(ms== -1) {
590                 ms=INFINITE;
591         }
592
593         ret=WaitForMultipleObjects(numhandles, handles, FALSE, ms);
594
595         g_free(handles);
596         
597 #ifdef THREAD_WAIT_DEBUG
598         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) returning %d",
599                   GetCurrentThreadId (), ret);
600 #endif
601
602         /*
603          * These need to be here.  See MSDN dos on WaitForMultipleObjects.
604          */
605         if (ret >= WAIT_OBJECT_0 && ret <= WAIT_OBJECT_0 + numhandles - 1) {
606                 return ret - WAIT_OBJECT_0;
607         }
608         else if (ret >= WAIT_ABANDONED_0 && ret <= WAIT_ABANDONED_0 + numhandles - 1) {
609                 return ret - WAIT_ABANDONED_0;
610         }
611         else {
612                 return ret;
613         }
614 }
615
616 /* FIXME: exitContext isnt documented */
617 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
618 {
619         guint32 ret;
620         
621         MONO_ARCH_SAVE_REGS;
622
623 #ifdef THREAD_WAIT_DEBUG
624         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) waiting for %p, %d ms",
625                   GetCurrentThreadId (), handle, ms);
626 #endif
627         
628         if(ms== -1) {
629                 ms=INFINITE;
630         }
631         
632         ret=WaitForSingleObject(handle, ms);
633         if(ret==WAIT_FAILED) {
634 #ifdef THREAD_WAIT_DEBUG
635                 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
636                           GetCurrentThreadId ());
637 #endif
638                 return(FALSE);
639         } else if(ret==WAIT_TIMEOUT) {
640 #ifdef THREAD_WAIT_DEBUG
641                 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
642                           GetCurrentThreadId ());
643 #endif
644                 return(FALSE);
645         }
646         
647         return(TRUE);
648 }
649
650 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned,char *name) { 
651         MONO_ARCH_SAVE_REGS;
652    
653         return(CreateMutex(NULL,owned,name));                    
654 }                                                                   
655
656 void ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) { 
657         MONO_ARCH_SAVE_REGS;
658
659         ReleaseMutex(handle);
660 }
661
662 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual,
663                                                                                                                           MonoBoolean initial,
664                                                                                                                           char *name) {
665         MONO_ARCH_SAVE_REGS;
666
667         return (CreateEvent(NULL,manual,initial,name));
668 }
669
670 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
671         MONO_ARCH_SAVE_REGS;
672
673         return (SetEvent(handle));
674 }
675
676 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
677         MONO_ARCH_SAVE_REGS;
678
679         return (ResetEvent(handle));
680 }
681
682 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
683 {
684         MONO_ARCH_SAVE_REGS;
685
686         return InterlockedIncrement (location);
687 }
688
689 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
690 {
691         gint32 lowret;
692         gint32 highret;
693
694         MONO_ARCH_SAVE_REGS;
695
696         EnterCriticalSection(&interlocked_mutex);
697
698         lowret = InterlockedIncrement((gint32 *) location);
699         if (0 == lowret)
700                 highret = InterlockedIncrement((gint32 *) location + 1);
701         else
702                 highret = *((gint32 *) location + 1);
703
704         LeaveCriticalSection(&interlocked_mutex);
705
706         return (gint64) highret << 32 | (gint64) lowret;
707 }
708
709 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
710 {
711         MONO_ARCH_SAVE_REGS;
712
713         return InterlockedDecrement(location);
714 }
715
716 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
717 {
718         gint32 lowret;
719         gint32 highret;
720
721         MONO_ARCH_SAVE_REGS;
722
723         EnterCriticalSection(&interlocked_mutex);
724
725         lowret = InterlockedDecrement((gint32 *) location);
726         if (-1 == lowret)
727                 highret = InterlockedDecrement((gint32 *) location + 1);
728         else
729                 highret = *((gint32 *) location + 1);
730
731         LeaveCriticalSection(&interlocked_mutex);
732
733         return (gint64) highret << 32 | (gint64) lowret;
734 }
735
736 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location1, gint32 value)
737 {
738         MONO_ARCH_SAVE_REGS;
739
740         return InterlockedExchange(location1, value);
741 }
742
743 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location1, MonoObject *value)
744 {
745         MONO_ARCH_SAVE_REGS;
746
747         return (MonoObject *) InterlockedExchangePointer((gpointer *) location1, value);
748 }
749
750 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location1, gfloat value)
751 {
752         IntFloatUnion val, ret;
753
754         MONO_ARCH_SAVE_REGS;
755
756         val.fval = value;
757         ret.ival = InterlockedExchange((gint32 *) location1, val.ival);
758
759         return ret.fval;
760 }
761
762 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location1, gint32 value, gint32 comparand)
763 {
764         MONO_ARCH_SAVE_REGS;
765
766         return InterlockedCompareExchange(location1, value, comparand);
767 }
768
769 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location1, MonoObject *value, MonoObject *comparand)
770 {
771         MONO_ARCH_SAVE_REGS;
772
773         return (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location1, value, comparand);
774 }
775
776 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location1, gfloat value, gfloat comparand)
777 {
778         IntFloatUnion val, ret, cmp;
779
780         MONO_ARCH_SAVE_REGS;
781
782         val.fval = value;
783         cmp.fval = comparand;
784         ret.ival = InterlockedCompareExchange((gint32 *) location1, val.ival, cmp.ival);
785
786         return ret.fval;
787 }
788
789 int  
790 mono_thread_get_abort_signal (void)
791 {
792 #ifdef __MINGW32__
793         return -1;
794 #else
795 #ifndef SIGRTMIN
796         return SIGUSR1;
797 #else
798         return SIGRTMIN;
799 #endif
800 #endif /* __MINGW32__ */
801 }
802
803 void
804 ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state)
805 {
806         MONO_ARCH_SAVE_REGS;
807
808         thread->abort_state = state;
809         thread->abort_exc = mono_get_exception_thread_abort ();
810
811 #ifdef THREAD_DEBUG
812         g_message (G_GNUC_PRETTY_FUNCTION
813                    ": (%d) Abort requested for %p (%d)", GetCurrentThreadId (),
814                    thread, thread->tid);
815 #endif
816         
817 #ifdef __MINGW32__
818         g_assert_not_reached ();
819 #else
820         /* fixme: store the state somewhere */
821 #ifdef PTHREAD_POINTER_ID
822         pthread_kill (GUINT_TO_POINTER(thread->tid), mono_thread_get_abort_signal ());
823 #else
824         pthread_kill (thread->tid, mono_thread_get_abort_signal ());
825 #endif
826 #endif /* __MINGW32__ */
827 }
828
829 void
830 ves_icall_System_Threading_Thread_ResetAbort (void)
831 {
832         MonoThread *thread = mono_thread_current ();
833         
834         MONO_ARCH_SAVE_REGS;
835
836         if (!thread->abort_exc) {
837                 const char *msg = "Unable to reset abort because no abort was requested";
838                 mono_raise_exception (mono_get_exception_thread_state (msg));
839         } else {
840                 thread->abort_exc = NULL;
841                 thread->abort_state = NULL;
842         }
843 }
844
845 void mono_thread_init (MonoThreadStartCB start_cb,
846                        MonoThreadAttachCB attach_cb)
847 {
848         InitializeCriticalSection(&threads_mutex);
849         InitializeCriticalSection(&interlocked_mutex);
850         
851         current_object_key=TlsAlloc();
852 #ifdef THREAD_DEBUG
853         g_message (G_GNUC_PRETTY_FUNCTION ": Allocated current_object_key %d",
854                    current_object_key);
855 #endif
856
857         mono_thread_start_cb = start_cb;
858         mono_thread_attach_cb = attach_cb;
859
860         slothash_key=TlsAlloc();
861
862         /* Get a pseudo handle to the current process.  This is just a
863          * kludge so that wapi can build a process handle if needed.
864          * As a pseudo handle is returned, we don't need to clean
865          * anything up.
866          */
867         GetCurrentProcess ();
868 }
869
870 void mono_install_thread_callbacks (MonoThreadCallbacks *callbacks)
871 {
872         mono_thread_callbacks = callbacks;
873 }
874
875 #ifdef THREAD_DEBUG
876 static void print_tids (gpointer key, gpointer value, gpointer user)
877 {
878         g_message ("Waiting for: %d", GPOINTER_TO_UINT(key));
879 }
880 #endif
881
882 struct wait_data 
883 {
884         HANDLE handles[MAXIMUM_WAIT_OBJECTS];
885         MonoThread *threads[MAXIMUM_WAIT_OBJECTS];
886         guint32 num;
887 };
888
889 static void wait_for_tids (struct wait_data *wait)
890 {
891         guint32 i, ret;
892         
893 #ifdef THREAD_DEBUG
894         g_message(G_GNUC_PRETTY_FUNCTION
895                   ": %d threads to wait for in this batch", wait->num);
896 #endif
897
898         ret=WaitForMultipleObjects(wait->num, wait->handles, TRUE, INFINITE);
899         if(ret==WAIT_FAILED) {
900                 /* See the comment in build_wait_tids() */
901 #ifdef THREAD_DEBUG
902                 g_message (G_GNUC_PRETTY_FUNCTION ": Wait failed");
903 #endif
904                 return;
905         }
906         
907
908         for(i=0; i<wait->num; i++) {
909                 guint32 tid=wait->threads[i]->tid;
910                 
911                 if(mono_g_hash_table_lookup (threads, GUINT_TO_POINTER(tid))!=NULL) {
912                         /* This thread must have been killed, because
913                          * it hasn't cleaned itself up. (It's just
914                          * possible that the thread exited before the
915                          * parent thread had a chance to store the
916                          * handle, and now there is another pointer to
917                          * the already-exited thread stored.  In this
918                          * case, we'll just get two
919                          * mono_profiler_thread_end() calls for the
920                          * same thread.)
921                          */
922         
923 #ifdef THREAD_DEBUG
924                         g_message (G_GNUC_PRETTY_FUNCTION
925                                    ": cleaning up after thread %d", tid);
926 #endif
927                         thread_cleanup (tid);
928                 }
929         }
930 }
931
932 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
933 {
934         struct wait_data *wait=(struct wait_data *)user;
935
936         if(wait->num<MAXIMUM_WAIT_OBJECTS) {
937                 MonoThread *thread=(MonoThread *)value;
938                 
939                 wait->handles[wait->num]=thread->handle;
940                 wait->threads[wait->num]=thread;
941                 wait->num++;
942         } else {
943                 /* Just ignore the rest, we can't do anything with
944                  * them yet
945                  */
946         }
947 }
948
949 void mono_thread_manage (void)
950 {
951         struct wait_data *wait=g_new0 (struct wait_data, 1);
952         
953         /* join each thread that's still running */
954 #ifdef THREAD_DEBUG
955         g_message(G_GNUC_PRETTY_FUNCTION ": Joining each running thread...");
956 #endif
957         
958         if(threads==NULL) {
959 #ifdef THREAD_DEBUG
960                 g_message(G_GNUC_PRETTY_FUNCTION ": No threads");
961 #endif
962                 return;
963         }
964         
965         do {
966                 EnterCriticalSection (&threads_mutex);
967 #ifdef THREAD_DEBUG
968                 g_message(G_GNUC_PRETTY_FUNCTION
969                           ":There are %d threads to join",
970                           mono_g_hash_table_size (threads));
971                 mono_g_hash_table_foreach (threads, print_tids, NULL);
972 #endif
973
974                 wait->num=0;
975                 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
976         
977                 LeaveCriticalSection (&threads_mutex);
978                 if(wait->num>0) {
979                         /* Something to wait for */
980                         wait_for_tids (wait);
981                 }
982         } while(wait->num>0);
983         
984         g_free (wait);
985         
986         mono_g_hash_table_destroy(threads);
987         threads=NULL;
988 }
989
990 static void terminate_thread (gpointer key, gpointer value, gpointer user)
991 {
992         MonoThread *thread=(MonoThread *)value;
993         guint32 self=GPOINTER_TO_UINT (user);
994         
995         if(thread->tid!=self) {
996                 /*TerminateThread (thread->handle, -1);*/
997         }
998 }
999
1000 void mono_thread_abort_all_other_threads (void)
1001 {
1002         guint32 self=GetCurrentThreadId ();
1003
1004         EnterCriticalSection (&threads_mutex);
1005 #ifdef THREAD_DEBUG
1006         g_message(G_GNUC_PRETTY_FUNCTION ":There are %d threads to abort",
1007                   mono_g_hash_table_size (threads));
1008         mono_g_hash_table_foreach (threads, print_tids, NULL);
1009 #endif
1010
1011         mono_g_hash_table_foreach (threads, terminate_thread,
1012                                    GUINT_TO_POINTER (self));
1013         
1014         LeaveCriticalSection (&threads_mutex);
1015 }