Ver código fonte

libuv: unix,win: add uv_spawn option to set child CPU affinity mask

Implement it on Linux, FreeBSD, and Windows for now, and fail with
UV_ENOTSUP on other platforms.

Backported from upstream libuv PR 1527, scheduled for inclusion
in libuv 2.0.
Brad King 8 anos atrás
pai
commit
24de561a1a

+ 13 - 0
Utilities/cmlibuv/include/uv.h

@@ -925,6 +925,19 @@ typedef struct uv_process_options_s {
    */
    */
   uv_uid_t uid;
   uv_uid_t uid;
   uv_gid_t gid;
   uv_gid_t gid;
+  /*
+    Libuv can set the child process' CPU affinity mask.  This happens when
+    `cpumask` is non-NULL.  It must point to an array of char values
+    of length `cpumask_size`, whose value must be at least that returned by
+    uv_cpumask_size().  Each byte in the mask can be either zero (false)
+    or non-zero (true) to indicate whether the corresponding processor at
+    that index is included.
+
+    If enabled on an unsupported platform, uv_spawn() will fail with
+    UV_ENOTSUP.
+   */
+  char* cpumask;
+  size_t cpumask_size;
 } uv_process_options_t;
 } uv_process_options_t;
 
 
 /*
 /*

+ 45 - 0
Utilities/cmlibuv/src/unix/process.c

@@ -32,6 +32,7 @@
 #include <unistd.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <fcntl.h>
 #include <poll.h>
 #include <poll.h>
+#include <sched.h>
 
 
 #if defined(__APPLE__) && !TARGET_OS_IPHONE
 #if defined(__APPLE__) && !TARGET_OS_IPHONE
 # include <crt_externs.h>
 # include <crt_externs.h>
@@ -44,6 +45,14 @@ extern char **environ;
 # include <grp.h>
 # include <grp.h>
 #endif
 #endif
 
 
+#if defined(__linux__)
+# define uv__cpu_set_t cpu_set_t
+#elif defined(__FreeBSD__)
+# include <sys/param.h>
+# include <sys/cpuset.h>
+# include <pthread_np.h>
+# define uv__cpu_set_t cpuset_t
+#endif
 
 
 static void uv__chld(uv_signal_t* handle, int signum) {
 static void uv__chld(uv_signal_t* handle, int signum) {
   uv_process_t* process;
   uv_process_t* process;
@@ -285,6 +294,12 @@ static void uv__process_child_init(const uv_process_options_t* options,
   int err;
   int err;
   int fd;
   int fd;
   int n;
   int n;
+#if defined(__linux__) || defined(__FreeBSD__)
+  int r;
+  int i;
+  int cpumask_size;
+  uv__cpu_set_t cpuset;
+#endif
 
 
   if (options->flags & UV_PROCESS_DETACHED)
   if (options->flags & UV_PROCESS_DETACHED)
     setsid();
     setsid();
@@ -375,6 +390,26 @@ static void uv__process_child_init(const uv_process_options_t* options,
     _exit(127);
     _exit(127);
   }
   }
 
 
+#if defined(__linux__) || defined(__FreeBSD__)
+  if (options->cpumask != NULL) {
+    cpumask_size = uv_cpumask_size();
+    assert(options->cpumask_size >= (size_t)cpumask_size);
+
+    CPU_ZERO(&cpuset);
+    for (i = 0; i < cpumask_size; ++i) {
+      if (options->cpumask[i]) {
+        CPU_SET(i, &cpuset);
+      }
+    }
+
+    r = -pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
+    if (r != 0) {
+      uv__write_int(error_fd, r);
+      _exit(127);
+    }
+  }
+#endif
+
   if (options->env != NULL) {
   if (options->env != NULL) {
     environ = options->env;
     environ = options->env;
   }
   }
@@ -429,6 +464,16 @@ int uv_spawn(uv_loop_t* loop,
   int i;
   int i;
   int status;
   int status;
 
 
+  if (options->cpumask != NULL) {
+#if defined(__linux__) || defined(__FreeBSD__)
+    if (options->cpumask_size < (size_t)uv_cpumask_size()) {
+      return UV_EINVAL;
+    }
+#else
+    return UV_ENOTSUP;
+#endif
+  }
+
   assert(options->file != NULL);
   assert(options->file != NULL);
   assert(!(options->flags & ~(UV_PROCESS_DETACHED |
   assert(!(options->flags & ~(UV_PROCESS_DETACHED |
                               UV_PROCESS_SETGID |
                               UV_PROCESS_SETGID |

+ 56 - 0
Utilities/cmlibuv/src/win/process.c

@@ -954,6 +954,12 @@ int uv_spawn(uv_loop_t* loop,
     return UV_EINVAL;
     return UV_EINVAL;
   }
   }
 
 
+  if (options->cpumask != NULL) {
+    if (options->cpumask_size < (size_t)uv_cpumask_size()) {
+      return UV_EINVAL;
+    }
+  }
+
   assert(options->file != NULL);
   assert(options->file != NULL);
   assert(!(options->flags & ~(UV_PROCESS_DETACHED |
   assert(!(options->flags & ~(UV_PROCESS_DETACHED |
                               UV_PROCESS_SETGID |
                               UV_PROCESS_SETGID |
@@ -1084,6 +1090,12 @@ int uv_spawn(uv_loop_t* loop,
     process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
     process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
   }
   }
 
 
+  if (options->cpumask != NULL) {
+    /* Create the child in a suspended state so we have a chance to set
+       its process affinity before it runs.  */
+    process_flags |= CREATE_SUSPENDED;
+  }
+
   if (!CreateProcessW(application_path,
   if (!CreateProcessW(application_path,
                      arguments,
                      arguments,
                      NULL,
                      NULL,
@@ -1099,6 +1111,50 @@ int uv_spawn(uv_loop_t* loop,
     goto done;
     goto done;
   }
   }
 
 
+  if (options->cpumask != NULL) {
+    /* The child is currently suspended.  Set its process affinity
+       or terminate it if we can't.  */
+    int i;
+    int cpumasksize;
+    DWORD_PTR sysmask;
+    DWORD_PTR oldmask;
+    DWORD_PTR newmask;
+
+    cpumasksize = uv_cpumask_size();
+
+    if (!GetProcessAffinityMask(info.hProcess, &oldmask, &sysmask)) {
+      err = GetLastError();
+      TerminateProcess(info.hProcess, 1);
+      goto done;
+    }
+
+    newmask = 0;
+    for (i = 0; i < cpumasksize; i++) {
+      if (options->cpumask[i]) {
+        if (oldmask & (((DWORD_PTR)1) << i)) {
+          newmask |= ((DWORD_PTR)1) << i;
+        } else {
+          err = UV_EINVAL;
+          TerminateProcess(info.hProcess, 1);
+          goto done;
+        }
+      }
+    }
+
+    if (!SetProcessAffinityMask(info.hProcess, newmask)) {
+      err = GetLastError();
+      TerminateProcess(info.hProcess, 1);
+      goto done;
+    }
+
+    /* The process affinity of the child is set.  Let it run.  */
+    if (ResumeThread(info.hThread) == ((DWORD)-1)) {
+      err = GetLastError();
+      TerminateProcess(info.hProcess, 1);
+      goto done;
+    }
+  }
+
   /* Spawn succeeded */
   /* Spawn succeeded */
   /* Beyond this point, failure is reported asynchronously. */
   /* Beyond this point, failure is reported asynchronously. */