* An enhancement to the thread priority work added earlier. This will support starting a thread with a specified priority. Currently, the thread will start with the priority of the creator. The semantics of threads allow you to specify the priority of a thread before it has started. Therefore, we take note of this value at thread creation time.
* Missed committing this file the first time
* Correct typo
* Fix creation_flags reference
* MonoThreadParm really should not be in objects-internal
* Place priority in MonoInternalThread where it can be accessed by GetThreadPriority(). This enablses the priority to be set/retrieved before the thread is started. It also ensures that new threads default to a priority of THREAD_PRIORITY_NORMAL rather than inheriting the parent thread's priority.
* Revert changes to MonoInternalThread/Thread.cs. Properly use the handle for thread lookup. Initialize priority at thread creation time. Use stored value of priority if thread has not been started.
* Add code to handle retrieving the thread priority from a started thread that had been set before thread had started. This is necessary as before the thread is started the priority value can be manipulated. This value is stored in the MonoThread structure. Before the thread is started the value can be interrogated so we return this value rather than invoking the GetThreadPriority() API. When a thread is started this value is used to set the threads priority (if scheduling policy is SCHED_FIFO or SCHED_RR). For POSIX threads, after the thread is started we now keep the priority in the _Wapi_thread structure which is located via the thread handle. This structure doesn't exist until after the thread is started so we can't store the value there but after we can cache the priority value there and return it for non-SCHED_[RR|FIFO] threads. This way the expected value will always be set and returned by ThreadPriority property (currently for these threads it will simply return Normal). A test case has been added to the mono/tests directory.
* Use a copy of creation_flags so local modifications aren't propagated
* Use pthread_setschedparam as OSX doesn't support pthread_setschedprio
return(GUINT_TO_POINTER(fd));
}
-gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type,
+gboolean
+_wapi_lookup_handle (gpointer handle, WapiHandleType type,
gpointer *handle_specific)
{
struct _WapiHandleUnshared *handle_data;
{
pthread_t id;
GPtrArray *owned_mutexes;
+ gint32 priority;
};
typedef struct _WapiHandle_thread WapiHandle_thread;
#define THREAD_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3ff)
typedef guint32 (*WapiThreadStart)(gpointer);
+
+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;
gpointer wapi_create_thread_handle (void);
void wapi_thread_handle_set_exited (gpointer handle, guint32 exitstatus);
void wapi_ref_thread_handle (gpointer handle);
gpointer wapi_get_current_thread_handle (void);
-char* wapi_current_thread_desc (void);
+char *wapi_current_thread_desc (void);
extern gint32 GetThreadPriority (gpointer handle);
extern gboolean SetThreadPriority (gpointer handle, gint32 priority);
+extern int wapi_thread_priority_to_posix_priority (WapiThreadPriority, int);
+extern void wapi_init_thread_info_priority (gpointer, gint32);
+
G_END_DECLS
#endif /* _WAPI_THREADS_H_ */
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;
g_ptr_array_remove (thread->owned_mutexes, mutex);
}
+/**
+ * wapi_init_thread_info_priority:
+ * @param handle: The thread handle to set.
+ * @param priority: Priority to initialize with
+ *
+ * Initialize the priority field of the thread info
+ */
+void
+wapi_init_thread_info_priority (gpointer handle, gint32 priority)
+{
+ struct _WapiHandle_thread *thread_handle = NULL;
+ gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
+ (gpointer *)&thread_handle);
+
+ if (ok == TRUE)
+ thread_handle->priority = priority;
+}
+
/**
* _wapi_thread_posix_priority_to_priority:
*
}
/**
- * _wapi_thread_priority_to_posix_priority:
+ * 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)
+int
+wapi_thread_priority_to_posix_priority (WapiThreadPriority priority, int policy)
{
/* Necessary to get valid priority range */
#ifdef _POSIX_PRIORITY_SCHEDULING
gint32
GetThreadPriority (gpointer handle)
{
- struct _WapiHandle_thread *thread_handle;
+ struct _WapiHandle_thread *thread_handle = NULL;
int policy;
struct sched_param param;
gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
(gpointer *)&thread_handle);
- if (ok == FALSE) {
+ 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));
+ if ((policy == SCHED_FIFO) || (policy == SCHED_RR))
+ return (_wapi_thread_posix_priority_to_priority (param.sched_priority, policy));
+ else
+ return (thread_handle->priority);
case ESRCH:
g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
}
gboolean
SetThreadPriority (gpointer handle, gint32 priority)
{
- struct _WapiHandle_thread *thread_handle;
+ struct _WapiHandle_thread *thread_handle = NULL;
int policy,
posix_priority,
rv;
return FALSE;
}
- posix_priority = _wapi_thread_priority_to_posix_priority (priority, policy);
+ 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:
+ thread_handle->priority = priority;
return TRUE;
case ESRCH:
- g_warning ("pthread_setschedparam: error looking up thread id %x", (gsize)thread_handle->id);
+ g_warning ("pthread_setschedprio: error looking up thread id %x", (gsize)thread_handle->id);
break;
case ENOTSUP:
g_warning ("%s: priority %d not supported", __func__, priority);
unload_data *thread_data;
MonoNativeThreadId tid;
MonoDomain *caller_domain = mono_domain_get ();
+ MonoThreadParm tp;
/* printf ("UNLOAD STARTING FOR %s (%p) IN THREAD 0x%x.\n", domain->friendly_name, domain, mono_native_thread_id_get ()); */
* First we create a separate thread for unloading, since
* we might have to abort some threads, including the current one.
*/
- thread_handle = mono_threads_create_thread ((LPTHREAD_START_ROUTINE)unload_thread_main, thread_data, 0, CREATE_SUSPENDED, &tid);
+ tp.priority = 0;
+ tp.stack_size = 0;
+ tp.creation_flags = CREATE_SUSPENDED;
+ thread_handle = mono_threads_create_thread ((LPTHREAD_START_ROUTINE)unload_thread_main, thread_data, &tp, &tid);
if (thread_handle == NULL)
return;
mono_thread_info_resume (tid);
static void
transport_start_receive (void)
{
+ MonoThreadParm tp;
+
transport_connect ();
if (!listen_fd)
return;
- receiver_thread_handle = mono_threads_create_thread (receiver_thread, NULL, 0, 0, NULL);
+ tp.priority = 0;
+ tp.stack_size = 0;
+ tp.creation_flags = 0;
+ receiver_thread_handle = mono_threads_create_thread (receiver_thread, NULL, &tp, NULL);
g_assert (receiver_thread_handle);
}
struct _MonoInternalThread *internal_thread;
MonoObject *start_obj;
MonoException *pending_exception;
+ gint32 priority;
};
typedef struct {
MonoThread *thread;
thread = create_thread_object (domain);
+ thread->priority = THREAD_PRIORITY_NORMAL;
MONO_OBJECT_SETREF (thread, internal_thread, internal);
{
HANDLE thread_handle;
MonoNativeThreadId tid;
- guint32 create_flags;
+ MonoThreadParm tp;
/*
* Join joinable threads to prevent running out of threads since the finalizer
/* Create suspended, so we can do some housekeeping before the thread
* starts
*/
- create_flags = CREATE_SUSPENDED;
+ tp.priority = thread->priority;
+ tp.stack_size = stack_size;
+ tp.creation_flags = CREATE_SUSPENDED;
- thread_handle = mono_threads_create_thread ((LPTHREAD_START_ROUTINE)start_wrapper, start_info,
- stack_size, create_flags, &tid);
+ thread_handle = mono_threads_create_thread ((LPTHREAD_START_ROUTINE)start_wrapper, start_info, &tp, &tid);
if (thread_handle == NULL) {
/* The thread couldn't be created, so set an exception */
MonoInternalThread *internal = this_obj->internal_thread;
LOCK_THREAD (internal);
- priority = GetThreadPriority (internal->handle) + 2;
+ if (internal->handle != NULL)
+ priority = GetThreadPriority (internal->handle) + 2;
+ else
+ priority = this_obj->priority + 2;
UNLOCK_THREAD (internal);
return priority;
}
MonoInternalThread *internal = this_obj->internal_thread;
LOCK_THREAD (internal);
- SetThreadPriority (internal->handle, priority - 2);
+ this_obj->priority = priority - 2;
+ if (internal->handle != NULL)
+ SetThreadPriority (internal->handle, this_obj->priority);
UNLOCK_THREAD (internal);
}
mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
{
MonoDomain *orig;
- gboolean fresh_thread;
+ gboolean fresh_thread = FALSE;
if (!domain) {
/* Happens when called from AOTed code which is only used in the root domain. */
HANDLE handle;
gpointer *user_data;
MonoMethod **methods;
+ MonoThreadParm tp;
methods_len = acfg->methods->len;
user_data [1] = acfg;
user_data [2] = frag;
- handle = mono_threads_create_thread ((LPTHREAD_START_ROUTINE)compile_thread_main, user_data, 0, 0, NULL);
+ tp.priority = 0;
+ tp.stack_size = 0;
+ tp.creation_flags = 0;
+ handle = mono_threads_create_thread ((LPTHREAD_START_ROUTINE)compile_thread_main, user_data, &tp, NULL);
g_ptr_array_add (threads, handle);
}
g_free (methods);
static void
start_debugger_thread (void)
{
- debugger_thread_handle = mono_threads_create_thread (debugger_thread, NULL, 0, 0, NULL);
+ MonoThreadParm tp;
+
+ tp.priority = 0;
+ tp.stack_size = 0;
+ tp.creation_flags = 0;
+ debugger_thread_handle = mono_threads_create_thread (debugger_thread, NULL, &tp, NULL);
g_assert (debugger_thread_handle);
}
pinvoke_ppci.cs \
pinvoke_ppcf.cs \
pinvoke_ppcd.cs \
- bug-29585.cs
+ bug-29585.cs \
+ priority.cs
TEST_CS_SRC_DIST= \
$(BASE_TEST_CS_SRC) \
--- /dev/null
+using System;
+using System.Threading;
+using System.Runtime;
+using System.Text;
+
+public class Tests
+{
+ public static int Main ()
+ {
+ return TestDriver.RunTests (typeof (Tests));
+ }
+
+ public static void TestMethod()
+ {
+ Console.WriteLine("{0} with {1} priority",
+ Thread.CurrentThread.Name,
+ Thread.CurrentThread.Priority.ToString());
+ Thread.Sleep(6000);
+ Console.WriteLine("{0} with {1} priority",
+ Thread.CurrentThread.Name,
+ Thread.CurrentThread.Priority.ToString());
+ }
+
+ public static int test_0_thread_priority ()
+ {
+ int res = 0;
+
+ Thread Me = Thread.CurrentThread;
+ Thread TestThread = new Thread(new ThreadStart(TestMethod));
+
+ Console.WriteLine("Starting test thread with priority to AboveNormal");
+ ThreadPriority before = TestThread.Priority;
+ TestThread.Priority = ThreadPriority.AboveNormal;
+ TestThread.Name = "TestMethod";
+ TestThread.Start();
+ ThreadPriority after = TestThread.Priority;
+ Console.WriteLine("Priority: {0} {1}",before,after);
+ if (before != ThreadPriority.Normal)
+ res = 1;
+ else if (after != ThreadPriority.AboveNormal)
+ res = 2;
+ else {
+ TestThread.Priority = ThreadPriority.Normal;
+ after = TestThread.Priority;
+ Console.WriteLine("Setting test thread priority to Normal");
+ Thread.Sleep(1000);
+ Console.WriteLine("Priority: {0} {1}",before,after);
+
+ if (after != ThreadPriority.Normal)
+ res = 3;
+ else {
+ Console.WriteLine("Setting test thread priority to AboveNormal");
+ before = after;
+ TestThread.Priority=ThreadPriority.AboveNormal;
+ after = TestThread.Priority;
+ Thread.Sleep(1000);
+ Console.WriteLine("Priority: {0} {1}",before,after);
+
+ if (after != ThreadPriority.AboveNormal)
+ res = 4;
+ else {
+ before = after;
+ Console.WriteLine("Setting test thread priority to BelowNormal");
+ TestThread.Priority=ThreadPriority.BelowNormal;
+ after = TestThread.Priority;
+ Console.WriteLine("Priority: {0} {1}",before,after);
+ Thread.Sleep(1000);
+
+ if (after != ThreadPriority.BelowNormal)
+ res = 5;
+ else {
+ before = after;
+ Console.WriteLine("Setting test thread priority back to Normal");
+ TestThread.Priority=ThreadPriority.Normal;
+ after = TestThread.Priority;
+ Console.WriteLine("Priority: {0} {1}",before,after);
+ Thread.Sleep(1000);
+
+ if (after != ThreadPriority.Normal)
+ res = 6;
+ }
+ }
+ }
+ }
+ TestThread.Join();
+ return(res);
+ }
+}
void *(*start_routine)(void*);
void *arg;
int flags;
+ gint32 priority;
MonoCoopSem registered;
HANDLE handle;
} StartInfo;
info->runtime_thread = TRUE;
info->handle = handle;
+ wapi_init_thread_info_priority(handle, start_info->priority);
+
if (flags & CREATE_SUSPENDED) {
info->create_suspended = TRUE;
mono_coop_sem_init (&info->create_suspended_sem, 0);
}
HANDLE
-mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
+mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, MonoThreadParm *tp, MonoNativeThreadId *out_tid)
{
pthread_attr_t attr;
int res;
pthread_t thread;
StartInfo start_info;
+ guint32 stack_size;
+ int policy;
+ struct sched_param sp;
res = pthread_attr_init (&attr);
g_assert (!res);
- if (stack_size == 0) {
+ if (tp->stack_size == 0) {
#if HAVE_VALGRIND_MEMCHECK_H
if (RUNNING_ON_VALGRIND)
stack_size = 1 << 20;
#else
stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
#endif
- }
+ } else
+ stack_size = tp->stack_size;
#ifdef PTHREAD_STACK_MIN
if (stack_size < PTHREAD_STACK_MIN)
g_assert (!res);
#endif
+ /*
+ * For policies that respect priorities set the prirority for the new thread
+ */
+ pthread_getschedparam(pthread_self(), &policy, &sp);
+ if ((policy == SCHED_FIFO) || (policy == SCHED_RR)) {
+ sp.sched_priority = wapi_thread_priority_to_posix_priority (tp->priority, policy);
+ res = pthread_attr_setschedparam (&attr, &sp);
+ }
+
memset (&start_info, 0, sizeof (StartInfo));
start_info.start_routine = (void *(*)(void *)) start_routine;
start_info.arg = arg;
- start_info.flags = creation_flags;
+ start_info.flags = tp->creation_flags;
+ start_info.priority = tp->priority;
mono_coop_sem_init (&(start_info.registered), 0);
/* Actually start the thread */
}
HANDLE
-mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
+mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, MonoThreadParm *tp, MonoNativeThreadId *out_tid)
{
ThreadStartInfo *start_info;
HANDLE result;
DWORD thread_id;
+ guint32 creation_flags = tp->creation_flags;
int res;
start_info = g_malloc0 (sizeof (ThreadStartInfo));
return NULL;
}
- result = CreateThread (NULL, stack_size, inner_start_thread, start_info, creation_flags, &thread_id);
+ result = CreateThread (NULL, tp->stack_size, inner_start_thread, start_info, creation_flags, &thread_id);
if (result) {
res = mono_coop_sem_wait (&(start_info->registered), MONO_SEM_FLAGS_NONE);
g_assert (res != -1);
* Returns: a windows or io-layer handle for the thread.
*/
HANDLE
-mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
+mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, MonoThreadParm *tp, MonoNativeThreadId *out_tid)
{
- return mono_threads_core_create_thread (start, arg, stack_size, creation_flags, out_tid);
+ return mono_threads_core_create_thread (start, arg, tp, out_tid);
}
/*
typedef SuspendThreadResult (*MonoSuspendThreadCallback) (THREAD_INFO_TYPE *info, gpointer user_data);
+/*
+ * Parameters to pass for thread creation
+ */
+typedef struct {
+ int priority;
+ guint32 creation_flags;
+ guint32 stack_size;
+} MonoThreadParm;
+
static inline gboolean
mono_threads_filter_tools_threads (THREAD_INFO_TYPE *info)
{
mono_thread_info_is_live (THREAD_INFO_TYPE *info);
HANDLE
-mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid);
+mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, MonoThreadParm *tp, MonoNativeThreadId *out_tid);
int
mono_threads_get_max_stack_size (void);
void mono_threads_platform_free (THREAD_INFO_TYPE *info);
void mono_threads_core_abort_syscall (THREAD_INFO_TYPE *info);
gboolean mono_threads_core_needs_abort_syscall (void);
-HANDLE mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid);
+HANDLE mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, MonoThreadParm *, MonoNativeThreadId *out_tid);
void mono_threads_core_resume_created (THREAD_INFO_TYPE *info, MonoNativeThreadId tid);
void mono_threads_core_get_stack_bounds (guint8 **staddr, size_t *stsize);
gboolean mono_threads_core_yield (void);