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