Skip to content

Instantly share code, notes, and snippets.

@justincc
Last active August 29, 2015 14:06
Show Gist options
  • Save justincc/31e52218d098529b4696 to your computer and use it in GitHub Desktop.
Save justincc/31e52218d098529b4696 to your computer and use it in GitHub Desktop.
From 53d170e17ad6fd46f2d551e2df25277db6e4d4cf Mon Sep 17 00:00:00 2001
From: Justin Clark-Casey <justincc@justincc.org>
Date: Tue, 16 Sep 2014 18:03:42 +0100
Subject: [PATCH] This is a modified patch from
https://bugzilla.novell.com/show_bug.cgi?id=540524 updated to build against
mono 3.2.8
This uses Thread.Priority to change RT thread priority if mono is run via "$ sudo chrt --rr 10 <exec>" or similar
For many reasons this is not a good approach - this patch is primarily for test purposes.
---
mcs/class/corlib/System.Threading/Thread.cs | 10 +-
mono/io-layer/threads.h | 2 +
mono/io-layer/wthreads.c | 219 ++++++++++++++++++++++++++++
mono/metadata/icall-def.h | 2 +
mono/metadata/threads-types.h | 2 +
mono/metadata/threads.c | 37 +++++
6 files changed, 270 insertions(+), 2 deletions(-)
diff --git a/mcs/class/corlib/System.Threading/Thread.cs b/mcs/class/corlib/System.Threading/Thread.cs
index 5af6800..446af1f 100644
--- a/mcs/class/corlib/System.Threading/Thread.cs
+++ b/mcs/class/corlib/System.Threading/Thread.cs
@@ -585,13 +585,19 @@ namespace System.Threading {
}
}
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern static ThreadPriority GetPriority_internal (InternalThread thread);
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern static void SetPriority_internal (InternalThread thread, ThreadPriority priority);
+
public ThreadPriority Priority {
get {
- return(ThreadPriority.Lowest);
+ return GetPriority_internal (Internal);
}
set {
- // FIXME: Implement setter.
+ SetPriority_internal (Internal, value);
}
}
diff --git a/mono/io-layer/threads.h b/mono/io-layer/threads.h
index 12b7c0c..65ec7d8 100644
--- a/mono/io-layer/threads.h
+++ b/mono/io-layer/threads.h
@@ -59,6 +59,8 @@ extern void Sleep(guint32 ms);
extern guint32 SleepEx(guint32 ms, gboolean alertable);
extern guint32 QueueUserAPC (WapiApcProc apc_callback, gpointer thread_handle,
gpointer param);
+extern gint32 GetThreadPriority (gpointer handle);
+extern gboolean SetThreadPriority (gpointer handle, gint32 priority);
/* Kludge alert! Making this visible outside io-layer is broken, but I
* can't find any w32 call that will let me do this.
diff --git a/mono/io-layer/wthreads.c b/mono/io-layer/wthreads.c
index e623d71..0bcffcd 100644
--- a/mono/io-layer/wthreads.c
+++ b/mono/io-layer/wthreads.c
@@ -70,6 +70,14 @@ struct _WapiHandleOps _wapi_thread_ops = {
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;
static void thread_ops_init (void)
@@ -1306,3 +1314,214 @@ void _wapi_thread_disown_mutex (gpointer mutex)
g_ptr_array_remove (thread_handle->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)
+ {
+ g_debug("Get Using priority %i", THREAD_PRIORITY_HIGHEST);
+ return (THREAD_PRIORITY_HIGHEST);
+ }
+ else
+ {
+ g_debug("Get Using priority %i", priorities[i-1]);
+ 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);
+
+ g_debug("WAPI input in Set %i", priority);
+ g_debug("Min in Set %i", min);
+ g_debug("Max in Set %i", max);
+ g_debug("Policy in Set %i", 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)
+ {
+ g_debug("Set Using max priority %i", max);
+ return max;
+ }
+ else {
+ g_debug("Set Using priority %i",posix_priority);
+ return posix_priority;
+ }
+ }
+ }
+ }
+#endif
+
+ switch (policy) {
+ case SCHED_FIFO:
+ case SCHED_RR:
+ return 50;
+ case SCHED_BATCH:
+ 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) {
+/*
+ g_warning ("%s: error looking up thread handle %p", __func__,
+ handle);
+*/
+
+ return (THREAD_PRIORITY_NORMAL);
+ }
+
+ 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);
+ }
+
+ 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) {
+/*
+ g_warning ("%s: error looking up thread handle %p", __func__,
+ handle);
+*/
+
+ return ok;
+ }
+
+ 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;
+}
+
diff --git a/mono/metadata/icall-def.h b/mono/metadata/icall-def.h
index 63fb708..bb5ecdf 100644
--- a/mono/metadata/icall-def.h
+++ b/mono/metadata/icall-def.h
@@ -871,6 +871,7 @@ ICALL(THREAD_4, "FreeLocalSlotValues", mono_thread_free_local_slot_values)
ICALL(THREAD_55, "GetAbortExceptionState", ves_icall_System_Threading_Thread_GetAbortExceptionState)
ICALL(THREAD_7, "GetDomainID", ves_icall_System_Threading_Thread_GetDomainID)
ICALL(THREAD_8, "GetName_internal(System.Threading.InternalThread)", ves_icall_System_Threading_Thread_GetName_internal)
+ICALL(THREAD_56, "GetPriority_internal(System.Threading.InternalThread)", ves_icall_System_Threading_Thread_GetPriority_internal)
ICALL(THREAD_11, "GetState(System.Threading.InternalThread)", ves_icall_System_Threading_Thread_GetState)
ICALL(THREAD_53, "Interrupt_internal(System.Threading.InternalThread)", ves_icall_System_Threading_Thread_Interrupt_internal)
ICALL(THREAD_12, "Join_internal(System.Threading.InternalThread,int,intptr)", ves_icall_System_Threading_Thread_Join_internal)
@@ -878,6 +879,7 @@ ICALL(THREAD_13, "MemoryBarrier", ves_icall_System_Threading_Thread_MemoryBarrie
ICALL(THREAD_14, "ResetAbort_internal()", ves_icall_System_Threading_Thread_ResetAbort)
ICALL(THREAD_15, "Resume_internal()", ves_icall_System_Threading_Thread_Resume)
ICALL(THREAD_18, "SetName_internal(System.Threading.InternalThread,string)", ves_icall_System_Threading_Thread_SetName_internal)
+ICALL(THREAD_57, "SetPriority_internal(System.Threading.InternalThread,System.Threading.ThreadPriority)", ves_icall_System_Threading_Thread_SetPriority_internal)
ICALL(THREAD_21, "SetState(System.Threading.InternalThread,System.Threading.ThreadState)", ves_icall_System_Threading_Thread_SetState)
ICALL(THREAD_22, "Sleep_internal", ves_icall_System_Threading_Thread_Sleep_internal)
ICALL(THREAD_54, "SpinWait_nop", ves_icall_System_Threading_Thread_SpinWait_nop)
diff --git a/mono/metadata/threads-types.h b/mono/metadata/threads-types.h
index b090fcb..edb6bcf 100644
--- a/mono/metadata/threads-types.h
+++ b/mono/metadata/threads-types.h
@@ -67,6 +67,8 @@ gint32 ves_icall_System_Threading_Thread_GetDomainID (void) MONO_INTERNAL;
gboolean ves_icall_System_Threading_Thread_Yield (void) MONO_INTERNAL;
MonoString* ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThread *this_obj) MONO_INTERNAL;
void ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj, MonoString *name) MONO_INTERNAL;
+gint32 ves_icall_System_Threading_Thread_GetPriority_internal (MonoInternalThread *this_obj) MONO_INTERNAL;
+void ves_icall_System_Threading_Thread_SetPriority_internal (MonoInternalThread *this_obj, gint32 priority) MONO_INTERNAL;
MonoObject* ves_icall_System_Threading_Thread_GetCachedCurrentCulture (MonoInternalThread *this_obj) MONO_INTERNAL;
void ves_icall_System_Threading_Thread_SetCachedCurrentCulture (MonoThread *this_obj, MonoObject *culture) MONO_INTERNAL;
MonoObject* ves_icall_System_Threading_Thread_GetCachedCurrentUICulture (MonoInternalThread *this_obj) MONO_INTERNAL;
diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c
index 1b421c4..756d2c1 100755
--- a/mono/metadata/threads.c
+++ b/mono/metadata/threads.c
@@ -1342,6 +1342,43 @@ ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj
mono_thread_set_name_internal (this_obj, name, TRUE);
}
+/*
+ * ves_icall_System_Threading_Thread_GetPriority_internal:
+ * @param this_obj: The MonoInternalThread on which to operate.
+ *
+ * Gets the priority of the given thread.
+ * @return: The priority of the given thread.
+ */
+gint32
+ves_icall_System_Threading_Thread_GetPriority_internal (MonoInternalThread *this_obj)
+{
+ gint32 priority;
+
+ ensure_synch_cs_set (this_obj);
+ EnterCriticalSection (this_obj->synch_cs);
+
+ priority = GetThreadPriority (this_obj->handle) + 2;
+
+ LeaveCriticalSection (this_obj->synch_cs);
+ return priority;
+}
+
+/*
+ * ves_icall_System_Threading_Thread_SetPriority_internal:
+ * @param this_obj: The MonoInternalThread on which to operate.
+ * @param priority: The priority to set.
+ *
+ * Sets the priority of the given thread.
+ */
+void
+ves_icall_System_Threading_Thread_SetPriority_internal (MonoInternalThread *this_obj, gint32 priority)
+{
+ ensure_synch_cs_set (this_obj);
+ EnterCriticalSection (this_obj->synch_cs);
+ SetThreadPriority (this_obj->handle, priority - 2);
+ LeaveCriticalSection (this_obj->synch_cs);
+}
+
/* If the array is already in the requested domain, we just return it,
otherwise we return a copy in that domain. */
static MonoArray*
--
1.9.1
@aasimon
Copy link

aasimon commented Nov 6, 2014

A have completely rewritten the patch to use "nice" instead of pthread_setschedparam through the posix calls setpriority(2).
It even works for non-root users as long as the priority is only lowered.
I posted it here: https://gist.github.com/aasimon/c8ae6fc3cf5d9b82b6ca

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment