-
Notifications
You must be signed in to change notification settings - Fork 468
Basic Linux and Windows Thread Prioritisation for worker threads #842
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
4c5fb69
ca24a17
aabf51d
9208a63
9a6b76d
246cbfc
800fcbe
a3106e0
d5afe08
4be2a89
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,37 @@ | |
#include "protocol.h" // _dispatch_send_wakeup_runloop_thread | ||
#endif | ||
|
||
#if defined(__linux__) | ||
#include <errno.h> | ||
#include <sys/resource.h> | ||
#endif | ||
|
||
#if defined(_WIN32) | ||
// Wrapper around SetThreadDescription for UTF-8 strings | ||
void _dispatch_win32_set_thread_description(HANDLE hThread, const char *description) { | ||
int wcsize = MultiByteToWideChar(CP_UTF8, 0, description, -1, NULL, 0); | ||
if (wcsize == 0) { | ||
return; | ||
} | ||
|
||
wchar_t* wcstr = (wchar_t*)malloc(wcsize * sizeof(wchar_t)); | ||
if (wcstr == NULL) { | ||
return; | ||
} | ||
|
||
int result = MultiByteToWideChar(CP_UTF8, 0, description, -1, wcstr, wcsize); | ||
if (result == 0) { | ||
free(wcstr); | ||
return; | ||
} | ||
|
||
if (likely(wcstr != NULL)) { | ||
SetThreadDescription(hThread, wcstr); | ||
free(wcstr); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that we should just do this unconditionally.
|
||
} | ||
} | ||
#endif | ||
hmelder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
static inline void _dispatch_root_queues_init(void); | ||
static void _dispatch_lane_barrier_complete(dispatch_lane_class_t dqu, | ||
dispatch_qos_t qos, dispatch_wakeup_flags_t flags); | ||
|
@@ -6216,10 +6247,61 @@ _dispatch_worker_thread(void *context) | |
_dispatch_sigmask(); | ||
#endif | ||
_dispatch_introspection_thread_add(); | ||
dispatch_priority_t pri = dq->dq_priority; | ||
pthread_priority_t pp = _dispatch_get_priority(); | ||
|
||
// The Linux kernel does not have a direct analogue to the QoS-based | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor nit: since the comment refers to the logic in the given #if block, the preprocessor directive should come above this line. |
||
// thread policy engine found in XNU. | ||
// | ||
// We cannot use 'pthread_setschedprio', because all threads with default | ||
// scheduling policy (SCHED_OTHER) have the same pthread 'priority'. | ||
// For both CFS, which was introduced in Linux 2.6.23, and its successor | ||
// EEVDF (since 6.6) 'sched_get_priority_max' and 'sched_get_priority_min' | ||
// will just return 0. | ||
// | ||
// However, as outlined in "man 2 setpriority", the nice value is a | ||
// per‐thread attribute: different threads in the same process can have | ||
// different nice values. We can thus setup the thread's initial priority | ||
// by converting the QoS class and relative priority to a 'nice' value. | ||
#if defined(__linux__) | ||
pp = _dispatch_priority_to_pp_strip_flags(pri); | ||
int nice = _dispatch_pp_to_nice(pp); | ||
|
||
#if HAVE_PTHREAD_SETNAME_NP | ||
// pthread thread names are restricted to just 16 characters | ||
// including NUL. It does not make sense to pass the queue's | ||
// label as a name. | ||
pthread_setname_np(pthread_self(), "DispatchWorker"); | ||
#endif | ||
|
||
errno = 0; | ||
int rc = setpriority(PRIO_PROCESS, 0, nice); | ||
if (rc != -1 || errno == 0) { | ||
_dispatch_thread_setspecific(dispatch_priority_key, (void *)(uintptr_t)pp); | ||
} else { | ||
_dispatch_log("Failed to set thread priority for worker thread: pqc=%p errno=%d\n", pqc, errno); | ||
} | ||
#elif defined(_WIN32) | ||
pp = _dispatch_priority_to_pp_strip_flags(pri); | ||
int win_priority = _dispatch_pp_to_win32_priority(pp); | ||
|
||
HANDLE current = GetCurrentThread(); | ||
|
||
// Set thread description to the label of the root queue | ||
if (dq->dq_label) { | ||
_dispatch_win32_set_thread_description(current, dq->dq_label); | ||
} | ||
|
||
int rc = SetThreadPriority(current, win_priority); | ||
if (rc) { | ||
_dispatch_thread_setspecific(dispatch_priority_key, (void *)(uintptr_t)pp); | ||
} else { | ||
DWORD dwError = GetLastError(); | ||
_dispatch_log("Failed to set thread priority for worker thread: pqc=%p win_priority=%d dwError=%lu\n", pqc, win_priority, dwError); | ||
} | ||
#endif | ||
|
||
const int64_t timeout = 5ull * NSEC_PER_SEC; | ||
pthread_priority_t pp = _dispatch_get_priority(); | ||
dispatch_priority_t pri = dq->dq_priority; | ||
|
||
// If the queue is neither | ||
// - the manager | ||
|
@@ -6258,6 +6340,14 @@ _dispatch_worker_thread(void *context) | |
(void)os_atomic_inc2o(dq, dgq_thread_pool_size, release); | ||
_dispatch_root_queue_poke(dq, 1, 0); | ||
_dispatch_release(dq); // retained in _dispatch_root_queue_poke_slow | ||
|
||
#if defined(_WIN32) | ||
// Make sure to properly end the background processing mode | ||
if (win_priority == THREAD_MODE_BACKGROUND_BEGIN) { | ||
SetThreadPriority(current, THREAD_MODE_BACKGROUND_END); | ||
} | ||
#endif | ||
|
||
return NULL; | ||
} | ||
#if defined(_WIN32) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some platforms have similar functionality spelled
pthread_set_name_np
. Is the actual logic potentially applicable to other platforms, or is it Linux-specific?