svn path=/branches/mono-1-1-9/mcs/; revision=50439
[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 #ifdef PLATFORM_WIN32
14 #define _WIN32_WINNT 0x0500
15 #endif
16
17 #include <glib.h>
18 #include <signal.h>
19 #include <string.h>
20
21 #include <mono/metadata/object.h>
22 #include <mono/metadata/domain-internals.h>
23 #include <mono/metadata/profiler-private.h>
24 #include <mono/metadata/threads.h>
25 #include <mono/metadata/threadpool.h>
26 #include <mono/metadata/threads-types.h>
27 #include <mono/metadata/exception.h>
28 #include <mono/metadata/environment.h>
29 #include <mono/metadata/monitor.h>
30 #include <mono/metadata/gc-internal.h>
31 #include <mono/metadata/marshal.h>
32 #include <mono/io-layer/io-layer.h>
33 #include <mono/metadata/object-internals.h>
34 #include <mono/utils/mono-compiler.h>
35
36 #include <mono/os/gc_wrapper.h>
37
38 /*#define THREAD_DEBUG(a) do { a; } while (0)*/
39 #define THREAD_DEBUG(a)
40 /*#define THREAD_WAIT_DEBUG(a) do { a; } while (0)*/
41 #define THREAD_WAIT_DEBUG(a)
42 /*#define LIBGC_DEBUG(a) do { a; } while (0)*/
43 #define LIBGC_DEBUG(a)
44
45 struct StartInfo 
46 {
47         guint32 (*func)(void *);
48         MonoThread *obj;
49         MonoObject *delegate;
50         void *start_arg;
51         MonoDomain *domain;
52 };
53
54 typedef union {
55         gint32 ival;
56         gfloat fval;
57 } IntFloatUnion;
58
59 typedef union {
60         gint64 ival;
61         gdouble fval;
62 } LongDoubleUnion;
63  
64 typedef struct {
65         int idx;
66         int offset;
67 } StaticDataInfo;
68
69 /* Number of cached culture objects in the MonoThread->culture_info array */
70 #define NUM_CACHED_CULTURES 4
71
72 /*
73  * The "os_handle" field of the WaitHandle class.
74  */
75 static MonoClassField *wait_handle_os_handle_field = NULL;
76
77 /* Controls access to the 'threads' hash table */
78 static CRITICAL_SECTION threads_mutex;
79
80 /* Controls access to context static data */
81 static CRITICAL_SECTION contexts_mutex;
82
83 /* Holds current status of static data heap */
84 static StaticDataInfo thread_static_info;
85 static StaticDataInfo context_static_info;
86
87 /* The hash of existing threads (key is thread ID) that need joining
88  * before exit
89  */
90 static MonoGHashTable *threads=NULL;
91
92 /* The TLS key that holds the MonoObject assigned to each thread */
93 static guint32 current_object_key = -1;
94
95 #ifdef HAVE_KW_THREAD
96 /* we need to use both the Tls* functions and __thread because
97  * the gc needs to see all the threads 
98  */
99 static __thread MonoThread * tls_current_object MONO_TLS_FAST;
100 #define SET_CURRENT_OBJECT(x) do { \
101         tls_current_object = x; \
102         TlsSetValue (current_object_key, x); \
103 } while (FALSE)
104 #define GET_CURRENT_OBJECT() tls_current_object
105 #else
106 #define SET_CURRENT_OBJECT(x) TlsSetValue (current_object_key, x);
107 #define GET_CURRENT_OBJECT() (MonoThread*) TlsGetValue (current_object_key);
108 #endif
109
110 /* function called at thread start */
111 static MonoThreadStartCB mono_thread_start_cb = NULL;
112
113 /* function called at thread attach */
114 static MonoThreadAttachCB mono_thread_attach_cb = NULL;
115
116 /* function called at thread cleanup */
117 static MonoThreadCleanupFunc mono_thread_cleanup = NULL;
118
119 /* function called when a new thread has been created */
120 static MonoThreadCallbacks *mono_thread_callbacks = NULL;
121
122 /* The default stack size for each thread */
123 static guint32 default_stacksize = 0;
124 #define default_stacksize_for_thread(thread) ((thread)->stack_size? (thread)->stack_size: default_stacksize)
125
126 static void thread_adjust_static_data (MonoThread *thread);
127 static void mono_init_static_data_info (StaticDataInfo *static_data);
128 static guint32 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align);
129 static gboolean mono_thread_resume (MonoThread* thread);
130 static void mono_thread_start (MonoThread *thread);
131
132 /* Spin lock for InterlockedXXX 64 bit functions */
133 static CRITICAL_SECTION interlocked_mutex;
134
135 /* global count of thread interruptions requested */
136 static gint32 thread_interruption_requested = 0;
137
138 /* Event signaled when a thread changes its background mode */
139 static HANDLE background_change_event;
140
141 guint32
142 mono_thread_get_tls_key (void)
143 {
144         return current_object_key;
145 }
146
147 gint32
148 mono_thread_get_tls_offset (void)
149 {
150         int offset;
151         MONO_THREAD_VAR_OFFSET (tls_current_object,offset);
152         return offset;
153 }
154
155 /* handle_store() and handle_remove() manage the array of threads that
156  * still need to be waited for when the main thread exits.
157  */
158 static void handle_store(MonoThread *thread)
159 {
160         EnterCriticalSection(&threads_mutex);
161
162         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": thread %p ID %"G_GSIZE_FORMAT, thread, (gsize)thread->tid));
163
164         if(threads==NULL) {
165                 MONO_GC_REGISTER_ROOT (threads);
166                 threads=mono_g_hash_table_new(NULL, NULL);
167         }
168
169         /* We don't need to duplicate thread->handle, because it is
170          * only closed when the thread object is finalized by the GC.
171          */
172         mono_g_hash_table_insert(threads, (gpointer)(gsize)(thread->tid),
173                                  thread);
174         LeaveCriticalSection(&threads_mutex);
175 }
176
177 static void handle_remove(gsize tid)
178 {
179         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": thread ID %"G_GSIZE_FORMAT, tid));
180
181         EnterCriticalSection(&threads_mutex);
182
183         if (threads)
184                 mono_g_hash_table_remove (threads, (gpointer)tid);
185         
186         LeaveCriticalSection(&threads_mutex);
187
188         /* Don't close the handle here, wait for the object finalizer
189          * to do it. Otherwise, the following race condition applies:
190          *
191          * 1) Thread exits (and handle_remove() closes the handle)
192          *
193          * 2) Some other handle is reassigned the same slot
194          *
195          * 3) Another thread tries to join the first thread, and
196          * blocks waiting for the reassigned handle to be signalled
197          * (which might never happen).  This is possible, because the
198          * thread calling Join() still has a reference to the first
199          * thread's object.
200          */
201 }
202
203 static void thread_cleanup (MonoThread *thread)
204 {
205         g_assert (thread != NULL);
206         
207         mono_release_type_locks (thread);
208
209         if (!mono_monitor_enter (thread->synch_lock))
210                 return;
211
212         thread->state |= ThreadState_Stopped;
213         mono_monitor_exit (thread->synch_lock);
214
215         mono_profiler_thread_end (thread->tid);
216         handle_remove (thread->tid);
217
218         mono_thread_pop_appdomain_ref ();
219
220         if (thread->serialized_culture_info)
221                 g_free (thread->serialized_culture_info);
222
223         mono_gc_free_fixed (thread->culture_info);
224         mono_gc_free_fixed (thread->ui_culture_info);
225
226         if (mono_thread_cleanup)
227                 mono_thread_cleanup (thread);
228 }
229
230 static guint32 WINAPI start_wrapper(void *data)
231 {
232         struct StartInfo *start_info=(struct StartInfo *)data;
233         guint32 (*start_func)(void *);
234         void *start_arg;
235         guint32 tid;
236         MonoThread *thread=start_info->obj;
237         MonoObject *start_delegate = start_info->delegate;
238
239         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Start wrapper", GetCurrentThreadId ()));
240         
241         /* We can be sure start_info->obj->tid and
242          * start_info->obj->handle have been set, because the thread
243          * was created suspended, and these values were set before the
244          * thread resumed
245          */
246
247         tid=thread->tid;
248
249         SET_CURRENT_OBJECT (thread);
250         
251         if (!mono_domain_set (start_info->domain, FALSE)) {
252                 /* No point in raising an appdomain_unloaded exception here */
253                 /* FIXME: Cleanup here */
254                 return 0;
255         }
256
257         start_func = start_info->func;
258         start_arg = start_info->start_arg;
259
260         /* This MUST be called before any managed code can be
261          * executed, as it calls the callback function that (for the
262          * jit) sets the lmf marker.
263          */
264         mono_thread_new_init (tid, &tid, start_func);
265         thread->stack_ptr = &tid;
266
267         LIBGC_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
268                    ": (%"G_GSIZE_FORMAT",%d) Setting thread stack to %p",
269                    GetCurrentThreadId (), getpid (), thread->stack_ptr));
270
271         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
272                    ": (%"G_GSIZE_FORMAT") Setting current_object_key to %p",
273                    GetCurrentThreadId (), thread));
274
275         mono_profiler_thread_start (tid);
276
277         if(thread->start_notify!=NULL) {
278                 /* Let the thread that called Start() know we're
279                  * ready
280                  */
281                 ReleaseSemaphore (thread->start_notify, 1, NULL);
282         }
283         
284         g_free (start_info);
285
286         /* Every thread references the appdomain which created it */
287         mono_thread_push_appdomain_ref (mono_domain_get ());
288
289         thread_adjust_static_data (thread);
290 #ifdef DEBUG
291         g_message (G_GNUC_PRETTY_FUNCTION "start_wrapper for %"G_GSIZE_FORMAT,
292                    thread->tid);
293 #endif
294
295         /* start_func is set only for unamanged start functions */
296         if (start_func) {
297                 start_func (start_arg);
298         } else {
299                 void *args [1];
300                 g_assert (start_delegate != NULL);
301                 args [0] = start_arg;
302                 /* we may want to handle the exception here. See comment below on unhandled exceptions */
303                 mono_runtime_delegate_invoke (start_delegate, args, NULL);
304         }
305
306         /* If the thread calls ExitThread at all, this remaining code
307          * will not be executed, but the main thread will eventually
308          * call thread_cleanup() on this thread's behalf.
309          */
310
311         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Start wrapper terminating", GetCurrentThreadId ()));
312
313         /* Remove the reference to the thread object in the TLS data,
314          * so the thread object can be finalized.  This won't be
315          * reached if the thread threw an uncaught exception, so those
316          * thread handles will stay referenced :-( (This is due to
317          * missing support for scanning thread-specific data in the
318          * Boehm GC - the io-layer keeps a GC-visible hash of pointers
319          * to TLS data.)
320          */
321         SET_CURRENT_OBJECT (NULL);
322
323         thread_cleanup (thread);
324
325         return(0);
326 }
327
328 void mono_thread_new_init (gsize tid, gpointer stack_start, gpointer func)
329 {
330         if (mono_thread_start_cb) {
331                 mono_thread_start_cb (tid, stack_start, func);
332         }
333
334         if (mono_thread_callbacks)
335                 (* mono_thread_callbacks->thread_created) (tid, stack_start, func);
336 }
337
338 void mono_threads_set_default_stacksize (guint32 stacksize)
339 {
340         default_stacksize = stacksize;
341 }
342
343 guint32 mono_threads_get_default_stacksize (void)
344 {
345         return default_stacksize;
346 }
347
348 void mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
349 {
350         MonoThread *thread;
351         HANDLE thread_handle;
352         struct StartInfo *start_info;
353         gsize tid;
354         
355         thread=(MonoThread *)mono_object_new (domain,
356                                               mono_defaults.thread_class);
357
358         start_info=g_new0 (struct StartInfo, 1);
359         start_info->func = func;
360         start_info->obj = thread;
361         start_info->domain = domain;
362         start_info->start_arg = arg;
363         
364         /* Create suspended, so we can do some housekeeping before the thread
365          * starts
366          */
367         thread_handle = CreateThread(NULL, default_stacksize_for_thread (thread), (LPTHREAD_START_ROUTINE)start_wrapper, start_info,
368                                      CREATE_SUSPENDED, &tid);
369         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Started thread ID %"G_GSIZE_FORMAT" (handle %p)",
370                   tid, thread_handle));
371         if (thread_handle == NULL) {
372                 /* The thread couldn't be created, so throw an exception */
373                 mono_raise_exception (mono_get_exception_execution_engine ("Couldn't create thread"));
374                 return;
375         }
376
377         thread->handle=thread_handle;
378         thread->tid=tid;
379
380         thread->synch_lock=mono_object_new (domain, mono_defaults.object_class);
381                                                   
382         handle_store(thread);
383
384         ResumeThread (thread_handle);
385 }
386
387 MonoThread *
388 mono_thread_attach (MonoDomain *domain)
389 {
390         MonoThread *thread;
391         HANDLE thread_handle;
392         gsize tid;
393
394         if ((thread = mono_thread_current ())) {
395                 /* Already attached */
396                 return thread;
397         }
398
399         if (!mono_gc_register_thread (&domain)) {
400                 g_error ("Thread %p calling into managed code is not registered with the GC. On UNIX, this can be fixed by #include-ing <gc.h> before <pthread.h> in the file containing the thread creation code.", GetCurrentThread ());
401         }
402
403         thread = (MonoThread *)mono_object_new (domain,
404                                                 mono_defaults.thread_class);
405
406         thread_handle = GetCurrentThread ();
407         g_assert (thread_handle);
408
409         tid=GetCurrentThreadId ();
410
411 #ifdef PLATFORM_WIN32
412         /* 
413          * The handle returned by GetCurrentThread () is a pseudo handle, so it can't be used to
414          * refer to the thread from other threads for things like aborting.
415          */
416         DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle, 
417                                          THREAD_ALL_ACCESS, TRUE, 0);
418 #endif
419
420         thread->handle=thread_handle;
421         thread->tid=tid;
422         thread->synch_lock=mono_object_new (domain, mono_defaults.object_class);
423
424         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Attached thread ID %"G_GSIZE_FORMAT" (handle %p)",
425                   tid, thread_handle));
426
427         handle_store(thread);
428
429         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Setting current_object_key to %p",
430                    GetCurrentThreadId (), thread));
431
432         SET_CURRENT_OBJECT (thread);
433         mono_domain_set (domain, TRUE);
434
435         thread_adjust_static_data (thread);
436
437         if (mono_thread_attach_cb) {
438                 mono_thread_attach_cb (tid, &tid);
439         }
440
441         return(thread);
442 }
443
444 void
445 mono_thread_detach (MonoThread *thread)
446 {
447         g_return_if_fail (thread != NULL);
448
449         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION "mono_thread_detach for %"G_GSIZE_FORMAT, thread->tid));
450         SET_CURRENT_OBJECT (NULL);
451         
452         thread_cleanup (thread);
453 }
454
455 void
456 mono_thread_exit ()
457 {
458         MonoThread *thread = mono_thread_current ();
459
460         SET_CURRENT_OBJECT (NULL);
461         thread_cleanup (thread);
462
463         /* we could add a callback here for embedders to use. */
464         if (thread == mono_thread_get_main ())
465                 exit (mono_environment_exitcode_get ());
466         ExitThread (-1);
467 }
468
469 HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this,
470                                                          MonoObject *start)
471 {
472         guint32 (*start_func)(void *);
473         struct StartInfo *start_info;
474         MonoMethod *im;
475         HANDLE thread;
476         gsize tid;
477         
478         MONO_ARCH_SAVE_REGS;
479
480         THREAD_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION
481                   ": Trying to start a new thread: this (%p) start (%p)", this, start));
482
483         mono_monitor_enter (this->synch_lock);
484
485         if ((this->state & ThreadState_Unstarted) == 0) {
486                 mono_monitor_exit (this->synch_lock);
487                 mono_raise_exception (mono_get_exception_thread_state ("Thread has already been started."));
488                 return NULL;
489         }
490
491         if ((this->state & ThreadState_Aborted) != 0) {
492                 mono_monitor_exit (this->synch_lock);
493                 return this;
494         }
495         start_func = NULL;
496         {
497                 /* This is freed in start_wrapper */
498                 start_info = g_new0 (struct StartInfo, 1);
499                 start_info->func = start_func;
500                 start_info->start_arg = this->start_obj;
501                 start_info->delegate = start;
502                 start_info->obj = this;
503                 start_info->domain = mono_domain_get ();
504
505                 this->start_notify=CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
506                 if(this->start_notify==NULL) {
507                         mono_monitor_exit (this->synch_lock);
508                         g_warning (G_GNUC_PRETTY_FUNCTION ": CreateSemaphore error 0x%x", GetLastError ());
509                         return(NULL);
510                 }
511
512                 thread=CreateThread(NULL, default_stacksize_for_thread (this), (LPTHREAD_START_ROUTINE)start_wrapper, start_info,
513                                     CREATE_SUSPENDED, &tid);
514                 if(thread==NULL) {
515                         mono_monitor_exit (this->synch_lock);
516                         g_warning(G_GNUC_PRETTY_FUNCTION
517                                   ": CreateThread error 0x%x", GetLastError());
518                         return(NULL);
519                 }
520                 
521                 this->handle=thread;
522                 this->tid=tid;
523
524                 /* Don't call handle_store() here, delay it to Start.
525                  * We can't join a thread (trying to will just block
526                  * forever) until it actually starts running, so don't
527                  * store the handle till then.
528                  */
529
530                 mono_thread_start (this);
531                 
532                 this->state &= ~ThreadState_Unstarted;
533
534                 THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Started thread ID %"G_GSIZE_FORMAT" (handle %p)", tid, thread));
535
536                 mono_monitor_exit (this->synch_lock);
537                 return(thread);
538         }
539 }
540
541 void ves_icall_System_Threading_Thread_Thread_free_internal (MonoThread *this,
542                                                              HANDLE thread)
543 {
544         MONO_ARCH_SAVE_REGS;
545
546         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Closing thread %p, handle %p", this, thread));
547
548         CloseHandle (thread);
549 }
550
551 static void mono_thread_start (MonoThread *thread)
552 {
553         MONO_ARCH_SAVE_REGS;
554
555         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Launching thread %p (%"G_GSIZE_FORMAT")", GetCurrentThreadId (), thread, (gsize)thread->tid));
556
557         /* Only store the handle when the thread is about to be
558          * launched, to avoid the main thread deadlocking while trying
559          * to clean up a thread that will never be signalled.
560          */
561         handle_store (thread);
562
563         if (mono_thread_callbacks)
564                 (* mono_thread_callbacks->start_resume) (thread->tid);
565
566         ResumeThread (thread->handle);
567
568         if (mono_thread_callbacks)
569                 (* mono_thread_callbacks->end_resume) (thread->tid);
570
571         if(thread->start_notify!=NULL) {
572                 /* Wait for the thread to set up its TLS data etc, so
573                  * theres no potential race condition if someone tries
574                  * to look up the data believing the thread has
575                  * started
576                  */
577
578                 THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") waiting for thread %p (%"G_GSIZE_FORMAT") to start", GetCurrentThreadId (), thread, (gsize)thread->tid));
579
580                 WaitForSingleObjectEx (thread->start_notify, INFINITE, FALSE);
581                 CloseHandle (thread->start_notify);
582                 thread->start_notify = NULL;
583         }
584
585         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Done launching thread %p (%"G_GSIZE_FORMAT")", GetCurrentThreadId (), thread, (gsize)thread->tid));
586 }
587
588 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
589 {
590         MonoThread *thread = mono_thread_current ();
591         
592         MONO_ARCH_SAVE_REGS;
593
594         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms));
595
596         mono_monitor_enter (thread->synch_lock);
597         thread->state |= ThreadState_WaitSleepJoin;
598         mono_monitor_exit (thread->synch_lock);
599         
600         SleepEx(ms,TRUE);
601         
602         mono_monitor_enter (thread->synch_lock);
603         thread->state &= ~ThreadState_WaitSleepJoin;
604         mono_monitor_exit (thread->synch_lock);
605 }
606
607 gint32
608 ves_icall_System_Threading_Thread_GetDomainID (void) 
609 {
610         MONO_ARCH_SAVE_REGS;
611
612         return mono_domain_get()->domain_id;
613 }
614
615 MonoString* 
616 ves_icall_System_Threading_Thread_GetName_internal (MonoThread *this_obj)
617 {
618         MonoString* str;
619         mono_monitor_enter (this_obj->synch_lock);
620         
621         if (!this_obj->name)
622                 str = NULL;
623         else
624                 str = mono_string_new_utf16 (mono_domain_get (), this_obj->name, this_obj->name_len);
625         
626         mono_monitor_exit (this_obj->synch_lock);
627         return str;
628 }
629
630 void 
631 ves_icall_System_Threading_Thread_SetName_internal (MonoThread *this_obj, MonoString *name)
632 {
633         mono_monitor_enter (this_obj->synch_lock);
634         
635         if (this_obj->name) {
636                 mono_monitor_exit (this_obj->synch_lock);
637                 mono_raise_exception (mono_get_exception_invalid_operation ("Thread.Name can only be set once."));
638                 return;
639         }
640         if (name) {
641                 this_obj->name = g_new (gunichar2, mono_string_length (name));
642                 memcpy (this_obj->name, mono_string_chars (name), mono_string_length (name) * 2);
643                 this_obj->name_len = mono_string_length (name);
644         }
645         else
646                 this_obj->name = NULL;
647         
648         mono_monitor_exit (this_obj->synch_lock);
649 }
650
651 MonoObject*
652 ves_icall_System_Threading_Thread_GetCachedCurrentCulture (MonoThread *this)
653 {
654         MonoObject *res;
655         MonoDomain *domain;
656         int i;
657
658         /* No need to lock here */
659         if (this->culture_info) {
660                 domain = mono_domain_get ();
661                 for (i = 0; i < NUM_CACHED_CULTURES; ++i) {
662                         res = this->culture_info [i];
663                         if (res && res->vtable->domain == domain)
664                                 return res;
665                 }
666         }
667
668         return NULL;
669 }
670
671 MonoArray*
672 ves_icall_System_Threading_Thread_GetSerializedCurrentCulture (MonoThread *this)
673 {
674         MonoArray *res;
675
676         mono_monitor_enter (this->synch_lock);
677         if (this->serialized_culture_info) {
678                 res = mono_array_new (mono_domain_get (), mono_defaults.byte_class, this->serialized_culture_info_len);
679                 memcpy (mono_array_addr (res, guint8, 0), this->serialized_culture_info, this->serialized_culture_info_len);
680         }
681         else
682                 res = NULL;
683         mono_monitor_exit (this->synch_lock);
684
685         return res;
686 }
687
688 void
689 ves_icall_System_Threading_Thread_SetCachedCurrentCulture (MonoThread *this, MonoObject *culture)
690 {
691         int i;
692         MonoDomain *domain = mono_domain_get ();
693
694         mono_monitor_enter (this->synch_lock);
695         if (!this->culture_info) {
696                 this->culture_info = mono_gc_alloc_fixed (sizeof (MonoObject*) * NUM_CACHED_CULTURES, NULL);
697         }
698
699         for (i = 0; i < NUM_CACHED_CULTURES; ++i) {
700                 if (this->culture_info [i]) {
701                         if (this->culture_info [i]->vtable->domain == domain)
702                                 /* Replace */
703                                 break;
704                 }
705                 else
706                         /* Free entry */
707                         break;
708         }
709         if (i < NUM_CACHED_CULTURES)
710                 this->culture_info [i] = culture;
711         mono_monitor_exit (this->synch_lock);
712 }
713
714 void
715 ves_icall_System_Threading_Thread_SetSerializedCurrentCulture (MonoThread *this, MonoArray *arr)
716 {
717         mono_monitor_enter (this->synch_lock);
718         if (this->serialized_culture_info)
719                 g_free (this->serialized_culture_info);
720         this->serialized_culture_info = g_new0 (guint8, mono_array_length (arr));
721         this->serialized_culture_info_len = mono_array_length (arr);
722         memcpy (this->serialized_culture_info, mono_array_addr (arr, guint8, 0), mono_array_length (arr));
723         mono_monitor_exit (this->synch_lock);
724 }
725
726
727 MonoObject*
728 ves_icall_System_Threading_Thread_GetCachedCurrentUICulture (MonoThread *this)
729 {
730         MonoObject *res;
731         MonoDomain *domain;
732         int i;
733
734         /* No need to lock here */
735         if (this->ui_culture_info) {
736                 domain = mono_domain_get ();
737                 for (i = 0; i < NUM_CACHED_CULTURES; ++i) {
738                         res = this->ui_culture_info [i];
739                         if (res && res->vtable->domain == domain)
740                                 return res;
741                 }
742         }
743
744         return NULL;
745 }
746
747 MonoArray*
748 ves_icall_System_Threading_Thread_GetSerializedCurrentUICulture (MonoThread *this)
749 {
750         MonoArray *res;
751
752         mono_monitor_enter (this->synch_lock);
753         if (this->serialized_ui_culture_info) {
754                 res = mono_array_new (mono_domain_get (), mono_defaults.byte_class, this->serialized_ui_culture_info_len);
755                 memcpy (mono_array_addr (res, guint8, 0), this->serialized_ui_culture_info, this->serialized_ui_culture_info_len);
756         }
757         else
758                 res = NULL;
759         mono_monitor_exit (this->synch_lock);
760
761         return res;
762 }
763
764 void
765 ves_icall_System_Threading_Thread_SetCachedCurrentUICulture (MonoThread *this, MonoObject *culture)
766 {
767         int i;
768         MonoDomain *domain = mono_domain_get ();
769
770         mono_monitor_enter (this->synch_lock);
771         if (!this->ui_culture_info) {
772                 this->ui_culture_info = mono_gc_alloc_fixed (sizeof (MonoObject*) * NUM_CACHED_CULTURES, NULL);
773         }
774
775         for (i = 0; i < NUM_CACHED_CULTURES; ++i) {
776                 if (this->ui_culture_info [i]) {
777                         if (this->ui_culture_info [i]->vtable->domain == domain)
778                                 /* Replace */
779                                 break;
780                 }
781                 else
782                         /* Free entry */
783                         break;
784         }
785         if (i < NUM_CACHED_CULTURES)
786                 this->ui_culture_info [i] = culture;
787         mono_monitor_exit (this->synch_lock);
788 }
789
790 void
791 ves_icall_System_Threading_Thread_SetSerializedCurrentUICulture (MonoThread *this, MonoArray *arr)
792 {
793         mono_monitor_enter (this->synch_lock);
794         if (this->serialized_ui_culture_info)
795                 g_free (this->serialized_ui_culture_info);
796         this->serialized_ui_culture_info = g_new0 (guint8, mono_array_length (arr));
797         this->serialized_ui_culture_info_len = mono_array_length (arr);
798         memcpy (this->serialized_ui_culture_info, mono_array_addr (arr, guint8, 0), mono_array_length (arr));
799         mono_monitor_exit (this->synch_lock);
800 }
801
802 /* the jit may read the compiled code of this function */
803 MonoThread *
804 mono_thread_current (void)
805 {
806         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", GET_CURRENT_OBJECT ()));
807         return GET_CURRENT_OBJECT ();
808 }
809
810 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this,
811                                                          int ms, HANDLE thread)
812 {
813         gboolean ret;
814         
815         MONO_ARCH_SAVE_REGS;
816
817         mono_monitor_enter (this->synch_lock);
818         
819         if ((this->state & ThreadState_Unstarted) != 0) {
820                 mono_monitor_exit (this->synch_lock);
821                 mono_raise_exception (mono_get_exception_thread_state ("Thread has not been started."));
822                 return FALSE;
823         }
824         
825         this->state |= ThreadState_WaitSleepJoin;
826         mono_monitor_exit (this->synch_lock);
827
828         if(ms== -1) {
829                 ms=INFINITE;
830         }
831         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": joining thread handle %p, %d ms",
832                    thread, ms));
833         
834         ret=WaitForSingleObjectEx (thread, ms, TRUE);
835
836         mono_monitor_enter (this->synch_lock);
837         this->state &= ~ThreadState_WaitSleepJoin;
838         mono_monitor_exit (this->synch_lock);
839         
840         if(ret==WAIT_OBJECT_0) {
841                 THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": join successful"));
842
843                 return(TRUE);
844         }
845         
846         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": join failed"));
847
848         return(FALSE);
849 }
850
851 /* FIXME: exitContext isnt documented */
852 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
853 {
854         HANDLE *handles;
855         guint32 numhandles;
856         guint32 ret;
857         guint32 i;
858         MonoObject *waitHandle;
859         MonoClass *klass;
860         MonoThread *thread = mono_thread_current ();
861                 
862         MONO_ARCH_SAVE_REGS;
863
864         numhandles = mono_array_length(mono_handles);
865         handles = g_new0(HANDLE, numhandles);
866
867         if (wait_handle_os_handle_field == 0) {
868                 /* Get the field os_handle which will contain the actual handle */
869                 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");   
870                 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
871         }
872                 
873         for(i = 0; i < numhandles; i++) {       
874                 waitHandle = mono_array_get(mono_handles, MonoObject*, i);              
875                 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
876         }
877         
878         if(ms== -1) {
879                 ms=INFINITE;
880         }
881
882         mono_monitor_enter (thread->synch_lock);
883         thread->state |= ThreadState_WaitSleepJoin;
884         mono_monitor_exit (thread->synch_lock);
885         
886         ret=WaitForMultipleObjectsEx(numhandles, handles, TRUE, ms, TRUE);
887
888         mono_monitor_enter (thread->synch_lock);
889         thread->state &= ~ThreadState_WaitSleepJoin;
890         mono_monitor_exit (thread->synch_lock);
891
892         g_free(handles);
893
894         if(ret==WAIT_FAILED) {
895                 THREAD_WAIT_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Wait failed", GetCurrentThreadId ()));
896                 return(FALSE);
897         } else if(ret==WAIT_TIMEOUT || ret == WAIT_IO_COMPLETION) {
898                 /* Do we want to try again if we get
899                  * WAIT_IO_COMPLETION? The documentation for
900                  * WaitHandle doesn't give any clues.  (We'd have to
901                  * fiddle with the timeout if we retry.)
902                  */
903                 THREAD_WAIT_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Wait timed out", GetCurrentThreadId ()));
904                 return(FALSE);
905         }
906         
907         return(TRUE);
908 }
909
910 /* FIXME: exitContext isnt documented */
911 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
912 {
913         HANDLE *handles;
914         guint32 numhandles;
915         guint32 ret;
916         guint32 i;
917         MonoObject *waitHandle;
918         MonoClass *klass;
919         MonoThread *thread = mono_thread_current ();
920                 
921         MONO_ARCH_SAVE_REGS;
922
923         numhandles = mono_array_length(mono_handles);
924         handles = g_new0(HANDLE, numhandles);
925
926         if (wait_handle_os_handle_field == 0) {
927                 /* Get the field os_handle which will contain the actual handle */
928                 klass = mono_class_from_name(mono_defaults.corlib, "System.Threading", "WaitHandle");   
929                 wait_handle_os_handle_field = mono_class_get_field_from_name(klass, "os_handle");
930         }
931                 
932         for(i = 0; i < numhandles; i++) {       
933                 waitHandle = mono_array_get(mono_handles, MonoObject*, i);              
934                 mono_field_get_value(waitHandle, wait_handle_os_handle_field, &handles[i]);
935         }
936         
937         if(ms== -1) {
938                 ms=INFINITE;
939         }
940
941         mono_monitor_enter (thread->synch_lock);
942         thread->state |= ThreadState_WaitSleepJoin;
943         mono_monitor_exit (thread->synch_lock);
944
945         ret=WaitForMultipleObjectsEx(numhandles, handles, FALSE, ms, TRUE);
946
947         mono_monitor_enter (thread->synch_lock);
948         thread->state &= ~ThreadState_WaitSleepJoin;
949         mono_monitor_exit (thread->synch_lock);
950
951         g_free(handles);
952
953         THREAD_WAIT_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") returning %d", GetCurrentThreadId (), ret));
954
955         /*
956          * These need to be here.  See MSDN dos on WaitForMultipleObjects.
957          */
958         if (ret >= WAIT_OBJECT_0 && ret <= WAIT_OBJECT_0 + numhandles - 1) {
959                 return ret - WAIT_OBJECT_0;
960         }
961         else if (ret >= WAIT_ABANDONED_0 && ret <= WAIT_ABANDONED_0 + numhandles - 1) {
962                 return ret - WAIT_ABANDONED_0;
963         }
964         else {
965                 return ret;
966         }
967 }
968
969 /* FIXME: exitContext isnt documented */
970 gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
971 {
972         guint32 ret;
973         MonoThread *thread = mono_thread_current ();
974         
975         MONO_ARCH_SAVE_REGS;
976
977         THREAD_WAIT_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") waiting for %p, %d ms", GetCurrentThreadId (), handle, ms));
978         
979         if(ms== -1) {
980                 ms=INFINITE;
981         }
982         
983         mono_monitor_enter (thread->synch_lock);
984         thread->state |= ThreadState_WaitSleepJoin;
985         mono_monitor_exit (thread->synch_lock);
986
987         ret=WaitForSingleObjectEx (handle, ms, TRUE);
988         
989         mono_monitor_enter (thread->synch_lock);
990         thread->state &= ~ThreadState_WaitSleepJoin;
991         mono_monitor_exit (thread->synch_lock);
992
993         if(ret==WAIT_FAILED) {
994                 THREAD_WAIT_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Wait failed", GetCurrentThreadId ()));
995                 return(FALSE);
996         } else if(ret==WAIT_TIMEOUT || ret == WAIT_IO_COMPLETION) {
997                 /* Do we want to try again if we get
998                  * WAIT_IO_COMPLETION? The documentation for
999                  * WaitHandle doesn't give any clues.  (We'd have to
1000                  * fiddle with the timeout if we retry.)
1001                  */
1002                 THREAD_WAIT_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Wait timed out", GetCurrentThreadId ()));
1003                 return(FALSE);
1004         }
1005         
1006         return(TRUE);
1007 }
1008
1009 HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created)
1010
1011         HANDLE mutex;
1012         
1013         MONO_ARCH_SAVE_REGS;
1014    
1015         *created = TRUE;
1016         
1017         if (name == NULL) {
1018                 mutex = CreateMutex (NULL, owned, NULL);
1019         } else {
1020                 mutex = CreateMutex (NULL, owned, mono_string_chars (name));
1021                 
1022                 if (GetLastError () == ERROR_ALREADY_EXISTS) {
1023                         *created = FALSE;
1024                 }
1025         }
1026
1027         return(mutex);
1028 }                                                                   
1029
1030 void ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) { 
1031         MONO_ARCH_SAVE_REGS;
1032
1033         ReleaseMutex(handle);
1034 }
1035
1036 HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual, MonoBoolean initial, MonoString *name) {
1037         MONO_ARCH_SAVE_REGS;
1038
1039         return(CreateEvent (NULL, manual, initial,
1040                             name==NULL?NULL:mono_string_chars (name)));
1041 }
1042
1043 gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) {
1044         MONO_ARCH_SAVE_REGS;
1045
1046         return (SetEvent(handle));
1047 }
1048
1049 gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) {
1050         MONO_ARCH_SAVE_REGS;
1051
1052         return (ResetEvent(handle));
1053 }
1054
1055 void
1056 ves_icall_System_Threading_Events_CloseEvent_internal (HANDLE handle) {
1057         MONO_ARCH_SAVE_REGS;
1058
1059         CloseHandle (handle);
1060 }
1061
1062 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
1063 {
1064         MONO_ARCH_SAVE_REGS;
1065
1066         return InterlockedIncrement (location);
1067 }
1068
1069 gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location)
1070 {
1071         gint64 ret;
1072
1073         MONO_ARCH_SAVE_REGS;
1074
1075         EnterCriticalSection(&interlocked_mutex);
1076
1077         ret = ++ *location;
1078         
1079         LeaveCriticalSection(&interlocked_mutex);
1080
1081         
1082         return ret;
1083 }
1084
1085 gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location)
1086 {
1087         MONO_ARCH_SAVE_REGS;
1088
1089         return InterlockedDecrement(location);
1090 }
1091
1092 gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location)
1093 {
1094         gint64 ret;
1095
1096         MONO_ARCH_SAVE_REGS;
1097
1098         EnterCriticalSection(&interlocked_mutex);
1099
1100         ret = -- *location;
1101         
1102         LeaveCriticalSection(&interlocked_mutex);
1103
1104         return ret;
1105 }
1106
1107 gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location, gint32 value)
1108 {
1109         MONO_ARCH_SAVE_REGS;
1110
1111         return InterlockedExchange(location, value);
1112 }
1113
1114 MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location, MonoObject *value)
1115 {
1116         MONO_ARCH_SAVE_REGS;
1117
1118         return (MonoObject *) InterlockedExchangePointer((gpointer *) location, value);
1119 }
1120
1121 gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location, gfloat value)
1122 {
1123         IntFloatUnion val, ret;
1124
1125         MONO_ARCH_SAVE_REGS;
1126
1127         val.fval = value;
1128         ret.ival = InterlockedExchange((gint32 *) location, val.ival);
1129
1130         return ret.fval;
1131 }
1132
1133 gint64 
1134 ves_icall_System_Threading_Interlocked_Exchange_Long (gint64 *location, gint64 value)
1135 {
1136 #if SIZEOF_VOID_P == 8
1137         return (gint64) InterlockedExchangePointer((gpointer *) location, (gpointer)value);
1138 #else
1139         gint64 res;
1140
1141         /* 
1142          * According to MSDN, this function is only atomic with regards to the 
1143          * other Interlocked functions on 32 bit platforms.
1144          */
1145         EnterCriticalSection(&interlocked_mutex);
1146         res = *location;
1147         *location = value;
1148         LeaveCriticalSection(&interlocked_mutex);
1149
1150         return res;
1151 #endif
1152 }
1153
1154 gdouble 
1155 ves_icall_System_Threading_Interlocked_Exchange_Double (gdouble *location, gdouble value)
1156 {
1157 #if SIZEOF_VOID_P == 8
1158         LongDoubleUnion val, ret;
1159
1160         val.fval = value;
1161         ret.ival = (gint64)InterlockedExchangePointer((gpointer *) location, (gpointer)val.ival);
1162
1163         return ret.fval;
1164 #else
1165         gdouble res;
1166
1167         /* 
1168          * According to MSDN, this function is only atomic with regards to the 
1169          * other Interlocked functions on 32 bit platforms.
1170          */
1171         EnterCriticalSection(&interlocked_mutex);
1172         res = *location;
1173         *location = value;
1174         LeaveCriticalSection(&interlocked_mutex);
1175
1176         return res;
1177 #endif
1178 }
1179
1180 gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location, gint32 value, gint32 comparand)
1181 {
1182         MONO_ARCH_SAVE_REGS;
1183
1184         return InterlockedCompareExchange(location, value, comparand);
1185 }
1186
1187 MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location, MonoObject *value, MonoObject *comparand)
1188 {
1189         MONO_ARCH_SAVE_REGS;
1190
1191         return (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location, value, comparand);
1192 }
1193
1194 gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location, gfloat value, gfloat comparand)
1195 {
1196         IntFloatUnion val, ret, cmp;
1197
1198         MONO_ARCH_SAVE_REGS;
1199
1200         val.fval = value;
1201         cmp.fval = comparand;
1202         ret.ival = InterlockedCompareExchange((gint32 *) location, val.ival, cmp.ival);
1203
1204         return ret.fval;
1205 }
1206
1207 gdouble
1208 ves_icall_System_Threading_Interlocked_CompareExchange_Double (gdouble *location, gdouble value, gdouble comparand)
1209 {
1210 #if SIZEOF_VOID_P == 8
1211         LongDoubleUnion val, comp, ret;
1212
1213         val.fval = value;
1214         comp.fval = comparand;
1215         ret.ival = (gint64)InterlockedCompareExchangePointer((gpointer *) location, (gpointer)val.ival, (gpointer)comp.ival);
1216
1217         return ret.fval;
1218 #else
1219         gdouble old;
1220
1221         EnterCriticalSection(&interlocked_mutex);
1222         old = *location;
1223         if (old == comparand)
1224                 *location = value;
1225         LeaveCriticalSection(&interlocked_mutex);
1226
1227         return old;
1228 #endif
1229 }
1230
1231 gint64 
1232 ves_icall_System_Threading_Interlocked_CompareExchange_Long (gint64 *location, gint64 value, gint64 comparand)
1233 {
1234 #if SIZEOF_VOID_P == 8
1235         return (gint64)InterlockedCompareExchangePointer((gpointer *) location, (gpointer)value, (gpointer)comparand);
1236 #else
1237         gint64 old;
1238
1239         EnterCriticalSection(&interlocked_mutex);
1240         old = *location;
1241         if (old == comparand)
1242                 *location = value;
1243         LeaveCriticalSection(&interlocked_mutex);
1244         
1245         return old;
1246 #endif
1247 }
1248
1249 gint32 
1250 ves_icall_System_Threading_Interlocked_Add_Int (gint32 *location, gint32 value)
1251 {
1252 #if SIZEOF_VOID_P == 8
1253         /* Should be implemented as a JIT intrinsic */
1254         mono_raise_exception (mono_get_exception_not_implemented (NULL));
1255         return 0;
1256 #else
1257         gint32 orig;
1258
1259         EnterCriticalSection(&interlocked_mutex);
1260         orig = *location;
1261         *location = orig + value;
1262         LeaveCriticalSection(&interlocked_mutex);
1263
1264         return orig;
1265 #endif
1266 }
1267
1268 gint64 
1269 ves_icall_System_Threading_Interlocked_Add_Long (gint64 *location, gint64 value)
1270 {
1271 #if SIZEOF_VOID_P == 8
1272         /* Should be implemented as a JIT intrinsic */
1273         mono_raise_exception (mono_get_exception_not_implemented (NULL));
1274         return 0;
1275 #else
1276         gint64 orig;
1277
1278         EnterCriticalSection(&interlocked_mutex);
1279         orig = *location;
1280         *location = orig + value;
1281         LeaveCriticalSection(&interlocked_mutex);
1282
1283         return orig;
1284 #endif
1285 }
1286
1287 gint64 
1288 ves_icall_System_Threading_Interlocked_Read_Long (gint64 *location)
1289 {
1290 #if SIZEOF_VOID_P == 8
1291         /* 64 bit reads are already atomic */
1292         return *location;
1293 #else
1294         gint64 res;
1295
1296         EnterCriticalSection(&interlocked_mutex);
1297         res = *location;
1298         LeaveCriticalSection(&interlocked_mutex);
1299
1300         return res;
1301 #endif
1302 }
1303
1304 void
1305 ves_icall_System_Threading_Thread_ClrState (MonoThread* this, guint32 state)
1306 {
1307         mono_monitor_enter (this->synch_lock);
1308         this->state &= ~state;
1309         if (state & ThreadState_Background) {
1310                 /* If the thread changes the background mode, the main thread has to
1311                  * be notified, since it has to rebuild the list of threads to
1312                  * wait for.
1313                  */
1314                 SetEvent (background_change_event);
1315         }
1316         mono_monitor_exit (this->synch_lock);
1317 }
1318
1319 void
1320 ves_icall_System_Threading_Thread_SetState (MonoThread* this, guint32 state)
1321 {
1322         mono_monitor_enter (this->synch_lock);
1323         this->state |= state;
1324         if (state & ThreadState_Background) {
1325                 /* If the thread changes the background mode, the main thread has to
1326                  * be notified, since it has to rebuild the list of threads to
1327                  * wait for.
1328                  */
1329                 SetEvent (background_change_event);
1330         }
1331         mono_monitor_exit (this->synch_lock);
1332 }
1333
1334 guint32
1335 ves_icall_System_Threading_Thread_GetState (MonoThread* this)
1336 {
1337         guint32 state;
1338         mono_monitor_enter (this->synch_lock);
1339         state = this->state;
1340         mono_monitor_exit (this->synch_lock);
1341         return state;
1342 }
1343
1344 int  
1345 mono_thread_get_abort_signal (void)
1346 {
1347 #if defined (__MINGW32__) || defined (_MSC_VER)
1348         return -1;
1349 #else
1350 #ifndef SIGRTMIN
1351         return SIGUSR1;
1352 #else
1353         static int abort_signum = -1;
1354         int i;
1355         if (abort_signum != -1)
1356                 return abort_signum;
1357         /* we try to avoid SIGRTMIN and any one that might have been set already, see bug #75387 */
1358         for (i = SIGRTMIN + 1; i < SIGRTMAX; ++i) {
1359                 struct sigaction sinfo;
1360                 sigaction (i, NULL, &sinfo);
1361                 if (sinfo.sa_handler == SIG_DFL && (void*)sinfo.sa_sigaction == (void*)SIG_DFL) {
1362                         abort_signum = i;
1363                         return i;
1364                 }
1365         }
1366         /* fallback to the old way */
1367         return SIGRTMIN;
1368 #endif
1369 #endif /*defined (__MINGW32__) || defined (_MSC_VER) */
1370 }
1371
1372 #if defined (__MINGW32__) || defined (_MSC_VER)
1373 static void CALLBACK interruption_request_apc (ULONG_PTR param)
1374 {
1375         MonoException* exc = mono_thread_request_interruption (FALSE);
1376         if (exc) mono_raise_exception (exc);
1377 }
1378 #endif /* defined (__MINGW32__) || defined (_MSC_VER) */
1379
1380 /*
1381  * signal_thread_state_change
1382  *
1383  * Tells the thread that his state has changed and it has to enter the new
1384  * state as soon as possible.
1385  */
1386 static void signal_thread_state_change (MonoThread *thread)
1387 {
1388         if (thread == mono_thread_current ()) {
1389                 /* Do it synchronously */
1390                 MonoException *exc = mono_thread_request_interruption (FALSE); 
1391                 if (exc)
1392                         mono_raise_exception (exc);
1393         }
1394
1395 #if defined (__MINGW32__) || defined (_MSC_VER)
1396         QueueUserAPC ((PAPCFUNC)interruption_request_apc, thread->handle, NULL);
1397 #else
1398         /* fixme: store the state somewhere */
1399 #ifdef PTHREAD_POINTER_ID
1400         pthread_kill ((gpointer)(gsize)(thread->tid), mono_thread_get_abort_signal ());
1401 #else
1402         pthread_kill (thread->tid, mono_thread_get_abort_signal ());
1403 #endif
1404 #endif /* defined (__MINGW32__) || defined (__MSC_VER) */
1405 }
1406
1407 void
1408 ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state)
1409 {
1410         MONO_ARCH_SAVE_REGS;
1411
1412         mono_monitor_enter (thread->synch_lock);
1413
1414         if ((thread->state & ThreadState_AbortRequested) != 0 || 
1415                 (thread->state & ThreadState_StopRequested) != 0) 
1416         {
1417                 mono_monitor_exit (thread->synch_lock);
1418                 return;
1419         }
1420
1421         if ((thread->state & ThreadState_Unstarted) != 0) {
1422                 thread->state |= ThreadState_Aborted;
1423                 mono_monitor_exit (thread->synch_lock);
1424                 return;
1425         }
1426
1427         thread->state |= ThreadState_AbortRequested;
1428         thread->abort_state = state;
1429         thread->abort_exc = NULL;
1430
1431         mono_monitor_exit (thread->synch_lock);
1432
1433         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Abort requested for %p (%"G_GSIZE_FORMAT")", GetCurrentThreadId (), thread, (gsize)thread->tid));
1434         
1435         /* Make sure the thread is awake */
1436         mono_thread_resume (thread);
1437         
1438         signal_thread_state_change (thread);
1439 }
1440
1441 void
1442 ves_icall_System_Threading_Thread_ResetAbort (void)
1443 {
1444         MonoThread *thread = mono_thread_current ();
1445
1446         MONO_ARCH_SAVE_REGS;
1447         
1448         mono_monitor_enter (thread->synch_lock);
1449
1450         thread->state &= ~ThreadState_AbortRequested;
1451         
1452         if (!thread->abort_exc) {
1453                 const char *msg = "Unable to reset abort because no abort was requested";
1454                 mono_monitor_exit (thread->synch_lock);
1455                 mono_raise_exception (mono_get_exception_thread_state (msg));
1456         } else {
1457                 thread->abort_exc = NULL;
1458                 thread->abort_state = NULL;
1459         }
1460         
1461         mono_monitor_exit (thread->synch_lock);
1462 }
1463
1464 static gboolean
1465 mono_thread_suspend (MonoThread *thread)
1466 {
1467         MONO_ARCH_SAVE_REGS;
1468
1469         mono_monitor_enter (thread->synch_lock);
1470
1471         if ((thread->state & ThreadState_Unstarted) != 0 || 
1472                 (thread->state & ThreadState_Aborted) != 0 || 
1473                 (thread->state & ThreadState_Stopped) != 0)
1474         {
1475                 mono_monitor_exit (thread->synch_lock);
1476                 return FALSE;
1477         }
1478
1479         if ((thread->state & ThreadState_Suspended) != 0 || 
1480                 (thread->state & ThreadState_SuspendRequested) != 0 ||
1481                 (thread->state & ThreadState_StopRequested) != 0) 
1482         {
1483                 mono_monitor_exit (thread->synch_lock);
1484                 return TRUE;
1485         }
1486         
1487         thread->state |= ThreadState_SuspendRequested;
1488         mono_monitor_exit (thread->synch_lock);
1489
1490         signal_thread_state_change (thread);
1491         return TRUE;
1492 }
1493
1494 void
1495 ves_icall_System_Threading_Thread_Suspend (MonoThread *thread)
1496 {
1497         if (!mono_thread_suspend (thread))
1498                 mono_raise_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
1499 }
1500
1501 static gboolean
1502 mono_thread_resume (MonoThread *thread)
1503 {
1504         MONO_ARCH_SAVE_REGS;
1505
1506         mono_monitor_enter (thread->synch_lock);
1507
1508         if ((thread->state & ThreadState_SuspendRequested) != 0) {
1509                 thread->state &= ~ThreadState_SuspendRequested;
1510                 mono_monitor_exit (thread->synch_lock);
1511                 return TRUE;
1512         }
1513
1514         if ((thread->state & ThreadState_Suspended) == 0 ||
1515                 (thread->state & ThreadState_Unstarted) != 0 || 
1516                 (thread->state & ThreadState_Aborted) != 0 || 
1517                 (thread->state & ThreadState_Stopped) != 0)
1518         {
1519                 mono_monitor_exit (thread->synch_lock);
1520                 return FALSE;
1521         }
1522         
1523         thread->resume_event = CreateEvent (NULL, TRUE, FALSE, NULL);
1524         
1525         /* Awake the thread */
1526         SetEvent (thread->suspend_event);
1527
1528         mono_monitor_exit (thread->synch_lock);
1529
1530         /* Wait for the thread to awake */
1531         WaitForSingleObject (thread->resume_event, INFINITE);
1532         CloseHandle (thread->resume_event);
1533         thread->resume_event = NULL;
1534
1535         return TRUE;
1536 }
1537
1538 void
1539 ves_icall_System_Threading_Thread_Resume (MonoThread *thread)
1540 {
1541         if (!mono_thread_resume (thread))
1542                 mono_raise_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
1543 }
1544
1545 static gboolean
1546 find_wrapper (MonoMethod *m, gint no, gint ilo, gboolean managed, gpointer data)
1547 {
1548         if (managed)
1549                 return TRUE;
1550
1551         if (m->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE ||
1552                 m->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE ||
1553                 m->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH) 
1554         {
1555                 *((gboolean*)data) = TRUE;
1556                 return TRUE;
1557         }
1558         return FALSE;
1559 }
1560
1561 static gboolean 
1562 is_running_protected_wrapper (void)
1563 {
1564         gboolean found = FALSE;
1565         mono_stack_walk (find_wrapper, &found);
1566         return found;
1567 }
1568
1569 void mono_thread_stop (MonoThread *thread)
1570 {
1571         mono_monitor_enter (thread->synch_lock);
1572
1573         if ((thread->state & ThreadState_StopRequested) != 0 ||
1574                 (thread->state & ThreadState_Stopped) != 0)
1575         {
1576                 mono_monitor_exit (thread->synch_lock);
1577                 return;
1578         }
1579         
1580         /* Make sure the thread is awake */
1581         mono_thread_resume (thread);
1582
1583         thread->state |= ThreadState_StopRequested;
1584         thread->state &= ~ThreadState_AbortRequested;
1585         
1586         mono_monitor_exit (thread->synch_lock);
1587         
1588         signal_thread_state_change (thread);
1589 }
1590
1591 gint8
1592 ves_icall_System_Threading_Thread_VolatileRead1 (void *ptr)
1593 {
1594         return *((volatile gint8 *) (ptr));
1595 }
1596
1597 gint16
1598 ves_icall_System_Threading_Thread_VolatileRead2 (void *ptr)
1599 {
1600         return *((volatile gint16 *) (ptr));
1601 }
1602
1603 gint32
1604 ves_icall_System_Threading_Thread_VolatileRead4 (void *ptr)
1605 {
1606         return *((volatile gint32 *) (ptr));
1607 }
1608
1609 gint64
1610 ves_icall_System_Threading_Thread_VolatileRead8 (void *ptr)
1611 {
1612         return *((volatile gint64 *) (ptr));
1613 }
1614
1615 void *
1616 ves_icall_System_Threading_Thread_VolatileReadIntPtr (void *ptr)
1617 {
1618         return (void *)  *((volatile void **) ptr);
1619 }
1620
1621 void
1622 ves_icall_System_Threading_Thread_VolatileWrite1 (void *ptr, gint8 value)
1623 {
1624         *((volatile gint8 *) ptr) = value;
1625 }
1626
1627 void
1628 ves_icall_System_Threading_Thread_VolatileWrite2 (void *ptr, gint16 value)
1629 {
1630         *((volatile gint16 *) ptr) = value;
1631 }
1632
1633 void
1634 ves_icall_System_Threading_Thread_VolatileWrite4 (void *ptr, gint32 value)
1635 {
1636         *((volatile gint32 *) ptr) = value;
1637 }
1638
1639 void
1640 ves_icall_System_Threading_Thread_VolatileWrite8 (void *ptr, gint64 value)
1641 {
1642         *((volatile gint64 *) ptr) = value;
1643 }
1644
1645 void
1646 ves_icall_System_Threading_Thread_VolatileWriteIntPtr (void *ptr, void *value)
1647 {
1648         *((volatile void **) ptr) = value;
1649 }
1650
1651 void mono_thread_init (MonoThreadStartCB start_cb,
1652                        MonoThreadAttachCB attach_cb)
1653 {
1654         InitializeCriticalSection(&threads_mutex);
1655         InitializeCriticalSection(&interlocked_mutex);
1656         InitializeCriticalSection(&contexts_mutex);
1657         background_change_event = CreateEvent (NULL, TRUE, FALSE, NULL);
1658         
1659         mono_init_static_data_info (&thread_static_info);
1660         mono_init_static_data_info (&context_static_info);
1661
1662         current_object_key=TlsAlloc();
1663         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Allocated current_object_key %d",
1664                    current_object_key));
1665
1666         mono_thread_start_cb = start_cb;
1667         mono_thread_attach_cb = attach_cb;
1668
1669         /* Get a pseudo handle to the current process.  This is just a
1670          * kludge so that wapi can build a process handle if needed.
1671          * As a pseudo handle is returned, we don't need to clean
1672          * anything up.
1673          */
1674         GetCurrentProcess ();
1675 }
1676
1677 void
1678 mono_threads_install_cleanup (MonoThreadCleanupFunc func)
1679 {
1680         mono_thread_cleanup = func;
1681 }
1682
1683 void mono_install_thread_callbacks (MonoThreadCallbacks *callbacks)
1684 {
1685         mono_thread_callbacks = callbacks;
1686 }
1687
1688 G_GNUC_UNUSED
1689 static void print_tids (gpointer key, gpointer value, gpointer user)
1690 {
1691         /* GPOINTER_TO_UINT breaks horribly if sizeof(void *) >
1692          * sizeof(uint) and a cast to uint would overflow
1693          */
1694         /* Older versions of glib don't have G_GSIZE_FORMAT, so just
1695          * print this as a pointer.
1696          */
1697         g_message ("Waiting for: %p", key);
1698 }
1699
1700 struct wait_data 
1701 {
1702         HANDLE handles[MAXIMUM_WAIT_OBJECTS];
1703         MonoThread *threads[MAXIMUM_WAIT_OBJECTS];
1704         guint32 num;
1705 };
1706
1707 static void wait_for_tids (struct wait_data *wait, guint32 timeout)
1708 {
1709         guint32 i, ret;
1710         
1711         THREAD_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION
1712                   ": %d threads to wait for in this batch", wait->num));
1713
1714         ret=WaitForMultipleObjectsEx(wait->num, wait->handles, TRUE, timeout, FALSE);
1715
1716         if(ret==WAIT_FAILED) {
1717                 /* See the comment in build_wait_tids() */
1718                 THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Wait failed"));
1719                 return;
1720         }
1721         
1722         for(i=0; i<wait->num; i++)
1723                 CloseHandle (wait->handles[i]);
1724
1725         if (ret == WAIT_TIMEOUT)
1726                 return;
1727
1728         for(i=0; i<wait->num; i++) {
1729                 gsize tid = wait->threads[i]->tid;
1730                 
1731                 if(mono_g_hash_table_lookup (threads, (gpointer)tid)!=NULL) {
1732                         /* This thread must have been killed, because
1733                          * it hasn't cleaned itself up. (It's just
1734                          * possible that the thread exited before the
1735                          * parent thread had a chance to store the
1736                          * handle, and now there is another pointer to
1737                          * the already-exited thread stored.  In this
1738                          * case, we'll just get two
1739                          * mono_profiler_thread_end() calls for the
1740                          * same thread.)
1741                          */
1742         
1743                         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": cleaning up after thread %"G_GSIZE_FORMAT, tid));
1744                         thread_cleanup (wait->threads[i]);
1745                 }
1746         }
1747 }
1748
1749 static void wait_for_tids_or_state_change (struct wait_data *wait, guint32 timeout)
1750 {
1751         guint32 i, ret, count;
1752         
1753         THREAD_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION
1754                   ": %d threads to wait for in this batch", wait->num));
1755
1756         /* Add the thread state change event, so it wakes up if a thread changes
1757          * to background mode.
1758          */
1759         count = wait->num;
1760         if (count < MAXIMUM_WAIT_OBJECTS) {
1761                 wait->handles [count] = background_change_event;
1762                 count++;
1763         }
1764
1765         ret=WaitForMultipleObjectsEx (count, wait->handles, FALSE, timeout, FALSE);
1766
1767         if(ret==WAIT_FAILED) {
1768                 /* See the comment in build_wait_tids() */
1769                 THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Wait failed"));
1770                 return;
1771         }
1772         
1773         for(i=0; i<wait->num; i++)
1774                 CloseHandle (wait->handles[i]);
1775
1776         if (ret == WAIT_TIMEOUT)
1777                 return;
1778         
1779         if (ret < wait->num) {
1780                 gsize tid = wait->threads[ret]->tid;
1781                 EnterCriticalSection (&threads_mutex);
1782                 if (mono_g_hash_table_lookup (threads, (gpointer)tid)!=NULL) {
1783                         /* See comment in wait_for_tids about thread cleanup */
1784                         LeaveCriticalSection (&threads_mutex);
1785                         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
1786                                    ": cleaning up after thread %"G_GSIZE_FORMAT, tid));
1787                         thread_cleanup (wait->threads [ret]);
1788                 }
1789                 else
1790                         LeaveCriticalSection (&threads_mutex);
1791         }
1792 }
1793
1794 static void build_wait_tids (gpointer key, gpointer value, gpointer user)
1795 {
1796         struct wait_data *wait=(struct wait_data *)user;
1797
1798         if(wait->num<MAXIMUM_WAIT_OBJECTS) {
1799                 HANDLE handle;
1800                 MonoThread *thread=(MonoThread *)value;
1801
1802                 /* Ignore background threads, we abort them later */
1803                 mono_monitor_enter (thread->synch_lock);
1804                 if (thread->state & ThreadState_Background) {
1805                         mono_monitor_exit (thread->synch_lock);
1806                         return; /* just leave, ignore */
1807                 }
1808                 mono_monitor_exit (thread->synch_lock);
1809                 
1810                 if (mono_gc_is_finalizer_thread (thread))
1811                         return;
1812
1813                 if (thread == mono_thread_current ())
1814                         return;
1815
1816                 if (thread == mono_thread_get_main ())
1817                         return;
1818
1819                 handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
1820                 if (handle == NULL)
1821                         return;
1822                 
1823                 wait->handles[wait->num]=handle;
1824                 wait->threads[wait->num]=thread;
1825                 wait->num++;
1826         } else {
1827                 /* Just ignore the rest, we can't do anything with
1828                  * them yet
1829                  */
1830         }
1831 }
1832
1833 static gboolean
1834 remove_and_abort_threads (gpointer key, gpointer value, gpointer user)
1835 {
1836         struct wait_data *wait=(struct wait_data *)user;
1837         gsize self = GetCurrentThreadId ();
1838         MonoThread *thread = (MonoThread *) value;
1839         HANDLE handle;
1840
1841         /* The finalizer thread is not a background thread */
1842         if (thread->tid != self && (thread->state & ThreadState_Background) != 0) {
1843         
1844                 handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
1845                 if (handle == NULL)
1846                         return FALSE;
1847                 
1848                 if(thread->state & ThreadState_AbortRequested ||
1849                    thread->state & ThreadState_Aborted) {
1850                         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Thread id %"G_GSIZE_FORMAT" already aborting", (gsize)thread->tid));
1851                         return(TRUE);
1852                 }
1853
1854                 wait->handles[wait->num]=thread->handle;
1855                 wait->threads[wait->num]=thread;
1856                 wait->num++;
1857
1858                 THREAD_DEBUG (g_print (G_GNUC_PRETTY_FUNCTION ": Aborting id: %"G_GSIZE_FORMAT"\n", (gsize)thread->tid));
1859                 mono_thread_stop (thread);
1860                 return TRUE;
1861         }
1862
1863         return (thread->tid != self && !mono_gc_is_finalizer_thread (thread)); 
1864 }
1865
1866 void mono_thread_manage (void)
1867 {
1868         struct wait_data *wait=g_new0 (struct wait_data, 1);
1869         
1870         /* join each thread that's still running */
1871         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Joining each running thread..."));
1872         
1873         EnterCriticalSection (&threads_mutex);
1874         if(threads==NULL) {
1875                 THREAD_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION ": No threads"));
1876                 LeaveCriticalSection (&threads_mutex);
1877                 return;
1878         }
1879         LeaveCriticalSection (&threads_mutex);
1880         
1881         do {
1882                 EnterCriticalSection (&threads_mutex);
1883                 THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
1884                           ":There are %d threads to join", mono_g_hash_table_size (threads));
1885                         mono_g_hash_table_foreach (threads, print_tids, NULL));
1886         
1887                 ResetEvent (background_change_event);
1888                 wait->num=0;
1889                 mono_g_hash_table_foreach (threads, build_wait_tids, wait);
1890                 LeaveCriticalSection (&threads_mutex);
1891                 if(wait->num>0) {
1892                         /* Something to wait for */
1893                         wait_for_tids_or_state_change (wait, INFINITE);
1894                 }
1895                 THREAD_DEBUG (g_message ("I have %d threads after waiting.", wait->num));
1896         } while(wait->num>0);
1897
1898         mono_runtime_set_shutting_down ();
1899
1900         THREAD_DEBUG (g_message ("threadpool cleanup"));
1901         mono_thread_pool_cleanup ();
1902
1903         EnterCriticalSection(&threads_mutex);
1904
1905         /* 
1906          * Remove everything but the finalizer thread and self.
1907          * Also abort all the background threads
1908          * */
1909         wait->num = 0;
1910         mono_g_hash_table_foreach_remove (threads, remove_and_abort_threads, wait);
1911
1912         LeaveCriticalSection(&threads_mutex);
1913
1914         THREAD_DEBUG (g_message ("wait->num is now %d", wait->num));
1915         if(wait->num>0) {
1916                 /* Something to wait for */
1917                 wait_for_tids (wait, INFINITE);
1918         }
1919         /* 
1920          * give the subthreads a chance to really quit (this is mainly needed
1921          * to get correct user and system times from getrusage/wait/time(1)).
1922          * This could be removed if we avoid pthread_detach() and use pthread_join().
1923          */
1924 #ifndef PLATFORM_WIN32
1925         sched_yield ();
1926 #endif
1927
1928         g_free (wait);
1929 }
1930
1931 static void terminate_thread (gpointer key, gpointer value, gpointer user)
1932 {
1933         MonoThread *thread=(MonoThread *)value;
1934         
1935         if(thread->tid != (gsize)user) {
1936                 /*TerminateThread (thread->handle, -1);*/
1937         }
1938 }
1939
1940 void mono_thread_abort_all_other_threads (void)
1941 {
1942         gsize self = GetCurrentThreadId ();
1943
1944         EnterCriticalSection (&threads_mutex);
1945         THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ":There are %d threads to abort",
1946                   mono_g_hash_table_size (threads));
1947                 mono_g_hash_table_foreach (threads, print_tids, NULL));
1948
1949         mono_g_hash_table_foreach (threads, terminate_thread, (gpointer)self);
1950         
1951         LeaveCriticalSection (&threads_mutex);
1952 }
1953
1954 static void
1955 collect_threads (gpointer key, gpointer value, gpointer user_data)
1956 {
1957         MonoThread *thread = (MonoThread*)value;
1958         struct wait_data *wait = (struct wait_data*)user_data;
1959         HANDLE handle;
1960
1961         if (wait->num<MAXIMUM_WAIT_OBJECTS) {
1962                 handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
1963                 if (handle == NULL)
1964                         return;
1965
1966                 wait->handles [wait->num] = handle;
1967                 wait->threads [wait->num] = thread;
1968                 wait->num++;
1969         }
1970 }
1971
1972 /*
1973  * mono_thread_suspend_all_other_threads:
1974  *
1975  *  Suspend all managed threads except the finalizer thread and this thread.
1976  */
1977 void mono_thread_suspend_all_other_threads (void)
1978 {
1979         struct wait_data *wait = g_new0 (struct wait_data, 1);
1980         int i, waitnum;
1981         gsize self = GetCurrentThreadId ();
1982         gpointer *events;
1983         guint32 eventidx = 0;
1984
1985         /* 
1986          * Make a copy of the hashtable since we can't do anything with
1987          * threads while threads_mutex is held.
1988          */
1989         EnterCriticalSection (&threads_mutex);
1990         mono_g_hash_table_foreach (threads, collect_threads, wait);
1991         LeaveCriticalSection (&threads_mutex);
1992
1993         events = g_new0 (gpointer, wait->num);
1994         waitnum = 0;
1995         /* Get the suspended events that we'll be waiting for */
1996         for (i = 0; i < wait->num; ++i) {
1997                 MonoThread *thread = wait->threads [i];
1998
1999                 if ((thread->tid == self) || mono_gc_is_finalizer_thread (thread)) {
2000                         //CloseHandle (wait->handles [i]);
2001                         wait->threads [i] = NULL; /* ignore this thread in next loop */
2002                         continue;
2003                 }
2004
2005                 mono_monitor_enter (thread->synch_lock);
2006
2007                 if ((thread->state & ThreadState_Suspended) != 0 || 
2008                         (thread->state & ThreadState_SuspendRequested) != 0 ||
2009                         (thread->state & ThreadState_StopRequested) != 0 ||
2010                         (thread->state & ThreadState_Stopped) != 0) {
2011                         mono_monitor_exit (thread->synch_lock);
2012                         CloseHandle (wait->handles [i]);
2013                         wait->threads [i] = NULL; /* ignore this thread in next loop */
2014                         continue;
2015                 }
2016
2017                 /* Convert abort requests into suspend requests */
2018                 if ((thread->state & ThreadState_AbortRequested) != 0)
2019                         thread->state &= ~ThreadState_AbortRequested;
2020                         
2021                 thread->state |= ThreadState_SuspendRequested;
2022
2023                 if (thread->suspended_event == NULL)
2024                         thread->suspended_event = CreateEvent (NULL, TRUE, FALSE, NULL);
2025
2026                 events [eventidx++] = thread->suspended_event;
2027                 mono_monitor_exit (thread->synch_lock);
2028
2029                 /* Signal the thread to suspend */
2030                 signal_thread_state_change (thread);
2031         }
2032
2033         WaitForMultipleObjectsEx (eventidx, events, TRUE, INFINITE, FALSE);
2034         for (i = 0; i < wait->num; ++i) {
2035                 MonoThread *thread = wait->threads [i];
2036
2037                 if (thread == NULL)
2038                         continue;
2039
2040                 mono_monitor_enter (thread->synch_lock);
2041                 CloseHandle (thread->suspended_event);
2042                 thread->suspended_event = NULL;
2043                 mono_monitor_exit (thread->synch_lock);
2044         }
2045
2046         g_free (events);
2047         g_free (wait);
2048 }
2049
2050 /*
2051  * mono_thread_push_appdomain_ref:
2052  *
2053  *   Register that the current thread may have references to objects in domain 
2054  * @domain on its stack. Each call to this function should be paired with a 
2055  * call to pop_appdomain_ref.
2056  */
2057 void 
2058 mono_thread_push_appdomain_ref (MonoDomain *domain)
2059 {
2060         MonoThread *thread = mono_thread_current ();
2061
2062         if (thread) {
2063                 /* printf ("PUSH REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, domain->friendly_name); */
2064                 EnterCriticalSection (&threads_mutex);
2065                 thread->appdomain_refs = g_slist_prepend (thread->appdomain_refs, domain);
2066                 LeaveCriticalSection (&threads_mutex);
2067         }
2068 }
2069
2070 void
2071 mono_thread_pop_appdomain_ref (void)
2072 {
2073         MonoThread *thread = mono_thread_current ();
2074
2075         if (thread) {
2076                 /* printf ("POP REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, ((MonoDomain*)(thread->appdomain_refs->data))->friendly_name); */
2077                 EnterCriticalSection (&threads_mutex);
2078                 /* FIXME: How can the list be empty ? */
2079                 if (thread->appdomain_refs)
2080                         thread->appdomain_refs = g_slist_remove (thread->appdomain_refs, thread->appdomain_refs->data);
2081                 LeaveCriticalSection (&threads_mutex);
2082         }
2083 }
2084
2085 gboolean
2086 mono_thread_has_appdomain_ref (MonoThread *thread, MonoDomain *domain)
2087 {
2088         gboolean res;
2089         EnterCriticalSection (&threads_mutex);
2090         res = g_slist_find (thread->appdomain_refs, domain) != NULL;
2091         LeaveCriticalSection (&threads_mutex);
2092         return res;
2093 }
2094
2095 typedef struct abort_appdomain_data {
2096         struct wait_data wait;
2097         MonoDomain *domain;
2098 } abort_appdomain_data;
2099
2100 static void
2101 abort_appdomain_thread (gpointer key, gpointer value, gpointer user_data)
2102 {
2103         MonoThread *thread = (MonoThread*)value;
2104         abort_appdomain_data *data = (abort_appdomain_data*)user_data;
2105         MonoDomain *domain = data->domain;
2106
2107         if (mono_thread_has_appdomain_ref (thread, domain)) {
2108                 HANDLE handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
2109                 if (handle == NULL)
2110                         return;
2111
2112                 /* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread->tid, domain->friendly_name); */
2113
2114                 ves_icall_System_Threading_Thread_Abort (thread, NULL);
2115
2116                 if(data->wait.num<MAXIMUM_WAIT_OBJECTS) {
2117                         data->wait.handles [data->wait.num] = handle;
2118                         data->wait.threads [data->wait.num] = thread;
2119                         data->wait.num++;
2120                 } else {
2121                         /* Just ignore the rest, we can't do anything with
2122                          * them yet
2123                          */
2124                 }
2125         }
2126 }
2127
2128 /*
2129  * mono_threads_abort_appdomain_threads:
2130  *
2131  *   Abort threads which has references to the given appdomain.
2132  */
2133 gboolean
2134 mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
2135 {
2136         abort_appdomain_data user_data;
2137         guint32 start_time;
2138
2139         /* printf ("ABORT BEGIN.\n"); */
2140
2141         start_time = GetTickCount ();
2142         do {
2143                 EnterCriticalSection (&threads_mutex);
2144
2145                 user_data.domain = domain;
2146                 user_data.wait.num = 0;
2147                 mono_g_hash_table_foreach (threads, abort_appdomain_thread, &user_data);
2148                 LeaveCriticalSection (&threads_mutex);
2149
2150                 if (user_data.wait.num > 0)
2151                         /*
2152                          * We should wait for the threads either to abort, or to leave the
2153                          * domain. We can't do the latter, so we wait with a timeout.
2154                          */
2155                         wait_for_tids (&user_data.wait, 100);
2156
2157                 /* Update remaining time */
2158                 timeout -= GetTickCount () - start_time;
2159                 start_time = GetTickCount ();
2160
2161                 if (timeout < 0)
2162                         return FALSE;
2163         }
2164         while (user_data.wait.num > 0);
2165
2166         /* printf ("ABORT DONE.\n"); */
2167
2168         return TRUE;
2169 }
2170
2171 static void
2172 clear_cached_culture (gpointer key, gpointer value, gpointer user_data)
2173 {
2174         MonoThread *thread = (MonoThread*)value;
2175         MonoDomain *domain = (MonoDomain*)user_data;
2176         int i;
2177
2178         /* No locking needed here */
2179
2180         if (thread->culture_info) {
2181                 for (i = 0; i < NUM_CACHED_CULTURES; ++i) {
2182                         if (thread->culture_info [i] && thread->culture_info [i]->vtable->domain == domain)
2183                                 thread->culture_info [i] = NULL;
2184                 }
2185         }
2186         if (thread->ui_culture_info) {
2187                 for (i = 0; i < NUM_CACHED_CULTURES; ++i) {
2188                         if (thread->ui_culture_info [i] && thread->ui_culture_info [i]->vtable->domain == domain)
2189                                 thread->ui_culture_info [i] = NULL;
2190                 }
2191         }
2192 }
2193         
2194 /*
2195  * mono_threads_clear_cached_culture:
2196  *
2197  *   Clear the cached_current_culture from all threads if it is in the
2198  * given appdomain.
2199  */
2200 void
2201 mono_threads_clear_cached_culture (MonoDomain *domain)
2202 {
2203         EnterCriticalSection (&threads_mutex);
2204         mono_g_hash_table_foreach (threads, clear_cached_culture, domain);
2205         LeaveCriticalSection (&threads_mutex);
2206 }
2207
2208 /*
2209  * mono_thread_get_pending_exception:
2210  *
2211  *   Return an exception which needs to be raised when leaving a catch clause.
2212  * This is used for undeniable exception propagation.
2213  */
2214 MonoException*
2215 mono_thread_get_pending_exception (void)
2216 {
2217         MonoThread *thread = mono_thread_current ();
2218
2219         MONO_ARCH_SAVE_REGS;
2220
2221         if (thread && thread->abort_exc && !is_running_protected_wrapper ()) {
2222                 /*
2223                  * FIXME: Clear the abort exception and return an AppDomainUnloaded 
2224                  * exception if the thread no longer references a dying appdomain.
2225                  */
2226                 thread->abort_exc->trace_ips = NULL;
2227                 thread->abort_exc->stack_trace = NULL;
2228                 return thread->abort_exc;
2229         }
2230
2231         return NULL;
2232 }
2233
2234 #define NUM_STATIC_DATA_IDX 8
2235 static const int static_data_size [NUM_STATIC_DATA_IDX] = {
2236         1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
2237 };
2238
2239
2240 /*
2241  *  mono_alloc_static_data
2242  *
2243  *   Allocate memory blocks for storing threads or context static data
2244  */
2245 static void 
2246 mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset)
2247 {
2248         guint idx = (offset >> 24) - 1;
2249         int i;
2250
2251         gpointer* static_data = *static_data_ptr;
2252         if (!static_data) {
2253                 static_data = mono_gc_alloc_fixed (static_data_size [0], NULL);
2254                 *static_data_ptr = static_data;
2255                 static_data [0] = static_data;
2256         }
2257
2258         for (i = 1; i <= idx; ++i) {
2259                 if (static_data [i])
2260                         continue;
2261                 static_data [i] = mono_gc_alloc_fixed (static_data_size [i], NULL);
2262         }
2263 }
2264
2265 /*
2266  *  mono_init_static_data_info
2267  *
2268  *   Initializes static data counters
2269  */
2270 static void mono_init_static_data_info (StaticDataInfo *static_data)
2271 {
2272         static_data->idx = 0;
2273         static_data->offset = 0;
2274 }
2275
2276 /*
2277  *  mono_alloc_static_data_slot
2278  *
2279  *   Generates an offset for static data. static_data contains the counters
2280  *  used to generate it.
2281  */
2282 static guint32
2283 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align)
2284 {
2285         guint32 offset;
2286
2287         if (!static_data->idx && !static_data->offset) {
2288                 /* 
2289                  * we use the first chunk of the first allocation also as
2290                  * an array for the rest of the data 
2291                  */
2292                 static_data->offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX;
2293         }
2294         static_data->offset += align - 1;
2295         static_data->offset &= ~(align - 1);
2296         if (static_data->offset + size >= static_data_size [static_data->idx]) {
2297                 static_data->idx ++;
2298                 g_assert (size <= static_data_size [static_data->idx]);
2299                 /* 
2300                  * massive unloading and reloading of domains with thread-static
2301                  * data may eventually exceed the allocated storage...
2302                  * Need to check what the MS runtime does in that case.
2303                  * Note that for each appdomain, we need to allocate a separate
2304                  * thread data slot for security reasons. We could keep track
2305                  * of the slots per-domain and when the domain is unloaded
2306                  * out the slots on a sort of free list.
2307                  */
2308                 g_assert (static_data->idx < NUM_STATIC_DATA_IDX);
2309                 static_data->offset = 0;
2310         }
2311         offset = static_data->offset | ((static_data->idx + 1) << 24);
2312         static_data->offset += size;
2313         return offset;
2314 }
2315
2316 /* 
2317  * ensure thread static fields already allocated are valid for thread
2318  * This function is called when a thread is created or on thread attach.
2319  */
2320 static void
2321 thread_adjust_static_data (MonoThread *thread)
2322 {
2323         guint32 offset;
2324
2325         EnterCriticalSection (&threads_mutex);
2326         if (thread_static_info.offset || thread_static_info.idx > 0) {
2327                 /* get the current allocated size */
2328                 offset = thread_static_info.offset | ((thread_static_info.idx + 1) << 24);
2329                 mono_alloc_static_data (&(thread->static_data), offset);
2330         }
2331         LeaveCriticalSection (&threads_mutex);
2332 }
2333
2334 static void 
2335 alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
2336 {
2337         MonoThread *thread = value;
2338         guint32 offset = GPOINTER_TO_UINT (user);
2339         
2340         mono_alloc_static_data (&(thread->static_data), offset);
2341 }
2342
2343 /*
2344  * The offset for a special static variable is composed of three parts:
2345  * a bit that indicates the type of static data (0:thread, 1:context),
2346  * an index in the array of chunks of memory for the thread (thread->static_data)
2347  * and an offset in that chunk of mem. This allows allocating less memory in the 
2348  * common case.
2349  */
2350
2351 guint32
2352 mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align)
2353 {
2354         guint32 offset;
2355         if (static_type == SPECIAL_STATIC_THREAD)
2356         {
2357                 EnterCriticalSection (&threads_mutex);
2358                 offset = mono_alloc_static_data_slot (&thread_static_info, size, align);
2359                 /* This can be called during startup */
2360                 if (threads != NULL)
2361                         mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
2362                 LeaveCriticalSection (&threads_mutex);
2363         }
2364         else
2365         {
2366                 g_assert (static_type == SPECIAL_STATIC_CONTEXT);
2367                 EnterCriticalSection (&contexts_mutex);
2368                 offset = mono_alloc_static_data_slot (&context_static_info, size, align);
2369                 LeaveCriticalSection (&contexts_mutex);
2370                 offset |= 0x80000000;   /* Set the high bit to indicate context static data */
2371         }
2372         return offset;
2373 }
2374
2375 gpointer
2376 mono_get_special_static_data (guint32 offset)
2377 {
2378         /* The high bit means either thread (0) or static (1) data. */
2379
2380         guint32 static_type = (offset & 0x80000000);
2381         int idx;
2382
2383         offset &= 0x7fffffff;
2384         idx = (offset >> 24) - 1;
2385
2386         if (static_type == 0)
2387         {
2388                 MonoThread *thread = mono_thread_current ();
2389                 return ((char*) thread->static_data [idx]) + (offset & 0xffffff);
2390         }
2391         else
2392         {
2393                 /* Allocate static data block under demand, since we don't have a list
2394                 // of contexts
2395                 */
2396                 MonoAppContext *context = mono_context_get ();
2397                 if (!context->static_data || !context->static_data [idx]) {
2398                         EnterCriticalSection (&contexts_mutex);
2399                         mono_alloc_static_data (&(context->static_data), offset);
2400                         LeaveCriticalSection (&contexts_mutex);
2401                 }
2402                 return ((char*) context->static_data [idx]) + (offset & 0xffffff);      
2403         }
2404 }
2405
2406 static void gc_stop_world (gpointer key, gpointer value, gpointer user)
2407 {
2408         MonoThread *thread=(MonoThread *)value;
2409
2410         LIBGC_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": %"G_GSIZE_FORMAT" - %"G_GSIZE_FORMAT, (gsize)user, (gsize)thread->tid));
2411
2412         if(thread->tid == (gsize)user)
2413                 return;
2414
2415         SuspendThread (thread->handle);
2416 }
2417
2418 void mono_gc_stop_world (void)
2419 {
2420         gsize self = GetCurrentThreadId ();
2421
2422         LIBGC_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": %"G_GSIZE_FORMAT" - %p", self, threads));
2423
2424         EnterCriticalSection (&threads_mutex);
2425
2426         if (threads != NULL)
2427                 mono_g_hash_table_foreach (threads, gc_stop_world, (gpointer)self);
2428         
2429         LeaveCriticalSection (&threads_mutex);
2430 }
2431
2432 static void gc_start_world (gpointer key, gpointer value, gpointer user)
2433 {
2434         MonoThread *thread=(MonoThread *)value;
2435         
2436         LIBGC_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": %"G_GSIZE_FORMAT" - %"G_GSIZE_FORMAT, (gsize)user, (gsize)thread->tid));
2437
2438         if(thread->tid == (gsize)user)
2439                 return;
2440
2441         ResumeThread (thread->handle);
2442 }
2443
2444 void mono_gc_start_world (void)
2445 {
2446         gsize self = GetCurrentThreadId ();
2447
2448         LIBGC_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": %"G_GSIZE_FORMAT" - %p", self, threads));
2449
2450         EnterCriticalSection (&threads_mutex);
2451
2452         if (threads != NULL)
2453                 mono_g_hash_table_foreach (threads, gc_start_world, (gpointer)self);
2454         
2455         LeaveCriticalSection (&threads_mutex);
2456 }
2457
2458 #ifdef __MINGW32__
2459 static CALLBACK void dummy_apc (ULONG_PTR param)
2460 {
2461 }
2462 #else
2463 static guint32 dummy_apc (gpointer param)
2464 {
2465         return 0;
2466 }
2467 #endif
2468
2469 /*
2470  * mono_thread_execute_interruption
2471  * 
2472  * Performs the operation that the requested thread state requires (abort,
2473  * suspend or stop)
2474  */
2475 static MonoException* mono_thread_execute_interruption (MonoThread *thread)
2476 {
2477         mono_monitor_enter (thread->synch_lock);
2478
2479         if (thread->interruption_requested) {
2480                 /* this will consume pending APC calls */
2481                 WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
2482                 InterlockedDecrement (&thread_interruption_requested);
2483                 thread->interruption_requested = FALSE;
2484         }
2485
2486         if ((thread->state & ThreadState_AbortRequested) != 0) {
2487                 if (thread->abort_exc == NULL)
2488                         thread->abort_exc = mono_get_exception_thread_abort ();
2489                 mono_monitor_exit (thread->synch_lock);
2490                 return thread->abort_exc;
2491         }
2492         else if ((thread->state & ThreadState_SuspendRequested) != 0) {
2493                 thread->state &= ~ThreadState_SuspendRequested;
2494                 thread->state |= ThreadState_Suspended;
2495                 thread->suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL);
2496                 if (thread->suspended_event)
2497                         SetEvent (thread->suspended_event);
2498                 mono_monitor_exit (thread->synch_lock);
2499                 
2500                 WaitForSingleObject (thread->suspend_event, INFINITE);
2501                 
2502                 mono_monitor_enter (thread->synch_lock);
2503                 CloseHandle (thread->suspend_event);
2504                 thread->suspend_event = NULL;
2505                 thread->state &= ~ThreadState_Suspended;
2506         
2507                 /* The thread that requested the resume will have replaced this event
2508                  * and will be waiting for it
2509                  */
2510                 SetEvent (thread->resume_event);
2511                 mono_monitor_exit (thread->synch_lock);
2512                 return NULL;
2513         }
2514         else if ((thread->state & ThreadState_StopRequested) != 0) {
2515                 /* FIXME: do this through the JIT? */
2516                 mono_monitor_exit (thread->synch_lock);
2517                 mono_thread_exit ();
2518                 return NULL;
2519         }
2520         
2521         mono_monitor_exit (thread->synch_lock);
2522         return NULL;
2523 }
2524
2525 /*
2526  * mono_thread_request_interruption
2527  *
2528  * A signal handler can call this method to request the interruption of a
2529  * thread. The result of the interruption will depend on the current state of
2530  * the thread. If the result is an exception that needs to be throw, it is 
2531  * provided as return value.
2532  */
2533 MonoException* mono_thread_request_interruption (gboolean running_managed)
2534 {
2535         MonoThread *thread = mono_thread_current ();
2536
2537         /* The thread may already be stopping */
2538         if (thread == NULL) 
2539                 return NULL;
2540         
2541         mono_monitor_enter (thread->synch_lock);
2542         
2543         if (thread->interruption_requested) {
2544                 mono_monitor_exit (thread->synch_lock);
2545                 return NULL;
2546         }
2547
2548         if (!running_managed || is_running_protected_wrapper ()) {
2549                 /* Can't stop while in unmanaged code. Increase the global interruption
2550                    request count. When exiting the unmanaged method the count will be
2551                    checked and the thread will be interrupted. */
2552
2553                 InterlockedIncrement (&thread_interruption_requested);
2554                 thread->interruption_requested = TRUE;
2555                 mono_monitor_exit (thread->synch_lock);
2556
2557                 /* this will awake the thread if it is in WaitForSingleObject 
2558                    or similar */
2559                 QueueUserAPC ((PAPCFUNC)dummy_apc, thread->handle, NULL);
2560                 return NULL;
2561         }
2562         else {
2563                 mono_monitor_exit (thread->synch_lock);
2564                 return mono_thread_execute_interruption (thread);
2565         }
2566 }
2567
2568 gboolean mono_thread_interruption_requested ()
2569 {
2570         if (thread_interruption_requested) {
2571                 MonoThread *thread = mono_thread_current ();
2572                 /* The thread may already be stopping */
2573                 if (thread != NULL) 
2574                         return (thread->interruption_requested);
2575         }
2576         return FALSE;
2577 }
2578
2579 static void mono_thread_interruption_checkpoint_request (gboolean bypass_abort_protection)
2580 {
2581         MonoThread *thread = mono_thread_current ();
2582
2583         /* The thread may already be stopping */
2584         if (thread == NULL)
2585                 return;
2586
2587         if (thread->interruption_requested && (bypass_abort_protection || !is_running_protected_wrapper ())) {
2588                 MonoException* exc = mono_thread_execute_interruption (thread);
2589                 if (exc) mono_raise_exception (exc);
2590         }
2591 }
2592
2593 /*
2594  * Performs the interruption of the current thread, if one has been requested,
2595  * and the thread is not running a protected wrapper.
2596  */
2597 void mono_thread_interruption_checkpoint ()
2598 {
2599         mono_thread_interruption_checkpoint_request (FALSE);
2600 }
2601
2602 /*
2603  * Performs the interruption of the current thread, if one has been requested.
2604  */
2605 void mono_thread_force_interruption_checkpoint ()
2606 {
2607         mono_thread_interruption_checkpoint_request (TRUE);
2608 }
2609
2610 /**
2611  * mono_thread_interruption_request_flag:
2612  *
2613  * Returns the address of a flag that will be non-zero if an interruption has
2614  * been requested for a thread. The thread to interrupt may not be the current
2615  * thread, so an additional call to mono_thread_interruption_requested() or
2616  * mono_thread_interruption_checkpoint() is allways needed if the flag is not
2617  * zero.
2618  */
2619 gint32* mono_thread_interruption_request_flag ()
2620 {
2621         return &thread_interruption_requested;
2622 }
2623
2624 #ifdef WITH_INCLUDED_LIBGC
2625
2626 static void gc_push_all_stacks (gpointer key, gpointer value, gpointer user)
2627 {
2628         MonoThread *thread=(MonoThread *)value;
2629         gsize *selfp = (gsize *)user, self = *selfp;
2630
2631         LIBGC_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": %"G_GSIZE_FORMAT" - %"G_GSIZE_FORMAT" - %p", self, (gsize)thread->tid, thread->stack_ptr));
2632
2633         if(thread->tid == self) {
2634                 LIBGC_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": %p - %p", selfp, thread->stack_ptr));
2635                 GC_push_all_stack (selfp, thread->stack_ptr);
2636                 return;
2637         }
2638
2639 #ifdef PLATFORM_WIN32
2640         GC_win32_push_thread_stack (thread->handle, thread->stack_ptr);
2641 #else
2642         mono_wapi_push_thread_stack (thread->handle, thread->stack_ptr);
2643 #endif
2644 }
2645
2646 void mono_gc_push_all_stacks (void)
2647 {
2648         gsize self = GetCurrentThreadId ();
2649
2650         LIBGC_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": %"G_GSIZE_FORMAT" - %p", self, threads));
2651
2652         EnterCriticalSection (&threads_mutex);
2653
2654         if (threads != NULL)
2655                 mono_g_hash_table_foreach (threads, gc_push_all_stacks, &self);
2656         
2657         LeaveCriticalSection (&threads_mutex);
2658 }
2659
2660 #endif /* WITH_INCLUDED_LIBGC */