* (C) 2002-2006 Ximian, Inc.
* Copyright 2003-2011 Novell, Inc (http://www.novell.com)
* Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
#include <config.h>
#include <mono/io-layer/wapi.h>
#include <mono/io-layer/wapi-private.h>
#include <mono/io-layer/handles-private.h>
-#include <mono/io-layer/misc-private.h>
#include <mono/io-layer/thread-private.h>
#include <mono/io-layer/mutex-private.h>
+#include <mono/io-layer/io-trace.h>
#include <mono/utils/mono-threads.h>
#include <mono/utils/atomic.h>
-#include <mono/utils/mono-mutex.h>
+#include <mono/utils/mono-time.h>
+#include <mono/utils/mono-once.h>
+#include <mono/utils/mono-logger-internals.h>
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
-#if 0
-#define DEBUG(...) g_message(__VA_ARGS__)
-#else
-#define DEBUG(...)
-#endif
-
-#if 0
-#define WAIT_DEBUG(code) do { code } while (0)
-#else
-#define WAIT_DEBUG(code) do { } while (0)
-#endif
-
struct _WapiHandleOps _wapi_thread_ops = {
NULL, /* close */
NULL, /* signal */
NULL, /* special_wait */
NULL /* prewait */
};
+
+typedef enum {
+ THREAD_PRIORITY_LOWEST = -2,
+ THREAD_PRIORITY_BELOW_NORMAL = -1,
+ THREAD_PRIORITY_NORMAL = 0,
+ THREAD_PRIORITY_ABOVE_NORMAL = 1,
+ THREAD_PRIORITY_HIGHEST = 2
+} WapiThreadPriority;
static mono_once_t thread_ops_once = MONO_ONCE_INIT;
return;
}
- DEBUG ("%s: Thread %p terminating", __func__, handle);
+ MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p terminating", __func__, handle);
thread_handle = lookup_thread (handle);
- DEBUG ("%s: Thread %p abandoning held mutexes", __func__, handle);
+ MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p abandoning held mutexes", __func__, handle);
for (i = 0; i < thread_handle->owned_mutexes->len; i++) {
gpointer mutex = g_ptr_array_index (thread_handle->owned_mutexes, i);
thr_ret = _wapi_handle_unlock_handle (handle);
g_assert (thr_ret == 0);
- DEBUG("%s: Recording thread handle %p id %ld status as %d",
+ MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Recording thread handle %p id %ld status as %d",
__func__, handle, thread_handle->id, exitstatus);
/* The thread is no longer active, so unref it */
*/
_wapi_handle_ref (handle);
- DEBUG ("%s: started thread id %ld", __func__, thread->id);
+ MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: started thread id %ld", __func__, thread->id);
return handle;
}
return get_current_thread_handle ();
}
-/**
- * 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)
-{
- MonoNativeThreadId id;
-
- id = mono_native_thread_id_get ();
- return MONO_NATIVE_THREAD_ID_TO_UINT (id);
-}
-
-/**
- * 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.
- */
-guint32
-SleepEx (guint32 ms, gboolean alertable)
-{
- int ms_quot, ms_rem;
- int ret;
- gpointer current_thread = NULL;
-#if defined (__linux__) && !defined(PLATFORM_ANDROID)
- struct timespec start, target;
-#else
- struct timespec rem;
-#endif
-
- DEBUG("%s: Sleeping for %d ms", __func__, ms);
-
- if (alertable) {
- current_thread = get_current_thread_handle ();
-
- if (_wapi_thread_cur_apc_pending ())
- return WAIT_IO_COMPLETION;
- }
-
- if(ms==0) {
- sched_yield();
- return 0;
- }
-
- /* FIXME: check for INFINITE and sleep forever */
- ms_quot = ms / 1000;
- ms_rem = ms % 1000;
-
-#if defined (__linux__) && !defined(PLATFORM_ANDROID)
- /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
- ret = clock_gettime (CLOCK_MONOTONIC, &start);
- g_assert (ret == 0);
- target = start;
- target.tv_sec += ms_quot;
- target.tv_nsec += ms_rem * 1000000;
- if (target.tv_nsec > 999999999) {
- target.tv_nsec -= 999999999;
- target.tv_sec ++;
- }
-
- while (TRUE) {
- ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
-
- if (alertable && _wapi_thread_cur_apc_pending ())
- return WAIT_IO_COMPLETION;
-
- if (ret == 0)
- break;
- }
-
-#else
- struct timespec req;
-
- req.tv_sec=ms_quot;
- req.tv_nsec=ms_rem*1000000;
-
-again:
- memset (&rem, 0, sizeof (rem));
- ret=nanosleep(&req, &rem);
-
- if (alertable && _wapi_thread_cur_apc_pending ())
- return WAIT_IO_COMPLETION;
-
- if(ret==-1) {
- /* Sleep interrupted with rem time remaining */
-#ifdef DEBUG_ENABLED
- guint32 rems=rem.tv_sec*1000 + rem.tv_nsec/1000000;
-
- g_message("%s: Still got %d ms to go", __func__, rems);
-#endif
- req=rem;
- goto again;
- }
-
-#endif /* __linux__ */
-
- return 0;
-}
-
-void
-Sleep(guint32 ms)
-{
- SleepEx(ms, FALSE);
-}
-
gboolean
_wapi_thread_cur_apc_pending (void)
{
g_ptr_array_remove (thread->owned_mutexes, mutex);
}
+/**
+ * _wapi_thread_posix_priority_to_priority:
+ *
+ * Convert a POSIX priority to a WapiThreadPriority.
+ * sched_priority is a POSIX priority,
+ * policy is the current scheduling policy
+ */
+static WapiThreadPriority
+_wapi_thread_posix_priority_to_priority (int sched_priority, int policy)
+{
+/* Necessary to get valid priority range */
+#ifdef _POSIX_PRIORITY_SCHEDULING
+ int max,
+ min,
+ i,
+ priority,
+ chunk;
+ WapiThreadPriority priorities[] = {
+ THREAD_PRIORITY_LOWEST,
+ THREAD_PRIORITY_LOWEST,
+ THREAD_PRIORITY_BELOW_NORMAL,
+ THREAD_PRIORITY_NORMAL,
+ THREAD_PRIORITY_ABOVE_NORMAL,
+ THREAD_PRIORITY_HIGHEST,
+ THREAD_PRIORITY_HIGHEST
+ };
+
+ max = sched_get_priority_max (policy);
+ min = sched_get_priority_min (policy);
+
+ /* Partition priority range linearly,
+ assign each partition a thread priority */
+ if (max != min && 0 <= max && 0 <= min) {
+ for (i=1, priority=min, chunk=(max-min)/7;
+ i<6 && sched_priority > priority;
+ ++i) {
+ priority += chunk;
+ }
+
+ if (max <= priority)
+ {
+ return (THREAD_PRIORITY_HIGHEST);
+ }
+ else
+ {
+ return (priorities[i-1]);
+ }
+ }
+#endif
+
+ return (THREAD_PRIORITY_NORMAL);
+}
+
+/**
+ * _wapi_thread_priority_to_posix_priority:
+ *
+ * Convert a WapiThreadPriority to a POSIX priority.
+ * priority is a WapiThreadPriority,
+ * policy is the current scheduling policy
+ */
+static int
+_wapi_thread_priority_to_posix_priority (WapiThreadPriority priority, int policy)
+{
+/* Necessary to get valid priority range */
+#ifdef _POSIX_PRIORITY_SCHEDULING
+ int max,
+ min,
+ posix_priority,
+ i;
+ WapiThreadPriority priorities[] = {
+ THREAD_PRIORITY_LOWEST,
+ THREAD_PRIORITY_LOWEST,
+ THREAD_PRIORITY_BELOW_NORMAL,
+ THREAD_PRIORITY_NORMAL,
+ THREAD_PRIORITY_ABOVE_NORMAL,
+ THREAD_PRIORITY_HIGHEST,
+ THREAD_PRIORITY_HIGHEST
+ };
+
+ max = sched_get_priority_max (policy);
+ min = sched_get_priority_min (policy);
+
+ /* Partition priority range linearly,
+ numerically approximate matching ThreadPriority */
+ if (max != min && 0 <= max && 0 <= min) {
+ for (i=0; i<7; ++i) {
+ if (priorities[i] == priority) {
+ posix_priority = min + ((max-min)/7) * i;
+ if (max < posix_priority)
+ {
+ return max;
+ }
+ else {
+ return posix_priority;
+ }
+ }
+ }
+ }
+#endif
+
+ switch (policy) {
+ case SCHED_FIFO:
+ case SCHED_RR:
+ return 50;
+#ifdef SCHED_BATCH
+ case SCHED_BATCH:
+#endif
+ case SCHED_OTHER:
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+/**
+ * GetThreadPriority:
+ * @param handle: The thread handle to query.
+ *
+ * Gets the priority of the given thread.
+ * @return: A MonoThreadPriority approximating the current POSIX
+ * thread priority, or THREAD_PRIORITY_NORMAL on error.
+ */
+gint32
+GetThreadPriority (gpointer handle)
+{
+ struct _WapiHandle_thread *thread_handle;
+ int policy;
+ struct sched_param param;
+ gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
+ (gpointer *)&thread_handle);
+
+ if (ok == FALSE) {
+ return (THREAD_PRIORITY_NORMAL);
+ }
+
+ switch (pthread_getschedparam (thread_handle->id, &policy, ¶m)) {
+ case 0:
+ return (_wapi_thread_posix_priority_to_priority (param.sched_priority, policy));
+ case ESRCH:
+ g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
+ }
+
+ return (THREAD_PRIORITY_NORMAL);
+}
+
+/**
+ * SetThreadPriority:
+ * @param handle: The thread handle to query.
+ * @param priority: The priority to give to the thread.
+ *
+ * Sets the priority of the given thread.
+ * @return: TRUE on success, FALSE on failure or error.
+ */
+gboolean
+SetThreadPriority (gpointer handle, gint32 priority)
+{
+ struct _WapiHandle_thread *thread_handle;
+ int policy,
+ posix_priority,
+ rv;
+ struct sched_param param;
+ gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
+ (gpointer *)&thread_handle);
+
+ if (ok == FALSE) {
+ return ok;
+ }
+
+ rv = pthread_getschedparam (thread_handle->id, &policy, ¶m);
+ if (rv) {
+ if (ESRCH == rv)
+ g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
+ return FALSE;
+ }
+
+ posix_priority = _wapi_thread_priority_to_posix_priority (priority, policy);
+ if (0 > posix_priority)
+ return FALSE;
+
+ param.sched_priority = posix_priority;
+ switch (pthread_setschedparam (thread_handle->id, policy, ¶m)) {
+ case 0:
+ return TRUE;
+ case ESRCH:
+ g_warning ("pthread_setschedparam: error looking up thread id %x", (gsize)thread_handle->id);
+ break;
+ case ENOTSUP:
+ g_warning ("%s: priority %d not supported", __func__, priority);
+ break;
+ case EPERM:
+ g_warning ("%s: permission denied", __func__);
+ break;
+ }
+
+ return FALSE;
+}
+
char*
wapi_current_thread_desc (void)
{