There are a couple of multi-group affinity operations that cannot be done
without heroic measures.  Those are marked with XXX in the code.  Further
discussion would be helpful to me.

---
 newlib/libc/include/sched.h     |  13 ++
 winsup/cygwin/common.din        |   4 +
 winsup/cygwin/include/pthread.h |   2 +
 winsup/cygwin/sched.cc          | 237 ++++++++++++++++++++++++++++++++
 winsup/cygwin/thread.cc         |  19 +++
 5 files changed, 275 insertions(+)

diff --git a/newlib/libc/include/sched.h b/newlib/libc/include/sched.h
index 1016235bb..a4d3fea6a 100644
--- a/newlib/libc/include/sched.h
+++ b/newlib/libc/include/sched.h
@@ -92,6 +92,19 @@ int sched_yield( void );
 
 #if __GNU_VISIBLE
 int sched_getcpu(void);
+
+#ifdef __CYGWIN__
+/* Affinity-related definitions, here until numerous enough to separate out */
+typedef uint64_t cpu_set_t;
+#define CPU_SETSIZE 1024
+#define CPU_GROUPSIZE 64
+#define CPU_GROUPMAX (CPU_SETSIZE / CPU_GROUPSIZE)
+
+int sched_getaffinity (pid_t, size_t, cpu_set_t *);
+int sched_get_thread_affinity (void *, size_t, cpu_set_t *);
+int sched_setaffinity (pid_t, size_t, const cpu_set_t *);
+int sched_set_thread_affinity (void *, size_t, const cpu_set_t *);
+#endif
 #endif
 
 #ifdef __cplusplus
diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din
index 68b95d470..81292ab7b 100644
--- a/winsup/cygwin/common.din
+++ b/winsup/cygwin/common.din
@@ -1084,6 +1084,7 @@ pthread_create SIGFE
 pthread_detach SIGFE
 pthread_equal SIGFE
 pthread_exit SIGFE
+pthread_getaffinity_np SIGFE
 pthread_getattr_np SIGFE
 pthread_getconcurrency SIGFE
 pthread_getcpuclockid SIGFE
@@ -1128,6 +1129,7 @@ pthread_rwlockattr_getpshared SIGFE
 pthread_rwlockattr_init SIGFE
 pthread_rwlockattr_setpshared SIGFE
 pthread_self SIGFE
+pthread_setaffinity_np SIGFE
 pthread_setcancelstate SIGFE
 pthread_setcanceltype SIGFE
 pthread_setconcurrency SIGFE
@@ -1248,10 +1250,12 @@ scandirat SIGFE
 scanf SIGFE
 sched_get_priority_max SIGFE
 sched_get_priority_min SIGFE
+sched_getaffinity SIGFE
 sched_getcpu SIGFE
 sched_getparam SIGFE
 sched_getscheduler NOSIGFE
 sched_rr_get_interval SIGFE
+sched_setaffinity SIGFE
 sched_setparam SIGFE
 sched_setscheduler SIGFE
 sched_yield SIGFE
diff --git a/winsup/cygwin/include/pthread.h b/winsup/cygwin/include/pthread.h
index 2ccf1cf8b..4ef3aeab7 100644
--- a/winsup/cygwin/include/pthread.h
+++ b/winsup/cygwin/include/pthread.h
@@ -226,8 +226,10 @@ void pthread_testcancel (void);
 /* Non posix calls */
 
 #if __GNU_VISIBLE
+int pthread_getaffinity_np (pthread_t, size_t, cpu_set_t *);
 int pthread_getattr_np (pthread_t, pthread_attr_t *);
 int pthread_getname_np (pthread_t, char *, size_t) 
__attribute__((__nonnull__(2)));
+int pthread_setaffinity_np (pthread_t, size_t, const cpu_set_t *);
 int pthread_setname_np (pthread_t, const char *) 
__attribute__((__nonnull__(2)));
 int pthread_sigqueue (pthread_t *, int, const union sigval);
 int pthread_timedjoin_np (pthread_t, void **, const struct timespec *);
diff --git a/winsup/cygwin/sched.cc b/winsup/cygwin/sched.cc
index 10168e641..2d527da69 100644
--- a/winsup/cygwin/sched.cc
+++ b/winsup/cygwin/sched.cc
@@ -424,4 +424,241 @@ sched_getcpu ()
   return pnum.Group * __get_cpus_per_group () + pnum.Number;
 }
 
+/* figure out which processor group the set bits indicate; can only be one */
+static int
+whichgroup (size_t sizeof_set, const cpu_set_t *set)
+{
+  //XXX code assumes __get_cpus_per_group() is fixed at 64
+  int res = -1;
+
+  for (unsigned int i = 0; i < sizeof_set / sizeof (cpu_set_t); ++i)
+    if (set[i])
+      {
+       if (res >= 0)
+         return -1; // error return if more than one group indicated
+       else
+         res = (int) i; // remember first group found
+      }
+
+  return res;
+}
+
+int
+sched_get_thread_affinity (HANDLE thread, size_t sizeof_set, cpu_set_t *set)
+{
+  int status = 0;
+
+  //XXX code assumes __get_cpus_per_group() is fixed at 64
+  if (thread)
+    {
+      memset (set, 0, sizeof_set);
+      if (wincap.has_processor_groups ())
+       {
+         GROUP_AFFINITY ga;
+
+         if (!GetThreadGroupAffinity (thread, &ga))
+           {
+             status = geterrno_from_win_error (GetLastError (), EPERM);
+             goto done;
+           }
+         set[ga.Group] = ga.Mask;
+       }
+      else
+       {
+         // There is no GetThreadAffinityMask() function, so simulate one by
+         // iterating through CPUs trying to set affinity, which returns the
+         // previous affinity.  On success, restore original affinity.
+         // This strategy is due to Damon on StackOverflow.
+         KAFFINITY cpumask = 1;
+         KAFFINITY oldmask = 0;
+
+         // Iterate through CPUs until success setting thread affinity to it
+         while (cpumask)
+           {
+             oldmask = SetThreadAffinityMask (thread, cpumask);
+             if (oldmask)
+               { // that one worked, so restore original mask
+                 SetThreadAffinityMask (thread, oldmask);
+                 set[0] = oldmask;
+                 goto done;
+               }
+             if (GetLastError () != ERROR_INVALID_PARAMETER)
+               { // that one failed in an unexpected way
+                 status = geterrno_from_win_error (GetLastError (), EPERM);
+                 goto done;
+               }
+             cpumask <<= 1;
+           }
+         status = ENOSYS; //XXX strategy failed.. figure out a new one
+       }
+    }
+  else
+    status = ESRCH;
+
+done:
+  return status;
+}
+
+int
+sched_getaffinity (pid_t pid, size_t sizeof_set, cpu_set_t *set)
+{
+  HANDLE process = 0;
+  int status = 0;
+
+  //XXX code assumes __get_cpus_per_group() is fixed at 64
+  pinfo p (pid ? pid : getpid ());
+  if (p)
+    {
+      process = pid && pid != myself->pid ?
+                OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION, FALSE,
+                             p->dwProcessId) : GetCurrentProcess ();
+      KAFFINITY procmask;
+      KAFFINITY sysmask;
+
+      if (!GetProcessAffinityMask (process, &procmask, &sysmask))
+        {
+oops:
+          status = geterrno_from_win_error (GetLastError (), EPERM);
+          goto done;
+        }
+      memset (set, 0, sizeof_set);
+      if (wincap.has_processor_groups ())
+        {
+          USHORT groupcount = CPU_GROUPMAX;
+          USHORT grouparray[CPU_GROUPMAX];
+
+          if (!GetProcessGroupAffinity (process, &groupcount, grouparray))
+            goto oops;
+          if (groupcount == 1)
+           set[grouparray[0]] = procmask;
+         else
+            status = ENOSYS;//XXX multi-group code TBD...
+           // There is no way to assemble the complete process affinity mask
+           // without querying at least one thread per group in grouparray,
+           // and we don't know which group a thread is in without querying
+           // it, so must query all threads.  I'd call that a heroic measure.
+        }
+      else
+        set[0] = procmask;
+    }
+  else
+    status = ESRCH;
+
+done:
+  if (process && process != GetCurrentProcess ())
+    CloseHandle (process);
+
+  return status;
+}
+
+int
+sched_set_thread_affinity (HANDLE thread, size_t sizeof_set, const cpu_set_t 
*set)
+{
+  int group = whichgroup (sizeof_set, set);
+  int status = 0;
+
+  //XXX code assumes __get_cpus_per_group() is fixed at 64
+  if (thread)
+    {
+      if (wincap.has_processor_groups ())
+       {
+         GROUP_AFFINITY ga;
+
+         if (group < 0)
+           {
+             status = EINVAL;
+             goto done;
+           }
+         memset (&ga, 0, sizeof (ga));
+         ga.Mask = set[group];
+         ga.Group = group;
+         if (!SetThreadGroupAffinity (thread, &ga, NULL))
+           {
+             status = geterrno_from_win_error (GetLastError (), EPERM);
+             goto done;
+           }
+       }
+      else
+       {
+         if (group != 0)
+           {
+             status = EINVAL;
+             goto done;
+           }
+         if (!SetThreadAffinityMask (thread, set[0]))
+           {
+             status = geterrno_from_win_error (GetLastError (), EPERM);
+             goto done;
+           }
+       }
+    }
+  else
+    status = ESRCH;
+
+done:
+  return status;
+}
+
+int
+sched_setaffinity (pid_t pid, size_t sizeof_set, const cpu_set_t *set)
+{
+  int group = whichgroup (sizeof_set, set);
+  HANDLE process = 0;
+  int status = 0;
+
+  //XXX code assumes __get_cpus_per_group() is fixed at 64
+  pinfo p (pid ? pid : getpid ());
+  if (p)
+    {
+      process = pid && pid != myself->pid ?
+               OpenProcess (PROCESS_SET_INFORMATION, FALSE,
+                            p->dwProcessId) : GetCurrentProcess ();
+      if (wincap.has_processor_groups ())
+       {
+         USHORT groupcount = CPU_GROUPMAX;
+         USHORT grouparray[CPU_GROUPMAX];
+
+         if (!GetProcessGroupAffinity (process, &groupcount, grouparray))
+           {
+             status = geterrno_from_win_error (GetLastError (), EPERM);
+             goto done;
+           }
+         if (group < 0)
+           {
+             status = EINVAL;
+             goto done;
+           }
+         if (groupcount == 1 && grouparray[0] == group)
+           {
+             if (!SetProcessAffinityMask (process, set[group]))
+               status = geterrno_from_win_error (GetLastError (), EPERM);
+             goto done;
+           }
+         status = ENOSYS; //XXX can't do it without heroic measures
+         goto done;
+       }
+      else
+       {
+         if (group != 0)
+           {
+             status = EINVAL;
+             goto done;
+           }
+         if (!SetProcessAffinityMask (process, set[0]))
+           {
+             status = geterrno_from_win_error (GetLastError (), EPERM);
+             goto done;
+           }
+       }
+    }
+  else
+    status = ESRCH;
+
+done:
+  if (process && process != GetCurrentProcess ())
+    CloseHandle (process);
+
+  return status;
+}
+
 } /* extern C */
diff --git a/winsup/cygwin/thread.cc b/winsup/cygwin/thread.cc
index f353dd497..43a6c88b3 100644
--- a/winsup/cygwin/thread.cc
+++ b/winsup/cygwin/thread.cc
@@ -23,6 +23,7 @@ details. */
 #include "winsup.h"
 #include "miscfuncs.h"
 #include "path.h"
+#include <sched.h>
 #include <stdlib.h>
 #include "sigproc.h"
 #include "fhandler.h"
@@ -2606,6 +2607,24 @@ pthread_timedjoin_np (pthread_t thread, void 
**return_val,
   return pthread::join (&thread, (void **) return_val, &timeout);
 }
 
+extern "C" int
+pthread_getaffinity_np (pthread_t thread, size_t sizeof_set, cpu_set_t *set)
+{
+  if (!pthread::is_good_object (&thread))
+    return ESRCH;
+
+  return sched_get_thread_affinity (thread->win32_obj_id, sizeof_set, set);
+}
+
+extern "C" int
+pthread_setaffinity_np (pthread_t thread, size_t sizeof_set, const cpu_set_t 
*set)
+{
+  if (!pthread::is_good_object (&thread))
+    return ESRCH;
+
+  return sched_set_thread_affinity (thread->win32_obj_id, sizeof_set, set);
+}
+
 extern "C" int
 pthread_getattr_np (pthread_t thread, pthread_attr_t *attr)
 {
-- 
2.17.0

Reply via email to