grammar updates
[mono.git] / mono / metadata / threads.c
1 /*
2  * threads.c: Thread support internal calls
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *      Paolo Molaro (lupus@ximian.com)
7  *      Patrik Torstensson (patrik.torstensson@labs2.com)
8  *
9  * (C) 2001 Ximian, Inc.
10  */
11
12 #include <config.h>
13 #include <glib.h>
14 #include <signal.h>
15
16 #include <mono/metadata/object.h>
17 #include <mono/metadata/appdomain.h>
18 #include <mono/metadata/profiler-private.h>
19 #include <mono/metadata/threads.h>
20 #include <mono/metadata/threadpool.h>
21 #include <mono/metadata/threads-types.h>
22 #include <mono/metadata/exception.h>
23 #include <mono/metadata/environment.h>
24 #include <mono/metadata/monitor.h>
25 #include <mono/io-layer/io-layer.h>
26
27 #include <mono/os/gc_wrapper.h>
28
29 #undef THREAD_DEBUG
30 #undef THREAD_WAIT_DEBUG
31
32 struct StartInfo 
33 {
34         guint32 (*func)(void *);
35         MonoThread *obj;
36         void *this;
37         MonoDomain *domain;
38 };
39
40 typedef union {
41         gint32 ival;
42         gfloat fval;
43 } IntFloatUnion;
44  
45 /*
46  * The "os_handle" field of the WaitHandle class.
47  */
48 static MonoClassField *wait_handle_os_handle_field = NULL;
49
50 /* Controls access to the 'threads' hash table */
51 static CRITICAL_SECTION threads_mutex;
52
53 /* The hash of existing threads (key is thread ID) that need joining
54  * before exit
55  */
56 static MonoGHashTable *threads=NULL;
57
58 /* The TLS key that holds the MonoObject assigned to each thread */
59 static guint32 current_object_key = -1;
60
61 /* function called at thread start */
62 static MonoThreadStartCB mono_thread_start_cb = NULL;
63
64 /* function called at thread attach */
65 static MonoThreadAttachCB mono_thread_attach_cb = NULL;
66
67 /* function called at thread cleanup */
68 static MonoThreadCleanupFunc mono_thread_cleanup = NULL;
69
70 /* function called when a new thread has been created */
71 static MonoThreadCallbacks *mono_thread_callbacks = NULL;
72
73 /* The TLS key that holds the LocalDataStoreSlot hash in each thread */
74 static guint32 slothash_key = -1;
75
76 /* The default stack size for each thread */
77 static guint32 default_stacksize = 0;
78
79 static void thread_adjust_static_data (MonoThread *thread);
80
81 /* Spin lock for InterlockedXXX 64 bit functions */
82 static CRITICAL_SECTION interlocked_mutex;
83
84 /* handle_store() and handle_remove() manage the array of threads that
85  * still need to be waited for when the main thread exits.
86  */
87 static void handle_store(MonoThread *thread)
88 {
89         EnterCriticalSection(&threads_mutex);
90
91 #ifdef THREAD_DEBUG
92         g_message(G_GNUC_PRETTY_FUNCTION ": thread %p ID %d", thread,
93                   thread->tid);
94 #endif
95
96         if(threads==NULL) {
97                 threads=mono_g_hash_table_new(NULL, NULL);
98         }
99
100         /* We don't need to duplicate thread->handle, because it is
101          * only closed when the thread object is finalized by the GC.
102          */
103         mono_g_hash_table_insert(threads, GUINT_TO_POINTER(thread->tid), thread);
104         LeaveCriticalSection(&threads_mutex);
105 }
106
107 static void handle_remove(guint32 tid)
108 {
109 #ifdef THREAD_DEBUG
110         g_message(G_GNUC_PRETTY_FUNCTION ": thread ID %d", tid);
111 #endif
112
113         EnterCriticalSection(&threads_mutex);
114
115         mono_g_hash_table_remove (threads, GUINT_TO_POINTER(tid));
116         
117         LeaveCriticalSection(&threads_mutex);
118
119         /* Don't close the handle here, wait for the object finalizer
120          * to do it. Otherwise, the following race condition applies:
121          *
122          * 1) Thread exits (and handle_remove() closes the handle)
123          *
124          * 2) Some other handle is reassigned the same slot
125          *
126          * 3) Another thread tries to join the first thread, and
127          * blocks waiting for the reassigned handle to be signalled
128          * (which might never happen).  This is possible, because the
129          * thread calling Join() still has a reference to the first
130          * thread's object.
131          */
132 }
133
134 static void thread_cleanup (MonoThread *thread)
135 {
136         mono_monitor_try_enter ((MonoObject *)thread, INFINITE);
137         thread->state |= ThreadState_Stopped;
138         mono_monitor_exit ((MonoObject *)thread);
139
140         mono_profiler_thread_end (thread->tid);
141         handle_remove (thread->tid);
142
143         mono_thread_pop_appdomain_ref ();
144
145         if (mono_thread_cleanup)
146                 mono_thread_cleanup (thread);
147 }
148
149 static guint32 start_wrapper(void *data)
150 {
151         struct StartInfo *start_info=(struct StartInfo *)data;
152         guint32 (*start_func)(void *);
153         void *this;
154         guint32 tid;
155         MonoThread *thread=start_info->obj;
156         
157 #ifdef THREAD_DEBUG
158         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper",
159                   GetCurrentThreadId ());
160 #endif
161         
162         /* We can be sure start_info->obj->tid and
163          * start_info->obj->handle have been set, because the thread
164          * was created suspended, and these values were set before the
165          * thread resumed
166          */
167
168         tid=thread->tid;
169
170         TlsSetValue (current_object_key, thread);
171         
172         if (!mono_domain_set (start_info->domain, FALSE)) {
173                 /* No point in raising an appdomain_unloaded exception here */
174                 /* FIXME: Cleanup here */
175                 return 0;
176         }
177
178         start_func = start_info->func;
179         this = start_info->this;
180
181         /* This MUST be called before any managed code can be
182          * executed, as it calls the callback function that (for the
183          * jit) sets the lmf marker.
184          */
185         mono_thread_new_init (tid, &tid, start_func);
186         thread->stack_ptr = &tid;
187
188 #ifdef LIBGC_DEBUG
189         g_message (G_GNUC_PRETTY_FUNCTION
190                    ": (%d,%d) Setting thread stack to %p",
191                    GetCurrentThreadId (), getpid (), thread->stack_ptr);
192 #endif
193
194 #ifdef THREAD_DEBUG
195         g_message (G_GNUC_PRETTY_FUNCTION
196                    ": (%d) Setting current_object_key to %p",
197                    GetCurrentThreadId (), thread);
198 #endif
199
200         mono_profiler_thread_start (tid);
201
202         if(thread->start_notify!=NULL) {
203                 /* Let the thread that called Start() know we're
204                  * ready
205                  */
206                 ReleaseSemaphore (thread->start_notify, 1, NULL);
207         }
208         
209         g_free (start_info);
210
211         /* Every thread references the appdomain which created it */
212         mono_thread_push_appdomain_ref (mono_domain_get ());
213
214         thread_adjust_static_data (thread);
215
216         start_func (this);
217
218         /* If the thread calls ExitThread at all, this remaining code
219          * will not be executed, but the main thread will eventually
220          * call thread_cleanup() on this thread's behalf.
221          */
222
223 #ifdef THREAD_DEBUG
224         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper terminating",
225                   GetCurrentThreadId ());
226 #endif
227
228         /* Remove the reference to the thread object in the TLS data,
229          * so the thread object can be finalized.  This won't be
230          * reached if the thread threw an uncaught exception, so those
231          * thread handles will stay referenced :-( (This is due to
232          * missing support for scanning thread-specific data in the
233          * Boehm GC - the io-layer keeps a GC-visible hash of pointers
234          * to TLS data.)
235          */
236         TlsSetValue (current_object_key, NULL);
237         
238         thread_cleanup (thread);
239         
240         return(0);
241 }
242
243 void mono_thread_new_init (guint32 tid, gpointer stack_start, gpointer func)
244 {
245         if (mono_thread_start_cb) {
246                 mono_thread_start_cb (tid, stack_start, func);
247         }
248
249         if (mono_thread_callbacks)
250                 (* mono_thread_callbacks->thread_created) (tid, stack_start, func);
251 }
252
253 void mono_threads_set_default_stacksize (guint32 stacksize)
254 {
255         default_stacksize = stacksize;
256 }
257
258 guint32 mono_threads_get_default_stacksize (void)
259 {
260         return default_stacksize;
261 }
262
263 void mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
264 {
265         MonoThread *thread;
266         HANDLE thread_handle;
267         struct StartInfo *start_info;
268         guint32 tid;
269         
270         thread=(MonoThread *)mono_object_new (domain,
271                                               mono_defaults.thread_class);
272
273         start_info=g_new0 (struct StartInfo, 1);
274         start_info->func = func;
275         start_info->obj = thread;
276         start_info->domain = domain;
277         start_info->this = arg;
278                 
279         /* Create suspended, so we can do some housekeeping before the thread
280          * starts
281          */
282         thread_handle = CreateThread(NULL, default_stacksize, start_wrapper, start_info,
283                                      CREATE_SUSPENDED, &tid);
284 #ifdef THREAD_DEBUG
285         g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d (handle %p)",
286                   tid, thread_handle);
287 #endif
288         g_assert (thread_handle);
289
290         thread->handle=thread_handle;
291         thread->tid=tid;
292
293         handle_store(thread);
294
295         ResumeThread (thread_handle);
296 }
297
298 MonoThread *
299 mono_thread_attach (MonoDomain *domain)
300 {
301         MonoThread *thread;
302         HANDLE thread_handle;
303         guint32 tid;
304
305         if ((thread = mono_thread_current ())) {
306                 g_warning ("mono_thread_attach called for an already attached thread");
307                 if (mono_thread_attach_cb) {
308                         mono_thread_attach_cb (tid, &tid);
309                 }
310                 return thread;
311         }
312
313         thread = (MonoThread *)mono_object_new (domain,
314                                                 mono_defaults.thread_class);
315
316         thread_handle = GetCurrentThread ();
317         g_assert (thread_handle);
318
319         tid=GetCurrentThreadId ();
320
321         thread->handle=thread_handle;
322         thread->tid=tid;
323
324 #ifdef THREAD_DEBUG
325         g_message(G_GNUC_PRETTY_FUNCTION ": Attached thread ID %d (handle %p)",
326                   tid, thread_handle);
327 #endif
328
329         handle_store(thread);
330
331 #ifdef THREAD_DEBUG
332         g_message (G_GNUC_PRETTY_FUNCTION
333                    ": (%d) Setting current_object_key to %p",
334                    GetCurrentThreadId (), thread);
335 #endif
336
337         TlsSetValue (current_object_key, thread);
338         mono_domain_set (domain, TRUE);
339
340         thread_adjust_static_data (thread);
341
342         if (mono_thread_attach_cb) {
343                 mono_thread_attach_cb (tid, &tid);
344         }
345
346         return(thread);
347 }
348
349 void
350 ves_icall_System_Threading_ThreadPool_GetAvailableThreads (int *workerThreads, int *completionPortThreads)
351 {
352         int busy;
353
354         MONO_ARCH_SAVE_REGS;
355
356         busy = (int) InterlockedCompareExchange (&busy_worker_threads, 0, -1);
357         *workerThreads = mono_max_worker_threads - busy;
358         *completionPortThreads = 0;
359 }
360
361 void
362 ves_icall_System_Threading_ThreadPool_GetMaxThreads (int *workerThreads, int *completionPortThreads)
363 {
364
365         MONO_ARCH_SAVE_REGS;
366
367         *workerThreads = mono_max_worker_threads;
368         *completionPortThreads = 0;
369 }
370
371 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this,
372                                                          MonoObject *start)
373 {
374         MonoMulticastDelegate *delegate = (MonoMulticastDelegate*)start;
375         guint32 (*start_func)(void *);
376         struct StartInfo *start_info;
377         MonoMethod *im;
378         HANDLE thread;
379         guint32 tid;
380         
381         MONO_ARCH_SAVE_REGS;
382
383 #ifdef THREAD_DEBUG
384         g_message(G_GNUC_PRETTY_FUNCTION
385                   ": Trying to start a new thread: this (%p) start (%p)",
386                   this, start);
387 #endif
388         
389         im = mono_get_delegate_invoke (start->vtable->klass);
390         if (mono_thread_callbacks)
391                 start_func = (* mono_thread_callbacks->thread_start_compile_func) (im);
392         else
393                 start_func = mono_compile_method (im);
394
395         if(start_func==NULL) {
396                 g_warning(G_GNUC_PRETTY_FUNCTION
397                           ": Can't locate start method!");
398                 return(NULL);
399         } else {
400                 /* This is freed in start_wrapper */
401                 start_info = g_new0 (struct StartInfo, 1);
402                 start_info->func = start_func;
403                 start_info->this = delegate;
404                 start_info->obj = this;
405                 start_info->domain = mono_domain_get ();
406
407                 this->start_notify=CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
408                 if(this->start_notify==NULL) {
409                         g_warning (G_GNUC_PRETTY_FUNCTION ": CreateSemaphore error 0x%x", GetLastError ());
410                         return(NULL);
411                 }
412
413                 thread=CreateThread(NULL, default_stacksize, start_wrapper, start_info,
414                                     CREATE_SUSPENDED, &tid);
415                 if(thread==NULL) {
416                         g_warning(G_GNUC_PRETTY_FUNCTION
417                                   ": CreateThread error 0x%x", GetLastError());
418                         return(NULL);
419                 }
420                 
421                 this->handle=thread;
422                 this->tid=tid;
423
424                 /* Don't call handle_store() here, delay it to Start.
425                  * We can't join a thread (trying to will just block
426                  * forever) until it actually starts running, so don't
427                  * store the handle till then.
428                  */
429
430 #ifdef THREAD_DEBUG
431                 g_message(G_GNUC_PRETTY_FUNCTION
432                           ": Started thread ID %d (handle %p)", tid, thread);
433 #endif
434
435                 return(thread);
436         }
437 }
438
439 void ves_icall_System_Threading_Thread_Thread_free_internal (MonoThread *this,
440                                                              HANDLE thread)
441 {
442         MONO_ARCH_SAVE_REGS;
443
444 #ifdef THREAD_DEBUG
445         g_message (G_GNUC_PRETTY_FUNCTION ": Closing thread %p, handle %p",
446                    this, thread);
447 #endif
448
449         CloseHandle (thread);
450 }
451
452 void ves_icall_System_Threading_Thread_Start_internal(MonoThread *this,
453                                                       HANDLE thread)
454 {
455         MONO_ARCH_SAVE_REGS;
456
457 #ifdef THREAD_DEBUG
458         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Launching thread %p (%d)",
459                   GetCurrentThreadId (), this, this->tid);
460 #endif
461
462         /* Only store the handle when the thread is about to be
463          * launched, to avoid the main thread deadlocking while trying
464          * to clean up a thread that will never be signalled.
465          */
466         handle_store(this);
467
468         if (mono_thread_callbacks)
469                 (* mono_thread_callbacks->start_resume) (this->tid);
470
471         ResumeThread(thread);
472
473         if (mono_thread_callbacks)
474                 (* mono_thread_callbacks->end_resume) (this->tid);
475
476         if(this->start_notify!=NULL) {
477                 /* Wait for the thread to set up its TLS data etc, so
478                  * theres no potential race condition if someone tries
479                  * to look up the data believing the thread has
480                  * started
481                  */
482
483 #ifdef THREAD_DEBUG
484                 g_message(G_GNUC_PRETTY_FUNCTION
485                           ": (%d) waiting for thread %p (%d) to start",
486                           GetCurrentThreadId (), this, this->tid);
487 #endif
488
489                 WaitForSingleObject (this->start_notify, INFINITE);
490                 CloseHandle (this->start_notify);
491                 this->start_notify=NULL;
492         }
493
494 #ifdef THREAD_DEBUG
495         g_message(G_GNUC_PRETTY_FUNCTION
496                   ": (%d) Done launching thread %p (%d)",
497                   GetCurrentThreadId (), this, this->tid);
498 #endif
499 }
500
501 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
502 {
503         MONO_ARCH_SAVE_REGS;
504
505 #ifdef THREAD_DEBUG
506         g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
507 #endif
508
509         Sleep(ms);
510 }
511
512 gint32
513 ves_icall_System_Threading_Thread_GetDomainID (void) 
514 {
515         MONO_ARCH_SAVE_REGS;
516
517         return mono_domain_get()->domain_id;
518 }
519
520 MonoThread *
521 mono_thread_current (void)
522 {
523         MonoThread *thread;
524         
525         MONO_ARCH_SAVE_REGS;
526
527         /* Find the current thread object */
528         thread=TlsGetValue (current_object_key);
529         
530 #ifdef THREAD_DEBUG
531         g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", thread);
532 #endif
533
534         return (thread);
535 }
536
537 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this,
538                                                          int ms, HANDLE thread)
539 {
540         gboolean ret;
541         
542         MONO_ARCH_SAVE_REGS;
543
544         if(ms== -1) {
545                 ms=INFINITE;
546         }
547 #ifdef THREAD_DEBUG
548         g_message (G_GNUC_PRETTY_FUNCTION ": joining thread handle %p, %d ms",
549                    thread, ms);
550 #endif
551         
552         ret=WaitForSingleObject(thread, ms);
553         if(ret==WAIT_OBJECT_0) {
554 #ifdef THREAD_DEBUG
555                 g_message (G_GNUC_PRETTY_FUNCTION ": join successful");
556 #endif
557
558                 return(TRUE);
559         }
560         
561 #ifdef THREAD_DEBUG
562                 g_message (G_GNUC_PRETTY_FUNCTION ": join failed");
563 #endif
564
565         return(FALSE);
566 }
567
568 void ves_icall_System_Threading_Thread_SlotHash_store(MonoObject *data)
569 {
570         MONO_ARCH_SAVE_REGS;
571
572 #ifdef THREAD_DEBUG
573         g_message(G_GNUC_PRETTY_FUNCTION ": Storing key %p", data);
574 #endif
575
576         /* Object location stored here */
577         TlsSetValue(slothash_key, data);
578 }
579
580 MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void)
581 {
582         MonoObject *data;
583
584         MONO_ARCH_SAVE_REGS;
585
586         data=TlsGetValue(slothash_key);
587         
588 #ifdef THREAD_DEBUG
589         g_message(G_GNUC_PRETTY_FUNCTION ": Retrieved key %p", data);
590 #endif
591         
592         return(data);
593 }
594
595 /* FIXME: exitContext isnt documented */
596 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
597 {
598         HANDLE *handles;
599         guint32 numhandles;
600         guint32 ret;
601         guint32 i;
602         MonoObject *waitHandle;
603         MonoClass *klass;
604                 
605         MONO_ARCH_SAVE_REGS;
606
607         numhandles = mono_array_length(mono_handles);
608         handles = g_new0(HANDLE, numhandles);
609
610         if (wait_handle_os_handle_field == 0) {
611                 /* Get the field os_handle which will contain the actual handle */
612                 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");   
613                 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
614         }
615                 
616         for(i = 0; i < numhandles; i++) {       
617                 waitHandle = mono_array_get(mono_handles, MonoObject*, i);              
618                 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
619         }
620         
621         if(ms== -1) {
622                 ms=INFINITE;
623         }
624         
625         ret=WaitForMultipleObjects(numhandles, handles, TRUE, ms);
626
627         g_free(handles);
628         
629         if(ret==WAIT_FAILED) {
630 #ifdef THREAD_WAIT_DEBUG
631                 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
632                           GetCurrentThreadId ());
633 #endif
634                 return(FALSE);
635         } else if(ret==WAIT_TIMEOUT) {
636 #ifdef THREAD_WAIT_DEBUG
637                 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
638                           GetCurrentThreadId ());
639 #endif
640                 return(FALSE);
641         }
642         
643         return(TRUE);
644 }
645
646 /* FIXME: exitContext isnt documented */
647 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
648 {
649         HANDLE *handles;
650         guint32 numhandles;
651         guint32 ret;
652         guint32 i;
653         MonoObject *waitHandle;
654         MonoClass *klass;
655                 
656         MONO_ARCH_SAVE_REGS;
657
658         numhandles = mono_array_length(mono_handles);
659         handles = g_new0(HANDLE, numhandles);
660
661         if (wait_handle_os_handle_field == 0) {
662                 /* Get the field os_handle which will contain the actual handle */
663                 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");   
664                 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
665         }
666                 
667         for(i = 0; i < numhandles; i++) {       
668                 waitHandle = mono_array_get(mono_handles, MonoObject*, i);              
669                 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
670         }
671         
672         if(ms== -1) {
673                 ms=INFINITE;
674         }
675
676         ret=WaitForMultipleObjects(numhandles, handles, FALSE, ms);
677
678         g_free(handles);
679         
680 #ifdef THREAD_WAIT_DEBUG
681         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) returning %d",
682                   GetCurrentThreadId (), ret);
683 #endif
684
685         /*
686          * These need to be here.  See MSDN dos on WaitForMultipleObjects.
687          */
688         if (ret >= WAIT_OBJECT_0 && ret <= WAIT_OBJECT_0 + numhandles - 1) {
689                 return ret - WAIT_OBJECT_0;
690         }
691         else if (ret >= WAIT_ABANDONED_0 && ret <= WAIT_ABANDONED_0 + numhandles - 1) {
692                 return ret - WAIT_ABANDONED_0;
693         }
694         else {
695                 return ret;
696         }
697 }
698
699 /* FIXME: exitContext isnt documented */
700 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
701 {
702         guint32 ret;
703         
704         MONO_ARCH_SAVE_REGS;
705
706 #ifdef THREAD_WAIT_DEBUG
707         g_message(G_GNUC_PRETTY_FUNCTION ": (%d) waiting for %p, %d ms",
708                   GetCurrentThreadId (), handle, ms);
709 #endif
710         
711         if(ms== -1) {
712                 ms=INFINITE;
713         }
714         
715         ret=WaitForSingleObject(handle, ms);
716         if(ret==WAIT_FAILED) {
717 #ifdef THREAD_WAIT_DEBUG
718                 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed",
719                           GetCurrentThreadId ());
720 #endif
721                 return(FALSE);
722         } else if(ret==WAIT_TIMEOUT) {
723 #ifdef THREAD_WAIT_DEBUG
724                 g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out",
725                           GetCurrentThreadId ());
726 #endif
727                 return(FALSE);
728         }
729         
730         return(TRUE);
731 }
732
733 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned,char *name) { 
734         MONO_ARCH_SAVE_REGS;
735    
736         return(CreateMutex(NULL,owned,name));                    
737 }                                                                   
738
739 void ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) { 
740         MONO_ARCH_SAVE_REGS;
741
742         ReleaseMutex(handle);
743 }
744
745 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual, MonoBoolean initial, char *name) {
746         MONO_ARCH_SAVE_REGS;
747
748         return (CreateEvent(NULL,manual,initial,name));
749 }
750
751 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
752         MONO_ARCH_SAVE_REGS;
753
754         return (SetEvent(handle));
755 }
756
757 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
758         MONO_ARCH_SAVE_REGS;
759
760         return (ResetEvent(handle));
761 }
762
763 void
764 ves_icall_System_Threading_Events_CloseEvent_internal (HANDLE handle) {
765         MONO_ARCH_SAVE_REGS;
766
767         CloseHandle (handle);
768 }
769
770 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
771 {
772         MONO_ARCH_SAVE_REGS;
773
774         return InterlockedIncrement (location);
775 }
776
777 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
778 {
779         gint32 lowret;
780         gint32 highret;
781
782         MONO_ARCH_SAVE_REGS;
783
784         EnterCriticalSection(&interlocked_mutex);
785
786         lowret = InterlockedIncrement((gint32 *) location);
787         if (0 == lowret)
788                 highret = InterlockedIncrement((gint32 *) location + 1);
789         else
790                 highret = *((gint32 *) location + 1);
791
792         LeaveCriticalSection(&interlocked_mutex);
793
794         return (gint64) highret << 32 | (gint64) lowret;
795 }
796
797 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
798 {
799         MONO_ARCH_SAVE_REGS;
800
801         return InterlockedDecrement(location);
802 }
803
804 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
805 {
806         gint32 lowret;
807         gint32 highret;
808
809         MONO_ARCH_SAVE_REGS;
810
811         EnterCriticalSection(&interlocked_mutex);
812
813         lowret = InterlockedDecrement((gint32 *) location);
814         if (-1 == lowret)
815                 highret = InterlockedDecrement((gint32 *) location + 1);
816         else
817                 highret = *((gint32 *) location + 1);
818
819         LeaveCriticalSection(&interlocked_mutex);
820
821         return (gint64) highret << 32 | (gint64) lowret;
822 }
823
824 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location1, gint32 value)
825 {
826         MONO_ARCH_SAVE_REGS;
827
828         return InterlockedExchange(location1, value);
829 }
830
831 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location1, MonoObject *value)
832 {
833         MONO_ARCH_SAVE_REGS;
834
835         return (MonoObject *) InterlockedExchangePointer((gpointer *) location1, value);
836 }
837
838 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location1, gfloat value)
839 {
840         IntFloatUnion val, ret;
841
842         MONO_ARCH_SAVE_REGS;
843
844         val.fval = value;
845         ret.ival = InterlockedExchange((gint32 *) location1, val.ival);
846
847         return ret.fval;
848 }
849
850 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location1, gint32 value, gint32 comparand)
851 {
852         MONO_ARCH_SAVE_REGS;
853
854         return InterlockedCompareExchange(location1, value, comparand);
855 }
856
857 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location1, MonoObject *value, MonoObject *comparand)
858 {
859         MONO_ARCH_SAVE_REGS;
860
861         return (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location1, value, comparand);
862 }
863
864 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location1, gfloat value, gfloat comparand)
865 {
866         IntFloatUnion val, ret, cmp;
867
868         MONO_ARCH_SAVE_REGS;
869
870         val.fval = value;
871         cmp.fval = comparand;
872         ret.ival = InterlockedCompareExchange((gint32 *) location1, val.ival, cmp.ival);
873
874         return ret.fval;
875 }
876
877 int  
878 mono_thread_get_abort_signal (void)
879 {
880 #ifdef __MINGW32__
881         return -1;
882 #else
883 #ifndef SIGRTMIN
884         return SIGUSR1;
885 #else
886         return SIGRTMIN;
887 #endif
888 #endif /* __MINGW32__ */
889 }
890
891 void
892 ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state)
893 {
894         MONO_ARCH_SAVE_REGS;
895
896         thread->abort_state = state;
897         thread->abort_exc = NULL;
898
899 #ifdef THREAD_DEBUG
900         g_message (G_GNUC_PRETTY_FUNCTION
901                    ": (%d) Abort requested for %p (%d)", GetCurrentThreadId (),
902                    thread, thread->tid);
903 #endif
904         
905 #ifdef __MINGW32__
906         g_assert_not_reached ();
907 #else
908         /* fixme: store the state somewhere */
909 #ifdef PTHREAD_POINTER_ID
910         pthread_kill (GUINT_TO_POINTER(thread->tid), mono_thread_get_abort_signal ());
911 #else
912         pthread_kill (thread->tid, mono_thread_get_abort_signal ());
913 #endif
914 #endif /* __MINGW32__ */
915 }
916
917 void
918 ves_icall_System_Threading_Thread_ResetAbort (void)
919 {
920         MonoThread *thread = mono_thread_current ();
921         
922         MONO_ARCH_SAVE_REGS;
923
924         if (!thread->abort_exc) {
925                 const char *msg = "Unable to reset abort because no abort was requested";
926                 mono_raise_exception (mono_get_exception_thread_state (msg));
927         } else {
928                 thread->abort_exc = NULL;
929                 thread->abort_state = NULL;
930         }
931 }
932
933 void mono_thread_init (MonoThreadStartCB start_cb,
934                        MonoThreadAttachCB attach_cb)
935 {
936         InitializeCriticalSection(&threads_mutex);
937         InitializeCriticalSection(&interlocked_mutex);
938         
939         current_object_key=TlsAlloc();
940 #ifdef THREAD_DEBUG
941         g_message (G_GNUC_PRETTY_FUNCTION ": Allocated current_object_key %d",
942                    current_object_key);
943 #endif
944
945         mono_thread_start_cb = start_cb;
946         mono_thread_attach_cb = attach_cb;
947
948         slothash_key=TlsAlloc();
949
950         /* Get a pseudo handle to the current process.  This is just a
951          * kludge so that wapi can build a process handle if needed.
952          * As a pseudo handle is returned, we don't need to clean
953          * anything up.
954          */
955         GetCurrentProcess ();
956 }
957
958 void
959 mono_threads_install_cleanup (MonoThreadCleanupFunc func)
960 {
961         mono_thread_cleanup = func;
962 }
963
964 void mono_install_thread_callbacks (MonoThreadCallbacks *callbacks)
965 {
966         mono_thread_callbacks = callbacks;
967 }
968
969 #ifdef THREAD_DEBUG
970 static void print_tids (gpointer key, gpointer value, gpointer user)
971 {
972         g_message ("Waiting for: %d", GPOINTER_TO_UINT(key));
973 }
974 #endif
975
976 struct wait_data 
977 {
978         HANDLE handles[MAXIMUM_WAIT_OBJECTS];
979         MonoThread *threads[MAXIMUM_WAIT_OBJECTS];
980         guint32 num;
981 };
982
983 static void wait_for_tids (struct wait_data *wait, guint32 timeout)
984 {
985         guint32 i, ret;
986         
987 #ifdef THREAD_DEBUG
988         g_message(G_GNUC_PRETTY_FUNCTION
989                   ": %d threads to wait for in this batch", wait->num);
990 #endif
991
992         ret=WaitForMultipleObjects(wait->num, wait->handles, TRUE, timeout);
993         if(ret==WAIT_FAILED) {
994                 /* See the comment in build_wait_tids() */
995 #ifdef THREAD_DEBUG
996                 g_message (G_GNUC_PRETTY_FUNCTION ": Wait failed");
997 #endif
998                 return;
999         }
1000         
1001
1002         for(i=0; i<wait->num; i++) {
1003                 guint32 tid=wait->threads[i]->tid;
1004                 
1005                 if(mono_g_hash_table_lookup (threads, GUINT_TO_POINTER(tid))!=NULL) {
1006                         /* This thread must have been killed, because
1007                          * it hasn't cleaned itself up. (It's just
1008                          * possible that the thread exited before the
1009                          * parent thread had a chance to store the
1010                          * handle, and now there is another pointer to
1011                          * the already-exited thread stored.  In this
1012                          * case, we'll just get two
1013                          * mono_profiler_thread_end() calls for the
1014                          * same thread.)
1015                          */
1016         
1017 #ifdef THREAD_DEBUG
1018                         g_message (G_GNUC_PRETTY_FUNCTION
1019                                    ": cleaning up after thread %d", tid);
1020 #endif
1021                         thread_cleanup (wait->threads[i]);
1022                 }
1023         }
1024 }
1025
1026 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
1027 {
1028         struct wait_data *wait=(struct wait_data *)user;
1029
1030         if(wait->num<MAXIMUM_WAIT_OBJECTS) {
1031                 MonoThread *thread=(MonoThread *)value;
1032
1033                 /* BUG: For now we just ignore background threads, we should abort them
1034                 */
1035                 if (thread->state & ThreadState_Background)
1036                         return; /* just leave, ignore */
1037                 
1038                 wait->handles[wait->num]=thread->handle;
1039                 wait->threads[wait->num]=thread;
1040                 wait->num++;
1041         } else {
1042                 /* Just ignore the rest, we can't do anything with
1043                  * them yet
1044                  */
1045         }
1046 }
1047
1048 void mono_thread_manage (void)
1049 {
1050         struct wait_data *wait=g_new0 (struct wait_data, 1);
1051         
1052         /* join each thread that's still running */
1053 #ifdef THREAD_DEBUG
1054         g_message(G_GNUC_PRETTY_FUNCTION ": Joining each running thread...");
1055 #endif
1056         
1057         if(threads==NULL) {
1058 #ifdef THREAD_DEBUG
1059                 g_message(G_GNUC_PRETTY_FUNCTION ": No threads");
1060 #endif
1061                 return;
1062         }
1063         
1064         do {
1065                 EnterCriticalSection (&threads_mutex);
1066 #ifdef THREAD_DEBUG
1067                 g_message(G_GNUC_PRETTY_FUNCTION
1068                           ":There are %d threads to join",
1069                           mono_g_hash_table_size (threads));
1070                 mono_g_hash_table_foreach (threads, print_tids, NULL);
1071 #endif
1072
1073                 wait->num=0;
1074                 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
1075         
1076                 LeaveCriticalSection (&threads_mutex);
1077                 if(wait->num>0) {
1078                         /* Something to wait for */
1079                         wait_for_tids (wait, INFINITE);
1080                 }
1081         } while(wait->num>0);
1082         
1083         g_free (wait);
1084         
1085         mono_g_hash_table_destroy(threads);
1086         threads=NULL;
1087 }
1088
1089 static void terminate_thread (gpointer key, gpointer value, gpointer user)
1090 {
1091         MonoThread *thread=(MonoThread *)value;
1092         guint32 self=GPOINTER_TO_UINT (user);
1093         
1094         if(thread->tid!=self) {
1095                 /*TerminateThread (thread->handle, -1);*/
1096         }
1097 }
1098
1099 void mono_thread_abort_all_other_threads (void)
1100 {
1101         guint32 self=GetCurrentThreadId ();
1102
1103         EnterCriticalSection (&threads_mutex);
1104 #ifdef THREAD_DEBUG
1105         g_message(G_GNUC_PRETTY_FUNCTION ":There are %d threads to abort",
1106                   mono_g_hash_table_size (threads));
1107         mono_g_hash_table_foreach (threads, print_tids, NULL);
1108 #endif
1109
1110         mono_g_hash_table_foreach (threads, terminate_thread,
1111                                    GUINT_TO_POINTER (self));
1112         
1113         LeaveCriticalSection (&threads_mutex);
1114 }
1115
1116 /*
1117  * mono_thread_push_appdomain_ref:
1118  *
1119  *   Register that the current thread may have references to objects in domain 
1120  * @domain on its stack. Each call to this function should be paired with a 
1121  * call to pop_appdomain_ref.
1122  */
1123 void 
1124 mono_thread_push_appdomain_ref (MonoDomain *domain)
1125 {
1126         MonoThread *thread = mono_thread_current ();
1127
1128         if (thread) {
1129                 //printf ("PUSH REF: %p -> %s.\n", thread, domain->friendly_name);
1130                 EnterCriticalSection (&threads_mutex);
1131                 thread->appdomain_refs = g_slist_prepend (thread->appdomain_refs, domain);
1132                 LeaveCriticalSection (&threads_mutex);
1133         }
1134 }
1135
1136 void
1137 mono_thread_pop_appdomain_ref (void)
1138 {
1139         MonoThread *thread = mono_thread_current ();
1140
1141         if (thread) {
1142                 //printf ("POP REF: %p -> %s.\n", thread, ((MonoDomain*)(thread->appdomain_refs->data))->friendly_name);
1143                 EnterCriticalSection (&threads_mutex);
1144                 thread->appdomain_refs = g_slist_remove (thread->appdomain_refs, thread->appdomain_refs->data);
1145                 LeaveCriticalSection (&threads_mutex);
1146         }
1147 }
1148
1149 static gboolean
1150 mono_thread_has_appdomain_ref (MonoThread *thread, MonoDomain *domain)
1151 {
1152         gboolean res;
1153         EnterCriticalSection (&threads_mutex);
1154         res = g_slist_find (thread->appdomain_refs, domain) != NULL;
1155         LeaveCriticalSection (&threads_mutex);
1156         return res;
1157 }
1158
1159 typedef struct abort_appdomain_data {
1160         struct wait_data wait;
1161         MonoDomain *domain;
1162 } abort_appdomain_data;
1163
1164 static void
1165 abort_appdomain_thread (gpointer key, gpointer value, gpointer user_data)
1166 {
1167         MonoThread *thread = (MonoThread*)value;
1168         abort_appdomain_data *data = (abort_appdomain_data*)user_data;
1169         MonoDomain *domain = data->domain;
1170
1171         if (mono_thread_has_appdomain_ref (thread, domain)) {
1172                 //printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread, domain->friendly_name);
1173                 ves_icall_System_Threading_Thread_Abort (thread, (MonoObject*)domain->domain);
1174
1175                 if(data->wait.num<MAXIMUM_WAIT_OBJECTS) {
1176                         data->wait.handles [data->wait.num] = thread->handle;
1177                         data->wait.threads [data->wait.num] = thread;
1178                         data->wait.num++;
1179                 } else {
1180                         /* Just ignore the rest, we can't do anything with
1181                          * them yet
1182                          */
1183                 }
1184         }
1185 }
1186
1187 /*
1188  * mono_threads_abort_appdomain_threads:
1189  *
1190  *   Abort threads which has references to the given appdomain.
1191  */
1192 gboolean
1193 mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
1194 {
1195         abort_appdomain_data user_data;
1196         guint32 start_time;
1197
1198         //printf ("ABORT BEGIN.\n");
1199
1200         start_time = GetTickCount ();
1201         do {
1202                 EnterCriticalSection (&threads_mutex);
1203
1204                 user_data.domain = domain;
1205                 user_data.wait.num = 0;
1206                 mono_g_hash_table_foreach (threads, abort_appdomain_thread, &user_data);
1207                 LeaveCriticalSection (&threads_mutex);
1208
1209                 if (user_data.wait.num > 0)
1210                         wait_for_tids (&user_data.wait, timeout);
1211
1212                 /* Update remaining time */
1213                 timeout -= GetTickCount () - start_time;
1214                 start_time = GetTickCount ();
1215
1216                 if (timeout < 0)
1217                         return FALSE;
1218         }
1219         while (user_data.wait.num > 0);
1220
1221         //printf ("ABORT DONE.\n");
1222
1223         return TRUE;
1224 }
1225
1226 /*
1227  * mono_thread_get_pending_exception:
1228  *
1229  *   Return an exception which needs to be raised when leaving a catch clause.
1230  * This is used for undeniable exception propagation.
1231  */
1232 MonoException*
1233 mono_thread_get_pending_exception (void)
1234 {
1235         MonoThread *thread = mono_thread_current ();
1236
1237         MONO_ARCH_SAVE_REGS;
1238
1239         if (thread && thread->abort_exc) {
1240                 /*
1241                  * FIXME: Clear the abort exception and return an AppDomainUnloaded 
1242                  * exception if the thread no longer references a dying appdomain.
1243                  */
1244                 thread->abort_exc->trace_ips = NULL;
1245                 thread->abort_exc->stack_trace = NULL;
1246                 return thread->abort_exc;
1247         }
1248
1249         return NULL;
1250 }
1251
1252 static int static_data_idx = 0;
1253 static int static_data_offset = 0;
1254 #define NUM_STATIC_DATA_IDX 8
1255 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
1256         1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
1257 };
1258
1259 static void 
1260 thread_alloc_static_data (MonoThread *thread, guint32 offset)
1261 {
1262         guint idx = (offset >> 24) - 1;
1263         int i;
1264
1265         if (!thread->static_data) {
1266 #if HAVE_BOEHM_GC
1267                 thread->static_data = GC_MALLOC (static_data_size [0]);
1268 #else
1269                 thread->static_data = g_malloc0 (static_data_size [0]);
1270 #endif
1271                 thread->static_data [0] = thread->static_data;
1272         }
1273         for (i = 1; i < idx; ++i) {
1274                 if (thread->static_data [i])
1275                         continue;
1276 #if HAVE_BOEHM_GC
1277                 thread->static_data [i] = GC_MALLOC (static_data_size [i]);
1278 #else
1279                 thread->static_data [i] = g_malloc0 (static_data_size [i]);
1280 #endif
1281         }
1282         
1283 }
1284
1285 /* 
1286  * ensure thread static fields already allocated are valid for thread
1287  * This function is called when a thread is created or on thread attach.
1288  */
1289 static void
1290 thread_adjust_static_data (MonoThread *thread)
1291 {
1292         guint32 offset;
1293
1294         EnterCriticalSection (&threads_mutex);
1295         if (static_data_offset || static_data_idx > 0) {
1296                 /* get the current allocated size */
1297                 offset = static_data_offset | ((static_data_idx + 1) << 24);
1298                 thread_alloc_static_data (thread, offset);
1299         }
1300         LeaveCriticalSection (&threads_mutex);
1301 }
1302
1303 static void 
1304 alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
1305 {
1306         MonoThread *thread = value;
1307         guint32 offset = GPOINTER_TO_UINT (user);
1308         
1309         thread_alloc_static_data (thread, offset);
1310 }
1311
1312 /*
1313  * The offset for a thread static variable is composed of two parts:
1314  * an index in the array of chunks of memory for the thread (thread->static_data)
1315  * and an offset in that chunk of mem. This allows allocating less memory in the 
1316  * common case.
1317  */
1318 guint32
1319 mono_threads_alloc_static_data (guint32 size, guint32 align)
1320 {
1321         guint32 offset;
1322         
1323         EnterCriticalSection (&threads_mutex);
1324
1325         if (!static_data_idx && !static_data_offset) {
1326                 /* 
1327                  * we use the first chunk of the first allocation also as
1328                  * an array for the rest of the data 
1329                  */
1330                 static_data_offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX;
1331         }
1332         static_data_offset += align - 1;
1333         static_data_offset &= ~(align - 1);
1334         if (static_data_offset + size >= static_data_size [static_data_idx]) {
1335                 static_data_idx ++;
1336                 g_assert (size <= static_data_size [static_data_idx]);
1337                 /* 
1338                  * massive unloading and reloading of domains with thread-static
1339                  * data may eventually exceed the allocated storage...
1340                  * Need to check what the MS runtime does in that case.
1341                  * Note that for each appdomain, we need to allocate a separate
1342                  * thread data slot for security reasons. We could keep track
1343                  * of the slots per-domain and when the domain is unloaded
1344                  * out the slots on a sort of free list.
1345                  */
1346                 g_assert (static_data_idx < NUM_STATIC_DATA_IDX);
1347                 static_data_offset = 0;
1348         }
1349         offset = static_data_offset | ((static_data_idx + 1) << 24);
1350         static_data_offset += size;
1351         
1352         mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
1353
1354         LeaveCriticalSection (&threads_mutex);
1355         return offset;
1356 }
1357
1358 gpointer
1359 mono_threads_get_static_data (guint32 offset)
1360 {
1361         MonoThread *thread = mono_thread_current ();
1362         int idx = offset >> 24;
1363         
1364         return ((char*) thread->static_data [idx - 1]) + (offset & 0xffffff);
1365 }
1366
1367 static void gc_stop_world (gpointer key, gpointer value, gpointer user)
1368 {
1369         MonoThread *thread=(MonoThread *)value;
1370         guint32 self=GPOINTER_TO_UINT (user);
1371
1372 #ifdef LIBGC_DEBUG
1373         g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d", self, thread->tid);
1374 #endif
1375         
1376         if(thread->tid==self)
1377                 return;
1378
1379         SuspendThread (thread->handle);
1380 }
1381
1382 void mono_gc_stop_world (void)
1383 {
1384         guint32 self=GetCurrentThreadId ();
1385
1386 #ifdef LIBGC_DEBUG
1387         g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
1388 #endif
1389
1390         EnterCriticalSection (&threads_mutex);
1391
1392         if (threads != NULL)
1393                 mono_g_hash_table_foreach (threads, gc_stop_world, GUINT_TO_POINTER (self));
1394         
1395         LeaveCriticalSection (&threads_mutex);
1396 }
1397
1398 static void gc_start_world (gpointer key, gpointer value, gpointer user)
1399 {
1400         MonoThread *thread=(MonoThread *)value;
1401         guint32 self=GPOINTER_TO_UINT (user);
1402         
1403 #ifdef LIBGC_DEBUG
1404         g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d", self, thread->tid);
1405 #endif
1406         
1407         if(thread->tid==self)
1408                 return;
1409
1410         ResumeThread (thread->handle);
1411 }
1412
1413 void mono_gc_start_world (void)
1414 {
1415         guint32 self=GetCurrentThreadId ();
1416
1417 #ifdef LIBGC_DEBUG
1418         g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
1419 #endif
1420
1421         EnterCriticalSection (&threads_mutex);
1422
1423         if (threads != NULL)
1424                 mono_g_hash_table_foreach (threads, gc_start_world, GUINT_TO_POINTER (self));
1425         
1426         LeaveCriticalSection (&threads_mutex);
1427 }
1428
1429 #ifdef WITH_INCLUDED_LIBGC
1430
1431 static void gc_push_all_stacks (gpointer key, gpointer value, gpointer user)
1432 {
1433         MonoThread *thread=(MonoThread *)value;
1434         guint32 *selfp=(guint32 *)user, self = *selfp;
1435
1436 #ifdef LIBGC_DEBUG
1437         g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d - %p", self, thread->tid, thread->stack_ptr);
1438 #endif
1439         
1440         if(thread->tid==self) {
1441 #ifdef LIBGC_DEBUG
1442                 g_message (G_GNUC_PRETTY_FUNCTION ": %p - %p", selfp, thread->stack_ptr);
1443 #endif
1444                 GC_push_all_stack (selfp, thread->stack_ptr);
1445                 return;
1446         }
1447
1448 #ifdef PLATFORM_WIN32
1449         GC_win32_push_thread_stack (thread->handle, thread->stack_ptr);
1450 #else
1451         mono_wapi_push_thread_stack (thread->handle, thread->stack_ptr);
1452 #endif
1453 }
1454
1455 void mono_gc_push_all_stacks (void)
1456 {
1457         guint32 self=GetCurrentThreadId ();
1458
1459 #ifdef LIBGC_DEBUG
1460         g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
1461 #endif
1462
1463         EnterCriticalSection (&threads_mutex);
1464
1465         if (threads != NULL)
1466                 mono_g_hash_table_foreach (threads, gc_push_all_stacks, &self);
1467         
1468         LeaveCriticalSection (&threads_mutex);
1469 }
1470
1471 #endif /* WITH_INCLUDED_LIBGC */