Skip to content

Instantly share code, notes, and snippets.

@aasimon
Created November 6, 2014 07:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aasimon/c8ae6fc3cf5d9b82b6ca to your computer and use it in GitHub Desktop.
Save aasimon/c8ae6fc3cf5d9b82b6ca to your computer and use it in GitHub Desktop.
Patch for SetThreadPriority support in mono-3.2.8
diff --git a/mcs/class/corlib/System.Threading/Thread.cs b/mcs/class/corlib/System.Threading/Thread.cs
index 5af6800..112751d 100644
--- a/mcs/class/corlib/System.Threading/Thread.cs
+++ b/mcs/class/corlib/System.Threading/Thread.cs
@@ -98,13 +98,13 @@ namespace System.Threading {
private IntPtr android_tid;
private IntPtr thread_pinning_ref;
private int ignore_next_signal;
+ private int thread_priority;
/*
* These fields are used to avoid having to increment corlib versions
* when a new field is added to the unmanaged MonoThread structure.
*/
private IntPtr unused0;
private IntPtr unused1;
- private IntPtr unused2;
#endregion
#pragma warning restore 169, 414, 649
@@ -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/thread-private.h b/mono/io-layer/thread-private.h
index 1c62618..53467b8 100644
--- a/mono/io-layer/thread-private.h
+++ b/mono/io-layer/thread-private.h
@@ -37,6 +37,7 @@ struct _WapiHandle_thread
guint32 create_flags;
/* Fields below this point are only valid for the owning process */
pthread_t id;
+ pid_t pid;
GPtrArray *owned_mutexes;
gpointer handle;
/*
diff --git a/mono/io-layer/threads.h b/mono/io-layer/threads.h
index 12b7c0c..7b21556 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, gint32 *thread_priority);
+extern gboolean SetThreadPriority (gpointer handle, gint32 *thread_priorit, 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..dfa1aac 100644
--- a/mono/io-layer/wthreads.c
+++ b/mono/io-layer/wthreads.c
@@ -20,6 +20,7 @@
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
+#include <sys/resource.h>
#include <mono/io-layer/wapi.h>
#include <mono/io-layer/wapi-private.h>
@@ -70,6 +71,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 +1315,118 @@ void _wapi_thread_disown_mutex (gpointer mutex)
g_ptr_array_remove (thread_handle->owned_mutexes, mutex);
}
+
+/**
+ * _wapi_posix_priority_to_thread_priority:
+ *
+ * Convert a POSIX priority to a WapiThreadPriority.
+ * @sched_priority The POSIX priority to covert ( see getpriority(2) )
+ * Returns the corresponding WapiThreadPriority.
+ */
+static WapiThreadPriority _wapi_posix_priority_to_thread_priority (int sched_priority)
+{
+/* Necessary to get valid priority range */
+#ifdef _POSIX_PRIORITY_SCHEDULING
+ switch(sched_priority) {
+ case -20 ... -11: return (THREAD_PRIORITY_HIGHEST);
+ case -10 ... -1: return (THREAD_PRIORITY_ABOVE_NORMAL);
+ case 0: return (THREAD_PRIORITY_NORMAL);
+ case 1 ... 10: return (THREAD_PRIORITY_BELOW_NORMAL);
+ case 11 ... 19: return (THREAD_PRIORITY_LOWEST);
+ default: break;
+ }
+#endif
+
+ return (THREAD_PRIORITY_NORMAL);
+}
+
+/**
+ * _wapi_thread_priority_to_posix_priority:
+ *
+ * Convert a WapiThreadPriority to a POSIX priority.
+ *
+ * @sched_priority The WapiThreadPriorityThe covert.
+ *
+ * Returns the corresponding POSIX priority ( see getpriority(2) )
+ */
+static int _wapi_thread_priority_to_posix_priority (WapiThreadPriority priority)
+{
+/* Necessary to get valid priority range */
+#ifdef _POSIX_PRIORITY_SCHEDULING
+ switch(priority) {
+ case THREAD_PRIORITY_HIGHEST: return (-20);
+ case THREAD_PRIORITY_ABOVE_NORMAL: return (-10);
+ case THREAD_PRIORITY_NORMAL: return ( 0);
+ case THREAD_PRIORITY_BELOW_NORMAL: return ( 10);
+ case THREAD_PRIORITY_LOWEST: return ( 19);
+ }
+
+#endif
+ return (0);
+}
+
+/**
+ * GetThreadPriority:
+ * Gets the priority of the given thread.
+ *
+ * @handle: The thread handle to query.
+ *
+ * Returns a MonoThreadPriority approximating the current POSIX
+ * thread priority, or THREAD_PRIORITY_NORMAL on error.
+ */
+gint32 GetThreadPriority (gpointer handle, gint32 *thread_priority)
+{
+ struct _WapiHandle_thread *thread_handle;
+ int prio;
+ gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, (gpointer *)&thread_handle);
+
+ if (ok == FALSE) return (THREAD_PRIORITY_NORMAL);
+
+ if (thread_handle->pid == 0) {
+ /* Thread not yet started; return intended priority. */
+ return *thread_priority;
+ }
+
+ errno = 0;
+ prio = getpriority (PRIO_PROCESS, thread_handle->pid);
+ if (prio == -1) {
+ g_warning ("%s: getpriority could not get priority: %s", __func__, strerror (errno));
+ return (THREAD_PRIORITY_NORMAL);
+ }
+
+ return _wapi_posix_priority_to_thread_priority (prio);
+}
+
+/**
+ * SetThreadPriority:
+ * Sets the priority of the given thread.
+ *
+ * @handle: The thread handle to query.
+ * @priority: The priority to give to the thread.
+ *
+ * Returns TRUE on success, FALSE on failure or error.
+ */
+gboolean SetThreadPriority (gpointer handle, gint32 *thread_priority, gint32 priority)
+{
+ struct _WapiHandle_thread *thread_handle;
+ int ret;
+ gboolean ok;
+
+ *thread_priority = _wapi_thread_priority_to_posix_priority (priority);
+
+ ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, (gpointer *)&thread_handle);
+
+ if (ok == FALSE) return (FALSE);
+
+ if (thread_handle->pid != 0) {
+ /* Thread already running; set priority now. */
+ errno = 0;
+ ret = setpriority (PRIO_PROCESS, thread_handle->pid, *thread_priority);
+ if (ret == -1) {
+ g_warning ("%s: setpriority could not set priority %d: %s", __func__, *thread_priority, strerror (errno));
+ return (FALSE);
+ }
+ }
+
+ return (TRUE);
+}
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/object-internals.h b/mono/metadata/object-internals.h
index d969533..4c3d914 100644
--- a/mono/metadata/object-internals.h
+++ b/mono/metadata/object-internals.h
@@ -436,6 +436,7 @@ struct _MonoInternalThread {
gpointer thread_pinning_ref;
gint32 ignore_next_signal;
MonoMethod *async_invoke_method;
+ int thread_priority; /* Used by Get/SetThreadPriority */
/*
* These fields are used to avoid having to increment corlib versions
* when a new field is added to this structure.
@@ -443,7 +444,6 @@ struct _MonoInternalThread {
* same field there.
*/
gpointer unused1;
- gpointer unused2;
};
struct _MonoThread {
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..3727d82 100755
--- a/mono/metadata/threads.c
+++ b/mono/metadata/threads.c
@@ -37,6 +37,11 @@
#include <mono/io-layer/io-layer.h>
#ifndef HOST_WIN32
#include <mono/io-layer/threads.h>
+#include <mono/io-layer/wapi-private.h>
+#include <mono/io-layer/handles-private.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/resource.h>
#endif
#include <mono/metadata/object-internals.h>
#include <mono/metadata/mono-debug-debugger.h>
@@ -550,6 +555,11 @@ static guint32 WINAPI start_wrapper_internal(void *data)
guint32 (*start_func)(void *);
void *start_arg;
gsize tid;
+#ifndef HOST_WIN32
+ gboolean ok;
+ struct _WapiHandle_thread *thread_handle;
+ int ret;
+#endif
/*
* We don't create a local to hold start_info->obj, so hopefully it won't get pinned during a
* GC stack walk.
@@ -606,6 +616,23 @@ static guint32 WINAPI start_wrapper_internal(void *data)
THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, GetCurrentThreadId (), internal));
+#ifndef HOST_WIN32
+ ok = _wapi_lookup_handle (internal->handle, WAPI_HANDLE_THREAD, (gpointer *)&thread_handle);
+
+ if (ok != FALSE) {
+ thread_handle->pid = syscall(SYS_gettid);
+
+ if(internal->thread_priority != 0) {
+ /* Thread running; set priority now. */
+ errno = 0;
+ ret = setpriority (PRIO_PROCESS, thread_handle->pid,internal->thread_priority);
+ if (ret == -1) {
+ g_warning ("%s: setpriority could not set priority %d: %s", __func__, internal->thread_priority, strerror (errno));
+ }
+ }
+ }
+#endif
+
/* On 2.0 profile (and higher), set explicitly since state might have been
Unknown */
if (internal->apartment_state == ThreadApartmentState_Unknown)
@@ -847,7 +874,7 @@ mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gb
/* Check that the managed and unmanaged layout of MonoInternalThread matches */
if (mono_check_corlib_version () == NULL)
- g_assert (((char*)&internal->unused2 - (char*)internal) == mono_defaults.internal_thread_class->fields [mono_defaults.internal_thread_class->field.count - 1].offset);
+ g_assert (((char*)&internal->unused1 - (char*)internal) == mono_defaults.internal_thread_class->fields [mono_defaults.internal_thread_class->field.count - 1].offset);
return internal;
}
@@ -1342,6 +1369,41 @@ 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, &this_obj->thread_priority) + 2; /* convert to [-2; 2] range to [0;4] */
+ 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, &this_obj->thread_priority, priority - 2); /* convert from [0; 4] range to [-2; 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*
@aasimon
Copy link
Author

aasimon commented Nov 6, 2014

In the patch I added "int thread_priority" to the struct _MonoInternalThread in mono/metadata/object-internals.h and I'm a bit uncertain about the consequences...
It states in a comment:

  • These fields are used to avoid having to increment corlib versions
  • when a new field is added to this structure.
  • Please synchronize any changes with InternalThread in Thread.cs, i.e. add the
  • same field there.

I removed gpointer unused2; to retain binary compatibility, but since I added an int (32 bit) and removed a pointer (32 bits on 32bit system, 64 bits on 64bit systems) it will break binary compatibility on 64bit systems.
This could be addressed using a union {int thread_priority; gpointer unused2;} foobar; instead, but this will clutter the code...

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