I’m trying to implement a cross-platform wrapper to adjust the current thread priority; on POSIX the “correct” thing seems something like
/// set the priority adjustment for the current thread
/// @param pri priority between -2 and 2; 0 is the "normal" priority
/// @return true if successful
bool set_thread_priority(int pri) {
struct sched_param sp;
int policy;
pthread_t self = pthread_self();
if (!pthread_getschedparam(self, &policy, &sp)) return false;
int pmin = sched_get_priority_min(policy);
int pmax = sched_get_priority_max(policy);
sp.sched_priority = pmin + (pmax-pmin) * (pri + 2) / 4;
if (!pthread_setschedparam(self, policy, &sp)) return false;
return true;
}
This works as I’d expect on macOS (where threads seem to have a priority range [15, 47], with default 31), but does not work at all on Linux, as with the default policy (0 AKA SCHED_OTHER
) the range of allowed priorities is [0, 0], i.e. no priority adjustment is possible.
You can switch the scheduling policy to something like SCHED_FIFO
or SCHED_RR
and use their priority values, but that’s not at all what I want (from man 7 sched
they always have priority over SCHED_OTHER
tasks, and in general they are suited for very specific scenarios).
I did find out instead that what works is to use the nice value (through nice(2)
/setpriority(2)
), which, contrary to what POSIX says, is thread-specific instead of process-specific (seems like a pretty direct consequence of the very blurred line between threads and processes on Linux, everything ultimately being just a “task” with various possible shared stuff with other tasks). So I ended up with:
bool czu::set_thread_priority(int pri) {
if (pri < -2) pri = -2;
if (pri > 2) pri = 2;
#ifdef _WIN32
// %pri was conveniently chosen so that it matches SetThreadPriority
return SetThreadPriority(GetCurrentThread(), pri);
#elif __linux
// On Linux the pthread_setschedparam stuff works only with particular
// scheduling policies, specifically it does not work with the default one;
// instead, nice(2)/setpriority(2) works, and affects only the current
// thread instead of doing what POSIX would say (whole process).
// Here the allowed range is [20, -19] (20 = minimum priority); we can
// ignore clamping -20 as setpriority is already guaranteed to do it.
if (setpriority(PRIO_PROCESS, 0, pri*-10)) return false;
return true;
#else
struct sched_param sp;
int policy;
pthread_t self = pthread_self();
if (!pthread_getschedparam(self, &policy, &sp)) return false;
int pmin = sched_get_priority_min(policy);
int pmax = sched_get_priority_max(policy);
sp.sched_priority = pmin + (pmax-pmin) * (pri + 2) / 4;
if (!pthread_setschedparam(self, policy, &sp)) return false;
return true;
#endif
}
which seems good enough, but then I noticed in man 2 setpriority:
According to POSIX, the nice value is a per-process setting. However, under the current Linux/NPTL implementation of POSIX threads, the nice value is a per-thread attribute: different threads in the same process can have different nice values. Portable applications should avoid relying on the Linux behavior, which may be made standards conformant in the future.
So, if I want to be future-proof but still be able to adjust priority of threads with SCHED_OTHER
default scheduling policy what am I supposed to do?
A possibility that comes to mind is to work in fallback: run the sched_get_priority_min
/sched_get_priority_max
dance, and if it turns out that it’s [0,0] AND we are on Linux, assume we are in a situation where the nice value is per-thread. Is there some better possibility/API that I missed?