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