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