Merge pull request #2816 from xmcclure/profile-clean-0
[mono.git] / mono / io-layer / wthreads.c
index 781e3318f276ec37fd24cc39e700f13c10c746ee..304fd35adf807e349c77529ee2813686d0e6553a 100644 (file)
@@ -7,6 +7,7 @@
  * (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-lazy-init.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 */
@@ -57,6 +46,14 @@ struct _WapiHandleOps _wapi_thread_ops = {
        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;
 
@@ -120,11 +117,11 @@ wapi_thread_handle_set_exited (gpointer handle, guint32 exitstatus)
                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);
@@ -142,7 +139,7 @@ wapi_thread_handle_set_exited (gpointer handle, guint32 exitstatus)
        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 */
@@ -182,7 +179,7 @@ wapi_create_thread_handle (void)
         */
        _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;
 }
@@ -199,193 +196,231 @@ wapi_get_current_thread_handle (void)
        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)
+gboolean
+_wapi_thread_cur_apc_pending (void)
 {
-       MonoNativeThreadId id;
-
-       id = mono_native_thread_id_get ();
-       return MONO_NATIVE_THREAD_ID_TO_UINT (id);
+       return mono_thread_info_is_interrupt_state (mono_thread_info_current ());
 }
 
-static mono_lazy_init_t sleepex_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
-static mono_mutex_t sleepex_mutex;
-static mono_cond_t sleepex_cond;
-
-static void
-sleepex_initialize (void)
+void
+_wapi_thread_own_mutex (gpointer mutex)
 {
-       mono_mutex_init (&sleepex_mutex);
-       mono_cond_init (&sleepex_cond, NULL);
-}
+       WapiHandle_thread *thread;
+       
+       thread = get_current_thread ();
 
-static void
-sleepex_interrupt (gpointer data)
-{
-       mono_mutex_lock (&sleepex_mutex);
-       mono_cond_broadcast (&sleepex_cond);
-       mono_mutex_unlock (&sleepex_mutex);
+       _wapi_handle_ref (mutex);
+       
+       g_ptr_array_add (thread->owned_mutexes, mutex);
 }
 
-static inline guint32
-sleepex_interruptable (guint32 ms)
+void
+_wapi_thread_disown_mutex (gpointer mutex)
 {
-       gboolean interrupted;
-       guint32 start, now, end;
-
-       g_assert (INFINITE == G_MAXUINT32);
-
-       start = mono_msec_ticks ();
-
-       if (start < G_MAXUINT32 - ms) {
-               end = start + ms;
-       } else {
-               /* start + ms would overflow guint32 */
-               end = G_MAXUINT32;
-       }
+       WapiHandle_thread *thread;
 
-       mono_lazy_initialize (&sleepex_init, sleepex_initialize);
+       thread = get_current_thread ();
 
-       mono_mutex_lock (&sleepex_mutex);
+       _wapi_handle_unref (mutex);
+       
+       g_ptr_array_remove (thread->owned_mutexes, mutex);
+}
 
-       for (now = mono_msec_ticks (); ms == INFINITE || now - start < ms; now = mono_msec_ticks ()) {
-               mono_thread_info_install_interrupt (sleepex_interrupt, NULL, &interrupted);
-               if (interrupted) {
-                       mono_mutex_unlock (&sleepex_mutex);
-                       return WAIT_IO_COMPLETION;
+/**
+ * _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);
                }
-
-               if (ms < INFINITE)
-                       mono_cond_timedwait_ms (&sleepex_cond, &sleepex_mutex, end - now);
                else
-                       mono_cond_wait (&sleepex_cond, &sleepex_mutex);
-
-               mono_thread_info_uninstall_interrupt (&interrupted);
-               if (interrupted) {
-                       mono_mutex_unlock (&sleepex_mutex);
-                       return WAIT_IO_COMPLETION;
+               {
+                       return (priorities[i-1]);
                }
        }
+#endif
 
-       mono_mutex_unlock (&sleepex_mutex);
-
-       return 0;
+       return (THREAD_PRIORITY_NORMAL);
 }
 
 /**
- * SleepEx:
- * @ms: The time in milliseconds to suspend for
- * @alertable: if TRUE, the wait can be interrupted by an APC call
+ * _wapi_thread_priority_to_posix_priority:
  *
- * 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.
+ *   Convert a WapiThreadPriority to a POSIX priority.
+ * priority is a WapiThreadPriority,
+ * policy is the current scheduling policy
  */
-guint32
-SleepEx (guint32 ms, gboolean alertable)
+static int 
+_wapi_thread_priority_to_posix_priority (WapiThreadPriority priority, int policy)
 {
-       if (ms == 0) {
-               MonoThreadInfo *info;
-
-               mono_thread_info_yield ();
-
-               info = mono_thread_info_current ();
-               if (info && mono_thread_info_is_interrupt_state (info))
-                       return WAIT_IO_COMPLETION;
-
-               return 0;
-       }
-
-       if (alertable)
-               return sleepex_interruptable (ms);
-
-       DEBUG("%s: Sleeping for %d ms", __func__, ms);
-
-       if (ms == INFINITE) {
-               do {
-                       sleep (G_MAXUINT32);
-               } while (1);
-       } else {
-               int ret;
-#if defined (__linux__) && !defined(PLATFORM_ANDROID)
-               struct timespec start, target;
-
-               /* 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 / 1000;
-               target.tv_nsec += (ms % 1000) * 1000000;
-               if (target.tv_nsec > 999999999) {
-                       target.tv_nsec -= 999999999;
-                       target.tv_sec ++;
+/* 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;
+                               }
+                       }
                }
-
-               do {
-                       ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
-               } while (ret != 0);
-#else
-               struct timespec req, rem;
-
-               req.tv_sec = ms / 1000;
-               req.tv_nsec = (ms % 1000) * 1000000;
-
-               do {
-                       memset (&rem, 0, sizeof (rem));
-                       ret = nanosleep (&req, &rem);
-               } while (ret != 0);
-#endif /* __linux__ */
        }
+#endif
 
-       return 0;
-}
-
-void
-Sleep(guint32 ms)
-{
-       SleepEx(ms, FALSE);
-}
-
-gboolean
-_wapi_thread_cur_apc_pending (void)
-{
-       return mono_thread_info_is_interrupt_state (mono_thread_info_current ());
+       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;
+       }
 }
 
-void
-_wapi_thread_own_mutex (gpointer mutex)
+/**
+ * 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)
 {
-       WapiHandle_thread *thread;
+       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);
+       }
        
-       thread = get_current_thread ();
-
-       _wapi_handle_ref (mutex);
+       switch (pthread_getschedparam (thread_handle->id, &policy, &param)) {
+               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);
+       }
        
-       g_ptr_array_add (thread->owned_mutexes, mutex);
+       return (THREAD_PRIORITY_NORMAL);
 }
 
-void
-_wapi_thread_disown_mutex (gpointer mutex)
+/**
+ * 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)
 {
-       WapiHandle_thread *thread;
-
-       thread = get_current_thread ();
-
-       _wapi_handle_unref (mutex);
+       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;
+       }
        
-       g_ptr_array_remove (thread->owned_mutexes, mutex);
+       rv = pthread_getschedparam (thread_handle->id, &policy, &param);
+       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, &param)) {
+               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*