1 | |
2 | // |
3 | // This source file is part of appleseed. |
4 | // Visit http://appleseedhq.net/ for additional information and resources. |
5 | // |
6 | // This software is released under the MIT license. |
7 | // |
8 | // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited |
9 | // Copyright (c) 2014-2017 Francois Beaune, The appleseedhq Organization |
10 | // |
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy |
12 | // of this software and associated documentation files (the "Software"), to deal |
13 | // in the Software without restriction, including without limitation the rights |
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
15 | // copies of the Software, and to permit persons to whom the Software is |
16 | // furnished to do so, subject to the following conditions: |
17 | // |
18 | // The above copyright notice and this permission notice shall be included in |
19 | // all copies or substantial portions of the Software. |
20 | // |
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
27 | // THE SOFTWARE. |
28 | // |
29 | |
30 | // Interface header. |
31 | #include "thread.h" |
32 | |
33 | // appleseed.foundation headers. |
34 | #include "foundation/platform/compiler.h" |
35 | #include "foundation/platform/defaulttimers.h" |
36 | #ifdef _WIN32 |
37 | #include "foundation/platform/windows.h" |
38 | #endif |
39 | #include "foundation/utility/job/iabortswitch.h" |
40 | #include "foundation/utility/log.h" |
41 | |
42 | // Boost headers. |
43 | #include "boost/chrono.hpp" |
44 | |
45 | // Standard headers. |
46 | #include <cassert> |
47 | |
48 | // Platform headers. |
49 | #if defined __APPLE__ |
50 | #include <pthread.h> |
51 | #elif defined __FreeBSD__ |
52 | #include <pthread.h> |
53 | #include <pthread_np.h> |
54 | #elif defined __linux__ |
55 | #include <sys/prctl.h> |
56 | #endif |
57 | |
58 | using namespace boost; |
59 | |
60 | namespace foundation |
61 | { |
62 | |
63 | // |
64 | // Utility functions implementation. |
65 | // |
66 | |
67 | // Windows. |
68 | #if defined _WIN32 |
69 | |
70 | // |
71 | // Reference: |
72 | // |
73 | // https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx |
74 | // |
75 | |
76 | #pragma pack(push, 8) |
77 | struct ThreadNameInfo |
78 | { |
79 | DWORD dwType; // must be 0x1000 |
80 | LPCSTR szName; // pointer to name (in user address space) |
81 | DWORD dwThreadID; // thread ID (-1 = caller thread) |
82 | DWORD dwFlags; // reserved for future use, must be zero |
83 | }; |
84 | #pragma pack(pop) |
85 | |
86 | void set_current_thread_name(const char* name) |
87 | { |
88 | __try |
89 | { |
90 | ThreadNameInfo info; |
91 | info.dwType = 0x1000; |
92 | info.szName = name; |
93 | info.dwThreadID = -1; |
94 | info.dwFlags = 0; |
95 | |
96 | const DWORD VisualStudioException = 0x406D1388; |
97 | RaiseException(VisualStudioException, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); |
98 | } |
99 | __except (EXCEPTION_EXECUTE_HANDLER) |
100 | { |
101 | } |
102 | } |
103 | |
104 | // OS X. |
105 | #elif defined __APPLE__ |
106 | |
107 | void set_current_thread_name(const char* name) |
108 | { |
109 | pthread_setname_np(name); |
110 | } |
111 | |
112 | // FreeBSD. |
113 | #elif defined __FreeBSD__ |
114 | |
115 | void set_current_thread_name(const char* name) |
116 | { |
117 | pthread_set_name_np(pthread_self(), name); |
118 | } |
119 | |
120 | // Linux. |
121 | #elif defined __linux__ |
122 | |
123 | void set_current_thread_name(const char* name) |
124 | { |
125 | prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0); |
126 | } |
127 | |
128 | // Other platforms. |
129 | #else |
130 | |
131 | void set_current_thread_name(const char* name) |
132 | { |
133 | // Do nothing. |
134 | } |
135 | |
136 | #endif |
137 | |
138 | void sleep(const uint32 ms) |
139 | { |
140 | this_thread::sleep_for(chrono::milliseconds(ms)); |
141 | } |
142 | |
143 | void sleep(const uint32 ms, IAbortSwitch& abort_switch) |
144 | { |
145 | const chrono::milliseconds one_ms(1); |
146 | |
147 | DefaultWallclockTimer timer; |
148 | |
149 | const uint64 freq = timer.frequency(); |
150 | const uint64 start_time = timer.read_start(); |
151 | |
152 | while (!abort_switch.is_aborted()) |
153 | { |
154 | const uint64 elapsed_ticks = timer.read_end() - start_time; |
155 | const uint64 elapsed_ms = (1000 * elapsed_ticks) / freq; |
156 | |
157 | if (elapsed_ms >= ms) |
158 | break; |
159 | |
160 | this_thread::sleep_for(one_ms); |
161 | } |
162 | } |
163 | |
164 | void yield() |
165 | { |
166 | this_thread::yield(); |
167 | } |
168 | |
169 | |
170 | // |
171 | // ProcessPriorityContext class implementation (Windows). |
172 | // |
173 | // Reference: |
174 | // |
175 | // http://msdn.microsoft.com/en-us/library/windows/desktop/ms685100(v=vs.85).aspx |
176 | // |
177 | |
178 | #ifdef _WIN32 |
179 | |
180 | namespace |
181 | { |
182 | DWORD get_priority_class(const ProcessPriority priority) |
183 | { |
184 | switch (priority) |
185 | { |
186 | case ProcessPriorityLowest: return IDLE_PRIORITY_CLASS; |
187 | case ProcessPriorityLow: return BELOW_NORMAL_PRIORITY_CLASS; |
188 | case ProcessPriorityNormal: return NORMAL_PRIORITY_CLASS; |
189 | case ProcessPriorityHigh: return ABOVE_NORMAL_PRIORITY_CLASS; |
190 | case ProcessPriorityHighest: return HIGH_PRIORITY_CLASS; |
191 | default: |
192 | APPLESEED_UNREACHABLE; |
193 | return NORMAL_PRIORITY_CLASS; |
194 | } |
195 | } |
196 | |
197 | void set_current_process_priority_class(const DWORD priority_class, Logger* logger) |
198 | { |
199 | if (!SetPriorityClass(GetCurrentProcess(), priority_class)) |
200 | { |
201 | if (logger) |
202 | { |
203 | LOG_WARNING( |
204 | *logger, |
205 | "failed to set process priority class to %lu (%lu)." , |
206 | priority_class, |
207 | GetLastError()); |
208 | } |
209 | } |
210 | } |
211 | } |
212 | |
213 | struct ProcessPriorityContext::Impl |
214 | { |
215 | Logger* m_logger; |
216 | DWORD m_initial_priority_class; |
217 | }; |
218 | |
219 | ProcessPriorityContext::ProcessPriorityContext( |
220 | const ProcessPriority priority, |
221 | Logger* logger) |
222 | : impl(new Impl()) |
223 | { |
224 | impl->m_logger = logger; |
225 | impl->m_initial_priority_class = GetPriorityClass(GetCurrentProcess()); |
226 | |
227 | set_current_process_priority_class( |
228 | get_priority_class(priority), |
229 | impl->m_logger); |
230 | } |
231 | |
232 | ProcessPriorityContext::~ProcessPriorityContext() |
233 | { |
234 | set_current_process_priority_class( |
235 | impl->m_initial_priority_class, |
236 | impl->m_logger); |
237 | |
238 | delete impl; |
239 | } |
240 | |
241 | #else |
242 | |
243 | ProcessPriorityContext::ProcessPriorityContext( |
244 | const ProcessPriority priority, |
245 | Logger* logger) |
246 | { |
247 | } |
248 | |
249 | ProcessPriorityContext::~ProcessPriorityContext() |
250 | { |
251 | } |
252 | |
253 | #endif |
254 | |
255 | |
256 | // |
257 | // ThreadPriorityContext class implementation (Windows). |
258 | // |
259 | // Reference: |
260 | // |
261 | // http://msdn.microsoft.com/en-us/library/windows/desktop/ms685100(v=vs.85).aspx |
262 | // |
263 | |
264 | #ifdef _WIN32 |
265 | |
266 | namespace |
267 | { |
268 | int get_thread_priority_level(const ProcessPriority priority) |
269 | { |
270 | switch (priority) |
271 | { |
272 | case ProcessPriorityLowest: return THREAD_PRIORITY_LOWEST; |
273 | case ProcessPriorityLow: return THREAD_PRIORITY_BELOW_NORMAL; |
274 | case ProcessPriorityNormal: return THREAD_PRIORITY_NORMAL; |
275 | case ProcessPriorityHigh: return THREAD_PRIORITY_ABOVE_NORMAL; |
276 | case ProcessPriorityHighest: return THREAD_PRIORITY_HIGHEST; |
277 | default: |
278 | APPLESEED_UNREACHABLE; |
279 | return THREAD_PRIORITY_NORMAL; |
280 | } |
281 | } |
282 | |
283 | void set_current_thread_priority_level(const int thread_priority, Logger* logger) |
284 | { |
285 | if (!SetThreadPriority(GetCurrentThread(), thread_priority)) |
286 | { |
287 | if (logger) |
288 | { |
289 | LOG_WARNING( |
290 | *logger, |
291 | "failed to set thread priority level to %d (%lu)." , |
292 | thread_priority, |
293 | GetLastError()); |
294 | } |
295 | } |
296 | } |
297 | } |
298 | |
299 | struct ThreadPriorityContext::Impl |
300 | { |
301 | Logger* m_logger; |
302 | int m_initial_priority_level; |
303 | }; |
304 | |
305 | ThreadPriorityContext::ThreadPriorityContext( |
306 | const ProcessPriority priority, |
307 | Logger* logger) |
308 | : impl(new Impl()) |
309 | { |
310 | impl->m_logger = logger; |
311 | impl->m_initial_priority_level = GetThreadPriority(GetCurrentThread()); |
312 | |
313 | set_current_thread_priority_level( |
314 | get_thread_priority_level(priority), |
315 | impl->m_logger); |
316 | } |
317 | |
318 | ThreadPriorityContext::~ThreadPriorityContext() |
319 | { |
320 | set_current_thread_priority_level( |
321 | impl->m_initial_priority_level, |
322 | impl->m_logger); |
323 | |
324 | delete impl; |
325 | } |
326 | |
327 | #else |
328 | |
329 | ThreadPriorityContext::ThreadPriorityContext( |
330 | const ProcessPriority priority, |
331 | Logger* logger) |
332 | { |
333 | } |
334 | |
335 | ThreadPriorityContext::~ThreadPriorityContext() |
336 | { |
337 | } |
338 | |
339 | #endif |
340 | |
341 | |
342 | // |
343 | // BenchmarkingThreadContext class implementation (Windows). |
344 | // |
345 | |
346 | #ifdef _WIN32 |
347 | |
348 | struct BenchmarkingThreadContext::Impl |
349 | { |
350 | ProcessPriorityContext m_process_priority_context; |
351 | ThreadPriorityContext m_thread_priority_context; |
352 | uint64 m_thread_affinity_mask; |
353 | |
354 | explicit Impl(Logger* logger) |
355 | : m_process_priority_context(ProcessPriorityHighest, logger) |
356 | , m_thread_priority_context(ProcessPriorityHighest, logger) |
357 | , m_thread_affinity_mask(SetThreadAffinityMask(GetCurrentThread(), 1)) |
358 | { |
359 | } |
360 | |
361 | ~Impl() |
362 | { |
363 | SetThreadAffinityMask(GetCurrentThread(), m_thread_affinity_mask); |
364 | } |
365 | }; |
366 | |
367 | BenchmarkingThreadContext::BenchmarkingThreadContext(Logger* logger) |
368 | : impl(new Impl(logger)) |
369 | { |
370 | } |
371 | |
372 | BenchmarkingThreadContext::~BenchmarkingThreadContext() |
373 | { |
374 | delete impl; |
375 | } |
376 | |
377 | #else |
378 | |
379 | BenchmarkingThreadContext::BenchmarkingThreadContext(Logger* logger) |
380 | { |
381 | } |
382 | |
383 | BenchmarkingThreadContext::~BenchmarkingThreadContext() |
384 | { |
385 | } |
386 | |
387 | #endif |
388 | |
389 | } // namespace foundation |
390 | |