* Dick Porter (dick@ximian.com)
*
* (C) 2002-2006 Ximian, Inc.
+ * Copyright 2003-2011 Novell, Inc (http://www.novell.com)
+ * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
*/
#include <config.h>
#include <stdio.h>
#include <glib.h>
#include <string.h>
-#include <mono/utils/gc_wrapper.h>
#include <pthread.h>
#include <signal.h>
#include <sched.h>
#include <mono/io-layer/misc-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>
#include <mono/io-layer/atomic.h>
+#include <mono/utils/mono-threads.h>
+#include <mono/utils/gc_wrapper.h>
+
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
-#undef DEBUG
-#undef TLS_DEBUG
+#if 0
+#define DEBUG(...) g_message(__VA_ARGS__)
+#else
+#define DEBUG(...)
+#endif
#if 0
#define WAIT_DEBUG(code) do { code } while (0)
pid_t pid = _wapi_getpid ();
pthread_t tid = pthread_self ();
-#ifdef DEBUG
- g_message ("%s: Thread %p abandoning held mutexes", __func__, handle);
-#endif
+ DEBUG ("%s: Thread %p abandoning held mutexes", __func__, handle);
if (handle == NULL) {
handle = _wapi_thread_handle_from_id (pthread_self ());
return;
}
-#ifdef DEBUG
- g_message ("%s: Thread %p terminating", __func__, handle);
-#endif
+ DEBUG ("%s: Thread %p terminating", __func__, handle);
_wapi_thread_abandon_mutexes (handle);
g_assert (thr_ret == 0);
pthread_cleanup_pop (0);
-#ifdef DEBUG
- g_message("%s: Recording thread handle %p id %ld status as %d",
+ DEBUG("%s: Recording thread handle %p id %ld status as %d",
__func__, handle, thread_handle->id, exitstatus);
-#endif
/* The thread is no longer active, so unref it */
_wapi_handle_unref (handle);
/* Call pthread_exit() to call destructors and really exit the
* thread
*/
- pthread_exit (NULL);
+ mono_gc_pthread_exit (NULL);
}
static void thread_attached_exit (gpointer handle)
struct _WapiHandle_thread *thread = (struct _WapiHandle_thread *)args;
int thr_ret;
- thr_ret = pthread_detach (pthread_self ());
+ thr_ret = mono_gc_pthread_detach (pthread_self ());
g_assert (thr_ret == 0);
thr_ret = pthread_setspecific (thread_hash_key,
shutting down we still end up here, and at this
point the thread_hash_key might already be
destroyed. */
- pthread_exit (NULL);
+ mono_gc_pthread_exit (NULL);
}
- thread->id = pthread_self();
+ DEBUG ("%s: started thread id %ld", __func__, thread->id);
-#ifdef DEBUG
- g_message ("%s: started thread id %ld", __func__, thread->id);
-#endif
+ /* We set it again here since passing &thread->id to pthread_create is racy
+ as the thread can start running before the value is set.*/
+ thread->id = pthread_self ();
if (thread->create_flags & CREATE_SUSPENDED) {
_wapi_thread_suspend (thread);
#endif
}
+#ifdef PTHREAD_STACK_MIN
+ if (stacksize < PTHREAD_STACK_MIN)
+ stacksize = PTHREAD_STACK_MIN;
+#endif
+
#ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
thr_ret = pthread_attr_setstacksize(&attr, stacksize);
g_assert (thr_ret == 0);
MONO_SEM_INIT (&thread_handle_p->suspend_sem, 0);
thread_handle_p->handle = handle;
- ret = pthread_create (&thread_handle_p->id, &attr,
- thread_start_routine, (void *)thread_handle_p);
+
+ ret = mono_threads_pthread_create (&thread_handle_p->id, &attr,
+ thread_start_routine, (void *)thread_handle_p);
+
if (ret != 0) {
-#ifdef DEBUG
- g_message ("%s: Thread create error: %s", __func__,
- strerror(ret));
-#endif
+ g_warning ("%s: Error creating native thread handle %s (%d)", __func__,
+ strerror (ret), ret);
+ SetLastError (ERROR_GEN_FAILURE);
/* Two, because of the reference we took above */
unrefs = 2;
}
ct_ret = handle;
-#ifdef DEBUG
- g_message("%s: Started thread handle %p ID %ld", __func__, handle,
+ DEBUG("%s: Started thread handle %p ID %ld", __func__, handle,
thread_handle_p->id);
-#endif
if (tid != NULL) {
#ifdef PTHREAD_POINTER_ID
(ret = pthread_getspecific (thread_hash_key)) != NULL) {
/* We know the handle */
-#ifdef DEBUG
- g_message ("%s: Returning %p for self thread %ld from TLS",
+ DEBUG ("%s: Returning %p for self thread %ld from TLS",
__func__, ret, tid);
-#endif
return(ret);
}
-#ifdef DEBUG
- g_message ("%s: Returning NULL for unknown or non-self thread %ld",
+ DEBUG ("%s: Returning NULL for unknown or non-self thread %ld",
__func__, tid);
-#endif
return(NULL);
return(FALSE);
}
-#ifdef DEBUG
- g_message ("%s: looking at thread %ld from process %d", __func__, thread_handle->id, 0);
-#endif
+ DEBUG ("%s: looking at thread %ld from process %d", __func__, thread_handle->id, 0);
if (pthread_equal (thread_handle->id, tid)) {
-#ifdef DEBUG
- g_message ("%s: found the thread we are looking for",
+ DEBUG ("%s: found the thread we are looking for",
__func__);
-#endif
return(TRUE);
}
}
-#ifdef DEBUG
- g_message ("%s: not found %ld, returning FALSE", __func__, tid);
-#endif
+ DEBUG ("%s: not found %ld, returning FALSE", __func__, tid);
return(FALSE);
}
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
+ DEBUG ("%s: looking up thread %"G_GSIZE_FORMAT, __func__, tid);
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
+ DEBUG ("%s: returning thread handle %p", __func__, ret);
return(ret);
}
thread_exit(exitcode, thread);
} else {
/* Just blow this thread away */
- pthread_exit (NULL);
+ mono_gc_pthread_exit (NULL);
}
}
return (FALSE);
}
-#ifdef DEBUG
- g_message ("%s: Finding exit status for thread handle %p id %ld",
+ DEBUG ("%s: Finding exit status for thread handle %p id %ld",
__func__, handle, thread_handle->id);
-#endif
if (exitcode == NULL) {
-#ifdef DEBUG
- g_message ("%s: Nowhere to store exit code", __func__);
-#endif
+ DEBUG ("%s: Nowhere to store exit code", __func__);
return(FALSE);
}
if (thread_handle->state != THREAD_STATE_EXITED) {
-#ifdef DEBUG
- g_message ("%s: Thread still active (state %d, exited is %d)",
+ DEBUG ("%s: Thread still active (state %d, exited is %d)",
__func__, thread_handle->state,
THREAD_STATE_EXITED);
-#endif
*exitcode = STILL_ACTIVE;
return(TRUE);
}
thr_ret = pthread_setspecific (thread_attached_key, (void *)handle);
g_assert (thr_ret == 0);
-#ifdef DEBUG
- g_message("%s: Attached thread handle %p ID %ld", __func__, handle,
+ DEBUG("%s: Attached thread handle %p ID %ld", __func__, handle,
thread_handle_p->id);
-#endif
if (tid != NULL) {
#ifdef PTHREAD_POINTER_ID
return(0xFFFFFFFF);
}
-/*
- * 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];
-}
-
-/**
- * TlsAlloc:
- *
- * Allocates a Thread Local Storage (TLS) index. Any thread in the
- * same process can use this index to store and retrieve values that
- * are local to that thread.
- *
- * Return value: The index value, or %TLS_OUT_OF_INDEXES if no index
- * is available.
- */
-guint32 TlsAlloc(void)
-{
- guint32 i;
- int thr_ret;
-
- MONO_SPIN_LOCK (TLS_spinlock);
-
- for(i=0; i<TLS_MINIMUM_AVAILABLE; i++) {
- if(TLS_used[i]==FALSE) {
- TLS_used[i]=TRUE;
- thr_ret = pthread_key_create(&TLS_keys[i], NULL);
- g_assert (thr_ret == 0);
-
- MONO_SPIN_UNLOCK (TLS_spinlock);
-
-#ifdef TLS_DEBUG
- g_message ("%s: returning key %d", __func__, i);
-#endif
-
- return(i);
- }
- }
-
- MONO_SPIN_UNLOCK (TLS_spinlock);
-
-#ifdef TLS_DEBUG
- g_message ("%s: out of indices", __func__);
-#endif
-
-
- return(TLS_OUT_OF_INDEXES);
-}
-
-#define MAKE_GC_ID(idx) (GUINT_TO_POINTER((idx)|(GetCurrentThreadId()<<8)))
-
-/**
- * TlsFree:
- * @idx: The TLS index to free
- *
- * Releases a TLS index, making it available for reuse. This call
- * will delete any TLS data stored under index @idx in all threads.
- *
- * Return value: %TRUE on success, %FALSE otherwise.
- */
-gboolean TlsFree(guint32 idx)
-{
- int thr_ret;
-
-#ifdef TLS_DEBUG
- g_message ("%s: freeing key %d", __func__, idx);
-#endif
-
- MONO_SPIN_LOCK (TLS_spinlock);
-
- if(TLS_used[idx]==FALSE) {
- MONO_SPIN_UNLOCK (TLS_spinlock);
-
- return(FALSE);
- }
-
- TLS_used[idx]=FALSE;
- thr_ret = pthread_key_delete(TLS_keys[idx]);
- g_assert (thr_ret == 0);
-
- MONO_SPIN_UNLOCK (TLS_spinlock);
-
- return(TRUE);
-}
-
-/**
- * TlsGetValue:
- * @idx: The TLS index to retrieve
- *
- * Retrieves the TLS data stored under index @idx.
- *
- * Return value: The value stored in the TLS index @idx in the current
- * thread, or %NULL on error. As %NULL can be a valid return value,
- * in this case GetLastError() returns %ERROR_SUCCESS.
- */
-gpointer TlsGetValue(guint32 idx)
-{
- gpointer ret;
-
-#ifdef TLS_DEBUG
- g_message ("%s: looking up key %d", __func__, idx);
-#endif
-
- ret=pthread_getspecific(TLS_keys[idx]);
-
-#ifdef TLS_DEBUG
- g_message ("%s: returning %p", __func__, ret);
-#endif
-
- return(ret);
-}
-
-/**
- * TlsSetValue:
- * @idx: The TLS index to store
- * @value: The value to store under index @idx
- *
- * Stores @value at TLS index @idx.
- *
- * Return value: %TRUE on success, %FALSE otherwise.
- */
-gboolean TlsSetValue(guint32 idx, gpointer value)
-{
- int ret;
-
-#ifdef TLS_DEBUG
- g_message ("%s: setting key %d to %p", __func__, idx, value);
-#endif
-
- MONO_SPIN_LOCK (TLS_spinlock);
-
- if(TLS_used[idx]==FALSE) {
-#ifdef TLS_DEBUG
- g_message ("%s: key %d unused", __func__, idx);
-#endif
-
- MONO_SPIN_UNLOCK (TLS_spinlock);
-
- return(FALSE);
- }
-
- ret=pthread_setspecific(TLS_keys[idx], value);
- if(ret!=0) {
-#ifdef TLS_DEBUG
- g_message ("%s: pthread_setspecific error: %s", __func__,
- strerror (ret));
-#endif
-
- MONO_SPIN_UNLOCK (TLS_spinlock);
-
- return(FALSE);
- }
-
- MONO_SPIN_UNLOCK (TLS_spinlock);
-
- return(TRUE);
-}
-
/**
* SleepEx:
* @ms: The time in milliseconds to suspend for
int ret;
gpointer current_thread = NULL;
-#ifdef DEBUG
- g_message("%s: Sleeping for %d ms", __func__, ms);
-#endif
+ DEBUG("%s: Sleeping for %d ms", __func__, ms);
if (alertable) {
current_thread = _wapi_thread_handle_from_id (pthread_self ());
req.tv_nsec=ms_rem*1000000;
again:
+ memset (&rem, 0, sizeof (rem));
ret=nanosleep(&req, &rem);
if (alertable && _wapi_thread_apc_pending (current_thread)) {
if(ret==-1) {
/* Sleep interrupted with rem time remaining */
-#ifdef DEBUG
+#ifdef DEBUG_ENABLED
guint32 rems=rem.tv_sec*1000 + rem.tv_nsec/1000000;
g_message("%s: Still got %d ms to go", __func__, rems);
ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
(gpointer *)&thread);
if (ok == FALSE) {
-#ifdef DEBUG
/* This might happen at process shutdown, as all
* thread handles are forcibly closed. If a thread
* still has an alertable wait the final
* _wapi_thread_apc_pending check will probably fail
* to find the handle
*/
- g_warning ("%s: error looking up thread handle %p", __func__,
+ DEBUG ("%s: error looking up thread handle %p", __func__,
handle);
-#endif
return (FALSE);
}
_wapi_handle_unref (wait_handle);
}
+
+gpointer wapi_prepare_interrupt_thread (gpointer thread_handle)
+{
+ struct _WapiHandle_thread *thread;
+ gboolean ok;
+ gpointer prev_handle, wait_handle;
+
+ ok = _wapi_lookup_handle (thread_handle, WAPI_HANDLE_THREAD,
+ (gpointer *)&thread);
+ g_assert (ok);
+
+ while (TRUE) {
+ wait_handle = thread->wait_handle;
+
+ /*
+ * Atomically obtain the handle the thread is waiting on, and
+ * change it to a flag value.
+ */
+ prev_handle = InterlockedCompareExchangePointer (&thread->wait_handle,
+ INTERRUPTION_REQUESTED_HANDLE, wait_handle);
+ if (prev_handle == INTERRUPTION_REQUESTED_HANDLE)
+ /* Already interrupted */
+ return 0;
+ if (prev_handle == wait_handle)
+ break;
+
+ /* Try again */
+ }
+
+ WAIT_DEBUG (printf ("%p: state -> INTERRUPTED.\n", thread_handle->id););
+
+ return wait_handle;
+}
+
+void wapi_finish_interrupt_thread (gpointer wait_handle)
+{
+ pthread_cond_t *cond;
+ mono_mutex_t *mutex;
+ guint32 idx;
+
+ if (!wait_handle)
+ /* Not waiting */
+ return;
+
+ /* If we reach here, then wait_handle is set to the flag value,
+ * which means that the target thread is either
+ * - before the first CAS in timedwait, which means it won't enter the
+ * wait.
+ * - it is after the first CAS, so it is already waiting, or it will
+ * enter the wait, and it will be interrupted by the broadcast.
+ */
+ idx = GPOINTER_TO_UINT(wait_handle);
+ cond = &_WAPI_PRIVATE_HANDLES(idx).signal_cond;
+ mutex = &_WAPI_PRIVATE_HANDLES(idx).signal_mutex;
+
+ mono_mutex_lock (mutex);
+ mono_cond_broadcast (cond);
+ mono_mutex_unlock (mutex);
+
+ /* ref added by set_wait_handle */
+ _wapi_handle_unref (wait_handle);
+}
+
+
+/*
+ * wapi_self_interrupt:
+ *
+ * This is not part of the WIN32 API.
+ * Set the 'interrupted' state of the calling thread if it's NULL.
+ */
+void wapi_self_interrupt (void)
+{
+ struct _WapiHandle_thread *thread;
+ gboolean ok;
+ gpointer prev_handle, wait_handle;
+ gpointer thread_handle;
+
+
+ thread_handle = OpenThread (0, 0, GetCurrentThreadId ());
+ ok = _wapi_lookup_handle (thread_handle, WAPI_HANDLE_THREAD,
+ (gpointer *)&thread);
+ g_assert (ok);
+
+ while (TRUE) {
+ wait_handle = thread->wait_handle;
+
+ /*
+ * Atomically obtain the handle the thread is waiting on, and
+ * change it to a flag value.
+ */
+ prev_handle = InterlockedCompareExchangePointer (&thread->wait_handle,
+ INTERRUPTION_REQUESTED_HANDLE, wait_handle);
+ if (prev_handle == INTERRUPTION_REQUESTED_HANDLE)
+ /* Already interrupted */
+ goto cleanup;
+ /*We did not get interrupted*/
+ if (prev_handle == wait_handle)
+ break;
+
+ /* Try again */
+ }
+
+ if (wait_handle) {
+ /* ref added by set_wait_handle */
+ _wapi_handle_unref (wait_handle);
+ }
+
+cleanup:
+ _wapi_handle_unref (thread_handle);
+}
+
/*
* wapi_clear_interruption:
*
* This is not part of the WIN32 API.
* Clear the 'interrupted' state of the calling thread.
+ * This function is signal safe
*/
void wapi_clear_interruption (void)
{
_wapi_handle_unref (handle);
WAIT_DEBUG (printf ("%p: state -> NORMAL.\n", GetCurrentThreadId ()););
} else {
- g_assert (prev_handle == INTERRUPTION_REQUESTED_HANDLE);
+ /*It can be NULL if it was asynchronously cleared*/
+ g_assert (prev_handle == INTERRUPTION_REQUESTED_HANDLE || prev_handle == NULL);
WAIT_DEBUG (printf ("%p: finished waiting.\n", GetCurrentThreadId ()););
}