A first attempt to add SCHED_BATCH. Still TODO: - Add SCHED_IDLE/BATCH to winsup/doc/posix.xml - Provide correct values in (18) and (19) of /proc/PID/stat for SCHED_BATCH. - Provide correct value in (18) of /proc/PID/stat for SCHED_FIFO/RR.
-- Regards, Christian
From 0822917252fdade3edc240b4fbfd3c0f47ef1deb Mon Sep 17 00:00:00 2001 From: Christian Franke <christian.fra...@t-online.de> Date: Fri, 6 Dec 2024 17:32:29 +0100 Subject: [PATCH] Cygwin: sched_setscheduler: accept SCHED_BATCH Add SCHED_BATCH to <sys/sched.h>. SCHED_BATCH is similar to SCHED_OTHER, except that the nice value is mapped to a one step lower Windows priority. Rework the mapping functions to ease the addition of this functionality. Signed-off-by: Christian Franke <christian.fra...@t-online.de> --- newlib/libc/include/sys/sched.h | 8 ++ winsup/cygwin/local_includes/miscfuncs.h | 4 +- winsup/cygwin/miscfuncs.cc | 155 +++++++++++++---------- winsup/cygwin/release/3.6.0 | 11 +- winsup/cygwin/sched.cc | 15 ++- winsup/cygwin/syscalls.cc | 20 +-- 6 files changed, 129 insertions(+), 84 deletions(-) diff --git a/newlib/libc/include/sys/sched.h b/newlib/libc/include/sys/sched.h index c96355c24..265215211 100644 --- a/newlib/libc/include/sys/sched.h +++ b/newlib/libc/include/sys/sched.h @@ -38,6 +38,14 @@ extern "C" { #define SCHED_FIFO 1 #define SCHED_RR 2 +#if __GNU_VISIBLE +#if defined(__CYGWIN__) +#define SCHED_BATCH 0 +#else +#define SCHED_BATCH 3 +#endif +#endif + #if defined(_POSIX_SPORADIC_SERVER) #define SCHED_SPORADIC 4 #endif diff --git a/winsup/cygwin/local_includes/miscfuncs.h b/winsup/cygwin/local_includes/miscfuncs.h index 57dcbfeab..6001a1636 100644 --- a/winsup/cygwin/local_includes/miscfuncs.h +++ b/winsup/cygwin/local_includes/miscfuncs.h @@ -44,8 +44,8 @@ is_alt_numpad_event (PINPUT_RECORD pirec) && pirec->Event.KeyEvent.wVirtualScanCode == 0x38; } -int winprio_to_nice (DWORD); -DWORD nice_to_winprio (int &); +int winprio_to_nice (DWORD prio, bool batch = false); +DWORD nice_to_winprio (int &nice, bool batch = false); bool set_and_check_winprio (HANDLE proc, DWORD prio, bool set = true); bool create_pipe (PHANDLE, PHANDLE, LPSECURITY_ATTRIBUTES, DWORD); diff --git a/winsup/cygwin/miscfuncs.cc b/winsup/cygwin/miscfuncs.cc index 0146704ea..6faf04937 100644 --- a/winsup/cygwin/miscfuncs.cc +++ b/winsup/cygwin/miscfuncs.cc @@ -103,84 +103,111 @@ yield () Sleep (0L); } -/* Get a default value for the nice factor. When changing these values, - have a look into the below function nice_to_winprio. The values must - match the layout of the static "priority" array. */ -int -winprio_to_nice (DWORD prio) +/* + Mapping of nice value from/to Windows priority + ('batch' is used for SCHED_BATCH policy). + + nice_to_winprio() winprio_to_nice() + !batch batch Level Windows priority class !batch batch + 12...19 4...19 0 IDLE_PRIORITY_CLASS 16 8 + 4...11 -4....3 1 BELOW_NORMAL_PRIORITY_CLASS 8 0 + -4....3 -12...-5 2 NORMAL_PRIORITY_CLASS 0 -8 + -12...-5 -13..-19 3 ABOVE_NORMAL_PRIORITY_CLASS -8 -16 + -13..-19 -20 4 HIGH_PRIORITY_CLASS -16 -20 + -20 - 5 REALTIME_PRIORITY_CLASS -20 -20 +*/ + +/* *_PRIORITY_CLASS -> 0...5 */ +constexpr int +winprio_to_level (DWORD prio) { switch (prio) { - case REALTIME_PRIORITY_CLASS: - return -20; - case HIGH_PRIORITY_CLASS: - return -16; - case ABOVE_NORMAL_PRIORITY_CLASS: - return -8; - case NORMAL_PRIORITY_CLASS: - return 0; - case BELOW_NORMAL_PRIORITY_CLASS: - return 8; - case IDLE_PRIORITY_CLASS: - return 16; + case IDLE_PRIORITY_CLASS: return 0; + case BELOW_NORMAL_PRIORITY_CLASS: return 1; + default: return 2; + case ABOVE_NORMAL_PRIORITY_CLASS: return 3; + case HIGH_PRIORITY_CLASS: return 4; + case REALTIME_PRIORITY_CLASS: return 5; } - return 0; +} + +/* 0...5 -> *_PRIORITY_CLASS */ +constexpr DWORD +level_to_winprio (int level) +{ + switch (level) + { + case 0: return IDLE_PRIORITY_CLASS; + case 1: return BELOW_NORMAL_PRIORITY_CLASS; + default: return NORMAL_PRIORITY_CLASS; + case 3: return ABOVE_NORMAL_PRIORITY_CLASS; + case 4: return HIGH_PRIORITY_CLASS; + case 5: return REALTIME_PRIORITY_CLASS; + } +} + +/* *_PRIORITY_CLASS -> nice value */ +constexpr int +winprio_to_nice_impl (DWORD prio, bool batch = false) +{ + int level = winprio_to_level (prio); + if (batch && level < 5) + level++; + return (level < 5 ? NZERO - 1 - 3 - level * 8 : -NZERO); +} + +/* nice value -> *_PRIORITY_CLASS */ +constexpr DWORD +nice_to_winprio_impl (int nice, bool batch = false) +{ + int level = (nice > -NZERO ? (NZERO - 1 - nice) / 8 : 5); + if (batch && level > 0) + level--; + return level_to_winprio (level); +} + +/* Check consistency at compile time. */ +constexpr bool +check_nice_winprio_mapping () +{ + for (int nice = -NZERO; nice < NZERO; nice++) + for (int batch = 0; batch <= 1; batch++) { + DWORD prio = nice_to_winprio_impl (nice, !!batch); + int nice2 = winprio_to_nice_impl (prio, !!batch); + DWORD prio2 = nice_to_winprio_impl (nice2, !!batch); + if (prio != prio2) + return false; + } + return true; +} + +static_assert (check_nice_winprio_mapping()); +static_assert (nice_to_winprio_impl(NZERO-1, false) == IDLE_PRIORITY_CLASS); +static_assert (nice_to_winprio_impl(0, true) == BELOW_NORMAL_PRIORITY_CLASS); +static_assert (winprio_to_nice_impl(BELOW_NORMAL_PRIORITY_CLASS, true) == 0); +static_assert (nice_to_winprio_impl(0, false) == NORMAL_PRIORITY_CLASS); +static_assert (winprio_to_nice_impl(NORMAL_PRIORITY_CLASS, false) == 0); +static_assert (nice_to_winprio_impl(-NZERO, false) == REALTIME_PRIORITY_CLASS); + +/* Get a default value for the nice factor. */ +int +winprio_to_nice (DWORD prio, bool batch /* = false */) +{ + return winprio_to_nice_impl (prio, batch); } /* Get a Win32 priority matching the incoming nice factor. The incoming nice is limited to the interval [-NZERO,NZERO-1]. */ DWORD -nice_to_winprio (int &nice) +nice_to_winprio (int &nice, bool batch /* = false */) { - static const DWORD priority[] = - { - REALTIME_PRIORITY_CLASS, /* 0 */ - HIGH_PRIORITY_CLASS, /* 1 */ - HIGH_PRIORITY_CLASS, - HIGH_PRIORITY_CLASS, - HIGH_PRIORITY_CLASS, - HIGH_PRIORITY_CLASS, - HIGH_PRIORITY_CLASS, - HIGH_PRIORITY_CLASS, /* 7 */ - ABOVE_NORMAL_PRIORITY_CLASS, /* 8 */ - ABOVE_NORMAL_PRIORITY_CLASS, - ABOVE_NORMAL_PRIORITY_CLASS, - ABOVE_NORMAL_PRIORITY_CLASS, - ABOVE_NORMAL_PRIORITY_CLASS, - ABOVE_NORMAL_PRIORITY_CLASS, - ABOVE_NORMAL_PRIORITY_CLASS, - ABOVE_NORMAL_PRIORITY_CLASS, /* 15 */ - NORMAL_PRIORITY_CLASS, /* 16 */ - NORMAL_PRIORITY_CLASS, - NORMAL_PRIORITY_CLASS, - NORMAL_PRIORITY_CLASS, - NORMAL_PRIORITY_CLASS, - NORMAL_PRIORITY_CLASS, - NORMAL_PRIORITY_CLASS, - NORMAL_PRIORITY_CLASS, /* 23 */ - BELOW_NORMAL_PRIORITY_CLASS, /* 24 */ - BELOW_NORMAL_PRIORITY_CLASS, - BELOW_NORMAL_PRIORITY_CLASS, - BELOW_NORMAL_PRIORITY_CLASS, - BELOW_NORMAL_PRIORITY_CLASS, - BELOW_NORMAL_PRIORITY_CLASS, - BELOW_NORMAL_PRIORITY_CLASS, - BELOW_NORMAL_PRIORITY_CLASS, /* 31 */ - IDLE_PRIORITY_CLASS, /* 32 */ - IDLE_PRIORITY_CLASS, - IDLE_PRIORITY_CLASS, - IDLE_PRIORITY_CLASS, - IDLE_PRIORITY_CLASS, - IDLE_PRIORITY_CLASS, - IDLE_PRIORITY_CLASS, - IDLE_PRIORITY_CLASS /* 39 */ - }; if (nice < -NZERO) nice = -NZERO; else if (nice > NZERO - 1) nice = NZERO - 1; - DWORD prio = priority[nice + NZERO]; - return prio; + + return nice_to_winprio_impl (nice, batch); } /* Set Win32 priority or return false on failure. Also return diff --git a/winsup/cygwin/release/3.6.0 b/winsup/cygwin/release/3.6.0 index 8ca91f0c9..11f745b23 100644 --- a/winsup/cygwin/release/3.6.0 +++ b/winsup/cygwin/release/3.6.0 @@ -54,11 +54,12 @@ What changed: to POSIX and Linux (glibc >= 2.2.4) behavior. - sched_setscheduler(2) now emulates changes between SCHED_OTHER, - SCHED_IDLE, SCHED_FIFO and SCHED_RR. If SCHED_OTHER is selected, the - Windows priority is set according to the nice value. If SCHED_IDLE is + SCHED_BATCH, SCHED_IDLE, SCHED_FIFO and SCHED_RR. If SCHED_OTHER or + SCHED_BATCH is selected, the Windows priority is set according to the + nice value where SCHED_BATCH sets a one step lower priority. If + SCHED_IDLE is selected, the nice value is preserved and the Windows + priority is set to IDLE_PRIORITY_CLASS. If SCHED_FIFO or SCHED_RR is selected, the nice value is preserved and the Windows priority is set - to IDLE_PRIORITY_CLASS. If SCHED_FIFO or SCHED_RR is selected, the - nice value is preserved and the Windows priority is set according to - the realtime priority. + according to the realtime priority. Note: Windows does not offer alternative scheduling policies so this could only emulate API behavior. diff --git a/winsup/cygwin/sched.cc b/winsup/cygwin/sched.cc index 8b4e7efc4..ec62ea83c 100644 --- a/winsup/cygwin/sched.cc +++ b/winsup/cygwin/sched.cc @@ -34,6 +34,7 @@ sched_get_priority_max (int policy) switch (policy) { case SCHED_OTHER: + case SCHED_BATCH: case SCHED_IDLE: return 0; case SCHED_FIFO: @@ -51,6 +52,7 @@ sched_get_priority_min (int policy) switch (policy) { case SCHED_OTHER: + case SCHED_BATCH: case SCHED_IDLE: return 0; case SCHED_FIFO: @@ -95,7 +97,8 @@ sched_getparam (pid_t pid, struct sched_param *param) return -1; } - if (p->sched_policy == SCHED_OTHER || p->sched_policy == SCHED_IDLE) + if (p->sched_policy == SCHED_OTHER || p->sched_policy == SCHED_BATCH + || p->sched_policy == SCHED_IDLE) { /* No realtime policy. */ param->sched_priority = 0; @@ -234,9 +237,10 @@ sched_setparam_pinfo (pinfo & p, const struct sched_param *param) /* calculate our desired priority class. We only reserve a small area (31/32) for realtime priority. */ DWORD pclass; - if (p->sched_policy == SCHED_OTHER && pri == 0) + bool batch = (p->sched_policy == SCHED_BATCH); + if ((p->sched_policy == SCHED_OTHER || batch) && pri == 0) /* No realtime policy, reapply the nice value. */ - pclass = nice_to_winprio (p->nice); + pclass = nice_to_winprio (p->nice, batch); else if (p->sched_policy == SCHED_IDLE && pri == 0) /* Idle policy, ignore the nice value. */ pclass = IDLE_PRIORITY_CLASS; @@ -422,8 +426,9 @@ sched_setscheduler (pid_t pid, int policy, const struct sched_param *param) { if (!(pid >= 0 && param && - (((policy == SCHED_OTHER || policy == SCHED_IDLE) && param->sched_priority == 0) || - ((policy == SCHED_FIFO || policy == SCHED_RR) && valid_sched_parameters(param))))) + (((policy == SCHED_OTHER || policy == SCHED_BATCH || policy == SCHED_IDLE) + && param->sched_priority == 0) || ((policy == SCHED_FIFO || policy == SCHED_RR) + && valid_sched_parameters(param))))) { set_errno (EINVAL); return -1; diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index 0deefeff6..5ff0f02b9 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -3816,9 +3816,6 @@ vhangup () extern "C" int setpriority (int which, id_t who, int value) { - DWORD prio = nice_to_winprio (value); - int error = 0; - switch (which) { case PRIO_PROCESS: @@ -3827,8 +3824,10 @@ setpriority (int which, id_t who, int value) if ((pid_t) who == myself->pid) { /* If realtime policy is set, keep prio but check its validity. */ + bool batch = (myself->sched_policy == SCHED_BATCH); + DWORD prio = nice_to_winprio (value, batch); if (!set_and_check_winprio (GetCurrentProcess (), prio, - (myself->sched_policy == SCHED_OTHER))) + (myself->sched_policy == SCHED_OTHER || batch))) { set_errno (EACCES); return -1; @@ -3850,6 +3849,8 @@ setpriority (int which, id_t who, int value) set_errno (EINVAL); return -1; } + + int error = 0; winpids pids ((DWORD) PID_MAP_RW); for (DWORD i = 0; i < pids.npids; ++i) { @@ -3878,9 +3879,11 @@ setpriority (int which, id_t who, int value) error = EPERM; else { + bool batch = (p->sched_policy == SCHED_BATCH); + DWORD prio = nice_to_winprio (value, batch); /* If realtime policy is set, keep prio but check its validity. */ if (!set_and_check_winprio (proc_h, prio, - (p->sched_policy == SCHED_OTHER))) + (p->sched_policy == SCHED_OTHER || batch))) error = EACCES; else p->nice = value; @@ -3909,11 +3912,12 @@ getpriority (int which, id_t who) who = myself->pid; if ((pid_t) who == myself->pid) { - if (myself->sched_policy == SCHED_OTHER) + bool batch = (myself->sched_policy == SCHED_BATCH); + if (myself->sched_policy == SCHED_OTHER || batch) { DWORD winprio = GetPriorityClass (GetCurrentProcess()); - if (winprio != nice_to_winprio (myself->nice)) - myself->nice = winprio_to_nice (winprio); + if (winprio != nice_to_winprio (myself->nice, batch)) + myself->nice = winprio_to_nice (winprio, batch); } return myself->nice; } -- 2.45.1