summaryrefslogtreecommitdiffstats
path: root/winsup/cygwin/sched.cc
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/sched.cc')
-rw-r--r--winsup/cygwin/sched.cc343
1 files changed, 172 insertions, 171 deletions
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 <unistd.h>
#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);
-
- /* calculate our desired priority class and thread priority */
-
- if (winpri < 7)
- Class = IDLE_PRIORITY_CLASS;
- else if (winpri > 10)
- Class = HIGH_PRIORITY_CLASS;
+ pri = param->sched_priority;
+
+ /* 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;