1
2 //
3 // This source file is part of appleseed.
4 // Visit https://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-2018 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 #pragma once
31
32 // appleseed.foundation headers.
33 #include "foundation/core/concepts/noncopyable.h"
34 #include "foundation/platform/atomic.h"
35 #include "foundation/platform/types.h"
36
37 // appleseed.main headers.
38 #include "main/dllsymbol.h"
39
40 // Boost headers.
41 #include "boost/smart_ptr/detail/spinlock.hpp"
42 #include "boost/thread/locks.hpp"
43 #include "boost/thread/mutex.hpp"
44 #include "boost/thread/thread.hpp"
45
46 // Forward declarations.
47 namespace foundation { class IAbortSwitch; }
48 namespace foundation { class Logger; }
49
50 namespace foundation
51 {
52
53 //
54 // Utility functions.
55 //
56
57 // Set the name of the current thread.
58 // For portability, limit the name to 16 characters, including the terminating zero.
59 APPLESEED_DLLSYMBOL void set_current_thread_name(const char* name);
60
61 // Suspend the current thread for a given number of milliseconds.
62 APPLESEED_DLLSYMBOL void sleep(const uint32 ms);
63 APPLESEED_DLLSYMBOL void sleep(const uint32 ms, IAbortSwitch& abort_switch);
64
65 // Give up the remainder of the current thread's time slice, to allow other threads to run.
66 APPLESEED_DLLSYMBOL void yield();
67
68
69 //
70 // A simple spinlock.
71 //
72
73 class Spinlock
74 : public NonCopyable
75 {
76 public:
77 Spinlock();
78
79 bool try_lock();
80 void lock();
81 void unlock();
82
83 class ScopedLock
84 : public NonCopyable
85 {
86 public:
87 explicit ScopedLock(Spinlock& spinlock);
88
89 private:
90 boost::detail::spinlock::scoped_lock m_lock;
91 };
92
93 private:
94 boost::detail::spinlock m_sp;
95 };
96
97
98 //
99 // A read/write lock.
100 //
101
102 struct NoWaitPolicy
103 {
pauseNoWaitPolicy104 static void pause(const uint32 iteration) {}
105 };
106
107 struct YieldWaitPolicy
108 {
pauseYieldWaitPolicy109 static void pause(const uint32 iteration) { yield(); }
110 };
111
112 template <uint32 ms>
113 struct SleepWaitPolicy
114 {
pauseSleepWaitPolicy115 static void pause(const uint32 iteration) { sleep(ms); }
116 };
117
118 template <typename WaitPolicy = NoWaitPolicy>
119 class ReadWriteLock
120 : public NonCopyable
121 {
122 public:
123 ReadWriteLock();
124
125 bool try_lock_read();
126 void lock_read();
127 void unlock_read();
128
129 bool try_lock_write();
130 void lock_write();
131 void unlock_write();
132
133 class ScopedReadLock
134 : public NonCopyable
135 {
136 public:
137 explicit ScopedReadLock(ReadWriteLock& lock);
138 ~ScopedReadLock();
139
140 private:
141 ReadWriteLock& m_lock;
142 };
143
144 class ScopedWriteLock
145 : public NonCopyable
146 {
147 public:
148 explicit ScopedWriteLock(ReadWriteLock& lock);
149 ~ScopedWriteLock();
150
151 private:
152 ReadWriteLock& m_lock;
153 };
154
155 private:
156 boost::atomic<uint32> m_readers;
157 boost::mutex m_mutex;
158 };
159
160
161 //
162 // Process/thread priority levels.
163 //
164
165 enum ProcessPriority
166 {
167 ProcessPriorityLowest,
168 ProcessPriorityLow,
169 ProcessPriorityNormal,
170 ProcessPriorityHigh,
171 ProcessPriorityHighest
172 };
173
174
175 //
176 // An object to set the priority for the current process.
177 //
178
179 class APPLESEED_DLLSYMBOL ProcessPriorityContext
180 : public NonCopyable
181 {
182 public:
183 // The constructor sets the priority of the current process.
184 ProcessPriorityContext(
185 const ProcessPriority priority,
186 Logger* logger = nullptr);
187
188 // The destructor restores previous settings.
189 ~ProcessPriorityContext();
190
191 private:
192 struct Impl;
193 Impl* impl;
194 };
195
196
197 //
198 // An object to set the priority for the current thread.
199 //
200
201 class APPLESEED_DLLSYMBOL ThreadPriorityContext
202 : public NonCopyable
203 {
204 public:
205 // The constructor sets the priority of the current thread.
206 ThreadPriorityContext(
207 const ProcessPriority priority,
208 Logger* logger = nullptr);
209
210 // The destructor restores previous settings.
211 ~ThreadPriorityContext();
212
213 private:
214 struct Impl;
215 Impl* impl;
216 };
217
218
219 //
220 // An object to configure the current process and thread for accurate microbenchmarking.
221 //
222
223 class APPLESEED_DLLSYMBOL BenchmarkingThreadContext
224 : public NonCopyable
225 {
226 public:
227 // The constructor enables the benchmarking mode.
228 explicit BenchmarkingThreadContext(Logger* logger = nullptr);
229
230 // The destructor restores previous settings.
231 ~BenchmarkingThreadContext();
232
233 private:
234 struct Impl;
235 Impl* impl;
236 };
237
238
239 //
240 // Wraps a thread function pointer into a callable thread function object.
241 //
242
243 template <typename Function>
244 class ThreadFunctionWrapper
245 {
246 public:
ThreadFunctionWrapper(Function * function)247 explicit ThreadFunctionWrapper(Function* function)
248 : m_function(function)
249 {
250 }
251
ThreadFunctionWrapper(const ThreadFunctionWrapper & rhs)252 ThreadFunctionWrapper(const ThreadFunctionWrapper& rhs)
253 : m_function(rhs.m_function)
254 {
255 }
256
operator()257 void operator()()
258 {
259 (*m_function)();
260 }
261
262 private:
263 Function* m_function;
264 };
265
266
267 //
268 // A cross-thread, cross-DLL boolean flag.
269 //
270
271 class APPLESEED_DLLSYMBOL ThreadFlag
272 {
273 public:
274 // Constructor, clears the flag.
275 ThreadFlag();
276
277 // Clear the flag.
278 void clear();
279
280 // Set the flag.
281 void set();
282
283 // Check the flag.
284 bool is_clear() const;
285 bool is_set() const;
286
287 private:
288 mutable volatile uint32 m_flag;
289 };
290
291
292 //
293 // Spinlock class implementation.
294 //
295
Spinlock()296 inline Spinlock::Spinlock()
297 {
298 boost::detail::spinlock initialized_sp = BOOST_DETAIL_SPINLOCK_INIT;
299 std::memcpy(&m_sp, &initialized_sp, sizeof(initialized_sp));
300 }
301
try_lock()302 inline bool Spinlock::try_lock()
303 {
304 return m_sp.try_lock();
305 }
306
lock()307 inline void Spinlock::lock()
308 {
309 m_sp.lock();
310 }
311
unlock()312 inline void Spinlock::unlock()
313 {
314 m_sp.unlock();
315 }
316
ScopedLock(Spinlock & spinlock)317 inline Spinlock::ScopedLock::ScopedLock(Spinlock& spinlock)
318 : m_lock(spinlock.m_sp)
319 {
320 }
321
322
323 //
324 // ReadWriteLock class implementation.
325 //
326
327 template <typename WaitPolicy>
ReadWriteLock()328 inline ReadWriteLock<WaitPolicy>::ReadWriteLock()
329 : m_readers(0)
330 {
331 }
332
333 template <typename WaitPolicy>
try_lock_read()334 inline bool ReadWriteLock<WaitPolicy>::try_lock_read()
335 {
336 // Make sure there are no writers active.
337 if (!m_mutex.try_lock())
338 return false;
339
340 // A new reader is active.
341 ++m_readers;
342
343 // Let other readers start.
344 m_mutex.unlock();
345
346 return true;
347 }
348
349 template <typename WaitPolicy>
lock_read()350 inline void ReadWriteLock<WaitPolicy>::lock_read()
351 {
352 // Make sure there are no writers active.
353 m_mutex.lock();
354
355 // A new reader is active.
356 ++m_readers;
357
358 // Let other readers start.
359 m_mutex.unlock();
360 }
361
362 template <typename WaitPolicy>
unlock_read()363 inline void ReadWriteLock<WaitPolicy>::unlock_read()
364 {
365 --m_readers;
366 }
367
368 template <typename WaitPolicy>
try_lock_write()369 inline bool ReadWriteLock<WaitPolicy>::try_lock_write()
370 {
371 // Make sure no reader can start.
372 if (!m_mutex.try_lock())
373 return false;
374
375 // Wait until active readers have terminated.
376 for (uint32 i = 0; m_readers > 0; ++i)
377 WaitPolicy::pause(i);
378
379 return true;
380 }
381
382 template <typename WaitPolicy>
lock_write()383 inline void ReadWriteLock<WaitPolicy>::lock_write()
384 {
385 // Make sure no reader can start.
386 m_mutex.lock();
387
388 // Wait until active readers have terminated.
389 for (uint32 i = 0; m_readers > 0; ++i)
390 WaitPolicy::pause(i);
391 }
392
393 template <typename WaitPolicy>
unlock_write()394 inline void ReadWriteLock<WaitPolicy>::unlock_write()
395 {
396 m_mutex.unlock();
397 }
398
399 template <typename WaitPolicy>
ScopedReadLock(ReadWriteLock & lock)400 inline ReadWriteLock<WaitPolicy>::ScopedReadLock::ScopedReadLock(ReadWriteLock& lock)
401 : m_lock(lock)
402 {
403 m_lock.lock_read();
404 }
405
406 template <typename WaitPolicy>
~ScopedReadLock()407 inline ReadWriteLock<WaitPolicy>::ScopedReadLock::~ScopedReadLock()
408 {
409 m_lock.unlock_read();
410 }
411
412 template <typename WaitPolicy>
ScopedWriteLock(ReadWriteLock & lock)413 inline ReadWriteLock<WaitPolicy>::ScopedWriteLock::ScopedWriteLock(ReadWriteLock& lock)
414 : m_lock(lock)
415 {
416 m_lock.lock_write();
417 }
418
419 template <typename WaitPolicy>
~ScopedWriteLock()420 inline ReadWriteLock<WaitPolicy>::ScopedWriteLock::~ScopedWriteLock()
421 {
422 m_lock.unlock_write();
423 }
424
425
426 //
427 // ThreadFlag class implementation.
428 //
429
ThreadFlag()430 inline ThreadFlag::ThreadFlag()
431 {
432 clear();
433 }
434
clear()435 inline void ThreadFlag::clear()
436 {
437 atomic_write(&m_flag, 0);
438 }
439
set()440 inline void ThreadFlag::set()
441 {
442 atomic_write(&m_flag, 1);
443 }
444
is_clear()445 inline bool ThreadFlag::is_clear() const
446 {
447 return atomic_read(&m_flag) == 0;
448 }
449
is_set()450 inline bool ThreadFlag::is_set() const
451 {
452 return !is_clear();
453 }
454
455 } // namespace foundation
456