diff --git a/CMakeLists.txt b/CMakeLists.txt index a133edb8500..4f13efc8e6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,6 +98,7 @@ set(uv_test_sources test/test-poll-closesocket.c test/test-poll-oob.c test/test-poll.c + test/test-process-priority.c test/test-process-title-threadsafe.c test/test-process-title.c test/test-queue-foreach-delete.c diff --git a/Makefile.am b/Makefile.am index 04aecab5555..a217faab3ce 100644 --- a/Makefile.am +++ b/Makefile.am @@ -224,6 +224,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-poll-close-doesnt-corrupt-stack.c \ test/test-poll-closesocket.c \ test/test-poll-oob.c \ + test/test-process-priority.c \ test/test-process-title.c \ test/test-process-title-threadsafe.c \ test/test-queue-foreach-delete.c \ diff --git a/docs/src/misc.rst b/docs/src/misc.rst index 07908c98ff8..957740a4b9d 100644 --- a/docs/src/misc.rst +++ b/docs/src/misc.rst @@ -517,3 +517,31 @@ API storage required to hold the value. .. versionadded:: 1.12.0 + +.. c:function:: int uv_os_getpriority(uv_pid_t pid, int* priority) + + Retrieves the scheduling priority of the process specified by `pid`. The + returned value of `priority` is between -20 (high priority) and 19 (low + priority). + + .. note:: + On Windows, the returned priority will equal one of the `UV_PRIORITY` + constants. + + .. versionadded:: REPLACEME + +.. c:function:: int uv_os_setpriority(uv_pid_t pid, int priority) + + Sets the scheduling priority of the process specified by `pid`. The + `priority` value range is between -20 (high priority) and 19 (low priority). + The constants `UV_PRIORITY_LOW`, `UV_PRIORITY_BELOW_NORMAL`, + `UV_PRIORITY_NORMAL`, `UV_PRIORITY_ABOVE_NORMAL`, `UV_PRIORITY_HIGH`, and + `UV_PRIORITY_HIGHEST` are also provided for convenience. + + .. note:: + On Windows, this function utilizes `SetPriorityClass()`. The `priority` + argument is mapped to a Windows priority class. When retrieving the + process priority, the result will equal one of the `UV_PRIORITY` + constants, and not necessarily the exact value of `priority`. + + .. versionadded:: REPLACEME diff --git a/include/uv.h b/include/uv.h index c6919d31514..717c2e570b9 100644 --- a/include/uv.h +++ b/include/uv.h @@ -1100,6 +1100,16 @@ UV_EXTERN void uv_os_free_passwd(uv_passwd_t* pwd); UV_EXTERN uv_pid_t uv_os_getpid(void); UV_EXTERN uv_pid_t uv_os_getppid(void); +#define UV_PRIORITY_LOW 19 +#define UV_PRIORITY_BELOW_NORMAL 10 +#define UV_PRIORITY_NORMAL 0 +#define UV_PRIORITY_ABOVE_NORMAL -7 +#define UV_PRIORITY_HIGH -14 +#define UV_PRIORITY_HIGHEST -20 + +UV_EXTERN int uv_os_getpriority(uv_pid_t pid, int* priority); +UV_EXTERN int uv_os_setpriority(uv_pid_t pid, int priority); + UV_EXTERN int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count); UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count); diff --git a/src/unix/core.c b/src/unix/core.c index 2680209911b..f92446ff42b 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -1350,3 +1350,31 @@ uv_pid_t uv_os_getpid(void) { uv_pid_t uv_os_getppid(void) { return getppid(); } + + +int uv_os_getpriority(uv_pid_t pid, int* priority) { + int r; + + if (priority == NULL) + return UV_EINVAL; + + errno = 0; + r = getpriority(PRIO_PROCESS, (int) pid); + + if (r == -1 && errno != 0) + return UV__ERR(errno); + + *priority = r; + return 0; +} + + +int uv_os_setpriority(uv_pid_t pid, int priority) { + if (priority < UV_PRIORITY_HIGHEST || priority > UV_PRIORITY_LOW) + return UV_EINVAL; + + if (setpriority(PRIO_PROCESS, (int) pid, priority) != 0) + return UV__ERR(errno); + + return 0; +} diff --git a/src/win/util.c b/src/win/util.c index 3e86ff15048..c994984fe65 100644 --- a/src/win/util.c +++ b/src/win/util.c @@ -1530,3 +1530,97 @@ int uv_os_gethostname(char* buffer, size_t* size) { *size = len; return 0; } + + +static int uv__get_handle(uv_pid_t pid, int access, HANDLE* handle) { + int r; + + if (pid == 0) + *handle = GetCurrentProcess(); + else + *handle = OpenProcess(access, FALSE, pid); + + if (*handle == NULL) { + r = GetLastError(); + + if (r == ERROR_INVALID_PARAMETER) + return UV_ESRCH; + else + return uv_translate_sys_error(r); + } + + return 0; +} + + +int uv_os_getpriority(uv_pid_t pid, int* priority) { + HANDLE handle; + int r; + + if (priority == NULL) + return UV_EINVAL; + + r = uv__get_handle(pid, PROCESS_QUERY_LIMITED_INFORMATION, &handle); + + if (r != 0) + return r; + + r = GetPriorityClass(handle); + + if (r == 0) { + r = uv_translate_sys_error(GetLastError()); + } else { + /* Map Windows priority classes to Unix nice values. */ + if (r == REALTIME_PRIORITY_CLASS) + *priority = UV_PRIORITY_HIGHEST; + else if (r == HIGH_PRIORITY_CLASS) + *priority = UV_PRIORITY_HIGH; + else if (r == ABOVE_NORMAL_PRIORITY_CLASS) + *priority = UV_PRIORITY_ABOVE_NORMAL; + else if (r == NORMAL_PRIORITY_CLASS) + *priority = UV_PRIORITY_NORMAL; + else if (r == BELOW_NORMAL_PRIORITY_CLASS) + *priority = UV_PRIORITY_BELOW_NORMAL; + else /* IDLE_PRIORITY_CLASS */ + *priority = UV_PRIORITY_LOW; + + r = 0; + } + + CloseHandle(handle); + return r; +} + + +int uv_os_setpriority(uv_pid_t pid, int priority) { + HANDLE handle; + int priority_class; + int r; + + /* Map Unix nice values to Windows priority classes. */ + if (priority < UV_PRIORITY_HIGHEST || priority > UV_PRIORITY_LOW) + return UV_EINVAL; + else if (priority < UV_PRIORITY_HIGH) + priority_class = REALTIME_PRIORITY_CLASS; + else if (priority < UV_PRIORITY_ABOVE_NORMAL) + priority_class = HIGH_PRIORITY_CLASS; + else if (priority < UV_PRIORITY_NORMAL) + priority_class = ABOVE_NORMAL_PRIORITY_CLASS; + else if (priority < UV_PRIORITY_BELOW_NORMAL) + priority_class = NORMAL_PRIORITY_CLASS; + else if (priority < UV_PRIORITY_LOW) + priority_class = BELOW_NORMAL_PRIORITY_CLASS; + else + priority_class = IDLE_PRIORITY_CLASS; + + r = uv__get_handle(pid, PROCESS_SET_INFORMATION, &handle); + + if (r != 0) + return r; + + if (SetPriorityClass(handle, priority_class) == 0) + r = uv_translate_sys_error(GetLastError()); + + CloseHandle(handle); + return r; +} diff --git a/test/test-list.h b/test/test-list.h index ae4ad76fdaf..b501722d4dc 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -214,6 +214,7 @@ TEST_DECLARE (pipe_close_stdout_read_stdin) TEST_DECLARE (pipe_set_non_blocking) TEST_DECLARE (pipe_set_chmod) TEST_DECLARE (process_ref) +TEST_DECLARE (process_priority) TEST_DECLARE (has_ref) TEST_DECLARE (active) TEST_DECLARE (embed) @@ -689,6 +690,7 @@ TASK_LIST_START TEST_ENTRY (pipe_ref4) TEST_HELPER (pipe_ref4, pipe_echo_server) TEST_ENTRY (process_ref) + TEST_ENTRY (process_priority) TEST_ENTRY (has_ref) TEST_ENTRY (loop_handles) diff --git a/test/test-process-priority.c b/test/test-process-priority.c new file mode 100644 index 00000000000..ebee6b90afd --- /dev/null +++ b/test/test-process-priority.c @@ -0,0 +1,81 @@ +/* Copyright libuv contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + + +TEST_IMPL(process_priority) { + int priority; + int r; + int i; + +#if defined(__MVS__) + if (uv_os_setpriority(0, 0) == UV_ENOSYS) + RETURN_SKIP("functionality not supported on zOS"); +#endif + + /* Verify that passing a NULL pointer returns UV_EINVAL. */ + r = uv_os_getpriority(0, NULL); + ASSERT(r == UV_EINVAL); + + /* Verify that all valid values work. */ + for (i = UV_PRIORITY_HIGHEST; i <= UV_PRIORITY_LOW; i++) { + r = uv_os_setpriority(0, i); + + /* If UV_EACCES is returned, the current user doesn't have permission to + set this specific priority. */ + if (r == UV_EACCES) + continue; + + ASSERT(r == 0); + ASSERT(uv_os_getpriority(0, &priority) == 0); + + /* Verify that the priority values match on Unix, and are range mapped + on Windows. */ +#ifndef _WIN32 + ASSERT(priority == i); +#else + if (i < UV_PRIORITY_HIGH) + ASSERT(priority == UV_PRIORITY_HIGHEST); + else if (i < UV_PRIORITY_ABOVE_NORMAL) + ASSERT(priority == UV_PRIORITY_HIGH); + else if (i < UV_PRIORITY_NORMAL) + ASSERT(priority == UV_PRIORITY_ABOVE_NORMAL); + else if (i < UV_PRIORITY_BELOW_NORMAL) + ASSERT(priority == UV_PRIORITY_NORMAL); + else if (i < UV_PRIORITY_LOW) + ASSERT(priority == UV_PRIORITY_BELOW_NORMAL); + else + ASSERT(priority == UV_PRIORITY_LOW); +#endif + + /* Verify that the current PID and 0 are equivalent. */ + ASSERT(uv_os_getpriority(uv_os_getpid(), &r) == 0); + ASSERT(priority == r); + } + + /* Verify that invalid priorities return UV_EINVAL. */ + ASSERT(uv_os_setpriority(0, UV_PRIORITY_HIGHEST - 1) == UV_EINVAL); + ASSERT(uv_os_setpriority(0, UV_PRIORITY_LOW + 1) == UV_EINVAL); + + return 0; +} diff --git a/test/test.gyp b/test/test.gyp index 917533618bf..855eda1c500 100644 --- a/test/test.gyp +++ b/test/test.gyp @@ -80,6 +80,7 @@ 'test-poll-close-doesnt-corrupt-stack.c', 'test-poll-closesocket.c', 'test-poll-oob.c', + 'test-process-priority.c', 'test-process-title.c', 'test-process-title-threadsafe.c', 'test-queue-foreach-delete.c',