Tue May 13 16:41:49 CEST 2003 Paolo Molaro <lupus@ximian.com>
[mono.git] / mono / metadata / threads.c
index 38a9825086ea2f3f9a1cf4fd5eeb86a81d4bd597..faa768f34e046894af69b80f5c4d9952c0444252 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Author:
  *     Dick Porter (dick@ximian.com)
+ *     Paolo Molaro (lupus@ximian.com)
  *     Patrik Torstensson (patrik.torstensson@labs2.com)
  *
  * (C) 2001 Ximian, Inc.
 #include <mono/metadata/threads-types.h>
 #include <mono/metadata/exception.h>
 #include <mono/metadata/environment.h>
+#include <mono/metadata/monitor.h>
 #include <mono/io-layer/io-layer.h>
 
 #include <mono/os/gc_wrapper.h>
 
 #undef THREAD_DEBUG
-#undef THREAD_LOCK_DEBUG
 #undef THREAD_WAIT_DEBUG
 
 struct StartInfo 
@@ -48,11 +49,6 @@ static MonoClassField *wait_handle_os_handle_field = NULL;
 /* Controls access to the 'threads' hash table */
 static CRITICAL_SECTION threads_mutex;
 
-/* Controls access to the sync field in MonoObjects, to avoid race
- * conditions when adding sync data to an object for the first time.
- */
-static CRITICAL_SECTION monitor_mutex;
-
 /* The hash of existing threads (key is thread ID) that need joining
  * before exit
  */
@@ -73,6 +69,8 @@ static MonoThreadCallbacks *mono_thread_callbacks = NULL;
 /* The TLS key that holds the LocalDataStoreSlot hash in each thread */
 static guint32 slothash_key;
 
+static void thread_adjust_static_data (MonoThread *thread);
+
 /* Spin lock for InterlockedXXX 64 bit functions */
 static CRITICAL_SECTION interlocked_mutex;
 
@@ -92,15 +90,6 @@ static void handle_store(MonoThread *thread)
                threads=mono_g_hash_table_new(g_direct_hash, g_direct_equal);
        }
 
-       /* GHashTable will remove a previous entry if a duplicate key
-        * is stored, which is exactly what we want: we store the
-        * thread both in the start_wrapper (in the subthread), and as
-        * soon as possible in the parent thread.  This is to minimise
-        * the window in which the thread exists but we haven't
-        * recorded it.
-        */
-       mono_g_hash_table_remove (threads, GUINT_TO_POINTER(thread->tid));
-       
        /* We don't need to duplicate thread->handle, because it is
         * only closed when the thread object is finalized by the GC.
         */
@@ -135,10 +124,14 @@ static void handle_remove(guint32 tid)
         */
 }
 
-static void thread_cleanup (guint32 tid)
+static void thread_cleanup (MonoThread *thread)
 {
-       mono_profiler_thread_end (tid);
-       handle_remove (tid);
+       mono_monitor_try_enter ((MonoObject *)thread, INFINITE);
+       thread->state |= ThreadState_Stopped;
+       mono_monitor_exit ((MonoObject *)thread);
+       
+       mono_profiler_thread_end (thread->tid);
+       handle_remove (thread->tid);
 }
 
 static guint32 start_wrapper(void *data)
@@ -147,9 +140,11 @@ static guint32 start_wrapper(void *data)
        guint32 (*start_func)(void *);
        void *this;
        guint32 tid;
+       MonoThread *thread=start_info->obj;
        
 #ifdef THREAD_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper");
+       g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper",
+                 GetCurrentThreadId ());
 #endif
        
        /* We can be sure start_info->obj->tid and
@@ -158,7 +153,7 @@ static guint32 start_wrapper(void *data)
         * thread resumed
         */
 
-       tid=start_info->obj->tid;
+       tid=thread->tid;
        
        mono_domain_set (start_info->domain);
 
@@ -170,14 +165,35 @@ static guint32 start_wrapper(void *data)
         * jit) sets the lmf marker.
         */
        mono_thread_new_init (tid, &tid, start_func);
+       thread->stack_ptr = &tid;
+
+#ifdef LIBGC_DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION
+                  ": (%d,%d) Setting thread stack to %p",
+                  GetCurrentThreadId (), getpid (), thread->stack_ptr);
+#endif
+
+#ifdef THREAD_DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION
+                  ": (%d) Setting current_object_key to %p",
+                  GetCurrentThreadId (), thread);
+#endif
 
-       TlsSetValue (current_object_key, start_info->obj);
-       handle_store(start_info->obj);
+       TlsSetValue (current_object_key, thread);
 
        mono_profiler_thread_start (tid);
 
+       if(thread->start_notify!=NULL) {
+               /* Let the thread that called Start() know we're
+                * ready
+                */
+               ReleaseSemaphore (thread->start_notify, 1, NULL);
+       }
+       
        g_free (start_info);
 
+       thread_adjust_static_data (thread);
+
        start_func (this);
 
        /* If the thread calls ExitThread at all, this remaining code
@@ -186,10 +202,11 @@ static guint32 start_wrapper(void *data)
         */
 
 #ifdef THREAD_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper terminating");
+       g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper terminating",
+                 GetCurrentThreadId ());
 #endif
        
-       thread_cleanup (tid);
+       thread_cleanup (thread);
        
        return(0);
 }
@@ -199,6 +216,9 @@ void mono_thread_new_init (guint32 tid, gpointer stack_start, gpointer func)
        if (mono_thread_start_cb) {
                mono_thread_start_cb (tid, stack_start, func);
        }
+
+       if (mono_thread_callbacks)
+               (* mono_thread_callbacks->thread_created) (tid, stack_start, func);
 }
 
 void mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
@@ -269,9 +289,17 @@ mono_thread_attach (MonoDomain *domain)
 
        handle_store(thread);
 
+#ifdef THREAD_DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION
+                  ": (%d) Setting current_object_key to %p",
+                  GetCurrentThreadId (), thread);
+#endif
+
        TlsSetValue (current_object_key, thread);
        mono_domain_set (domain);
 
+       thread_adjust_static_data (thread);
+
        if (mono_thread_attach_cb) {
                mono_thread_attach_cb (tid, &tid);
        }
@@ -315,6 +343,12 @@ HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this,
                start_info->obj = this;
                start_info->domain = mono_domain_get ();
 
+               this->start_notify=CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
+               if(this->start_notify==NULL) {
+                       g_warning (G_GNUC_PRETTY_FUNCTION ": CreateSemaphore error 0x%x", GetLastError ());
+                       return(NULL);
+               }
+
                thread=CreateThread(NULL, 0, start_wrapper, start_info,
                                    CREATE_SUSPENDED, &tid);
                if(thread==NULL) {
@@ -322,7 +356,7 @@ HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this,
                                  ": CreateThread error 0x%x", GetLastError());
                        return(NULL);
                }
-
+               
                this->handle=thread;
                this->tid=tid;
 
@@ -360,7 +394,8 @@ void ves_icall_System_Threading_Thread_Start_internal(MonoThread *this,
        MONO_ARCH_SAVE_REGS;
 
 #ifdef THREAD_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION ": Launching thread %p", this);
+       g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Launching thread %p (%d)",
+                 GetCurrentThreadId (), this, this->tid);
 #endif
 
        /* Only store the handle when the thread is about to be
@@ -376,6 +411,30 @@ void ves_icall_System_Threading_Thread_Start_internal(MonoThread *this,
 
        if (mono_thread_callbacks)
                (* mono_thread_callbacks->end_resume) (this->tid);
+
+       if(this->start_notify!=NULL) {
+               /* Wait for the thread to set up its TLS data etc, so
+                * theres no potential race condition if someone tries
+                * to look up the data believing the thread has
+                * started
+                */
+
+#ifdef THREAD_DEBUG
+               g_message(G_GNUC_PRETTY_FUNCTION
+                         ": (%d) waiting for thread %p (%d) to start",
+                         GetCurrentThreadId (), this, this->tid);
+#endif
+
+               WaitForSingleObject (this->start_notify, INFINITE);
+               CloseHandle (this->start_notify);
+               this->start_notify=NULL;
+       }
+
+#ifdef THREAD_DEBUG
+       g_message(G_GNUC_PRETTY_FUNCTION
+                 ": (%d) Done launching thread %p (%d)",
+                 GetCurrentThreadId (), this, this->tid);
+#endif
 }
 
 void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
@@ -472,412 +531,6 @@ MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void)
        return(data);
 }
 
-static void mon_finalize (void *o, void *unused)
-{
-       MonoThreadsSync *mon=(MonoThreadsSync *)o;
-       
-#ifdef THREAD_LOCK_DEBUG
-       g_message (G_GNUC_PRETTY_FUNCTION ": Finalizing sync");
-#endif
-       
-       CloseHandle (mon->monitor);
-       CloseHandle (mon->sema);
-       CloseHandle (mon->waiters_done);
-       DeleteCriticalSection (&mon->waiters_count_lock);
-}
-
-static MonoThreadsSync *mon_new(void)
-{
-       MonoThreadsSync *new;
-       
-#if HAVE_BOEHM_GC
-       new=(MonoThreadsSync *)GC_MALLOC (sizeof(MonoThreadsSync));
-       GC_REGISTER_FINALIZER (new, mon_finalize, NULL, NULL, NULL);
-#else
-       /* This should be freed when the object that owns it is
-        * deleted
-        */
-       new=(MonoThreadsSync *)g_new0 (MonoThreadsSync, 1);
-#endif
-       
-       new->monitor=CreateMutex(NULL, FALSE, NULL);
-       if(new->monitor==NULL) {
-               /* Throw some sort of system exception? (ditto for the
-                * sem and event handles below)
-                */
-       }
-
-       new->waiters_count=0;
-       new->was_broadcast=FALSE;
-       InitializeCriticalSection(&new->waiters_count_lock);
-       new->sema=CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
-       new->waiters_done=CreateEvent(NULL, FALSE, FALSE, NULL);
-       
-#ifdef THREAD_LOCK_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION ": (%d) ThreadsSync %p mutex created: %p, sem: %p, event: %p", GetCurrentThreadId (), new, new->monitor, new->sema, new->waiters_done);
-#endif
-       
-       return(new);
-}
-
-gboolean ves_icall_System_Threading_Monitor_Monitor_try_enter(MonoObject *obj,
-                                                             int ms)
-{
-       MonoThreadsSync *mon;
-       guint32 ret;
-       
-       MONO_ARCH_SAVE_REGS;
-
-#ifdef THREAD_LOCK_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION
-                 ": (%d) Trying to lock object %p", GetCurrentThreadId(),
-                 obj);
-#endif
-
-       EnterCriticalSection(&monitor_mutex);
-
-       mon=obj->synchronisation;
-       if(mon==NULL) {
-               mon=mon_new();
-               obj->synchronisation=mon;
-       }
-       
-       /* Don't hold the monitor lock while waiting to acquire the
-        * object lock
-        */
-       LeaveCriticalSection(&monitor_mutex);
-       
-       /* Acquire the mutex */
-#ifdef THREAD_LOCK_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Acquiring monitor mutex %p",
-                 GetCurrentThreadId (), mon->monitor);
-#endif
-       ret=WaitForSingleObject(mon->monitor, ms);
-       if(ret==WAIT_OBJECT_0) {
-               mon->count++;
-               mon->tid=GetCurrentThreadId();
-       
-#ifdef THREAD_LOCK_DEBUG
-               g_message(G_GNUC_PRETTY_FUNCTION
-                         ": (%d) object %p now locked %d times",
-                         GetCurrentThreadId (), obj, mon->count);
-#endif
-
-               return(TRUE);
-       }
-
-       return(FALSE);
-}
-
-void ves_icall_System_Threading_Monitor_Monitor_exit(MonoObject *obj)
-{
-       MonoThreadsSync *mon;
-       
-       MONO_ARCH_SAVE_REGS;
-
-#ifdef THREAD_LOCK_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Unlocking %p",
-                 GetCurrentThreadId (), obj);
-#endif
-
-       /* No need to lock monitor_mutex here because we only adjust
-        * the monitor state if this thread already owns the lock
-        */
-       mon=obj->synchronisation;
-
-       if(mon==NULL) {
-               return;
-       }
-
-       if(mon->tid!=GetCurrentThreadId()) {
-               return;
-       }
-       
-       mon->count--;
-       
-#ifdef THREAD_LOCK_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %p now locked %d times",
-                 GetCurrentThreadId (), obj, mon->count);
-#endif
-
-       if(mon->count==0) {
-               mon->tid=0;     /* FIXME: check that 0 isnt a valid id */
-       }
-       
-#ifdef THREAD_LOCK_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Releasing mutex %p",
-                 GetCurrentThreadId (), mon->monitor);
-#endif
-
-       ReleaseMutex(mon->monitor);
-}
-
-gboolean ves_icall_System_Threading_Monitor_Monitor_test_owner(MonoObject *obj)
-{
-       MonoThreadsSync *mon;
-       gboolean ret=FALSE;
-       
-       MONO_ARCH_SAVE_REGS;
-
-#ifdef THREAD_LOCK_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION
-                 ": Testing if %p is owned by thread %d", obj,
-                 GetCurrentThreadId());
-#endif
-
-       EnterCriticalSection(&monitor_mutex);
-       
-       mon=obj->synchronisation;
-       if(mon==NULL) {
-               goto finished;
-       }
-
-       if(mon->tid!=GetCurrentThreadId()) {
-#ifdef THREAD_LOCK_DEBUG
-               g_message (G_GNUC_PRETTY_FUNCTION
-                          ": (%d) object %p is owned by thread %d",
-                          GetCurrentThreadId (), obj, mon->tid);
-#endif
-
-               goto finished;
-       }
-       
-       ret=TRUE;
-       
-finished:
-       LeaveCriticalSection(&monitor_mutex);
-
-       return(ret);
-}
-
-gboolean ves_icall_System_Threading_Monitor_Monitor_test_synchronised(MonoObject *obj)
-{
-       MonoThreadsSync *mon;
-       gboolean ret=FALSE;
-       
-       MONO_ARCH_SAVE_REGS;
-
-#ifdef THREAD_LOCK_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION
-                 ": (%d) Testing if %p is owned by any thread",
-                 GetCurrentThreadId (), obj);
-#endif
-
-       EnterCriticalSection(&monitor_mutex);
-       
-       mon=obj->synchronisation;
-       if(mon==NULL) {
-               goto finished;
-       }
-
-       if(mon->tid==0) {
-               goto finished;
-       }
-       
-       g_assert(mon->count);
-       
-       ret=TRUE;
-       
-finished:
-       LeaveCriticalSection(&monitor_mutex);
-
-       return(ret);
-}
-
-       
-void ves_icall_System_Threading_Monitor_Monitor_pulse(MonoObject *obj)
-{
-       gboolean have_waiters;
-       MonoThreadsSync *mon;
-       
-       MONO_ARCH_SAVE_REGS;
-
-#ifdef THREAD_LOCK_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION "(%d) Pulsing %p",
-                 GetCurrentThreadId (), obj);
-#endif
-
-       EnterCriticalSection(&monitor_mutex);
-       
-       mon=obj->synchronisation;
-       if(mon==NULL) {
-#ifdef THREAD_LOCK_DEBUG
-               g_message (G_GNUC_PRETTY_FUNCTION
-                          "(%d) object %p not locked", GetCurrentThreadId (),
-                          obj);
-#endif
-
-               LeaveCriticalSection(&monitor_mutex);
-               return;
-       }
-
-       if(mon->tid!=GetCurrentThreadId()) {
-#ifdef THREAD_LOCK_DEBUG
-               g_message (G_GNUC_PRETTY_FUNCTION
-                          "(%d) doesn't own lock (owned by %d)",
-                          GetCurrentThreadId (), mon->tid);
-#endif
-
-               LeaveCriticalSection(&monitor_mutex);
-               return;
-       }
-       LeaveCriticalSection(&monitor_mutex);
-       
-       EnterCriticalSection(&mon->waiters_count_lock);
-       have_waiters=(mon->waiters_count>0);
-       LeaveCriticalSection(&mon->waiters_count_lock);
-       
-       if(have_waiters==TRUE) {
-               ReleaseSemaphore(mon->sema, 1, 0);
-       }
-}
-
-void ves_icall_System_Threading_Monitor_Monitor_pulse_all(MonoObject *obj)
-{
-       gboolean have_waiters=FALSE;
-       MonoThreadsSync *mon;
-       
-       MONO_ARCH_SAVE_REGS;
-
-#ifdef THREAD_LOCK_DEBUG
-       g_message("(%d) Pulsing all %p", GetCurrentThreadId (), obj);
-#endif
-
-       EnterCriticalSection(&monitor_mutex);
-       
-       mon=obj->synchronisation;
-       if(mon==NULL) {
-               LeaveCriticalSection(&monitor_mutex);
-               return;
-       }
-
-       if(mon->tid!=GetCurrentThreadId()) {
-               LeaveCriticalSection(&monitor_mutex);
-               return;
-       }
-       LeaveCriticalSection(&monitor_mutex);
-       
-       EnterCriticalSection(&mon->waiters_count_lock);
-       if(mon->waiters_count>0) {
-               mon->was_broadcast=TRUE;
-               have_waiters=TRUE;
-       }
-       
-       if(have_waiters==TRUE) {
-               ReleaseSemaphore(mon->sema, mon->waiters_count, 0);
-               
-               LeaveCriticalSection(&mon->waiters_count_lock);
-               
-               WaitForSingleObject(mon->waiters_done, INFINITE);
-               mon->was_broadcast=FALSE;
-       } else {
-               LeaveCriticalSection(&mon->waiters_count_lock);
-       }
-}
-
-gboolean ves_icall_System_Threading_Monitor_Monitor_wait(MonoObject *obj,
-                                                        int ms)
-{
-       gboolean last_waiter;
-       MonoThreadsSync *mon;
-       guint32 save_count;
-       
-       MONO_ARCH_SAVE_REGS;
-
-#ifdef THREAD_LOCK_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION
-                 "(%d) Trying to wait for %p with timeout %dms",
-                 GetCurrentThreadId (), obj, ms);
-#endif
-
-       EnterCriticalSection(&monitor_mutex);
-       
-       mon=obj->synchronisation;
-       if(mon==NULL) {
-               LeaveCriticalSection(&monitor_mutex);
-               return(FALSE);
-       }
-
-       if(mon->tid!=GetCurrentThreadId()) {
-               LeaveCriticalSection(&monitor_mutex);
-               return(FALSE);
-       }
-       LeaveCriticalSection(&monitor_mutex);
-       
-       EnterCriticalSection(&mon->waiters_count_lock);
-       mon->waiters_count++;
-       LeaveCriticalSection(&mon->waiters_count_lock);
-       
-#ifdef THREAD_LOCK_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %p locked %d times",
-                 GetCurrentThreadId (), obj, mon->count);
-#endif
-
-       /* We need to put the lock count back afterwards */
-       save_count=mon->count;
-       
-       while(mon->count>1) {
-#ifdef THREAD_LOCK_DEBUG
-               g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Releasing mutex %p",
-                         GetCurrentThreadId (), mon->monitor);
-#endif
-
-               ReleaseMutex(mon->monitor);
-               mon->count--;
-       }
-       
-       /* We're releasing this mutex */
-       mon->count=0;
-       mon->tid=0;
-#ifdef THREAD_LOCK_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Signalling monitor mutex %p",
-                 GetCurrentThreadId (), mon->monitor);
-#endif
-
-       SignalObjectAndWait(mon->monitor, mon->sema, INFINITE, FALSE);
-       
-       EnterCriticalSection(&mon->waiters_count_lock);
-       mon->waiters_count++;
-       last_waiter=mon->was_broadcast && mon->waiters_count==0;
-       LeaveCriticalSection(&mon->waiters_count_lock);
-       
-       if(last_waiter) {
-#ifdef THREAD_LOCK_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Waiting for monitor mutex %p",
-                 GetCurrentThreadId (), mon->monitor);
-#endif
-               SignalObjectAndWait(mon->waiters_done, mon->monitor, INFINITE, FALSE);
-       } else {
-#ifdef THREAD_LOCK_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Waiting for monitor mutex %p",
-                 GetCurrentThreadId (), mon->monitor);
-#endif
-               WaitForSingleObject(mon->monitor, INFINITE);
-       }
-
-       /* We've reclaimed this mutex */
-       mon->count=save_count;
-       mon->tid=GetCurrentThreadId();
-
-       /* Lock the mutex the required number of times */
-       while(save_count>1) {
-#ifdef THREAD_LOCK_DEBUG
-               g_message(G_GNUC_PRETTY_FUNCTION
-                         ": (%d) Waiting for monitor mutex %p",
-                         GetCurrentThreadId (), mon->monitor);
-#endif
-               WaitForSingleObject(mon->monitor, INFINITE);
-               save_count--;
-       }
-       
-#ifdef THREAD_LOCK_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %p still locked %d times",
-                 GetCurrentThreadId (), obj, mon->count);
-#endif
-       
-       return(TRUE);
-}
-
 /* FIXME: exitContext isnt documented */
 gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
 {
@@ -1177,6 +830,12 @@ ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state)
        thread->abort_state = state;
        thread->abort_exc = mono_get_exception_thread_abort ();
 
+#ifdef THREAD_DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION
+                  ": (%d) Abort requested for %p (%d)", GetCurrentThreadId (),
+                  thread, thread->tid);
+#endif
+       
 #ifdef __MINGW32__
        g_assert_not_reached ();
 #else
@@ -1209,7 +868,6 @@ void mono_thread_init (MonoThreadStartCB start_cb,
                       MonoThreadAttachCB attach_cb)
 {
        InitializeCriticalSection(&threads_mutex);
-       InitializeCriticalSection(&monitor_mutex);
        InitializeCriticalSection(&interlocked_mutex);
        
        current_object_key=TlsAlloc();
@@ -1288,7 +946,7 @@ static void wait_for_tids (struct wait_data *wait)
                        g_message (G_GNUC_PRETTY_FUNCTION
                                   ": cleaning up after thread %d", tid);
 #endif
-                       thread_cleanup (tid);
+                       thread_cleanup (wait->threads[i]);
                }
        }
 }
@@ -1299,14 +957,12 @@ static void build_wait_tids (gpointer key, gpointer value, gpointer user)
 
        if(wait->num<MAXIMUM_WAIT_OBJECTS) {
                MonoThread *thread=(MonoThread *)value;
+
+               /* BUG: For now we just ignore background threads, we should abort them
+               */
+               if (thread->state & ThreadState_Background)
+                       return; /* just leave, ignore */
                
-               /* Theres a theoretical chance that thread->handle
-                * might be NULL if the child thread has called
-                * handle_store() but the parent thread hasn't set the
-                * handle pointer yet.  WaitForMultipleObjects will
-                * fail, and we'll just go round the loop again.  By
-                * that time the handle should be stored properly.
-                */
                wait->handles[wait->num]=thread->handle;
                wait->threads[wait->num]=thread;
                wait->num++;
@@ -1384,3 +1040,216 @@ void mono_thread_abort_all_other_threads (void)
        
        LeaveCriticalSection (&threads_mutex);
 }
+
+static int static_data_idx = 0;
+static int static_data_offset = 0;
+#define NUM_STATIC_DATA_IDX 8
+static const int static_data_size [NUM_STATIC_DATA_IDX] = {
+       1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216
+};
+
+static void 
+thread_alloc_static_data (MonoThread *thread, guint32 offset)
+{
+       guint idx = (offset >> 24) - 1;
+       int i;
+
+       if (!thread->static_data) {
+               thread->static_data = GC_MALLOC (static_data_size [0]);
+               thread->static_data [0] = thread->static_data;
+       }
+       for (i = 1; i < idx; ++i) {
+               if (thread->static_data [i])
+                       continue;
+               thread->static_data [i] = GC_MALLOC (static_data_size [i]);
+       }
+       
+}
+
+/* 
+ * ensure thread static fields already allocated are valid for thread
+ * This function is called when a thread is created or on thread attach.
+ */
+static void
+thread_adjust_static_data (MonoThread *thread)
+{
+       guint32 offset;
+
+       EnterCriticalSection (&threads_mutex);
+       if (static_data_offset || static_data_idx > 0) {
+               /* get the current allocated size */
+               offset = static_data_offset | ((static_data_idx + 1) << 24);
+               thread_alloc_static_data (thread, offset);
+       }
+       LeaveCriticalSection (&threads_mutex);
+}
+
+static void 
+alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
+{
+       MonoThread *thread = value;
+       guint32 offset = GPOINTER_TO_UINT (user);
+       
+       thread_alloc_static_data (thread, offset);
+}
+
+/*
+ * The offset for a thread static variable is composed of two parts:
+ * an index in the array of chunks of memory for the thread (thread->static_data)
+ * and an offset in that chunk of mem. This allows allocating less memory in the 
+ * common case.
+ */
+guint32
+mono_threads_alloc_static_data (guint32 size, guint32 align)
+{
+       guint32 offset;
+       
+       EnterCriticalSection (&threads_mutex);
+
+       if (!static_data_idx && !static_data_offset) {
+               /* 
+                * we use the first chunk of the first allocation also as
+                * an array for the rest of the data 
+                */
+               static_data_offset = sizeof (gpointer) * NUM_STATIC_DATA_IDX;
+       }
+       static_data_offset += align - 1;
+       static_data_offset &= ~(align - 1);
+       if (static_data_offset + size >= static_data_size [static_data_idx]) {
+               static_data_idx ++;
+               g_assert (size <= static_data_size [static_data_idx]);
+               /* 
+                * massive unloading and reloading of domains with thread-static
+                * data may eventually exceed the allocated storage...
+                * Need to check what the MS runtime does in that case.
+                * Note that for each appdomain, we need to allocate a separate
+                * thread data slot for security reasons. We could keep track
+                * of the slots per-domain and when the domain is unloaded
+                * out the slots on a sort of free list.
+                */
+               g_assert (static_data_idx < NUM_STATIC_DATA_IDX);
+               static_data_offset = 0;
+       }
+       offset = static_data_offset | ((static_data_idx + 1) << 24);
+       static_data_offset += size;
+       
+       mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
+
+       LeaveCriticalSection (&threads_mutex);
+       return offset;
+}
+
+gpointer
+mono_threads_get_static_data (guint32 offset)
+{
+       MonoThread *thread = mono_thread_current ();
+       int idx = offset >> 24;
+       
+       return ((char*) thread->static_data [idx - 1]) + (offset & 0xffffff);
+}
+
+#ifdef WITH_INCLUDED_LIBGC
+
+static void gc_stop_world (gpointer key, gpointer value, gpointer user)
+{
+       MonoThread *thread=(MonoThread *)value;
+       guint32 self=GPOINTER_TO_UINT (user);
+
+#ifdef LIBGC_DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d", self, thread->tid);
+#endif
+       
+       if(thread->tid==self)
+               return;
+
+       SuspendThread (thread->handle);
+}
+
+void mono_gc_stop_world (void)
+{
+       guint32 self=GetCurrentThreadId ();
+
+#ifdef LIBGC_DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
+#endif
+
+       EnterCriticalSection (&threads_mutex);
+
+       if (threads != NULL)
+               mono_g_hash_table_foreach (threads, gc_stop_world, GUINT_TO_POINTER (self));
+       
+       LeaveCriticalSection (&threads_mutex);
+}
+
+static void gc_start_world (gpointer key, gpointer value, gpointer user)
+{
+       MonoThread *thread=(MonoThread *)value;
+       guint32 self=GPOINTER_TO_UINT (user);
+       
+#ifdef LIBGC_DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d", self, thread->tid);
+#endif
+       
+       if(thread->tid==self)
+               return;
+
+       ResumeThread (thread->handle);
+}
+
+void mono_gc_start_world (void)
+{
+       guint32 self=GetCurrentThreadId ();
+
+#ifdef LIBGC_DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
+#endif
+
+       EnterCriticalSection (&threads_mutex);
+
+       if (threads != NULL)
+               mono_g_hash_table_foreach (threads, gc_start_world, GUINT_TO_POINTER (self));
+       
+       LeaveCriticalSection (&threads_mutex);
+}
+
+static void gc_push_all_stacks (gpointer key, gpointer value, gpointer user)
+{
+       MonoThread *thread=(MonoThread *)value;
+       guint32 *selfp=(guint32 *)user, self = *selfp;
+
+#ifdef LIBGC_DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d - %p", self, thread->tid, thread->stack_ptr);
+#endif
+       
+       if(thread->tid==self) {
+#ifdef LIBGC_DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": %p - %p", selfp, thread->stack_ptr);
+#endif
+               GC_push_all_stack (selfp, thread->stack_ptr);
+               return;
+       }
+
+#ifdef PLATFORM_WIN32
+       GC_win32_push_thread_stack (thread->handle, thread->stack_ptr);
+#else
+       mono_wapi_push_thread_stack (thread->handle, thread->stack_ptr);
+#endif
+}
+
+void mono_gc_push_all_stacks (void)
+{
+       guint32 self=GetCurrentThreadId ();
+
+#ifdef LIBGC_DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads);
+#endif
+
+       EnterCriticalSection (&threads_mutex);
+
+       if (threads != NULL)
+               mono_g_hash_table_foreach (threads, gc_push_all_stacks, &self);
+       
+       LeaveCriticalSection (&threads_mutex);
+}
+
+#endif /* WITH_INCLUDED_LIBGC */