X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;ds=sidebyside;f=mono%2Fio-layer%2Fthreads.c;h=989c6e80eeb1cd81a9ec54916c4eb3ec240fc17c;hb=3cf809f80ed339af9e08bd3fbae6538988ae5f3b;hp=ca601bd4f5409db80ddc8bc516e533ec97d8a003;hpb=cf2c79351d1b176b13139dc39a4ee60ba86f8dc3;p=mono.git diff --git a/mono/io-layer/threads.c b/mono/io-layer/threads.c index ca601bd4f54..989c6e80eeb 100644 --- a/mono/io-layer/threads.c +++ b/mono/io-layer/threads.c @@ -8,16 +8,16 @@ */ #include -#if HAVE_BOEHM_GC -#include -#include "mono/utils/mono-hash.h" -#endif +#include #include #include #include +#include #include #include #include +#include +#include #include #include @@ -26,10 +26,17 @@ #include #include #include +#include +#include + +#if HAVE_VALGRIND_MEMCHECK_H +#include +#endif #undef DEBUG #undef TLS_DEBUG + /* Hash threads with tids. I thought of using TLS for this, but that * would have to set the data in the new thread, which is more hassle */ @@ -37,16 +44,11 @@ static mono_once_t thread_hash_once = MONO_ONCE_INIT; static mono_mutex_t thread_hash_mutex = MONO_MUTEX_INITIALIZER; static GHashTable *thread_hash=NULL; -#if HAVE_BOEHM_GC -static MonoGHashTable *tls_gc_hash = NULL; -#endif - -static void thread_close_private (gpointer handle); -static void thread_own (gpointer handle); +static void thread_close (gpointer handle, gpointer data); +static gboolean thread_own (gpointer handle); struct _WapiHandleOps _wapi_thread_ops = { - NULL, /* close_shared */ - thread_close_private, /* close_private */ + thread_close, /* close */ NULL, /* signal */ thread_own, /* own */ NULL, /* is_owned */ @@ -54,97 +56,110 @@ 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) +static void thread_close (gpointer handle, gpointer data) { - struct _WapiHandlePrivate_thread *thread_handle; - gboolean ok; - - ok=_wapi_lookup_handle (handle, WAPI_HANDLE_UNUSED, NULL, - (gpointer *)&thread_handle); - if(ok==FALSE) { - g_warning (G_GNUC_PRETTY_FUNCTION - ": error looking up thread handle %p", handle); - return; - } - + struct _WapiHandle_thread *thread_handle = (struct _WapiHandle_thread *)data; + #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": closing thread handle %p with thread %p id %ld", - handle, thread_handle->thread, - thread_handle->thread->id); + g_message ("%s: closing thread handle %p", __func__, handle); #endif - if(thread_handle->thread!=NULL) { - _wapi_timed_thread_destroy (thread_handle->thread); - } + g_ptr_array_free (thread_handle->owned_mutexes, TRUE); } -static void thread_own (gpointer handle) +static gboolean thread_own (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; - } +#ifdef DEBUG + g_message ("%s: owning thread handle %p", __func__, handle); +#endif - if(thread_private_handle->joined==FALSE) { - _wapi_timed_thread_join (thread_private_handle->thread, NULL, - NULL); - thread_private_handle->joined=TRUE; + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, + (gpointer *)&thread_handle); + if (ok == FALSE) { + g_warning ("%s: error looking up thread handle %p", __func__, + handle); + return(FALSE); + } + + if (thread_handle->joined == FALSE) { + _wapi_timed_thread_join (thread_handle->thread, NULL, NULL); + thread_handle->joined = TRUE; } + + return(TRUE); } static void thread_exit(guint32 exitstatus, gpointer handle) { struct _WapiHandle_thread *thread_handle; - struct _WapiHandlePrivate_thread *thread_private_handle; gboolean ok; + int thr_ret; + int i; + + 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); + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, + (gpointer *)&thread_handle); + if (ok == FALSE) { + g_warning ("%s: error looking up thread handle %p", __func__, + handle); return; } - - _wapi_handle_lock_handle (handle); + + for (i = 0; i < thread_handle->owned_mutexes->len; i++) { + _wapi_mutex_abandon (g_ptr_array_index (thread_handle->owned_mutexes, i), getpid (), thread_handle->thread->id); + } #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION - ": Recording thread handle %p exit status", handle); + g_message ("%s: Recording thread handle %p exit status", __func__, + handle); #endif - thread_handle->exitstatus=exitstatus; - thread_handle->state=THREAD_STATE_EXITED; + thread_handle->exitstatus = exitstatus; + 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 - ": Recording thread handle %p id %ld status as %d", - handle, thread_private_handle->thread->id, exitstatus); + g_message("%s: Recording thread handle %p id %ld status as %d", + __func__, handle, thread_handle->thread->id, exitstatus); #endif /* Remove this thread from the hash */ - mono_mutex_lock(&thread_hash_mutex); - g_hash_table_remove(thread_hash, &thread_private_handle->thread->id); - mono_mutex_unlock(&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, (gpointer)(thread_handle->thread->id)); + + 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); @@ -152,7 +167,7 @@ static void thread_exit(guint32 exitstatus, gpointer handle) static void thread_hash_init(void) { - thread_hash=g_hash_table_new(g_int_hash, g_int_equal); + thread_hash = g_hash_table_new (NULL, NULL); } /** @@ -165,115 +180,202 @@ static void thread_hash_init(void) * @create: If 0, the new thread is ready to run immediately. If * %CREATE_SUSPENDED, the new thread will be in the suspended state, * requiring a ResumeThread() call to continue running. - * @tid: If non-NULL, the ID of the new thread is stored here. + * @tid: If non-NULL, the ID of the new thread is stored here. NB + * this is defined as a DWORD (ie 32bit) in the MS API, but we need to + * cope with 64 bit IDs for s390x and amd64. * * Creates a new threading handle. * * Return value: a new handle, or NULL */ -gpointer CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 stacksize G_GNUC_UNUSED, - WapiThreadStart start, gpointer param, guint32 create G_GNUC_UNUSED, - guint32 *tid) +gpointer CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 stacksize, + WapiThreadStart start, gpointer param, guint32 create, + gsize *tid) { - struct _WapiHandle_thread *thread_handle; - struct _WapiHandlePrivate_thread *thread_private_handle; + struct _WapiHandle_thread thread_handle = {0}, *thread_handle_p; + 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_hash_once, thread_hash_init); mono_once (&thread_ops_once, thread_ops_init); - if(start==NULL) { + if (start == NULL) { return(NULL); } + + thread_handle.state = THREAD_STATE_START; + thread_handle.owner_pid = getpid (); + thread_handle.owned_mutexes = g_ptr_array_new (); - handle=_wapi_handle_new (WAPI_HANDLE_THREAD); - if(handle==_WAPI_HANDLE_INVALID) { - g_warning (G_GNUC_PRETTY_FUNCTION - ": error creating thread handle"); - return(NULL); + handle = _wapi_handle_new (WAPI_HANDLE_THREAD, &thread_handle); + if (handle == _WAPI_HANDLE_INVALID) { + g_warning ("%s: error creating thread handle", __func__); + SetLastError (ERROR_GEN_FAILURE); + + 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, - (gpointer *)&thread_private_handle); - if(ok==FALSE) { - g_warning (G_GNUC_PRETTY_FUNCTION - ": error looking up thread handle %p", handle); - _wapi_handle_unlock_handle (handle); - return(NULL); + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, + (gpointer *)&thread_handle_p); + if (ok == FALSE) { + g_warning ("%s: error looking up thread handle %p", __func__, + handle); + SetLastError (ERROR_GEN_FAILURE); + + 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() */ - 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=_wapi_timed_thread_create(&thread_private_handle->thread, NULL, - start, thread_exit, param, handle); - if(ret!=0) { + /* Set a 2M stack size. This is the default on Linux, but BSD + * needs it. (The original bug report from Martin Dvorak + * 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); + + /* defaults of 2Mb for 32bits and 4Mb for 64bits */ + /* temporarily changed to use 1 MB: this allows more threads + * to be used, as well as using less virtual memory and so + * more is available for the GC heap. + */ + if (stacksize == 0){ +#if HAVE_VALGRIND_MEMCHECK_H + if (RUNNING_ON_VALGRIND) { + stacksize = 1 << 20; + } else { + stacksize = (SIZEOF_VOID_P / 4) * 1024 * 1024; + } +#else + stacksize = (SIZEOF_VOID_P / 4) * 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_handle_p->thread, &attr, + create, start, thread_exit, param, + handle); + if (ret != 0) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Thread create error: %s", - strerror(ret)); + g_message ("%s: Thread create error: %s", __func__, + strerror(ret)); #endif - mono_mutex_unlock(&thread_hash_mutex); - _wapi_handle_unlock_handle (handle); - _wapi_handle_unref (handle); + + /* Two, because of the reference we took above */ + unrefs = 2; - /* And again, because of the reference we took above */ - _wapi_handle_unref (handle); - return(NULL); + goto thread_hash_cleanup; } - - g_hash_table_insert(thread_hash, &thread_private_handle->thread->id, - handle); - mono_mutex_unlock(&thread_hash_mutex); + ct_ret = handle; + + g_hash_table_insert (thread_hash, + (gpointer)(thread_handle_p->thread->id), + handle); #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Started thread handle %p thread %p ID %ld", handle, - thread_private_handle->thread, - thread_private_handle->thread->id); + g_message("%s: Started thread handle %p thread %p ID %ld", __func__, + handle, thread_handle_p->thread, + thread_handle_p->thread->id); #endif - if(tid!=NULL) { - *tid=thread_private_handle->thread->id; + if (tid != NULL) { +#ifdef PTHREAD_POINTER_ID + /* Don't use GPOINTER_TO_UINT here, it can't cope with + * sizeof(void *) > sizeof(uint) when a cast to uint + * would overflow + */ + *tid = (gsize)(thread_handle_p->thread->id); +#else + *tid = thread_handle_p->thread->id; +#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); + +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(handle); + return(ct_ret); } -gpointer OpenThread (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 tid) +gpointer _wapi_thread_handle_from_id (pthread_t tid) { gpointer ret=NULL; + int thr_ret; + + 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); -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": looking up thread %d", tid); -#endif + ret = g_hash_table_lookup (thread_hash, (gpointer)(tid)); - mono_mutex_lock(&thread_hash_mutex); + thr_ret = mono_mutex_unlock(&thread_hash_mutex); + g_assert (thr_ret == 0); + pthread_cleanup_pop (0); - ret=g_hash_table_lookup(thread_hash, &tid); - mono_mutex_unlock(&thread_hash_mutex); + return(ret); +} + +/* NB tid is 32bit in MS API, but we need 64bit on amd64 and s390x + * (and probably others) + */ +gpointer OpenThread (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, gsize tid) +{ + gpointer ret=NULL; + + mono_once (&thread_hash_once, thread_hash_init); + mono_once (&thread_ops_once, thread_ops_init); +#ifdef DEBUG + g_message ("%s: looking up thread %"G_GSIZE_FORMAT, __func__, tid); +#endif + + ret = _wapi_thread_handle_from_id ((pthread_t)tid); if(ret!=NULL) { _wapi_handle_ref (ret); } #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": returning thread handle %p", ret); + g_message ("%s: returning thread handle %p", __func__, ret); #endif return(ret); @@ -290,10 +392,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); } @@ -310,43 +408,39 @@ void ExitThread(guint32 exitcode) gboolean GetExitCodeThread(gpointer handle, guint32 *exitcode) { 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); + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, + (gpointer *)&thread_handle); + if (ok == FALSE) { + g_warning ("%s: error looking up thread handle %p", __func__, + handle); + return (FALSE); } #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Finding exit status for thread handle %p id %ld", handle, - thread_private_handle->thread->id); + g_message ("%s: Finding exit status for thread handle %p id %ld", + __func__, handle, thread_handle->thread->id); #endif - if(exitcode==NULL) { + if (exitcode == NULL) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Nowhere to store exit code"); + g_message ("%s: Nowhere to store exit code", __func__); #endif return(FALSE); } - if(thread_handle->state!=THREAD_STATE_EXITED) { + if (thread_handle->state != THREAD_STATE_EXITED) { #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Thread still active (state %d, exited is %d)", - thread_handle->state, THREAD_STATE_EXITED); + g_message ("%s: Thread still active (state %d, exited is %d)", + __func__, thread_handle->state, + THREAD_STATE_EXITED); #endif - *exitcode=STILL_ACTIVE; + *exitcode = STILL_ACTIVE; return(TRUE); } - *exitcode=thread_handle->exitstatus; + *exitcode = thread_handle->exitstatus; return(TRUE); } @@ -357,13 +451,134 @@ gboolean GetExitCodeThread(gpointer handle, guint32 *exitcode) * Looks up the thread ID of the current thread. This ID can be * passed to OpenThread() to create a new handle on this thread. * - * Return value: the thread ID. + * Return value: the thread ID. NB this is defined as DWORD (ie 32 + * bit) in the MS API, but we need to cope with 64 bit IDs for s390x + * and amd64. This doesn't really break the API, it just embraces and + * extends it on 64bit platforms :) */ -guint32 GetCurrentThreadId(void) +gsize GetCurrentThreadId(void) { - pthread_t tid=pthread_self(); + pthread_t tid = pthread_self(); +#ifdef PTHREAD_POINTER_ID + /* Don't use GPOINTER_TO_UINT here, it can't cope with + * sizeof(void *) > sizeof(uint) when a cast to uint would + * overflow + */ + return((gsize)tid); +#else return(tid); +#endif +} + +static gpointer thread_attach(gsize *tid) +{ + struct _WapiHandle_thread thread_handle = {0}, *thread_handle_p; + 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); + + thread_handle.state = THREAD_STATE_START; + thread_handle.owner_pid = getpid (); + thread_handle.owned_mutexes = g_ptr_array_new (); + + handle = _wapi_handle_new (WAPI_HANDLE_THREAD, &thread_handle); + if (handle == _WAPI_HANDLE_INVALID) { + g_warning ("%s: error creating thread handle", __func__); + + SetLastError (ERROR_GEN_FAILURE); + 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_p); + if (ok == FALSE) { + g_warning ("%s: error looking up thread handle %p", __func__, + handle); + + SetLastError (ERROR_GEN_FAILURE); + goto cleanup; + } + + /* Hold a reference while the thread is active, because we use + * the handle to store thread exit information + */ + _wapi_handle_ref (handle); + + /* 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_handle_p->thread, thread_exit, + handle); + if (ret != 0) { +#ifdef DEBUG + g_message ("%s: Thread attach error: %s", __func__, + 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, + (gpointer)(thread_handle_p->thread->id), + handle); + +#ifdef DEBUG + g_message("%s: Attached thread handle %p thread %p ID %ld", __func__, + handle, thread_handle_p->thread, + thread_handle_p->thread->id); +#endif + + if (tid != NULL) { +#ifdef PTHREAD_POINTER_ID + /* Don't use GPOINTER_TO_UINT here, it can't cope with + * sizeof(void *) > sizeof(uint) when a cast to uint + * would overflow + */ + *tid = (gsize)(thread_handle_p->thread->id); +#else + *tid = thread_handle_p->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); } /** @@ -380,16 +595,18 @@ guint32 GetCurrentThreadId(void) gpointer GetCurrentThread(void) { gpointer ret=NULL; - guint32 tid; + gsize tid; - tid=GetCurrentThreadId(); - - mono_mutex_lock(&thread_hash_mutex); - - ret=g_hash_table_lookup(thread_hash, &tid); + mono_once(&thread_hash_once, thread_hash_init); + mono_once (&thread_ops_once, thread_ops_init); - mono_mutex_unlock(&thread_hash_mutex); + tid = GetCurrentThreadId(); + ret = _wapi_thread_handle_from_id ((pthread_t)tid); + if (!ret) { + ret = thread_attach (NULL); + } + return(ret); } @@ -402,9 +619,37 @@ gpointer GetCurrentThread(void) * * Return value: the previous suspend count, or 0xFFFFFFFF on error. */ -guint32 ResumeThread(gpointer handle G_GNUC_UNUSED) +guint32 ResumeThread(gpointer handle) { + struct _WapiHandle_thread *thread_handle; + gboolean ok; + + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, + (gpointer *)&thread_handle); + if (ok == FALSE) { + g_warning ("%s: error looking up thread handle %p", __func__, + handle); + + return (0xFFFFFFFF); + } + + if (thread_handle->thread == NULL) { + return(0xFFFFFFFF); + } + +#ifdef WITH_INCLUDED_LIBGC + if (thread_handle->thread->suspend_count <= 1) + _wapi_timed_thread_resume (thread_handle->thread); + + return (--thread_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_handle->thread); return(0xFFFFFFFF); +#endif } /** @@ -416,9 +661,43 @@ guint32 ResumeThread(gpointer handle G_GNUC_UNUSED) * * 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; + gpointer current; + gboolean ok; + + current = GetCurrentThread (); + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, + (gpointer *)&thread_handle); + if (ok == FALSE) { + g_warning ("%s: error looking up thread handle %p", __func__, + handle); + return (0xFFFFFFFF); + } + + if (thread_handle->thread == NULL) { + return(0xFFFFFFFF); + } + + if (!thread_handle->thread->suspend_count) { + if (handle == current) + _wapi_timed_thread_suspend (thread_handle->thread); + else { + pthread_kill (thread_handle->thread->id, SIGPWR); + while (MONO_SEM_WAIT (&thread_handle->thread->suspended_sem) != 0) { + if (errno != EINTR) { + return(0xFFFFFFFF); + } + } + } + } + + return (thread_handle->thread->suspend_count++); +#else return(0xFFFFFFFF); +#endif } /* @@ -433,7 +712,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}; -static mono_mutex_t TLS_mutex=MONO_MUTEX_INITIALIZER; +static guint32 TLS_spinlock=0; + +guint32 +mono_pthread_key_for_tls (guint32 idx) +{ + return (guint32)TLS_keys [idx]; +} /** * TlsAlloc: @@ -448,29 +733,30 @@ static mono_mutex_t TLS_mutex=MONO_MUTEX_INITIALIZER; guint32 TlsAlloc(void) { guint32 i; + int thr_ret; - mono_mutex_lock(&TLS_mutex); + MONO_SPIN_LOCK (TLS_spinlock); for(i=0; ithread, 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; + gboolean ok; + + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, + (gpointer *)&thread_handle); + if (ok == FALSE) { + g_warning ("%s: error looking up thread handle %p", __func__, + handle); + return (FALSE); + } + + return(_wapi_timed_thread_apc_pending (thread_handle->thread)); } -/* FIXME: implement alertable */ -void SleepEx(guint32 ms, gboolean alertable) +gboolean _wapi_thread_dispatch_apc_queue (gpointer handle) { - if(alertable==TRUE) { - g_warning(G_GNUC_PRETTY_FUNCTION ": alertable not implemented"); + struct _WapiHandle_thread *thread_handle; + gboolean ok; + + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, + (gpointer *)&thread_handle); + if (ok == FALSE) { + g_warning ("%s: error looking up thread handle %p", __func__, + handle); + return (0); } - Sleep(ms); + _wapi_timed_thread_dispatch_apc_queue (thread_handle->thread); + return(1); } + +void _wapi_thread_own_mutex (pthread_t tid, gpointer mutex) +{ + struct _WapiHandle_thread *thread_handle; + gboolean ok; + gpointer thread; + + thread = _wapi_thread_handle_from_id (tid); + if (thread == NULL) { + g_warning ("%s: error looking up thread by ID", __func__); + return; + } + + ok = _wapi_lookup_handle (thread, WAPI_HANDLE_THREAD, + (gpointer *)&thread_handle); + if (ok == FALSE) { + g_warning ("%s: error looking up thread handle %p", __func__, + thread); + return; + } + + g_ptr_array_add (thread_handle->owned_mutexes, mutex); +} + +void _wapi_thread_disown_mutex (pthread_t tid, gpointer mutex) +{ + struct _WapiHandle_thread *thread_handle; + gboolean ok; + gpointer thread; + + thread = _wapi_thread_handle_from_id (tid); + if (thread == NULL) { + g_warning ("%s: error looking up thread by ID", __func__); + return; + } + + ok = _wapi_lookup_handle (thread, WAPI_HANDLE_THREAD, + (gpointer *)&thread_handle); + if (ok == FALSE) { + g_warning ("%s: error looking up thread handle %p", __func__, + thread); + return; + } + + g_ptr_array_remove (thread_handle->owned_mutexes, mutex); +} + + + +#ifdef WITH_INCLUDED_LIBGC + +static void GC_suspend_handler (int sig) +{ + struct _WapiHandle_thread *thread_handle; + gpointer handle; + gboolean ok; + + handle = GetCurrentThread (); + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, + (gpointer *)&thread_handle); + if (ok == FALSE) { + g_warning ("%s: error looking up thread handle %p", __func__, + handle); + return; + } + + thread_handle->thread->stack_ptr = &ok; + MONO_SEM_POST (&thread_handle->thread->suspended_sem); + + _wapi_timed_thread_suspend (thread_handle->thread); + + thread_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; + gboolean ok; + + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, + (gpointer *)&thread_handle); + if (ok == FALSE) { + g_warning ("%s: error looking up thread handle %p", __func__, + handle); + return; + } + + GC_push_all_stack (thread_handle->thread->stack_ptr, stack_ptr); +} + +#endif /* WITH_INCLUDED_LIBGC */