mirror of
https://github.com/libuv/libuv
synced 2025-03-28 21:13:16 +00:00
unix,win: utility for setting priority for thread (#4075)
Add uv_thread_setpriority for setting priority for threads created by uv_thread_create. Add uv_thread_getpriority for getting thread priority. For Linux by default, if the scheduling policy is SCHED_OTHER and the priority is 0, we need to set the nice value. Fixes: https://github.com/libuv/libuv/issues/4051
This commit is contained in:
parent
31e4b90c3c
commit
e135dfe183
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,6 +7,7 @@
|
|||||||
*.sdf
|
*.sdf
|
||||||
*.suo
|
*.suo
|
||||||
.vs/
|
.vs/
|
||||||
|
.vscode/
|
||||||
*.VC.db
|
*.VC.db
|
||||||
*.VC.opendb
|
*.VC.opendb
|
||||||
core
|
core
|
||||||
|
@ -655,6 +655,7 @@ if(LIBUV_BUILD_TESTS)
|
|||||||
test/test-thread-affinity.c
|
test/test-thread-affinity.c
|
||||||
test/test-thread-equal.c
|
test/test-thread-equal.c
|
||||||
test/test-thread.c
|
test/test-thread.c
|
||||||
|
test/test-thread-priority.c
|
||||||
test/test-threadpool-cancel.c
|
test/test-threadpool-cancel.c
|
||||||
test/test-threadpool.c
|
test/test-threadpool.c
|
||||||
test/test-timer-again.c
|
test/test-timer-again.c
|
||||||
|
@ -286,6 +286,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
|||||||
test/test-thread-equal.c \
|
test/test-thread-equal.c \
|
||||||
test/test-thread.c \
|
test/test-thread.c \
|
||||||
test/test-thread-affinity.c \
|
test/test-thread-affinity.c \
|
||||||
|
test/test-thread-priority.c \
|
||||||
test/test-threadpool-cancel.c \
|
test/test-threadpool-cancel.c \
|
||||||
test/test-threadpool.c \
|
test/test-threadpool.c \
|
||||||
test/test-timer-again.c \
|
test/test-timer-again.c \
|
||||||
|
@ -132,6 +132,21 @@ Threads
|
|||||||
.. c:function:: int uv_thread_join(uv_thread_t *tid)
|
.. c:function:: int uv_thread_join(uv_thread_t *tid)
|
||||||
.. c:function:: int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2)
|
.. c:function:: int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2)
|
||||||
|
|
||||||
|
.. c:function:: int uv_thread_setpriority(uv_thread_t tid, int priority)
|
||||||
|
If the function succeeds, the return value is 0.
|
||||||
|
If the function fails, the return value is less than zero.
|
||||||
|
Sets the scheduling priority of the thread specified by tid. It requires elevated
|
||||||
|
privilege to set specific priorities on some platforms.
|
||||||
|
The priority can be set to the following constants. UV_THREAD_PRIORITY_HIGHEST,
|
||||||
|
UV_THREAD_PRIORITY_ABOVE_NORMAL, UV_THREAD_PRIORITY_NORMAL,
|
||||||
|
UV_THREAD_PRIORITY_BELOW_NORMAL, UV_THREAD_PRIORITY_LOWEST.
|
||||||
|
.. c:function:: int uv_thread_getpriority(uv_thread_t tid, int* priority)
|
||||||
|
If the function succeeds, the return value is 0.
|
||||||
|
If the function fails, the return value is less than zero.
|
||||||
|
Retrieves the scheduling priority of the thread specified by tid. The value in the
|
||||||
|
output parameter priority is platform dependent.
|
||||||
|
For Linux, when schedule policy is SCHED_OTHER (default), priority is 0.
|
||||||
|
|
||||||
Thread-local storage
|
Thread-local storage
|
||||||
^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
11
include/uv.h
11
include/uv.h
@ -1284,6 +1284,17 @@ UV_EXTERN uv_pid_t uv_os_getppid(void);
|
|||||||
UV_EXTERN int uv_os_getpriority(uv_pid_t pid, int* priority);
|
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_os_setpriority(uv_pid_t pid, int priority);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
UV_THREAD_PRIORITY_HIGHEST = 2,
|
||||||
|
UV_THREAD_PRIORITY_ABOVE_NORMAL = 1,
|
||||||
|
UV_THREAD_PRIORITY_NORMAL = 0,
|
||||||
|
UV_THREAD_PRIORITY_BELOW_NORMAL = -1,
|
||||||
|
UV_THREAD_PRIORITY_LOWEST = -2,
|
||||||
|
};
|
||||||
|
|
||||||
|
UV_EXTERN int uv_thread_getpriority(uv_thread_t tid, int* priority);
|
||||||
|
UV_EXTERN int uv_thread_setpriority(uv_thread_t tid, int priority);
|
||||||
|
|
||||||
UV_EXTERN unsigned int uv_available_parallelism(void);
|
UV_EXTERN unsigned int uv_available_parallelism(void);
|
||||||
UV_EXTERN int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count);
|
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);
|
UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count);
|
||||||
|
125
src/unix/core.c
125
src/unix/core.c
@ -90,6 +90,7 @@ extern char** environ;
|
|||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
# include <sched.h>
|
# include <sched.h>
|
||||||
# include <sys/syscall.h>
|
# include <sys/syscall.h>
|
||||||
|
# define gettid() syscall(SYS_gettid)
|
||||||
# define uv__accept4 accept4
|
# define uv__accept4 accept4
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1557,6 +1558,130 @@ int uv_os_setpriority(uv_pid_t pid, int priority) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the function succeeds, the return value is 0.
|
||||||
|
* If the function fails, the return value is non-zero.
|
||||||
|
* for Linux, when schedule policy is SCHED_OTHER (default), priority is 0.
|
||||||
|
* So the output parameter priority is actually the nice value.
|
||||||
|
*/
|
||||||
|
int uv_thread_getpriority(uv_thread_t tid, int* priority) {
|
||||||
|
int r;
|
||||||
|
int policy;
|
||||||
|
struct sched_param param;
|
||||||
|
#ifdef __linux__
|
||||||
|
pid_t pid = gettid();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (priority == NULL)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
|
r = pthread_getschedparam(tid, &policy, ¶m);
|
||||||
|
if (r != 0)
|
||||||
|
return UV__ERR(errno);
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
if (SCHED_OTHER == policy && pthread_equal(tid, pthread_self())) {
|
||||||
|
errno = 0;
|
||||||
|
r = getpriority(PRIO_PROCESS, pid);
|
||||||
|
if (r == -1 && errno != 0)
|
||||||
|
return UV__ERR(errno);
|
||||||
|
*priority = r;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
*priority = param.sched_priority;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
static int set_nice_for_calling_thread(int priority) {
|
||||||
|
int r;
|
||||||
|
int nice;
|
||||||
|
|
||||||
|
if (priority < UV_THREAD_PRIORITY_LOWEST || priority > UV_THREAD_PRIORITY_HIGHEST)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
|
pid_t pid = gettid();
|
||||||
|
nice = 0 - priority * 2;
|
||||||
|
r = setpriority(PRIO_PROCESS, pid, nice);
|
||||||
|
if (r != 0)
|
||||||
|
return UV__ERR(errno);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the function succeeds, the return value is 0.
|
||||||
|
* If the function fails, the return value is non-zero.
|
||||||
|
*/
|
||||||
|
int uv_thread_setpriority(uv_thread_t tid, int priority) {
|
||||||
|
int r;
|
||||||
|
int min;
|
||||||
|
int max;
|
||||||
|
int range;
|
||||||
|
int prio;
|
||||||
|
int policy;
|
||||||
|
struct sched_param param;
|
||||||
|
|
||||||
|
if (priority < UV_THREAD_PRIORITY_LOWEST || priority > UV_THREAD_PRIORITY_HIGHEST)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
|
r = pthread_getschedparam(tid, &policy, ¶m);
|
||||||
|
if (r != 0)
|
||||||
|
return UV__ERR(errno);
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
/**
|
||||||
|
* for Linux, when schedule policy is SCHED_OTHER (default), priority must be 0,
|
||||||
|
* we should set the nice value in this case.
|
||||||
|
*/
|
||||||
|
if (SCHED_OTHER == policy && pthread_equal(tid, pthread_self()))
|
||||||
|
return set_nice_for_calling_thread(priority);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __PASE__
|
||||||
|
min = 1;
|
||||||
|
max = 127;
|
||||||
|
#else
|
||||||
|
min = sched_get_priority_min(policy);
|
||||||
|
max = sched_get_priority_max(policy);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (min == -1 || max == -1)
|
||||||
|
return UV__ERR(errno);
|
||||||
|
|
||||||
|
range = max - min;
|
||||||
|
|
||||||
|
switch (priority) {
|
||||||
|
case UV_THREAD_PRIORITY_HIGHEST:
|
||||||
|
prio = max;
|
||||||
|
break;
|
||||||
|
case UV_THREAD_PRIORITY_ABOVE_NORMAL:
|
||||||
|
prio = min + range * 3 / 4;
|
||||||
|
break;
|
||||||
|
case UV_THREAD_PRIORITY_NORMAL:
|
||||||
|
prio = min + range / 2;
|
||||||
|
break;
|
||||||
|
case UV_THREAD_PRIORITY_BELOW_NORMAL:
|
||||||
|
prio = min + range / 4;
|
||||||
|
break;
|
||||||
|
case UV_THREAD_PRIORITY_LOWEST:
|
||||||
|
prio = min;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param.sched_priority != prio) {
|
||||||
|
param.sched_priority = prio;
|
||||||
|
r = pthread_setschedparam(tid, policy, ¶m);
|
||||||
|
if (r != 0)
|
||||||
|
return UV__ERR(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int uv_os_uname(uv_utsname_t* buffer) {
|
int uv_os_uname(uv_utsname_t* buffer) {
|
||||||
struct utsname buf;
|
struct utsname buf;
|
||||||
|
@ -1466,6 +1466,48 @@ int uv_os_setpriority(uv_pid_t pid, int priority) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int uv_thread_getpriority(uv_thread_t tid, int* priority) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (priority == NULL)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
|
r = GetThreadPriority(tid);
|
||||||
|
if (r == THREAD_PRIORITY_ERROR_RETURN)
|
||||||
|
return uv_translate_sys_error(GetLastError());
|
||||||
|
|
||||||
|
*priority = r;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uv_thread_setpriority(uv_thread_t tid, int priority) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
switch (priority) {
|
||||||
|
case UV_THREAD_PRIORITY_HIGHEST:
|
||||||
|
r = SetThreadPriority(tid, THREAD_PRIORITY_HIGHEST);
|
||||||
|
break;
|
||||||
|
case UV_THREAD_PRIORITY_ABOVE_NORMAL:
|
||||||
|
r = SetThreadPriority(tid, THREAD_PRIORITY_ABOVE_NORMAL);
|
||||||
|
break;
|
||||||
|
case UV_THREAD_PRIORITY_NORMAL:
|
||||||
|
r = SetThreadPriority(tid, THREAD_PRIORITY_NORMAL);
|
||||||
|
break;
|
||||||
|
case UV_THREAD_PRIORITY_BELOW_NORMAL:
|
||||||
|
r = SetThreadPriority(tid, THREAD_PRIORITY_BELOW_NORMAL);
|
||||||
|
break;
|
||||||
|
case UV_THREAD_PRIORITY_LOWEST:
|
||||||
|
r = SetThreadPriority(tid, THREAD_PRIORITY_LOWEST);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r == 0)
|
||||||
|
return uv_translate_sys_error(GetLastError());
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int uv_os_uname(uv_utsname_t* buffer) {
|
int uv_os_uname(uv_utsname_t* buffer) {
|
||||||
/* Implementation loosely based on
|
/* Implementation loosely based on
|
||||||
|
@ -468,6 +468,7 @@ TEST_DECLARE (thread_rwlock_trylock)
|
|||||||
TEST_DECLARE (thread_create)
|
TEST_DECLARE (thread_create)
|
||||||
TEST_DECLARE (thread_equal)
|
TEST_DECLARE (thread_equal)
|
||||||
TEST_DECLARE (thread_affinity)
|
TEST_DECLARE (thread_affinity)
|
||||||
|
TEST_DECLARE (thread_priority)
|
||||||
TEST_DECLARE (dlerror)
|
TEST_DECLARE (dlerror)
|
||||||
#if (defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))) && \
|
#if (defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))) && \
|
||||||
!defined(__sun)
|
!defined(__sun)
|
||||||
@ -1165,6 +1166,7 @@ TASK_LIST_START
|
|||||||
TEST_ENTRY (thread_create)
|
TEST_ENTRY (thread_create)
|
||||||
TEST_ENTRY (thread_equal)
|
TEST_ENTRY (thread_equal)
|
||||||
TEST_ENTRY (thread_affinity)
|
TEST_ENTRY (thread_affinity)
|
||||||
|
TEST_ENTRY (thread_priority)
|
||||||
TEST_ENTRY (dlerror)
|
TEST_ENTRY (dlerror)
|
||||||
TEST_ENTRY (ip4_addr)
|
TEST_ENTRY (ip4_addr)
|
||||||
TEST_ENTRY (ip6_addr_link_local)
|
TEST_ENTRY (ip6_addr_link_local)
|
||||||
|
105
test/test-thread-priority.c
Normal file
105
test/test-thread-priority.c
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/* 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"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h> /* memset */
|
||||||
|
|
||||||
|
#ifdef __POSIX__
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uv_sem_t sem;
|
||||||
|
|
||||||
|
static void simple_task(void *args) {
|
||||||
|
uv_sem_wait(&sem);
|
||||||
|
printf("in simple_task\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_IMPL(thread_priority) {
|
||||||
|
int priority;
|
||||||
|
#ifndef _WIN32
|
||||||
|
int min;
|
||||||
|
int max;
|
||||||
|
int policy;
|
||||||
|
struct sched_param param;
|
||||||
|
#endif
|
||||||
|
uv_thread_t task_id;
|
||||||
|
|
||||||
|
/* Verify that passing a NULL pointer returns UV_EINVAL. */
|
||||||
|
ASSERT_EQ(UV_EINVAL, uv_thread_getpriority(0, NULL));
|
||||||
|
ASSERT_OK(uv_sem_init(&sem, 1));
|
||||||
|
uv_sem_wait(&sem);
|
||||||
|
ASSERT_OK(uv_thread_create(&task_id, simple_task, NULL));
|
||||||
|
ASSERT_OK(uv_thread_getpriority(task_id, &priority));
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
ASSERT_EQ(priority, THREAD_PRIORITY_NORMAL);
|
||||||
|
#else
|
||||||
|
ASSERT_OK(pthread_getschedparam(task_id, &policy, ¶m));
|
||||||
|
#ifdef __PASE__
|
||||||
|
min = 1;
|
||||||
|
max = 127;
|
||||||
|
#else
|
||||||
|
min = sched_get_priority_min(policy);
|
||||||
|
max = sched_get_priority_max(policy);
|
||||||
|
#endif
|
||||||
|
ASSERT(priority >= min && priority <= max);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ASSERT_OK(uv_thread_setpriority(task_id, UV_THREAD_PRIORITY_LOWEST));
|
||||||
|
ASSERT_OK(uv_thread_getpriority(task_id, &priority));
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
ASSERT_EQ(priority, THREAD_PRIORITY_LOWEST);
|
||||||
|
#else
|
||||||
|
ASSERT_EQ(priority, min);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test set nice value for the calling thread with default schedule policy
|
||||||
|
*/
|
||||||
|
#ifdef __linux__
|
||||||
|
ASSERT_OK(uv_thread_getpriority(pthread_self(), &priority));
|
||||||
|
ASSERT_EQ(priority, 0);
|
||||||
|
ASSERT_OK(uv_thread_setpriority(pthread_self(), UV_THREAD_PRIORITY_LOWEST));
|
||||||
|
ASSERT_OK(uv_thread_getpriority(pthread_self(), &priority));
|
||||||
|
ASSERT_EQ(priority, (0 - UV_THREAD_PRIORITY_LOWEST * 2));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uv_sem_post(&sem);
|
||||||
|
|
||||||
|
ASSERT_OK(uv_thread_join(&task_id));
|
||||||
|
|
||||||
|
uv_sem_destroy(&sem);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user