2005-02-05 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / io-layer / threads.c
index 725b5b1fdbb490216b4334166525ed81a1fe922c..f464f0b94230e4f83d2dcb43c2636bb23e2aa0d9 100644 (file)
 #include <mono/os/gc_wrapper.h>
 #include "mono/utils/mono-hash.h"
 #endif
+#include <stdio.h>
 #include <glib.h>
 #include <string.h>
 #include <pthread.h>
+#include <signal.h>
 #include <sched.h>
 #include <sys/time.h>
 #include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include <mono/io-layer/wapi.h>
 #include <mono/io-layer/wapi-private.h>
 #include <mono/io-layer/mono-mutex.h>
 #include <mono/io-layer/thread-private.h>
 #include <mono/io-layer/mono-spinlock.h>
+#include <mono/io-layer/mutex-private.h>
+
+#if HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
 
 #undef DEBUG
 #undef TLS_DEBUG
-#undef TLS_PTHREAD_MUTEX
 
 
 /* Hash threads with tids. I thought of using TLS for this, but that
@@ -57,10 +65,18 @@ struct _WapiHandleOps _wapi_thread_ops = {
 
 static mono_once_t thread_ops_once=MONO_ONCE_INIT;
 
+#ifdef WITH_INCLUDED_LIBGC
+static void gc_init (void);
+#endif
+
 static void thread_ops_init (void)
 {
        _wapi_handle_register_capabilities (WAPI_HANDLE_THREAD,
                                            WAPI_HANDLE_CAP_WAIT);
+
+#ifdef WITH_INCLUDED_LIBGC
+       gc_init ();
+#endif
 }
 
 static void thread_close_private (gpointer handle)
@@ -83,9 +99,7 @@ static void thread_close_private (gpointer handle)
                  thread_handle->thread->id);
 #endif
 
-       if(thread_handle->thread!=NULL) {
-               _wapi_timed_thread_destroy (thread_handle->thread);
-       }
+       thread_handle->thread=NULL;
 }
 
 static void thread_own (gpointer handle)
@@ -115,6 +129,7 @@ static void thread_exit(guint32 exitstatus, gpointer handle)
        struct _WapiHandle_thread *thread_handle;
        struct _WapiHandlePrivate_thread *thread_private_handle;
        gboolean ok;
+       int thr_ret;
        
        ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
                                (gpointer *)&thread_handle,
@@ -124,8 +139,14 @@ static void thread_exit(guint32 exitstatus, gpointer handle)
                           ": error looking up thread handle %p", handle);
                return;
        }
+       
+       _wapi_mutex_check_abandoned (getpid (),
+                                    thread_private_handle->thread->id);
 
-       _wapi_handle_lock_handle (handle);
+       pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
+                             handle);
+       thr_ret = _wapi_handle_lock_handle (handle);
+       g_assert (thr_ret == 0);
 
 #ifdef DEBUG
        g_message (G_GNUC_PRETTY_FUNCTION
@@ -136,7 +157,9 @@ static void thread_exit(guint32 exitstatus, gpointer handle)
        thread_handle->state=THREAD_STATE_EXITED;
        _wapi_handle_set_signal_state (handle, TRUE, TRUE);
 
-       _wapi_handle_unlock_handle (handle);
+       thr_ret = _wapi_handle_unlock_handle (handle);
+       g_assert (thr_ret == 0);
+       pthread_cleanup_pop (0);
        
 #ifdef DEBUG
        g_message(G_GNUC_PRETTY_FUNCTION
@@ -145,9 +168,16 @@ static void thread_exit(guint32 exitstatus, gpointer handle)
 #endif
 
        /* Remove this thread from the hash */
-       mono_mutex_lock(&thread_hash_mutex);
+       pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
+                             (void *)&thread_hash_mutex);
+       thr_ret = mono_mutex_lock(&thread_hash_mutex);
+       g_assert (thr_ret == 0);
+       
        g_hash_table_remove(thread_hash, &thread_private_handle->thread->id);
-       mono_mutex_unlock(&thread_hash_mutex);
+
+       thr_ret = mono_mutex_unlock(&thread_hash_mutex);
+       g_assert (thr_ret == 0);
+       pthread_cleanup_pop (0);
 
        /* The thread is no longer active, so unref it */
        _wapi_handle_unref (handle);
@@ -174,15 +204,19 @@ static void thread_hash_init(void)
  *
  * Return value: a new handle, or NULL
  */
-gpointer CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 stacksize G_GNUC_UNUSED,
+gpointer CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 stacksize,
                      WapiThreadStart start, gpointer param, guint32 create,
                      guint32 *tid) 
 {
        struct _WapiHandle_thread *thread_handle;
        struct _WapiHandlePrivate_thread *thread_private_handle;
+       pthread_attr_t attr;
        gpointer handle;
        gboolean ok;
        int ret;
+       int thr_ret;
+       int i, unrefs = 0;
+       gpointer ct_ret = NULL;
        
        mono_once(&thread_hash_once, thread_hash_init);
        mono_once (&thread_ops_once, thread_ops_init);
@@ -198,7 +232,10 @@ gpointer CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 st
                return(NULL);
        }
 
-       _wapi_handle_lock_handle (handle);
+       pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
+                             handle);
+       thr_ret = _wapi_handle_lock_handle (handle);
+       g_assert (thr_ret == 0);
        
        ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
                                (gpointer *)&thread_handle,
@@ -206,8 +243,7 @@ gpointer CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 st
        if(ok==FALSE) {
                g_warning (G_GNUC_PRETTY_FUNCTION
                           ": error looking up thread handle %p", handle);
-               _wapi_handle_unlock_handle (handle);
-               return(NULL);
+               goto cleanup;
        }
 
        /* Hold a reference while the thread is active, because we use
@@ -220,9 +256,38 @@ gpointer CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 st
        /* Lock around the thread create, so that the new thread cant
         * race us to look up the thread handle in GetCurrentThread()
         */
-       mono_mutex_lock(&thread_hash_mutex);
+       pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
+                             (void *)&thread_hash_mutex);
+       thr_ret = mono_mutex_lock(&thread_hash_mutex);
+       g_assert (thr_ret == 0);
+       
+       /* Set a 2M stack size.  This is the default on Linux, but BSD
+        * needs it.  (The original bug report from Martin Dvorak <md@9ll.cz>
+        * set the size to 2M-4k.  I don't know why it's short by 4k, so
+        * I'm leaving it as 2M until I'm told differently.)
+        */
+       thr_ret = pthread_attr_init(&attr);
+       g_assert (thr_ret == 0);
        
-       ret=_wapi_timed_thread_create(&thread_private_handle->thread, NULL,
+       /* defaults of 2Mb for 32bits and 4Mb for 64bits */
+       if (stacksize == 0){
+#if HAVE_VALGRIND_MEMCHECK_H
+               if (RUNNING_ON_VALGRIND)
+                       stacksize = 1 << 20;
+               else
+                       stacksize = (SIZEOF_VOID_P / 2) * 1024 * 1024;
+#else
+               stacksize = (SIZEOF_VOID_P / 2) * 1024 * 1024;
+#endif
+               
+       }
+
+#ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
+       thr_ret = pthread_attr_setstacksize(&attr, stacksize);
+       g_assert (thr_ret == 0);
+#endif
+
+       ret=_wapi_timed_thread_create(&thread_private_handle->thread, &attr,
                                      create, start, thread_exit, param,
                                      handle);
        if(ret!=0) {
@@ -230,18 +295,14 @@ gpointer CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 st
                g_message(G_GNUC_PRETTY_FUNCTION ": Thread create error: %s",
                          strerror(ret));
 #endif
-               mono_mutex_unlock(&thread_hash_mutex);
-               _wapi_handle_unlock_handle (handle);
-               _wapi_handle_unref (handle);
-               
-               /* And again, because of the reference we took above */
-               _wapi_handle_unref (handle);
-               return(NULL);
+               /* Two, because of the reference we took above */
+               unrefs = 2;
+               goto thread_hash_cleanup;
        }
-
+       ct_ret = handle;
+       
        g_hash_table_insert(thread_hash, &thread_private_handle->thread->id,
                            handle);
-       mono_mutex_unlock(&thread_hash_mutex);
        
 #ifdef DEBUG
        g_message(G_GNUC_PRETTY_FUNCTION
@@ -258,23 +319,48 @@ gpointer CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 st
 #endif
        }
 
-       _wapi_handle_unlock_handle (handle);
+thread_hash_cleanup:
+       thr_ret = mono_mutex_unlock (&thread_hash_mutex);
+       g_assert (thr_ret == 0);
+       pthread_cleanup_pop (0);
        
-       return(handle);
+cleanup:
+       thr_ret = _wapi_handle_unlock_handle (handle);
+       g_assert (thr_ret == 0);
+       pthread_cleanup_pop (0);
+       
+       /* Must not call _wapi_handle_unref() with the handle already
+        * locked
+        */
+       for (i = 0; i < unrefs; i++) {
+               _wapi_handle_unref (handle);
+       }
+
+       return(ct_ret);
 }
 
 gpointer OpenThread (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 tid)
 {
        gpointer ret=NULL;
+       int thr_ret;
+       
+       mono_once(&thread_hash_once, thread_hash_init);
+       mono_once (&thread_ops_once, thread_ops_init);
        
 #ifdef DEBUG
        g_message (G_GNUC_PRETTY_FUNCTION ": looking up thread %d", tid);
 #endif
 
-       mono_mutex_lock(&thread_hash_mutex);
+       pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
+                             (void *)&thread_hash_mutex);
+       thr_ret = mono_mutex_lock(&thread_hash_mutex);
+       g_assert (thr_ret == 0);
        
        ret=g_hash_table_lookup(thread_hash, &tid);
-       mono_mutex_unlock(&thread_hash_mutex);
+
+       thr_ret = mono_mutex_unlock(&thread_hash_mutex);
+       g_assert (thr_ret == 0);
+       pthread_cleanup_pop (0);
        
        if(ret!=NULL) {
                _wapi_handle_ref (ret);
@@ -298,10 +384,6 @@ gpointer OpenThread (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSE
  */
 void ExitThread(guint32 exitcode)
 {
-       /* No thread created yet.  */
-       if (thread_hash == NULL)
-               exit(exitcode);
-
        _wapi_timed_thread_exit(exitcode);
 }
 
@@ -378,6 +460,108 @@ guint32 GetCurrentThreadId(void)
 #endif
 }
 
+static gpointer thread_attach(guint32 *tid)
+{
+       struct _WapiHandle_thread *thread_handle;
+       struct _WapiHandlePrivate_thread *thread_private_handle;
+       gpointer handle;
+       gboolean ok;
+       int ret;
+       int thr_ret;
+       int i, unrefs = 0;
+       gpointer ta_ret = NULL;
+       
+       mono_once(&thread_hash_once, thread_hash_init);
+       mono_once (&thread_ops_once, thread_ops_init);
+
+       handle=_wapi_handle_new (WAPI_HANDLE_THREAD);
+       if(handle==_WAPI_HANDLE_INVALID) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error creating thread handle");
+               return(NULL);
+       }
+
+       pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
+                             handle);
+       thr_ret = _wapi_handle_lock_handle (handle);
+       g_assert (thr_ret == 0);
+
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
+                               (gpointer *)&thread_handle,
+                               (gpointer *)&thread_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up thread handle %p", handle);
+               goto cleanup;
+       }
+
+       /* Hold a reference while the thread is active, because we use
+        * the handle to store thread exit information
+        */
+       _wapi_handle_ref (handle);
+
+       thread_handle->state=THREAD_STATE_START;
+
+       /* Lock around the thread create, so that the new thread cant
+        * race us to look up the thread handle in GetCurrentThread()
+        */
+       pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
+                             (void *)&thread_hash_mutex);
+       thr_ret = mono_mutex_lock(&thread_hash_mutex);
+       g_assert (thr_ret == 0);
+
+       ret=_wapi_timed_thread_attach(&thread_private_handle->thread,
+                                     thread_exit, handle);
+       if(ret!=0) {
+#ifdef DEBUG
+               g_message(G_GNUC_PRETTY_FUNCTION ": Thread attach error: %s",
+                         strerror(ret));
+#endif
+               /* Two, because of the reference we took above */
+               unrefs = 2;
+
+               goto thread_hash_cleanup;
+       }
+       ta_ret = handle;
+       
+       g_hash_table_insert(thread_hash, &thread_private_handle->thread->id,
+                           handle);
+
+#ifdef DEBUG
+       g_message(G_GNUC_PRETTY_FUNCTION
+                 ": Attached thread handle %p thread %p ID %ld", handle,
+                 thread_private_handle->thread,
+                 thread_private_handle->thread->id);
+#endif
+
+       if(tid!=NULL) {
+#ifdef PTHREAD_POINTER_ID
+               *tid=GPOINTER_TO_UINT(thread_private_handle->thread->id);
+#else
+               *tid=thread_private_handle->thread->id;
+#endif
+       }
+
+thread_hash_cleanup:
+       thr_ret = mono_mutex_unlock (&thread_hash_mutex);
+       g_assert (thr_ret == 0);
+       pthread_cleanup_pop (0);
+       
+cleanup:
+       thr_ret = _wapi_handle_unlock_handle (handle);
+       g_assert (thr_ret == 0);
+       pthread_cleanup_pop (0);
+
+       /* Must not call _wapi_handle_unref() with the handle already
+        * locked
+        */
+       for (i = 0; i < unrefs; i++) {
+               _wapi_handle_unref (handle);
+       }
+       
+       return(ta_ret);
+}
+
 /**
  * GetCurrentThread:
  *
@@ -393,15 +577,28 @@ gpointer GetCurrentThread(void)
 {
        gpointer ret=NULL;
        guint32 tid;
+       int thr_ret;
+       
+       mono_once(&thread_hash_once, thread_hash_init);
+       mono_once (&thread_ops_once, thread_ops_init);
        
        tid=GetCurrentThreadId();
        
-       mono_mutex_lock(&thread_hash_mutex);
+       pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
+                             (void *)&thread_hash_mutex);
+       thr_ret = mono_mutex_lock(&thread_hash_mutex);
+       g_assert (thr_ret == 0);
 
        ret=g_hash_table_lookup(thread_hash, &tid);
+
+       thr_ret = mono_mutex_unlock(&thread_hash_mutex);
+       g_assert (thr_ret == 0);
+       pthread_cleanup_pop (0);
        
-       mono_mutex_unlock(&thread_hash_mutex);
-       
+       if (!ret) {
+               ret = thread_attach (NULL);
+       }
+
        return(ret);
 }
 
@@ -429,13 +626,23 @@ guint32 ResumeThread(gpointer handle)
                return(0xFFFFFFFF);
        }
 
+       if (thread_private_handle->thread == NULL) {
+               return(0xFFFFFFFF);
+       }
+
+#ifdef WITH_INCLUDED_LIBGC
+       if (thread_private_handle->thread->suspend_count <= 1)
+               _wapi_timed_thread_resume (thread_private_handle->thread);
+       
+       return --thread_private_handle->thread->suspend_count;
+#else
        /* This is still a kludge that only copes with starting a
         * thread that was suspended on create, so don't bother with
         * the suspend count crap yet
         */
        _wapi_timed_thread_resume (thread_private_handle->thread);
-       
        return(0xFFFFFFFF);
+#endif
 }
 
 /**
@@ -447,9 +654,45 @@ guint32 ResumeThread(gpointer handle)
  *
  * Return value: the previous suspend count, or 0xFFFFFFFF on error.
  */
-guint32 SuspendThread(gpointer handle G_GNUC_UNUSED)
+guint32 SuspendThread(gpointer handle)
 {
+#ifdef WITH_INCLUDED_LIBGC
+       struct _WapiHandle_thread *thread_handle;
+       struct _WapiHandlePrivate_thread *thread_private_handle;
+       gpointer current;
+       gboolean ok;
+
+       current = GetCurrentThread ();
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
+                               (gpointer *)&thread_handle,
+                               (gpointer *)&thread_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up thread handle %p", handle);
+               return(0xFFFFFFFF);
+       }
+
+       if (thread_private_handle->thread == NULL) {
+               return(0xFFFFFFFF);
+       }
+
+       if (!thread_private_handle->thread->suspend_count) {
+               if (handle == current)
+                       _wapi_timed_thread_suspend (thread_private_handle->thread);
+               else {
+                       pthread_kill (thread_private_handle->thread->id, SIGPWR);
+                       while (MONO_SEM_WAIT (&thread_private_handle->thread->suspended_sem) != 0) {
+                               if (errno != EINTR) {
+                                       return(0xFFFFFFFF);
+                               }
+                       }
+               }
+       }
+
+       return thread_private_handle->thread->suspend_count++;
+#else
        return(0xFFFFFFFF);
+#endif
 }
 
 /*
@@ -464,11 +707,13 @@ guint32 SuspendThread(gpointer handle G_GNUC_UNUSED)
 
 static pthread_key_t TLS_keys[TLS_MINIMUM_AVAILABLE];
 static gboolean TLS_used[TLS_MINIMUM_AVAILABLE]={FALSE};
-#ifdef TLS_PTHREAD_MUTEX
-static mono_mutex_t TLS_mutex=MONO_MUTEX_INITIALIZER;
-#else
 static guint32 TLS_spinlock=0;
-#endif
+
+guint32
+mono_pthread_key_for_tls (guint32 idx)
+{
+       return (guint32)TLS_keys [idx];
+}
 
 /**
  * TlsAlloc:
@@ -483,23 +728,17 @@ static guint32 TLS_spinlock=0;
 guint32 TlsAlloc(void)
 {
        guint32 i;
+       int thr_ret;
        
-#ifdef TLS_PTHREAD_MUTEX
-       mono_mutex_lock(&TLS_mutex);
-#else
        MONO_SPIN_LOCK (TLS_spinlock);
-#endif
        
        for(i=0; i<TLS_MINIMUM_AVAILABLE; i++) {
                if(TLS_used[i]==FALSE) {
                        TLS_used[i]=TRUE;
-                       pthread_key_create(&TLS_keys[i], NULL);
+                       thr_ret = pthread_key_create(&TLS_keys[i], NULL);
+                       g_assert (thr_ret == 0);
 
-#ifdef TLS_PTHREAD_MUTEX
-                       mono_mutex_unlock(&TLS_mutex);
-#else
                        MONO_SPIN_UNLOCK (TLS_spinlock);
-#endif
        
 #ifdef TLS_DEBUG
                        g_message (G_GNUC_PRETTY_FUNCTION ": returning key %d",
@@ -510,11 +749,7 @@ guint32 TlsAlloc(void)
                }
        }
 
-#ifdef TLS_PTHREAD_MUTEX
-       mono_mutex_unlock(&TLS_mutex);
-#else
        MONO_SPIN_UNLOCK (TLS_spinlock);
-#endif
        
 #ifdef TLS_DEBUG
        g_message (G_GNUC_PRETTY_FUNCTION ": out of indices");
@@ -537,37 +772,29 @@ guint32 TlsAlloc(void)
  */
 gboolean TlsFree(guint32 idx)
 {
+       int thr_ret;
+       
 #ifdef TLS_DEBUG
        g_message (G_GNUC_PRETTY_FUNCTION ": freeing key %d", idx);
 #endif
 
-#ifdef TLS_PTHREAD_MUTEX
-       mono_mutex_lock(&TLS_mutex);
-#else
        MONO_SPIN_LOCK (TLS_spinlock);
-#endif
        
        if(TLS_used[idx]==FALSE) {
-#ifdef TLS_PTHREAD_MUTEX
-               mono_mutex_unlock(&TLS_mutex);
-#else
                MONO_SPIN_UNLOCK (TLS_spinlock);
-#endif
+
                return(FALSE);
        }
        
        TLS_used[idx]=FALSE;
-       pthread_key_delete(TLS_keys[idx]);
+       thr_ret = pthread_key_delete(TLS_keys[idx]);
+       g_assert (thr_ret == 0);
        
 #if HAVE_BOEHM_GC
        mono_g_hash_table_remove (tls_gc_hash, MAKE_GC_ID (idx));
 #endif
 
-#ifdef TLS_PTHREAD_MUTEX
-       mono_mutex_unlock(&TLS_mutex);
-#else
        MONO_SPIN_UNLOCK (TLS_spinlock);
-#endif
        
        return(TRUE);
 }
@@ -589,25 +816,6 @@ gpointer TlsGetValue(guint32 idx)
 #ifdef TLS_DEBUG
        g_message (G_GNUC_PRETTY_FUNCTION ": looking up key %d", idx);
 #endif
-
-#ifdef TLS_PTHREAD_MUTEX
-       mono_mutex_lock(&TLS_mutex);
-#else
-       MONO_SPIN_LOCK (TLS_spinlock);
-#endif
-       
-       if(TLS_used[idx]==FALSE) {
-#ifdef TLS_DEBUG
-               g_message (G_GNUC_PRETTY_FUNCTION ": key %d unused", idx);
-#endif
-
-#ifdef TLS_PTHREAD_MUTEX
-               mono_mutex_unlock(&TLS_mutex);
-#else
-               MONO_SPIN_UNLOCK (TLS_spinlock);
-#endif
-               return(NULL);
-       }
        
        ret=pthread_getspecific(TLS_keys[idx]);
 
@@ -615,12 +823,6 @@ gpointer TlsGetValue(guint32 idx)
        g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", ret);
 #endif
        
-#ifdef TLS_PTHREAD_MUTEX
-       mono_mutex_unlock(&TLS_mutex);
-#else
-       MONO_SPIN_UNLOCK (TLS_spinlock);
-#endif
-       
        return(ret);
 }
 
@@ -642,22 +844,15 @@ gboolean TlsSetValue(guint32 idx, gpointer value)
                   value);
 #endif
        
-#ifdef TLS_PTHREAD_MUTEX
-       mono_mutex_lock(&TLS_mutex);
-#else
        MONO_SPIN_LOCK (TLS_spinlock);
-#endif
        
        if(TLS_used[idx]==FALSE) {
 #ifdef TLS_DEBUG
                g_message (G_GNUC_PRETTY_FUNCTION ": key %d unused", idx);
 #endif
 
-#ifdef TLS_PTHREAD_MUTEX
-               mono_mutex_unlock(&TLS_mutex);
-#else
                MONO_SPIN_UNLOCK (TLS_spinlock);
-#endif
+
                return(FALSE);
        }
        
@@ -668,60 +863,72 @@ gboolean TlsSetValue(guint32 idx, gpointer value)
                           ": pthread_setspecific error: %s", strerror (ret));
 #endif
 
-#ifdef TLS_PTHREAD_MUTEX
-               mono_mutex_unlock(&TLS_mutex);
-#else
                MONO_SPIN_UNLOCK (TLS_spinlock);
-#endif
+
                return(FALSE);
        }
        
 #if HAVE_BOEHM_GC
-       if (!tls_gc_hash)
+       if (!tls_gc_hash) {
+               MONO_GC_REGISTER_ROOT (tls_gc_hash);
                tls_gc_hash = mono_g_hash_table_new(g_direct_hash, g_direct_equal);
+       }
        mono_g_hash_table_insert (tls_gc_hash, MAKE_GC_ID (idx), value);
 #endif
 
-#ifdef TLS_PTHREAD_MUTEX
-       mono_mutex_unlock(&TLS_mutex);
-#else
        MONO_SPIN_UNLOCK (TLS_spinlock);
-#endif
        
        return(TRUE);
 }
 
 /**
- * Sleep:
+ * SleepEx:
  * @ms: The time in milliseconds to suspend for
+ * @alertable: if TRUE, the wait can be interrupted by an APC call
  *
  * Suspends execution of the current thread for @ms milliseconds.  A
  * value of zero causes the thread to relinquish its time slice.  A
  * value of %INFINITE causes an infinite delay.
  */
-void Sleep(guint32 ms)
+guint32 SleepEx(guint32 ms, gboolean alertable)
 {
        struct timespec req, rem;
-       div_t divvy;
+       int ms_quot, ms_rem;
        int ret;
+       gpointer current_thread = NULL;
        
 #ifdef DEBUG
        g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
 #endif
 
+       if (alertable) {
+               current_thread = GetCurrentThread ();
+               if (_wapi_thread_apc_pending (current_thread)) {
+                       _wapi_thread_dispatch_apc_queue (current_thread);
+                       return WAIT_IO_COMPLETION;
+               }
+       }
+       
        if(ms==0) {
                sched_yield();
-               return;
+               return 0;
        }
        
        /* FIXME: check for INFINITE and sleep forever */
-       divvy=div((int)ms, 1000);
+       ms_quot = ms / 1000;
+       ms_rem = ms % 1000;
        
-       req.tv_sec=divvy.quot;
-       req.tv_nsec=divvy.rem*1000000;
+       req.tv_sec=ms_quot;
+       req.tv_nsec=ms_rem*1000000;
        
 again:
        ret=nanosleep(&req, &rem);
+
+       if (alertable && _wapi_thread_apc_pending (current_thread)) {
+               _wapi_thread_dispatch_apc_queue (current_thread);
+               return WAIT_IO_COMPLETION;
+       }
+       
        if(ret==-1) {
                /* Sleep interrupted with rem time remaining */
 #ifdef DEBUG
@@ -733,14 +940,146 @@ again:
                req=rem;
                goto again;
        }
+
+       return 0;
 }
 
-/* FIXME: implement alertable */
-void SleepEx(guint32 ms, gboolean alertable)
+void Sleep(guint32 ms)
 {
-       if(alertable==TRUE) {
-               g_warning(G_GNUC_PRETTY_FUNCTION ": alertable not implemented");
-       }
+       SleepEx(ms, FALSE);
+}
+
+gboolean
+BindIoCompletionCallback (gpointer handle,
+                         WapiOverlappedCB callback,
+                         guint64 flags)
+{
+       WapiHandleType type;
        
-       Sleep(ms);
+       type = _wapi_handle_type (handle);
+       if (type == WAPI_HANDLE_FILE || type == WAPI_HANDLE_PIPE)
+               return _wapi_io_add_callback (handle, callback, flags);
+
+       SetLastError (ERROR_NOT_SUPPORTED);
+       return FALSE;
+}
+
+guint32 QueueUserAPC (WapiApcProc apc_callback, gpointer handle, 
+                                       gpointer param)
+{
+       struct _WapiHandle_thread *thread_handle;
+       struct _WapiHandlePrivate_thread *thread_private_handle;
+       gboolean ok;
+
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
+                               (gpointer *)&thread_handle,
+                               (gpointer *)&thread_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up thread handle %p", handle);
+               return(0);
+       }
+
+       _wapi_timed_thread_queue_apc (thread_private_handle->thread, 
+                                                       apc_callback, param);
+       return(1);
+}
+
+gboolean _wapi_thread_cur_apc_pending (void)
+{
+       return _wapi_thread_apc_pending (GetCurrentThread ());
 }
+
+gboolean _wapi_thread_apc_pending (gpointer handle)
+{
+       struct _WapiHandle_thread *thread_handle;
+       struct _WapiHandlePrivate_thread *thread_private_handle;
+       gboolean ok;
+
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
+                               (gpointer *)&thread_handle,
+                               (gpointer *)&thread_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up thread handle %p", handle);
+               return(FALSE);
+       }
+
+       return _wapi_timed_thread_apc_pending (thread_private_handle->thread);
+}
+
+gboolean _wapi_thread_dispatch_apc_queue (gpointer handle)
+{
+       struct _WapiHandle_thread *thread_handle;
+       struct _WapiHandlePrivate_thread *thread_private_handle;
+       gboolean ok;
+
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
+                               (gpointer *)&thread_handle,
+                               (gpointer *)&thread_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up thread handle %p", handle);
+               return(0);
+       }
+
+       _wapi_timed_thread_dispatch_apc_queue (thread_private_handle->thread);
+       return(1);
+}
+
+
+
+#ifdef WITH_INCLUDED_LIBGC
+
+static void GC_suspend_handler (int sig)
+{
+       struct _WapiHandle_thread *thread_handle;
+       struct _WapiHandlePrivate_thread *thread_private_handle;
+       gpointer handle;
+       gboolean ok;
+
+       handle = GetCurrentThread ();
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
+                               (gpointer *)&thread_handle,
+                               (gpointer *)&thread_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up thread handle %p", handle);
+               return;
+       }
+
+       thread_private_handle->thread->stack_ptr = &ok;
+       MONO_SEM_POST (&thread_private_handle->thread->suspended_sem);
+
+       _wapi_timed_thread_suspend (thread_private_handle->thread);
+
+       thread_private_handle->thread->stack_ptr = NULL;
+}
+
+static void gc_init (void)
+{
+       struct sigaction act;
+
+       act.sa_handler = GC_suspend_handler;
+       g_assert (sigaction (SIGPWR, &act, NULL) == 0);
+}
+
+void mono_wapi_push_thread_stack (gpointer handle, gpointer stack_ptr)
+{
+       struct _WapiHandle_thread *thread_handle;
+       struct _WapiHandlePrivate_thread *thread_private_handle;
+       gboolean ok;
+
+       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
+                               (gpointer *)&thread_handle,
+                               (gpointer *)&thread_private_handle);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up thread handle %p", handle);
+               return;
+       }
+
+       GC_push_all_stack (thread_private_handle->thread->stack_ptr, stack_ptr);
+}
+
+#endif /* WITH_INCLUDED_LIBGC */