A very first attempt to let sched_setscheduler() do something possibly useful.

This patch is on top of
Cygwin: setpriority, sched_setparam: add missing process access right

--
Regards,
Christian

From 54a9b7be2ab4284203daaf154478118cc52409cf Mon Sep 17 00:00:00 2001
From: Christian Franke <christian.fra...@t-online.de>
Date: Fri, 29 Nov 2024 18:41:12 +0100
Subject: [PATCH] Cygwin: sched_setscheduler: accept SCHED_OTHER, SCHED_FIFO
 and SCHED_RR

If SCHED_OTHER is selected, set the Windows priority according to the
nice value.  If SCHED_FIFO or SCHED_RR is selected, preserve the nice
value and set the Windows priority according to the sched_priority
parameter.

Signed-off-by: Christian Franke <christian.fra...@t-online.de>
---
 winsup/cygwin/fork.cc                    |   1 +
 winsup/cygwin/local_includes/miscfuncs.h |   2 +-
 winsup/cygwin/local_includes/pinfo.h     |   4 +-
 winsup/cygwin/miscfuncs.cc               |  23 +++--
 winsup/cygwin/pinfo.cc                   |   1 +
 winsup/cygwin/release/3.6.0              |   8 ++
 winsup/cygwin/sched.cc                   | 122 ++++++++++++++---------
 winsup/cygwin/spawn.cc                   |   1 +
 winsup/cygwin/syscalls.cc                |  19 ++--
 9 files changed, 118 insertions(+), 63 deletions(-)

diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc
index 0742ab363..7d976e882 100644
--- a/winsup/cygwin/fork.cc
+++ b/winsup/cygwin/fork.cc
@@ -402,6 +402,7 @@ frok::parent (volatile char * volatile stack_here)
     }
 
   child->nice = myself->nice;
+  child->sched_policy = myself->sched_policy;
 
   /* Initialize things that are done later in dll_crt0_1 that aren't done
      for the forkee.  */
diff --git a/winsup/cygwin/local_includes/miscfuncs.h 
b/winsup/cygwin/local_includes/miscfuncs.h
index efd7e516b..57dcbfeab 100644
--- a/winsup/cygwin/local_includes/miscfuncs.h
+++ b/winsup/cygwin/local_includes/miscfuncs.h
@@ -46,7 +46,7 @@ is_alt_numpad_event (PINPUT_RECORD pirec)
 
 int winprio_to_nice (DWORD);
 DWORD nice_to_winprio (int &);
-bool set_and_check_winprio (HANDLE proc, DWORD prio);
+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/local_includes/pinfo.h 
b/winsup/cygwin/local_includes/pinfo.h
index 463f0e851..03e0c4d60 100644
--- a/winsup/cygwin/local_includes/pinfo.h
+++ b/winsup/cygwin/local_includes/pinfo.h
@@ -92,7 +92,9 @@ public:
   long start_time;
   struct rusage rusage_self;
   struct rusage rusage_children;
-  int nice;
+
+  int nice;          /* nice value for SCHED_OTHER. */
+  int sched_policy;  /* SCHED_OTHER, SCHED_FIFO or SCHED_RR. */
 
   /* Non-zero if process was stopped by a signal. */
   char stopsig;
diff --git a/winsup/cygwin/miscfuncs.cc b/winsup/cygwin/miscfuncs.cc
index ebe401b93..0146704ea 100644
--- a/winsup/cygwin/miscfuncs.cc
+++ b/winsup/cygwin/miscfuncs.cc
@@ -185,9 +185,10 @@ nice_to_winprio (int &nice)
 
 /* Set Win32 priority or return false on failure.  Also return
    false and revert to the original priority if a different (lower)
-   priority is set instead. */
+   priority is set instead.  Always revert to original priority if
+   set==false. */
 bool
-set_and_check_winprio (HANDLE proc, DWORD prio)
+set_and_check_winprio (HANDLE proc, DWORD prio, bool set /* = true */)
 {
   DWORD prev_prio = GetPriorityClass (proc);
   if (!prev_prio)
@@ -202,16 +203,20 @@ set_and_check_winprio (HANDLE proc, DWORD prio)
      the new priority (REALTIME_PRIORITY_CLASS) requires administrator
      privileges. */
   DWORD curr_prio = GetPriorityClass (proc);
-  if (curr_prio != prio)
+  bool ret = (curr_prio == prio);
+
+  if (set)
     {
-      debug_printf ("Failed to set priority 0x%x, revert from 0x%x to 0x%x",
-       prio, curr_prio, prev_prio);
-      SetPriorityClass (proc, prev_prio);
-      return false;
+      if (ret)
+       debug_printf ("Changed priority from 0x%x to 0x%x", prev_prio, 
curr_prio);
+      else
+       debug_printf ("Failed to set priority 0x%x, revert from 0x%x to 0x%x",
+         prio, curr_prio, prev_prio);
     }
+  if (!(set && ret))
+    SetPriorityClass (proc, prev_prio);
 
-  debug_printf ("Changed priority from 0x%x to 0x%x", prev_prio, curr_prio);
-  return true;
+  return ret;
 }
 
 /* Minimal overlapped pipe I/O implementation for signal and commune stuff. */
diff --git a/winsup/cygwin/pinfo.cc b/winsup/cygwin/pinfo.cc
index e31a67d8f..06c966f1e 100644
--- a/winsup/cygwin/pinfo.cc
+++ b/winsup/cygwin/pinfo.cc
@@ -102,6 +102,7 @@ pinfo_init (char **envp, int envc)
       myself->gid = ILLEGAL_GID;
       environ_init (NULL, 0);  /* call after myself has been set up */
       myself->nice = winprio_to_nice (GetPriorityClass (GetCurrentProcess ()));
+      myself->sched_policy = SCHED_OTHER;
       myself->ppid = 1;                /* always set last */
       debug_printf ("Set nice to %d", myself->nice);
     }
diff --git a/winsup/cygwin/release/3.6.0 b/winsup/cygwin/release/3.6.0
index 1b2f00ad8..9e924dabb 100644
--- a/winsup/cygwin/release/3.6.0
+++ b/winsup/cygwin/release/3.6.0
@@ -52,3 +52,11 @@ What changed:
 - nice(2) now returns the new nice value instead of 0 on success
   and sets errno to EPERM instead of EACCES on failure.  This confirms
   to POSIX and Linux (glibc >= 2.2.4) behavior.
+
+- sched_setscheduler(2) now emulates changes between SCHED_OTHER,
+  SCHED_FIFO and SCHED_RR.  If SCHED_OTHER is selected, the Windows
+  priority is set according to the nice value.  If SCHED_FIFO or
+  SCHED_RR is selected, the nice value is preserved and the Windows
+  priority is set 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 61d5e7be4..c48c433d7 100644
--- a/winsup/cygwin/sched.cc
+++ b/winsup/cygwin/sched.cc
@@ -33,9 +33,10 @@ sched_get_priority_max (int policy)
 {
   switch (policy)
     {
+    case SCHED_OTHER:
+      return 0;
     case SCHED_FIFO:
     case SCHED_RR:
-    case SCHED_OTHER:
       return 32;
     }
   set_errno (EINVAL);
@@ -48,9 +49,10 @@ sched_get_priority_min (int policy)
 {
   switch (policy)
     {
+    case SCHED_OTHER:
+      return 0;
     case SCHED_FIFO:
     case SCHED_RR:
-    case SCHED_OTHER:
       return 1;
     }
   set_errno (EINVAL);
@@ -90,6 +92,14 @@ sched_getparam (pid_t pid, struct sched_param *param)
       set_errno (ESRCH);
       return -1;
     }
+
+  if (p->sched_policy == SCHED_OTHER)
+    {
+      /* No realtime policy. */
+      param->sched_priority = 0;
+      return 0;
+    }
+
   process = OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION, FALSE,
                         p->dwProcessId);
   if (!process)
@@ -132,9 +142,6 @@ sched_getparam (pid_t pid, struct sched_param *param)
 }
 
 /* get the scheduler for pid
-
-   All process's on WIN32 run with SCHED_FIFO.  So we just give an answer.
-   (WIN32 uses a multi queue FIFO).
 */
 int
 sched_getscheduler (pid_t pid)
@@ -144,8 +151,13 @@ sched_getscheduler (pid_t pid)
       set_errno (EINVAL);
       return -1;
     }
-  else
-    return SCHED_FIFO;
+  pinfo p (pid ? pid : getpid ());
+  if (!p)
+    {
+      set_errno (ESRCH);
+      return -1;
+    }
+  return p->sched_policy;
 }
 
 /* get the time quantum for pid */
@@ -212,31 +224,18 @@ sched_rr_get_interval (pid_t pid, struct timespec 
*interval)
 }
 
 /* set the scheduling parameters */
-int
-sched_setparam (pid_t pid, const struct sched_param *param)
+static int
+sched_setparam_pinfo (pinfo & p, const struct sched_param *param)
 {
-  pid_t localpid;
-  int pri;
-  DWORD pclass;
-  HANDLE process;
-
-  if (!param || pid < 0)
-    {
-      set_errno (EINVAL);
-      return -1;
-    }
-
-  if (!valid_sched_parameters (param))
-    {
-      set_errno (EINVAL);
-      return -1;
-    }
-
-  pri = param->sched_priority;
+  int pri = param->sched_priority;
 
   /* calculate our desired priority class.  We only reserve a small area
      (31/32) for realtime priority. */
-  if (pri <= 6)
+  DWORD pclass;
+  if (p->sched_policy == SCHED_OTHER && pri == 0)
+    /* No realtime policy, reapply the nice value. */
+    pclass = nice_to_winprio (p->nice);
+  else if (1 <= pri && pri <= 6)
     pclass = IDLE_PRIORITY_CLASS;
   else if (pri <= 12)
     pclass = BELOW_NORMAL_PRIORITY_CLASS;
@@ -246,23 +245,16 @@ sched_setparam (pid_t pid, const struct sched_param 
*param)
     pclass = ABOVE_NORMAL_PRIORITY_CLASS;
   else if (pri <= 30)
     pclass = HIGH_PRIORITY_CLASS;
-  else
+  else if (pri <= 32)
     pclass = REALTIME_PRIORITY_CLASS;
-
-  localpid = pid ? pid : getpid ();
-
-  pinfo p (localpid);
-
-  /* set the class */
-
-  if (!p)
+  else
     {
-      set_errno (ESRCH);
+      set_errno (EINVAL);
       return -1;
     }
-  process = OpenProcess (PROCESS_SET_INFORMATION |
-                        PROCESS_QUERY_LIMITED_INFORMATION,
-                        FALSE, p->dwProcessId);
+  HANDLE process = OpenProcess (PROCESS_SET_INFORMATION |
+                               PROCESS_QUERY_LIMITED_INFORMATION,
+                               FALSE, p->dwProcessId);
   if (!process)
     {
       set_errno (ESRCH);
@@ -279,6 +271,26 @@ sched_setparam (pid_t pid, const struct sched_param *param)
   return 0;
 }
 
+int
+sched_setparam (pid_t pid, const struct sched_param *param)
+{
+  if (!(pid >= 0 && param && (param->sched_priority == 0 ||
+      valid_sched_parameters(param))))
+    {
+      set_errno (EINVAL);
+      return -1;
+    }
+
+  pinfo p (pid ? pid : getpid ());
+  if (!p)
+    {
+      set_errno (ESRCH);
+      return -1;
+    }
+
+  return sched_setparam_pinfo (p, param);
+}
+
 /* POSIX thread priorities loosely compare to Windows thread base priorities.
 
    Base priority is a function of process priority class and thread priority.
@@ -404,12 +416,30 @@ int
 sched_setscheduler (pid_t pid, int policy,
                    const struct sched_param *param)
 {
-  if (policy == SCHED_FIFO) /* returned by sched_getscheduler. */
-    return sched_setparam (pid, param);
+  if (!(pid >= 0 && param &&
+      ((policy == SCHED_OTHER && param->sched_priority == 0) ||
+      ((policy == SCHED_FIFO || policy == SCHED_RR) && 
valid_sched_parameters(param)))))
+    {
+      set_errno (EINVAL);
+      return -1;
+    }
 
-  /* on win32, you can't change the scheduler. Doh! */
-  set_errno (EINVAL);
-  return -1;
+  pinfo p (pid ? pid : getpid ());
+  if (!p)
+    {
+      set_errno (ESRCH);
+      return -1;
+    }
+
+  int prev_policy = p->sched_policy;
+  p->sched_policy = policy;
+  if (sched_setparam_pinfo (p, param))
+    {
+      p->sched_policy = prev_policy;
+      return -1;
+    }
+
+  return 0;
 }
 
 /* yield the cpu */
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
index 60a82991a..7f9f2df64 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -799,6 +799,7 @@ child_info_spawn::worker (const char *prog_arg, const char 
*const *argv,
            }
          child->start_time = time (NULL); /* Register child's starting time. */
          child->nice = myself->nice;
+         child->sched_policy = myself->sched_policy;
          postfork (child);
          if (mode != _P_DETACH
              && (!child.remember () || !child.attach ()))
diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc
index d4fba632c..2243da090 100644
--- a/winsup/cygwin/syscalls.cc
+++ b/winsup/cygwin/syscalls.cc
@@ -3826,7 +3826,9 @@ setpriority (int which, id_t who, int value)
        who = myself->pid;
       if ((pid_t) who == myself->pid)
        {
-         if (!set_and_check_winprio (GetCurrentProcess (), prio))
+         /* If realtime policy is set, keep prio but check its validity. */
+         if (!set_and_check_winprio (GetCurrentProcess (), prio,
+             (myself->sched_policy == SCHED_OTHER)))
            {
              set_errno (EACCES);
              return -1;
@@ -3876,7 +3878,9 @@ setpriority (int which, id_t who, int value)
            error = EPERM;
          else
            {
-             if (!set_and_check_winprio (proc_h, prio))
+             /* If realtime policy is set, keep prio but check its validity. */
+             if (!set_and_check_winprio (proc_h, prio,
+                 (p->sched_policy == SCHED_OTHER)))
                error = EACCES;
              else
                p->nice = value;
@@ -3905,10 +3909,13 @@ getpriority (int which, id_t who)
        who = myself->pid;
       if ((pid_t) who == myself->pid)
         {
-          DWORD winprio = GetPriorityClass(GetCurrentProcess());
-          if (winprio != nice_to_winprio(myself->nice))
-            myself->nice = winprio_to_nice(winprio);
-          return myself->nice;
+         if (myself->sched_policy == SCHED_OTHER)
+           {
+             DWORD winprio = GetPriorityClass (GetCurrentProcess());
+             if (winprio != nice_to_winprio (myself->nice))
+               myself->nice = winprio_to_nice (winprio);
+           }
+         return myself->nice;
         }
       break;
     case PRIO_PGRP:
-- 
2.45.1

Reply via email to