#pragma once #ifdef ARCH_WIN #include #endif #ifdef ARCH_LIN #include #include #include #include #include #include #endif class ThreadPriority { public: /** * Attempts to boost priority of current thread, but not all * the way to a "realtime" priority. In genera, should not require * admin rights. */ static bool boostNormal(); /** * Attempts to boost priority of current thread all the way to * a "realtime" priority. Will require admin rights */ static bool boostRealtime(); static void restore(); #ifdef ARCH_WIN static bool boostRealtimeWindows(); #endif private: static bool boostNormalPthread(); static bool boostRealtimePthread(); static void restorePthread(); #ifdef ARCH_LIN static bool boostNormalLinux(); #endif }; #ifdef ARCH_WIN inline bool ThreadPriority::boostRealtimeWindows() { HANDLE h = GetCurrentProcess(); auto x = GetPriorityClass(h); printf("cur priority class = %lx, realtime=%x", x, REALTIME_PRIORITY_CLASS); SetPriorityClass(h, HIGH_PRIORITY_CLASS); printf("set pri class to %x is now %lx\n", HIGH_PRIORITY_CLASS, GetPriorityClass(h)); bool b = SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); int y = GetThreadPriority(GetCurrentThread()); printf("set pri ret %d, is now %d want %d err=%ld\n", b, y, THREAD_PRIORITY_TIME_CRITICAL, GetLastError()); fflush(stdout); return y == THREAD_PRIORITY_TIME_CRITICAL; } #endif // Inside Visual Studio test we don't try to link in PThreads, // So they can't be used here. But they will work on all command line // test builds, including Windows. #if !defined(_MSC_VER) || defined(_VSCODE) inline bool ThreadPriority::boostNormal() { #ifdef ARCH_LIN return boostNormalLinux(); #else return boostNormalPthread(); #endif } inline bool ThreadPriority::boostRealtime() { #ifdef ARCH_WIN return boostRealtimeWindows(); #else return boostRealtimePthread(); #endif } inline void ThreadPriority::restore() { restorePthread(); } inline void ThreadPriority::restorePthread() { const pthread_t threadHandle = pthread_self(); struct sched_param params; params.sched_priority = 0; // Note that on mac, this is not the default. // fix this. const int newPolicy = SCHED_OTHER; int x = pthread_setschedparam(threadHandle, newPolicy, ¶ms); if (x != 0) { printf("failed to set reset sched %d\n", x); } } /** * Linux doesn't support pthread priorities, so * we use the common hack of setting niceness. */ #ifdef ARCH_LIN inline bool ThreadPriority::boostNormalLinux() { pid_t tid = syscall(SYS_gettid); const int priority = -20; int ret = setpriority(PRIO_PROCESS, tid, priority); if (ret != 0) { printf("set pri failed, errno = %d\n", errno); printf("EACCESS=%d\n", EACCES); } printf("priority now %d\n", getpriority(PRIO_PROCESS, tid)); return ret == 0; } #endif inline bool ThreadPriority::boostNormalPthread() { struct sched_param params; const pthread_t threadHandle = pthread_self(); int initPolicy = -10; pthread_getschedparam(threadHandle, &initPolicy, ¶ms); printf("in boost, policy was %d, pri was %d otherp=%d\n", initPolicy, params.sched_priority, SCHED_OTHER); const int initPriority = params.sched_priority; const int newPolicy = SCHED_OTHER; const int maxPriority = sched_get_priority_max(newPolicy); #if 1 { printf("for OTHER, pri range = %d,%d\n", sched_get_priority_min(newPolicy), sched_get_priority_max(newPolicy)); } #endif if (maxPriority <= initPriority) { // Linux seems to only offer one priority for SCHED_OTHER; printf("SCHED_OTHER only supports priority %d\n", maxPriority); return false; } params.sched_priority = maxPriority; int x = pthread_setschedparam(threadHandle, newPolicy, ¶ms); if (x != 0) { printf("failed to set pri %d for SCHED_OTHER. error= %d\n", maxPriority, x); } fflush(stdout); return x == 0; } inline bool ThreadPriority::boostRealtimePthread() { struct sched_param params; const pthread_t threadHandle = pthread_self(); const int newPolicy = SCHED_RR; const int maxPriority = sched_get_priority_max(newPolicy); const int minPriority = sched_get_priority_min(newPolicy); #if 0 if ((maxPriority <= 0) || (minPriority < 0)) { printf("algorithm doesn't work with rt %d, %d\n", minPriority, maxPriority); return false; } #endif // use the mean of min and max. These should all be higher than "non realtime" // on mac the mean was not as good as elevating as other, to let's try max/ //const int newPriority = (maxPriority + minPriority) / 2; const int newPriority = maxPriority; printf("realtime min = %d max = %d will use %d\n", minPriority, maxPriority, newPriority); params.sched_priority = newPriority; int x = pthread_setschedparam(threadHandle, newPolicy, ¶ms); if (x != 0) { printf("failed to set pri %d for SCHED_RR. error= %d\n", newPriority, x); } return x == 0; } #else inline bool ThreadPriority::boostNormal() { return true; } inline bool ThreadPriority::boostRealtime() { return true; } inline void ThreadPriority::restore() { } #endif