X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fio-layer%2Fthreads.c;h=989c6e80eeb1cd81a9ec54916c4eb3ec240fc17c;hb=3cf809f80ed339af9e08bd3fbae6538988ae5f3b;hp=f8ca4d8538dfdf9d248106fe588173306477d4ff;hpb=180aa81bdf011e89b2e7f0b7ba4fc805ff438b79;p=mono.git diff --git a/mono/io-layer/threads.c b/mono/io-layer/threads.c index f8ca4d8538d..989c6e80eeb 100644 --- a/mono/io-layer/threads.c +++ b/mono/io-layer/threads.c @@ -1,239 +1,173 @@ +/* + * threads.c: Thread handles + * + * Author: + * Dick Porter (dick@ximian.com) + * + * (C) 2002 Ximian, Inc. + */ + #include +#include #include #include #include +#include #include #include #include +#include +#include -#include "mono/io-layer/wapi.h" -#include "wapi-private.h" -#include "timed-thread.h" -#include "wait-private.h" -#include "handles-private.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include "pthread-compat.h" +#if HAVE_VALGRIND_MEMCHECK_H +#include +#endif -#define DEBUG +#undef DEBUG +#undef TLS_DEBUG -typedef enum { - THREAD_STATE_START, - THREAD_STATE_EXITED, -} WapiThreadState; -struct _WapiHandle_thread -{ - WapiHandle handle; - WapiThreadState state; - TimedThread *thread; - guint32 exitstatus; -}; - -static pthread_mutex_t thread_signal_mutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_cond_t thread_signal_cond = PTHREAD_COND_INITIALIZER; +/* 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 + */ +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; -static void thread_close(WapiHandle *handle); -static gboolean thread_wait(WapiHandle *handle, guint32 ms); -static guint32 thread_wait_multiple(gpointer data); +static void thread_close (gpointer handle, gpointer data); +static gboolean thread_own (gpointer handle); -static struct _WapiHandleOps thread_ops = { +struct _WapiHandleOps _wapi_thread_ops = { thread_close, /* close */ - NULL, /* getfiletype */ - NULL, /* readfile */ - NULL, /* writefile */ - NULL, /* seek */ - NULL, /* setendoffile */ - NULL, /* getfilesize */ - thread_wait, /* wait */ - thread_wait_multiple, /* wait_multiple */ + NULL, /* signal */ + thread_own, /* own */ + NULL, /* is_owned */ }; -static void thread_close(WapiHandle *handle) -{ - struct _WapiHandle_thread *thread_handle=(struct _WapiHandle_thread *)handle; - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": closing thread handle %p with thread %p id %ld", - thread_handle, thread_handle->thread, - thread_handle->thread->id); +static mono_once_t thread_ops_once=MONO_ONCE_INIT; + +#ifdef WITH_INCLUDED_LIBGC +static void gc_init (void); #endif - g_free(thread_handle->thread); +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 gboolean thread_wait(WapiHandle *handle, guint32 ms) +static void thread_close (gpointer handle, gpointer data) { - struct _WapiHandle_thread *thread_handle=(struct _WapiHandle_thread *)handle; - int ret; - - if(handle->signalled) { - /* Already signalled, so return straight away */ + struct _WapiHandle_thread *thread_handle = (struct _WapiHandle_thread *)data; + #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": thread handle %p already signalled, returning now", handle); + g_message ("%s: closing thread handle %p", __func__, handle); #endif - return(TRUE); - } + g_ptr_array_free (thread_handle->owned_mutexes, TRUE); +} +static gboolean thread_own (gpointer handle) +{ + struct _WapiHandle_thread *thread_handle; + gboolean ok; + #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": waiting for %d ms for thread handle %p with id %ld", ms, - thread_handle, thread_handle->thread->id); + g_message ("%s: owning thread handle %p", __func__, handle); #endif - if(ms==INFINITE) { - ret=_wapi_timed_thread_join(thread_handle->thread, NULL, NULL); - } else { - struct timespec timeout; - struct timeval now; - div_t divvy; - - divvy=div((int)ms, 1000); - gettimeofday(&now, NULL); - - timeout.tv_sec=now.tv_sec+divvy.quot; - timeout.tv_nsec=(now.tv_usec+divvy.rem)*1000; - - ret=_wapi_timed_thread_join(thread_handle->thread, &timeout, - NULL); + 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(ret==0) { - /* Thread joined */ - return(TRUE); - } else { - /* ret might be ETIMEDOUT for timeout, or other for error */ - return(FALSE); + if (thread_handle->joined == FALSE) { + _wapi_timed_thread_join (thread_handle->thread, NULL, NULL); + thread_handle->joined = TRUE; } + + return(TRUE); } -static guint32 thread_wait_multiple(gpointer data) +static void thread_exit(guint32 exitstatus, gpointer handle) { - WaitQueueItem *item=(WaitQueueItem *)data; - int ret; - guint32 numhandles, count; - struct timespec timeout; - struct timeval now; - div_t divvy; + struct _WapiHandle_thread *thread_handle; + gboolean ok; + int thr_ret; + int i; - numhandles=item->handles[WAPI_HANDLE_THREAD]->len; + 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 - ": waiting on %d thread handles for %d ms", numhandles, - item->timeout); -#endif - - /* First, check if any of the handles are already - * signalled. If waitall is specified we only return if all - * handles have been signalled. - */ - count=_wapi_handle_count_signalled(item->handles[WAPI_HANDLE_THREAD]); - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Preliminary check found %d handles signalled", count); -#endif - - if((item->waitall==TRUE && count==numhandles) || - (item->waitall==FALSE && count>0)) { - goto success; + 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; } - /* OK, we need to wait for some */ - if(item->timeout!=INFINITE) { - divvy=div((int)item->timeout, 1000); - gettimeofday(&now, NULL); - - timeout.tv_sec=now.tv_sec+divvy.quot; - timeout.tv_nsec=(now.tv_usec+divvy.rem)*1000; + 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); } - - /* We can restart from here without resetting the timeout, - * because it is calculated from absolute time, not an offset - */ -again: - pthread_mutex_lock(&thread_signal_mutex); - if(item->timeout==INFINITE) { - ret=pthread_cond_wait(&thread_signal_cond, - &thread_signal_mutex); - } else { - ret=pthread_cond_timedwait(&thread_signal_cond, - &thread_signal_mutex, - &timeout); - } - pthread_mutex_unlock(&thread_signal_mutex); - - if(ret==ETIMEDOUT) { - /* Check signalled state here, just in case a thread - * exited between the first check and the cond wait. - * We return the number of signalled handles, which - * may be fewer than the total. - */ -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Wait timed out"); -#endif - count=_wapi_handle_count_signalled( - item->handles[WAPI_HANDLE_THREAD]); - goto success; - } - #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Thread exited, checking status"); + g_message ("%s: Recording thread handle %p exit status", __func__, + handle); #endif + + thread_handle->exitstatus = exitstatus; + thread_handle->state = THREAD_STATE_EXITED; - /* Another thread exited, so see if it was one we are - * interested in - */ - count=_wapi_handle_count_signalled(item->handles[WAPI_HANDLE_THREAD]); + _wapi_handle_set_signal_state (handle, TRUE, TRUE); + thr_ret = _wapi_handle_unlock_handle (handle); + g_assert (thr_ret == 0); + pthread_cleanup_pop (0); + #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Check after thread exit found %d handles signalled", - count); + g_message("%s: Recording thread handle %p id %ld status as %d", + __func__, handle, thread_handle->thread->id, exitstatus); #endif - if((item->waitall==TRUE && count==numhandles) || - (item->waitall==FALSE && count>0)) { - goto success; - } - - /* Either we have waitall set with more handles to wait for, - * or the thread that exited wasn't interesting to us - */ -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Waiting a bit longer"); -#endif + /* Remove this thread from the hash */ + 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)); - goto again; + thr_ret = mono_mutex_unlock(&thread_hash_mutex); + g_assert (thr_ret == 0); + pthread_cleanup_pop (0); -success: - item->waited[WAPI_HANDLE_THREAD]=TRUE; - item->waitcount[WAPI_HANDLE_THREAD]=count; - - return(count); + /* The thread is no longer active, so unref it */ + _wapi_handle_unref (handle); } -static void thread_exit(guint32 exitstatus, gpointer userdata) +static void thread_hash_init(void) { - struct _WapiHandle_thread *thread_handle=(struct _WapiHandle_thread *)userdata; - - thread_handle->exitstatus=exitstatus; - thread_handle->state=THREAD_STATE_EXITED; - thread_handle->handle.signalled=TRUE; - -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Recording thread handle %p status as %d", thread_handle, - exitstatus); -#endif - - /* Signal any thread waiting on thread exit */ - pthread_mutex_lock(&thread_signal_mutex); - pthread_cond_broadcast(&thread_signal_cond); - pthread_mutex_unlock(&thread_signal_mutex); + thread_hash = g_hash_table_new (NULL, NULL); } /** @@ -246,52 +180,205 @@ static void thread_exit(guint32 exitstatus, gpointer userdata) * @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 */ -WapiHandle *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; - WapiHandle *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_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 (); - thread_handle=(struct _WapiHandle_thread *)g_new0(struct _WapiHandle_thread, 1); - thread_handle->state=THREAD_STATE_START; + 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); - ret=_wapi_timed_thread_create(&thread_handle->thread, NULL, start, - thread_exit, param, thread_handle); - if(ret!=0) { -#ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Thread create error: %s", - strerror(ret)); + 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); + + /* 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 - g_free(thread_handle); - return(NULL); } - handle=(WapiHandle *)thread_handle; - _WAPI_HANDLE_INIT(handle, WAPI_HANDLE_THREAD, thread_ops); +#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 ("%s: Thread create error: %s", __func__, + strerror(ret)); +#endif + + /* Two, because of the reference we took above */ + unrefs = 2; + + goto thread_hash_cleanup; + } + 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", thread_handle, - thread_handle->thread, thread_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) { +#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); - if(tid!=NULL) { - *tid=thread_handle->thread->id; + /* 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 _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); + + ret = g_hash_table_lookup (thread_hash, (gpointer)(tid)); + + thr_ret = mono_mutex_unlock(&thread_hash_mutex); + g_assert (thr_ret == 0); + pthread_cleanup_pop (0); + + 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 ("%s: returning thread handle %p", __func__, ret); +#endif + + return(ret); } /** @@ -318,39 +405,211 @@ void ExitThread(guint32 exitcode) * * Return value: %TRUE, or %FALSE on error. */ -gboolean GetExitCodeThread(WapiHandle *handle, guint32 *exitcode) +gboolean GetExitCodeThread(gpointer handle, guint32 *exitcode) { - struct _WapiHandle_thread *thread_handle=(struct _WapiHandle_thread *)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); + } #ifdef DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Finding exit status for thread handle %p id %ld", handle, - thread_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); } +/** + * GetCurrentThreadId: + * + * 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. 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 :) + */ +gsize GetCurrentThreadId(void) +{ + 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); +} + +/** + * GetCurrentThread: + * + * Looks up the handle associated with the current thread. Under + * Windows this is a pseudohandle, and must be duplicated with + * DuplicateHandle() for some operations. + * + * Return value: The current thread handle, or %NULL on failure. + * (Unknown whether Windows has a possible failure here. It may be + * necessary to implement the pseudohandle-constant behaviour). + */ +gpointer GetCurrentThread(void) +{ + gpointer ret=NULL; + gsize tid; + + mono_once(&thread_hash_once, thread_hash_init); + mono_once (&thread_ops_once, thread_ops_init); + + tid = GetCurrentThreadId(); + + ret = _wapi_thread_handle_from_id ((pthread_t)tid); + if (!ret) { + ret = thread_attach (NULL); + } + + return(ret); +} + /** * ResumeThread: * @handle: the thread handle to resume @@ -360,9 +619,37 @@ gboolean GetExitCodeThread(WapiHandle *handle, guint32 *exitcode) * * Return value: the previous suspend count, or 0xFFFFFFFF on error. */ -guint32 ResumeThread(WapiHandle *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 } /** @@ -374,9 +661,63 @@ guint32 ResumeThread(WapiHandle *handle G_GNUC_UNUSED) * * Return value: the previous suspend count, or 0xFFFFFFFF on error. */ -guint32 SuspendThread(WapiHandle *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 +} + +/* + * We assume here that TLS_MINIMUM_AVAILABLE is less than + * PTHREAD_KEYS_MAX, allowing enough overhead for a few TLS keys for + * library usage. + * + * Currently TLS_MINIMUM_AVAILABLE is 64 and _POSIX_THREAD_KEYS_MAX + * (the minimum value for PTHREAD_KEYS_MAX) is 128, so we should be + * fine. + */ + +static pthread_key_t TLS_keys[TLS_MINIMUM_AVAILABLE]; +static gboolean TLS_used[TLS_MINIMUM_AVAILABLE]={FALSE}; +static guint32 TLS_spinlock=0; + +guint32 +mono_pthread_key_for_tls (guint32 idx) +{ + return (guint32)TLS_keys [idx]; } /** @@ -391,9 +732,39 @@ guint32 SuspendThread(WapiHandle *handle G_GNUC_UNUSED) */ guint32 TlsAlloc(void) { + guint32 i; + int thr_ret; + + 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)); +} + +gboolean _wapi_thread_dispatch_apc_queue (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 (0); + } + + _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 */