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