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
58using namespace boost;
59
60namespace 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
138void sleep(const uint32 ms)
139{
140 this_thread::sleep_for(chrono::milliseconds(ms));
141}
142
143void 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
164void 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