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