dispenso
A library for task parallelism
 
Loading...
Searching...
No Matches
priority.cpp
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8#include <dispenso/priority.h>
9
10#if (defined(__unix__) || defined(unix)) && !defined(USG)
11#include <sys/param.h>
12#endif
13
14#if defined(__linux__)
15#include <pthread.h>
16#include <sys/resource.h>
17#include <unistd.h>
18#elif defined(__MACH__)
19#include <mach/mach_time.h>
20#include <mach/thread_act.h>
21#include <pthread.h>
22#elif defined(_WIN32)
23#include <Windows.h>
24#elif defined(BSD)
25#include <sys/rtprio.h>
26#include <sys/types.h>
27#endif
28
29namespace dispenso {
30
31namespace {
32DISPENSO_THREAD_LOCAL ThreadPriority g_threadPriority = ThreadPriority::kNormal;
33} // namespace
34
35ThreadPriority getCurrentThreadPriority() {
36 return g_threadPriority;
37}
38
39#ifdef __MACH__
40bool setCurrentThreadPriority(ThreadPriority prio) {
41 mach_port_t threadport = pthread_mach_thread_np(pthread_self());
42 if (prio == ThreadPriority::kRealtime) {
43 mach_timebase_info_data_t info;
44 mach_timebase_info(&info);
45 double msToAbsTime = ((double)info.denom / (double)info.numer) * 1000000.0;
46 thread_time_constraint_policy_data_t time_constraints;
47 time_constraints.period = 0;
48 time_constraints.computation = static_cast<uint32_t>(1.0 * msToAbsTime);
49 time_constraints.constraint = static_cast<uint32_t>(10.0 * msToAbsTime);
50 time_constraints.preemptible = 0;
51
52 if (thread_policy_set(
53 threadport,
54 THREAD_TIME_CONSTRAINT_POLICY,
55 (thread_policy_t)&time_constraints,
56 THREAD_TIME_CONSTRAINT_POLICY_COUNT) != KERN_SUCCESS) {
57 return false;
58 }
59 }
60
61 // https://fergofrog.com/code/cbowser/xnu/osfmk/kern/sched.h.html#_M/MAXPRI_USER
62 struct thread_precedence_policy ttcpolicy;
63
64 switch (prio) {
65 case ThreadPriority::kLow:
66 ttcpolicy.importance = 20;
67 break;
68 case ThreadPriority::kNormal:
69 ttcpolicy.importance = 37;
70 break;
71 case ThreadPriority::kHigh: // fallthrough
72 case ThreadPriority::kRealtime:
73 ttcpolicy.importance = 63;
74 break;
75 }
76
77 if (thread_policy_set(
78 threadport,
79 THREAD_PRECEDENCE_POLICY,
80 (thread_policy_t)&ttcpolicy,
81 THREAD_PRECEDENCE_POLICY_COUNT) != KERN_SUCCESS) {
82 return false;
83 }
84
85 g_threadPriority = prio;
86 return true;
87}
88#elif defined(_WIN32)
89// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadpriority
90bool setCurrentThreadPriority(ThreadPriority prio) {
91 if (prio == ThreadPriority::kRealtime) {
92 if (!SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS)) {
93 return false;
94 }
95 }
96
97 if (prio == ThreadPriority::kHigh) {
98 // Best effort
99 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
100 }
101
102 bool success = false;
103 switch (prio) {
104 case ThreadPriority::kLow:
105 success = SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
106 break;
107 case ThreadPriority::kNormal:
108 success = SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
109 break;
110 case ThreadPriority::kHigh: // fallthrough
111 case ThreadPriority::kRealtime:
112 success = SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
113 break;
114 }
115
116 if (!success) {
117 return false;
118 }
119
120 g_threadPriority = prio;
121 return true;
122}
123#elif defined(__linux__)
124bool setCurrentThreadPriority(ThreadPriority prio) {
125 if (prio == ThreadPriority::kRealtime) {
126 struct sched_param param;
127 param.sched_priority = 99;
128 if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &param)) {
129 return false;
130 }
131 }
132
133 switch (prio) {
134 case ThreadPriority::kLow:
135 errno = 0;
136 (void)!nice(10);
137 break;
138 case ThreadPriority::kNormal:
139 errno = 0;
140 (void)!nice(0);
141 break;
142 case ThreadPriority::kHigh: // fallthrough
143 case ThreadPriority::kRealtime: {
144 struct rlimit rlim;
145 getrlimit(RLIMIT_NICE, &rlim);
146 if (rlim.rlim_max <= 20) {
147 return false;
148 }
149 rlim.rlim_cur = rlim.rlim_max;
150 setrlimit(RLIMIT_NICE, &rlim);
151 errno = 0;
152 (void)!nice(static_cast<int>(20 - rlim.rlim_max));
153 }
154 }
155 if (errno != 0) {
156 return false;
157 }
158 g_threadPriority = prio;
159 return true;
160}
161#elif defined(__FreeBSD__)
162// TODO: Find someone who has a FreeBSD system to test this code.
163bool setCurrentThreadPriority(ThreadPriority prio) {
164 struct rtprio rtp;
165
166 if (prio == ThreadPriority::kRealtime) {
167 rtp.type = RTP_PRIO_REALTIME;
168 rtp.prio = 10;
169 if (rtprio_thread(RTP_SET, 0, &rtp)) {
170 return false;
171 }
172 } else {
173 rtp.type = RTP_PRIO_NORMAL;
174 switch (prio) {
175 case ThreadPriority::kLow:
176 rtp.prio = 31;
177 break;
178 case ThreadPriority::kNormal:
179 rtp.prio = 15;
180 break;
181 case ThreadPriority::kHigh: // fallthrough
182 case ThreadPriority::kRealtime:
183 rtp.prio = 0;
184 break;
185 }
186 if (rtprio_thread(RTP_SET, 0, &rtp)) {
187 return false;
188 }
189 }
190 g_threadPriority = prio;
191 return true;
192}
193#else
194bool setCurrentThreadPriority(ThreadPriority prio) {
195 return false;
196}
197
198#endif // platform
199
200} // namespace dispenso
ThreadPriority
Definition priority.h:29