diff --git a/winsup/cygwin/sched.cc b/winsup/cygwin/sched.cc index b39400d02..e0bd02d59 100644 --- a/winsup/cygwin/sched.cc +++ b/winsup/cygwin/sched.cc @@ -19,83 +19,60 @@ #include #include "registry.h" -/* Win32 priority to UNIX priority Mapping. - - For now, I'm just following the spec: any range of priorities is ok. - There are probably many many issues with this... - - FIXME: We don't support pre-Windows 2000 so we should fix the priority - computation. Here's the description for the current code: - - We don't want process's going realtime. Well, they probably could, but - the issues with avoiding the priority values 17-22 and 27-30 (not - supported before win2k) make that inefficient. - - However to complicate things most unixes use lower is better priorities. - - So we map -14 to 15, and 15 to 1 via (16- ((n+16) >> 1)). We then map 1 - to 15 to various process class and thread priority combinations. Then we - need to look at the threads process priority. As win95, 98 and NT 4 - don't support opening threads cross-process (unless a thread HANDLE is - passed around) for now, we'll just use the priority class. - - The code and logic are present to calculate the priority for thread, if a - thread handle can be obtained. Alternatively, if the symbols wouldn't be - resolved until they are used we could support this. - - Lastly, because we can't assume that the pid we're given are Windows pids, - we can't alter non-cygwin started programs. */ +/* Win32 priority to UNIX priority Mapping. */ extern "C" { +/* We support prio values from 1 to 32. This is marginally in line with Linux + (1 - 99) and matches the POSIX requirement to support at least 32 priority + values. */ + /* max priority for policy */ int sched_get_priority_max (int policy) { - if (policy < 1 || policy > 3) + switch (policy) { - set_errno (EINVAL); - return -1; + case SCHED_FIFO: + case SCHED_RR: + case SCHED_OTHER: + return 32; } - return -14; + set_errno (EINVAL); + return -1; } /* min priority for policy */ int sched_get_priority_min (int policy) { - if (policy < 1 || policy > 3) + switch (policy) { - set_errno (EINVAL); - return -1; + case SCHED_FIFO: + case SCHED_RR: + case SCHED_OTHER: + return 1; } - return 15; + set_errno (EINVAL); + return -1; } /* Check a scheduler parameter struct for valid settings */ -int +bool valid_sched_parameters (const struct sched_param *param) { - if (param->sched_priority < -14 || param->sched_priority > 15) - { - return 0; - } - return -1; - + return param->sched_priority >= 1 && param->sched_priority <= 32; } /* get sched params for process - Note, I'm never returning EPERM, - Always ESRCH. This is by design (If cygwin ever looks at paranoid security - Walking the pid values is a known hole in some os's) -*/ + Note, we're never returning EPERM, always ESRCH. This is by design. + Walking the pid values is a known hole in some OSes. */ int sched_getparam (pid_t pid, struct sched_param *param) { pid_t localpid; - int winpri; if (!param || pid < 0) { set_errno (EINVAL); @@ -104,130 +81,60 @@ sched_getparam (pid_t pid, struct sched_param *param) localpid = pid ? pid : getpid (); - DWORD Class; - int ThreadPriority; + DWORD pclass; HANDLE process; pinfo p (localpid); /* get the class */ - if (!p) { set_errno (ESRCH); return -1; } - process = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, p->dwProcessId); + process = OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION, FALSE, + p->dwProcessId); if (!process) { set_errno (ESRCH); return -1; } - Class = GetPriorityClass (process); + pclass = GetPriorityClass (process); CloseHandle (process); - if (!Class) + if (!pclass) { set_errno (ESRCH); return -1; } - ThreadPriority = THREAD_PRIORITY_NORMAL; - /* calculate the unix priority. */ - - switch (Class) + switch (pclass) { case IDLE_PRIORITY_CLASS: - switch (ThreadPriority) - { - case THREAD_PRIORITY_IDLE: - winpri = 1; - break; - case THREAD_PRIORITY_LOWEST: - winpri = 2; - break; - case THREAD_PRIORITY_BELOW_NORMAL: - winpri = 3; - break; - case THREAD_PRIORITY_NORMAL: - winpri = 4; - break; - case THREAD_PRIORITY_ABOVE_NORMAL: - winpri = 5; - break; - case THREAD_PRIORITY_HIGHEST: - default: - winpri = 6; - break; - } + param->sched_priority = 3; break; - case HIGH_PRIORITY_CLASS: - switch (ThreadPriority) - { - case THREAD_PRIORITY_IDLE: - winpri = 1; - break; - case THREAD_PRIORITY_LOWEST: - winpri = 11; - break; - case THREAD_PRIORITY_BELOW_NORMAL: - winpri = 12; - break; - case THREAD_PRIORITY_NORMAL: - winpri = 13; - break; - case THREAD_PRIORITY_ABOVE_NORMAL: - winpri = 14; - break; - case THREAD_PRIORITY_HIGHEST: - default: - winpri = 15; - break; - } + case BELOW_NORMAL_PRIORITY_CLASS: + param->sched_priority = 9; break; case NORMAL_PRIORITY_CLASS: default: - switch (ThreadPriority) - { - case THREAD_PRIORITY_IDLE: - winpri = 1; - break; - case THREAD_PRIORITY_LOWEST: - winpri = 7; - break; - case THREAD_PRIORITY_BELOW_NORMAL: - winpri = 8; - break; - case THREAD_PRIORITY_NORMAL: - winpri = 9; - break; - case THREAD_PRIORITY_ABOVE_NORMAL: - winpri = 10; - break; - case THREAD_PRIORITY_HIGHEST: - default: - winpri = 11; - break; - } + param->sched_priority = 15; + break; + case ABOVE_NORMAL_PRIORITY_CLASS: + param->sched_priority = 21; + break; + case HIGH_PRIORITY_CLASS: + param->sched_priority = 27; + break; + case REALTIME_PRIORITY_CLASS: + param->sched_priority = 32; break; } - /* reverse out winpri = (16- ((unixpri+16) >> 1)) */ - /* - winpri-16 = - (unixpri +16 ) >> 1 - - -(winpri-16) = unixpri +16 >> 1 - (-(winpri-16)) << 1 = unixpri+16 - ((-(winpri - 16)) << 1) - 16 = unixpri - */ - - param->sched_priority = ((-(winpri - 16)) << 1) - 16; - return 0; } /* get the scheduler for pid - All process's on WIN32 run with SCHED_FIFO. - So we just give an answer. + All process's on WIN32 run with SCHED_FIFO. So we just give an answer. (WIN32 uses a multi queue FIFO). */ int @@ -307,8 +214,8 @@ int sched_setparam (pid_t pid, const struct sched_param *param) { pid_t localpid; - int winpri; - DWORD Class; + int pri; + DWORD pclass; HANDLE process; if (!param || pid < 0) @@ -323,17 +230,22 @@ sched_setparam (pid_t pid, const struct sched_param *param) return -1; } - /* winpri = (16- ((unixpri+16) >> 1)) */ - winpri = 16 - ((param->sched_priority + 16) >> 1); + pri = param->sched_priority; - /* calculate our desired priority class and thread priority */ - - if (winpri < 7) - Class = IDLE_PRIORITY_CLASS; - else if (winpri > 10) - Class = HIGH_PRIORITY_CLASS; + /* calculate our desired priority class. We only reserve a small area + (31/32) for realtime priority. */ + if (pri <= 6) + pclass = IDLE_PRIORITY_CLASS; + else if (pri <= 12) + pclass = BELOW_NORMAL_PRIORITY_CLASS; + else if (pri <= 18) + pclass = NORMAL_PRIORITY_CLASS; + else if (pri <= 24) + pclass = ABOVE_NORMAL_PRIORITY_CLASS; + else if (pri <= 30) + pclass = HIGH_PRIORITY_CLASS; else - Class = NORMAL_PRIORITY_CLASS; + pclass = REALTIME_PRIORITY_CLASS; localpid = pid ? pid : getpid (); @@ -346,14 +258,13 @@ sched_setparam (pid_t pid, const struct sched_param *param) set_errno (ESRCH); return -1; } - process = - OpenProcess (PROCESS_SET_INFORMATION, FALSE, (DWORD) p->dwProcessId); + process = OpenProcess (PROCESS_SET_INFORMATION, FALSE, p->dwProcessId); if (!process) { - set_errno (2); //ESRCH); + set_errno (ESRCH); return -1; } - if (!SetPriorityClass (process, Class)) + if (!SetPriorityClass (process, pclass)) { CloseHandle (process); set_errno (EPERM); @@ -364,31 +275,121 @@ sched_setparam (pid_t pid, const struct sched_param *param) return 0; } -/* we map -14 to 15, and 15 to 1 via (16- ((n+16) >> 1)). This lines up with - the allowed values we return elsewhere in the sched* functions. We then - map in groups of three to allowed thread priority's. The reason for dropping - accuracy while still returning a wide range of values is to allow more - flexible code in the future. */ +/* POSIX thread priorities loosely compare to Windows thread base priorities. + + Base priority is a function of process priority class and thread priority. + https://msdn.microsoft.com/en-us/library/windows/desktop/ms685100%28v=vs.85%29.aspx + + Note 1: + + We deliberately handle the REALTIME prority class the same as the HIGH + priority class. Realtime has it's own range from 16 to 31 so half the + arena is reserved for REALTIME. The problem is that this isn't visible + nor expected in the POSIX scenario. Therefore we hide this here and + fold REALTIME into HIGH. + + Note 2: + + sched_get_thread_priority is only called internally and only for threads + of the current process, with no good reason for the caller to fail. + Therefore it never returns an error but a valid priority (base value + equivalent to process priority class + THREAD_PRIORITY_NORMAL... + + Note 3: + + ...multiplied by 2 to stretch the priorities over the entire range 1 - 32. +*/ + +static int +sched_base_prio_from_win_prio_class (DWORD pclass) +{ + int base; + + switch (pclass) + { + case IDLE_PRIORITY_CLASS: + base = 4; + break; + case BELOW_NORMAL_PRIORITY_CLASS: + base = 6; + break; + case NORMAL_PRIORITY_CLASS: + default: + base = 8; + break; + case ABOVE_NORMAL_PRIORITY_CLASS: + base = 10; + break; + case HIGH_PRIORITY_CLASS: + case REALTIME_PRIORITY_CLASS: /* See above note 1 */ + base = 13; + break; + } + return base; +} + +int +sched_get_thread_priority (HANDLE thread) +{ + int tprio; + DWORD pclass; + int priority; + + tprio = GetThreadPriority (thread); + pclass = GetPriorityClass (GetCurrentProcess ()); + switch (tprio) + { + case THREAD_PRIORITY_ERROR_RETURN: + priority = sched_base_prio_from_win_prio_class (pclass); + break; + case THREAD_PRIORITY_IDLE: + priority = 1; + break; + case THREAD_PRIORITY_TIME_CRITICAL: + priority = 15; + break; + default: + priority = tprio + sched_base_prio_from_win_prio_class (pclass); + break; + } + return priority << 1; /* See above note 3 */ +} + int sched_set_thread_priority (HANDLE thread, int priority) { - int real_pri; - real_pri = 16 - ((priority + 16) >> 1); - if (real_pri <1 || real_pri > 15) + DWORD pclass; + int tprio; + + pclass = GetPriorityClass (GetCurrentProcess ()); + if (!pclass) + return EPERM; + if (priority < 1 || priority > 32) return EINVAL; - if (real_pri < 4) - real_pri = THREAD_PRIORITY_LOWEST; - else if (real_pri < 7) - real_pri = THREAD_PRIORITY_BELOW_NORMAL; - else if (real_pri < 10) - real_pri = THREAD_PRIORITY_NORMAL; - else if (real_pri < 13) - real_pri = THREAD_PRIORITY_ABOVE_NORMAL; - else - real_pri = THREAD_PRIORITY_HIGHEST; + priority >>= 1; /* See above note 3 */ + if (priority < 1) + priority = 1; + else if (priority > 15) + priority = 15; - if (!SetThreadPriority (thread, real_pri)) + if (priority == 1) + tprio = THREAD_PRIORITY_IDLE; + else if (priority == 15) + tprio = THREAD_PRIORITY_TIME_CRITICAL; + else + { + tprio = priority - sched_base_prio_from_win_prio_class (pclass); + /* Intermediate values only allowed in REALTIME_PRIORITY_CLASS. */ + if (pclass != REALTIME_PRIORITY_CLASS) + { + if (tprio < THREAD_PRIORITY_LOWEST) + tprio = THREAD_PRIORITY_LOWEST; + else if (tprio > THREAD_PRIORITY_HIGHEST) + tprio = THREAD_PRIORITY_HIGHEST; + } + } + if (!SetThreadPriority (thread, tprio)) /* invalid handle, no access are the only expected errors. */ return EPERM; return 0; diff --git a/winsup/cygwin/thread.cc b/winsup/cygwin/thread.cc index ec59f1f61..350879ac0 100644 --- a/winsup/cygwin/thread.cc +++ b/winsup/cygwin/thread.cc @@ -36,7 +36,7 @@ details. */ extern "C" void __fp_lock_all (); extern "C" void __fp_unlock_all (); -extern "C" int valid_sched_parameters(const struct sched_param *); +extern "C" bool valid_sched_parameters(const struct sched_param *); extern "C" int sched_set_thread_priority(HANDLE thread, int priority); static inline verifyable_object_state verifyable_object_isvalid (void const * objectptr, thread_magic_t magic,