nlib
CriticalSection.h
Go to the documentation of this file.
1 
2 #pragma once
3 #ifndef INCLUDE_NN_NLIB_THREADING_CRITICALSECTION_H_
4 #define INCLUDE_NN_NLIB_THREADING_CRITICALSECTION_H_
5 
6 #include "nn/nlib/Config.h"
7 #include "nn/nlib/Swap.h"
8 
9 NLIB_NAMESPACE_BEGIN
10 class TimeSpan;
11 class DateTime;
12 namespace threading {
13 
14 class NLIB_LOCKABLE SimpleCriticalSection {
15  public:
16 #ifdef NLIB_CXX11_CONSTEXPR
18  : m_Mutex(NLIB_MUTEX_INITIALIZER) {}
19 #else
21  errno_t e = nlib_mutex_init(&m_Mutex);
22  NLIB_ASSERT_NOERR(e);
23  NLIB_UNUSED(e);
24  }
25 #endif
26  ~SimpleCriticalSection() NLIB_NOEXCEPT {
27  errno_t e = nlib_mutex_destroy(&m_Mutex);
28  NLIB_UNUSED(e);
29  NLIB_ASSERT_NOERR(e);
30  }
31  void lock() NLIB_NOEXCEPT NLIB_LOCK_FUNC() {
32  errno_t e = nlib_mutex_lock(&m_Mutex);
33  NLIB_UNUSED(e);
34  NLIB_ASSERT_NOERR(e);
35  }
36  void unlock() NLIB_NOEXCEPT NLIB_UNLOCK_FUNC() {
37  errno_t e = nlib_mutex_unlock(&m_Mutex);
38  NLIB_UNUSED(e);
39  NLIB_ASSERT_NOERR(e);
40  }
41  bool try_lock() NLIB_NOEXCEPT NLIB_TRYLOCK_FUNC(true) {
42  errno_t e = nlib_mutex_trylock(&m_Mutex);
43  if (e == 0) return true;
44  NLIB_ASSERT(e == EBUSY);
45  return false;
46  }
48  native_handle_type native_handle() NLIB_NOEXCEPT { return &m_Mutex; }
49 
50  private:
51  nlib_mutex m_Mutex;
52 };
53 
54 #ifndef NLIB_CRITICALSECTION_USE_FALLBACK
56  public:
57 #if defined(NLIB_CXX11_CONSTEXPR)
60 #else
62  errno_t e = nlib_mutex_recursive_init(&m_Mutex);
63  NLIB_ASSERT_NOERR(e);
64  NLIB_UNUSED(e);
65  }
66 #endif
67  ~CriticalSection() NLIB_NOEXCEPT { nlib_mutex_destroy(&m_Mutex); }
69  errno_t e = nlib_mutex_lock(&m_Mutex);
70  NLIB_ASSERT_NOERR(e);
71  NLIB_UNUSED(e);
72  }
74  errno_t e = nlib_mutex_unlock(&m_Mutex);
75  NLIB_ASSERT_NOERR(e);
76  NLIB_UNUSED(e);
77  }
79  errno_t e = nlib_mutex_trylock(&m_Mutex);
80  if (e == 0) return true;
81  NLIB_ASSERT(e == EBUSY);
82  return false;
83  }
85  native_handle_type native_handle() NLIB_NOEXCEPT { return &m_Mutex; }
86 
87  private:
88  nlib_mutex m_Mutex;
89 };
90 #endif
91 
92 #ifndef NLIB_TIMEDCRITICALSECTION_USE_FALLBACK
94  public:
95 #if defined(NLIB_CXX11_CONSTEXPR)
98 #else
101  NLIB_ASSERT_NOERR(e);
102  NLIB_UNUSED(e);
103  }
104 #endif
105  ~TimedCriticalSection() NLIB_NOEXCEPT { nlib_mutex_destroy(&m_Mutex); }
106  NLIB_VIS_PUBLIC bool try_lock_for(const TimeSpan& span) NLIB_NOEXCEPT;
107  NLIB_VIS_PUBLIC bool try_lock_until(const DateTime& abstime) NLIB_NOEXCEPT;
108  void lock() NLIB_NOEXCEPT {
109  errno_t e = nlib_mutex_lock(&m_Mutex);
110  NLIB_ASSERT_NOERR(e);
111  NLIB_UNUSED(e);
112  }
113  void unlock() NLIB_NOEXCEPT {
114  errno_t e = nlib_mutex_unlock(&m_Mutex);
115  NLIB_ASSERT_NOERR(e);
116  NLIB_UNUSED(e);
117  }
118  bool try_lock() NLIB_NOEXCEPT {
119  errno_t e = nlib_mutex_trylock(&m_Mutex);
120  if (e == 0) return true;
121  NLIB_ASSERT(e == EBUSY);
122  return false;
123  }
125  native_handle_type native_handle() NLIB_NOEXCEPT { return &m_Mutex; }
126 
127  private:
128  nlib_mutex m_Mutex;
129 };
130 #endif
131 
132 } // namespace threading
133 NLIB_NAMESPACE_END
134 
135 NLIB_NAMESPACE_BEGIN
136 namespace threading {
137 
138 class DummyCriticalSection {
139  public:
140  NLIB_CEXPR DummyCriticalSection() NLIB_NOEXCEPT {}
141  ~DummyCriticalSection() NLIB_NOEXCEPT {}
142  void lock() NLIB_NOEXCEPT {}
143  void unlock() NLIB_NOEXCEPT {}
144  bool try_lock() NLIB_NOEXCEPT { return true; }
145  bool try_lock_for(const TimeSpan&) NLIB_NOEXCEPT { return true; } // NOLINT
146  bool try_lock_until(const DateTime&) NLIB_NOEXCEPT { return true; } // NOLINT
147  typedef int native_handle_type;
148  native_handle_type native_handle() NLIB_NOEXCEPT { return 0; }
149 
150  private:
151  NLIB_DISALLOW_COPY_AND_ASSIGN(DummyCriticalSection);
152 };
153 
154 class CriticalSectionFallback {
155  public:
156  // NOTE:
157  // -1 must not be a thread-id
158  CriticalSectionFallback() NLIB_NOEXCEPT : m_Counter(0) {
159  m_Tid = -1;
160  }
161  ~CriticalSectionFallback() NLIB_NOEXCEPT {}
162  NLIB_VIS_PUBLIC void lock() NLIB_NOEXCEPT;
163  NLIB_VIS_PUBLIC void unlock() NLIB_NOEXCEPT;
164  NLIB_VIS_PUBLIC bool try_lock() NLIB_NOEXCEPT;
165  typedef SimpleCriticalSection::native_handle_type native_handle_type;
166  native_handle_type native_handle() { return m_Lock.native_handle(); }
167 
168  private:
169  SimpleCriticalSection m_Lock;
170  int32_t m_Counter;
171  nlib_thread_id m_Tid;
172 };
173 
174 #ifdef NLIB_CRITICALSECTION_USE_FALLBACK
175 typedef CriticalSectionFallback CriticalSection;
176 #endif
177 
178 class TimedCriticalSectionFallback {
179  public:
180  TimedCriticalSectionFallback() NLIB_NOEXCEPT {}
181  ~TimedCriticalSectionFallback() NLIB_NOEXCEPT {}
182  void lock() NLIB_NOEXCEPT { m_Lock.lock(); }
183  void unlock() NLIB_NOEXCEPT { m_Lock.unlock(); }
184  bool try_lock() NLIB_NOEXCEPT { return m_Lock.try_lock(); }
185  NLIB_VIS_PUBLIC bool try_lock_for(const TimeSpan& span) NLIB_NOEXCEPT;
186  NLIB_VIS_PUBLIC bool try_lock_until(const DateTime& abstime) NLIB_NOEXCEPT;
187  typedef CriticalSection::native_handle_type native_handle_type;
188  native_handle_type native_handle() { return m_Lock.native_handle(); }
189 
190  private:
191  CriticalSection m_Lock;
192 };
193 
194 #ifdef NLIB_TIMEDCRITICALSECTION_USE_FALLBACK
195 typedef TimedCriticalSectionFallback TimedCriticalSection;
196 #endif
197 
198 } // namespace threading
199 NLIB_NAMESPACE_END
200 
201 NLIB_NAMESPACE_BEGIN
202 namespace threading {
203 
204 struct AdoptLockType {};
205 struct TryToLockType {};
206 struct DeferLockType {};
207 
211 
212 template <class T>
213 class NLIB_SCOPED_LOCKABLE ScopedLock {
214  public:
215  typedef T mutex_type;
216  typedef typename mutex_type::native_handle_type native_handle_type;
217  explicit ScopedLock(mutex_type& m) NLIB_LOCK_FUNC(m_Lock) : m_Lock(m) { // NOLINT
218  m_Lock.lock();
219  }
220  ScopedLock(mutex_type& m, AdoptLockType) NLIB_NOEXCEPT NLIB_LOCK_FUNC(m_Lock) // NOLINT
221  : m_Lock(m) {}
222  ~ScopedLock() NLIB_NOEXCEPT NLIB_UNLOCK_FUNC(m_Lock) { m_Lock.unlock(); }
223  native_handle_type native_handle() { return m_Lock.native_handle(); }
224 
225  private:
226  mutex_type& m_Lock;
228 };
229 
230 template <>
231 class NLIB_SCOPED_LOCKABLE ScopedLock<SimpleCriticalSection> {
232  public:
233  typedef SimpleCriticalSection mutex_type;
234  typedef mutex_type::native_handle_type native_handle_type;
235  explicit ScopedLock(mutex_type& m) NLIB_LOCK_FUNC(m_Lock) : m_Lock(m) { // NOLINT
236  m_Lock.lock();
237  }
238  ScopedLock(mutex_type& m, AdoptLockType) NLIB_NOEXCEPT NLIB_LOCK_REQUIRED(m) // NOLINT
239  NLIB_LOCK_FUNC(m_Lock)
240  : m_Lock(m) {}
241  ~ScopedLock() NLIB_NOEXCEPT NLIB_UNLOCK_FUNC(m_Lock) { m_Lock.unlock(); }
242  native_handle_type native_handle() { return m_Lock.native_handle(); }
243 
244  private:
245  mutex_type& m_Lock;
246  NLIB_DISALLOW_COPY_AND_ASSIGN(ScopedLock);
247 };
248 
249 template<>
250 class NLIB_SCOPED_LOCKABLE ScopedLock<nlib_mutex> {
251  public:
252  typedef nlib_mutex mutex_type;
253  typedef nlib_mutex* native_handle_type;
254  explicit ScopedLock(mutex_type& m) NLIB_LOCK_FUNC(m_Lock) : m_Lock(m) { // NOLINT
255  errno_t e = nlib_mutex_lock(&m_Lock);
256  NLIB_UNUSED(e);
257  NLIB_ASSERT_NOERR(e);
258  }
259  ScopedLock(mutex_type& m, AdoptLockType) NLIB_NOEXCEPT NLIB_LOCK_REQUIRED(m) // NOLINT
260  NLIB_LOCK_FUNC(m_Lock)
261  : m_Lock(m) {}
262  ~ScopedLock() NLIB_NOEXCEPT NLIB_UNLOCK_FUNC(m_Lock) {
263  errno_t e = nlib_mutex_unlock(&m_Lock);
264  NLIB_UNUSED(e);
265  NLIB_ASSERT_NOERR(e);
266  }
267  native_handle_type native_handle() { return &m_Lock; }
268 
269  private:
270  nlib_mutex& m_Lock;
271  NLIB_DISALLOW_COPY_AND_ASSIGN(ScopedLock);
272 };
273 
274 template <class T>
275 class UniqueLock {
276  public:
277  typedef T mutex_type;
278  typedef typename mutex_type::native_handle_type native_handle_type;
279  UniqueLock() NLIB_NOEXCEPT : m_Locker(NULL), m_Locked(false) {}
280  explicit UniqueLock(mutex_type& rhs) : m_Locker(&rhs), m_Locked(true) { // NOLINT
281  rhs.lock();
282  }
283  UniqueLock(mutex_type& rhs, AdoptLockType) // NOLINT
284  : m_Locker(&rhs),
285  m_Locked(true) {}
286  UniqueLock(mutex_type& rhs, DeferLockType) // NOLINT
287  NLIB_NOEXCEPT : m_Locker(&rhs),
288  m_Locked(false) {}
289  UniqueLock(mutex_type& rhs, TryToLockType) // NOLINT
290  : m_Locker(&rhs),
291  m_Locked(rhs.try_lock()) {}
292  NLIB_MOVE_MEMBER_HELPER_2(UniqueLock, m_Locker, m_Locked)
294  if (m_Locked) m_Locker->unlock();
295  }
296  void lock() {
297  NLIB_ASSERT(m_Locker && !m_Locked);
298  if (!m_Locker || m_Locked) {
299  return;
300  }
301  m_Locker->lock();
302  m_Locked = true;
303  }
305  NLIB_ASSERT(m_Locker && m_Locked);
306  if (!m_Locker || !m_Locked) {
307  return;
308  }
309  m_Locker->unlock();
310  m_Locked = false;
311  }
313  NLIB_ASSERT(m_Locker && !m_Locked);
314  if (!m_Locker || m_Locked) {
315  return false;
316  }
317  m_Locked = m_Locker->try_lock();
318  return m_Locked;
319  }
320  bool try_lock_for(const TimeSpan& timeout) NLIB_NOEXCEPT {
321  NLIB_ASSERT(m_Locker && !m_Locked);
322  if (!m_Locker || m_Locked) {
323  return false;
324  }
325  m_Locked = m_Locker->try_lock_for(timeout);
326  return m_Locked;
327  }
328  bool try_lock_until(const DateTime& abstime) NLIB_NOEXCEPT {
329  NLIB_ASSERT(m_Locker && !m_Locked);
330  if (!m_Locker || m_Locked) {
331  return false;
332  }
333  m_Locked = m_Locker->try_lock_until(abstime);
334  return m_Locked;
335  }
336  void swap(UniqueLock& rhs) NLIB_NOEXCEPT { // NOLINT
337  using std::swap;
338  swap(m_Locker, rhs.m_Locker);
339  swap(m_Locked, rhs.m_Locked);
340  }
341  mutex_type* release() NLIB_NOEXCEPT {
342  mutex_type* p = m_Locker;
343  m_Locker = NULL;
344  m_Locked = false;
345  return p;
346  }
347  mutex_type* mutex() const NLIB_NOEXCEPT { return m_Locker; }
348  native_handle_type native_handle() { return m_Locker->native_handle(); }
349  bool owns_lock() const NLIB_NOEXCEPT { return m_Locked; }
350  NLIB_SAFE_BOOL(UniqueLock, owns_lock());
351 
352  private:
353  mutex_type* m_Locker;
354  bool m_Locked;
356 };
357 
358 namespace detail {
359 
360 template<class T>
361 NLIB_ALWAYS_INLINE nlib_mutex* GetRawMutex(T& obj) { // NOLINT
362  return obj.native_handle();
363 }
364 
365 template<>
366 NLIB_ALWAYS_INLINE nlib_mutex* GetRawMutex(nlib_mutex& obj) { // NOLINT
367  return &obj;
368 }
369 
370 } // namespace detail
371 
372 } // namespace threading
373 NLIB_NAMESPACE_END
374 
375 #ifndef NLIB_STD_SWAP_WORKAROUND
376 NLIB_NAMESPACE_BEGIN
377 #endif
378 
379 NLIB_DEFINE_STD_SWAP_T_BEGIN1(threading) // NOLINT
380 NLIB_DEFINE_STD_SWAP_T1(T, NLIB_NS::threading::UniqueLock) // NOLINT
381 NLIB_DEFINE_STD_SWAP_T_END1(threading) // NOLINT
382 
383 #ifndef NLIB_STD_SWAP_WORKAROUND
384 NLIB_NAMESPACE_END
385 #endif
386 
387 
388 #endif // INCLUDE_NN_NLIB_THREADING_CRITICALSECTION_H_
#define NLIB_NOEXCEPT
Defines noexcept geared to the environment, or the equivalent.
Definition: Platform.h:2151
UniqueLock() noexcept
Initializes an object without creating an association.
native_handle_type native_handle() noexcept
Gets a pointer to a native type mutex.
bool owns_lock() const noexcept
Returns true if the lock associated with UniqueLock is locked.
void lock() noexcept
Gets a lock, and enters the critical section. Blocks until it can get a lock.
~ScopedLock() noexcept NLIB_UNLOCK_FUNC(m_Lock)
Unlocks objects like CriticalSection (calls unlock).
#define NLIB_DISALLOW_COPY_AND_ASSIGN(TypeName)
Prohibits use of the copy constructor and assignment operator for the class specified by TypeName...
Definition: Config.h:126
#define NLIB_SAFE_BOOL(class_name, exp)
Defines a safe operator bool function in the class. Uses the C++11 explicit bool if it is available f...
Definition: Config.h:141
bool try_lock() noexcept NLIB_TRYLOCK_FUNC(true)
Gets a lock, and attempts to enter the critical section.
Simplest critical section. Not reentrant.
mutex_type * release() noexcept
Removes the association. Does not unlock.
errno_t nlib_mutex_lock(nlib_mutex *mutex)
Locks the specified mutex.
ScopedLock(mutex_type &m, AdoptLockType) noexcept NLIB_LOCK_FUNC(m_Lock)
Assumes an object like CriticalSection is already locked, and initializes the object without locking ...
mutex_type::native_handle_type native_handle_type
mutex_type::native_handle_type.
errno_t nlib_mutex_unlock(nlib_mutex *mutex)
Unlocks the specified mutex.
UniqueLock(mutex_type &rhs, AdoptLockType)
Assumes an object like CriticalSection is already locked, and initializes the object without locking ...
bool try_lock() noexcept
Attempts to lock the associated object like CriticalSection.
void unlock() noexcept
Releases the lock, and exits the critical section.
mutex_type::native_handle_type native_handle_type
mutex_type::native_handle_type.
The class for representing the date and time.
Definition: DateTime.h:249
void swap(UniqueLock &rhs) noexcept
Attempts to swap the associated CriticalSection or other object.
native_handle_type native_handle()
Returns an implementation-specific handle representing a lock.
constexpr SimpleCriticalSection() noexcept
Instantiates the object with default parameters (default constructor).
#define NLIB_RECURSIVE_MUTEX_INITIALIZER
A macro for statically initializing nlib_mutex. Makes it a recursive mutex.
errno_t nlib_mutex_destroy(nlib_mutex *mutex)
Destroys the specified mutex object and frees any associated resources.
#define NLIB_CEXPR
Defines constexpr if it is available for use. If not, holds an empty string.
void unlock() noexcept
Releases the lock, and exits the critical section.
Used in ScopedLock and UniqueLock by tag type.
constexpr CriticalSection() noexcept
Instantiates the object with default parameters (default constructor).
void lock() noexcept NLIB_LOCK_FUNC()
Gets a lock, and enters the critical section. Blocks until it can get a lock.
Class to wrap objects like CriticalSection.
nlib_mutex * native_handle_type
typedef to a pointer type to a native mutex.
bool try_lock() noexcept
Gets a lock, and attempts to enter the critical section.
native_handle_type native_handle()
Returns an implementation-specific handle representing a lock.
Used in ScopedLock and UniqueLock by tag type.
nlib_mutex * native_handle_type
typedef to a pointer type to a native mutex.
nlib_mutex * native_handle_type
typedef to a pointer type to a native mutex.
ScopedLock(mutex_type &m) NLIB_LOCK_FUNC(m_Lock)
Locks objects like CriticalSection (calls lock).
bool try_lock_for(const TimeSpan &timeout) noexcept
Attempts to lock with timeout the associated CriticalSection or other object.
constexpr const DeferLockType deferLock
A DeferLockType-type value.
A file that contains the configuration information for each development environment.
#define NLIB_RECURSIVE_TIMED_MUTEX_INITIALIZER
A macro for statically initializing nlib_mutex. Makes it a recursive mutex that can time out...
NLIB_CHECK_RESULT errno_t nlib_mutex_trylock(nlib_mutex *mutex)
Locks mutex, but only if it is not locked.
constexpr const AdoptLockType adoptLock
An AdoptLockType-type value.
Used in ScopedLock and UniqueLock by tag type.
void lock() noexcept
Gets a lock, and enters the critical section. Blocks until it can get a lock.
UniqueLock(mutex_type &rhs, TryToLockType)
Attempts to lock using try_lock when initializing.
int nlib_thread_id
A unique integer value for each thread.
Definition: Platform.h:859
Wraps objects like CriticalSection. Locks with a constructor, and unlocks with a destructor.
pthread_mutex_t nlib_mutex
The type for mutex variables.
#define NLIB_MUTEX_INITIALIZER
A macro for statically initializing nlib_mutex.
#define NLIB_ALWAYS_INLINE
Indicates that the compiler is forced to perform inline expansion of functions.
Definition: Platform_unix.h:59
void unlock() noexcept NLIB_UNLOCK_FUNC()
Releases the lock, and exits the critical section.
#define NLIB_VIS_PUBLIC
Symbols for functions and classes are made available outside of the library.
Definition: Platform_unix.h:51
UniqueLock(mutex_type &rhs)
Locks the object like CriticalSection and associates it with this object.
The class for representing the time.
Definition: DateTime.h:93
Critical section that can timeout in reentrant.
native_handle_type native_handle() noexcept
Gets a pointer to a native type mutex.
constexpr const TryToLockType tryToLock
A TryToLockType-type value.
void unlock() noexcept
Unlocks the associated CriticalSection or other object.
bool try_lock() noexcept
Gets a lock, and attempts to enter the critical section.
UniqueLock(mutex_type &rhs, DeferLockType) noexcept
Initializes an object without locking.
constexpr TimedCriticalSection() noexcept
Instantiates the object with default parameters (default constructor).
errno_t nlib_mutex_recursive_init(nlib_mutex *mutex)
Initializes a recursive mutex.
void lock()
Locks the associated CriticalSection or other object.
errno_t nlib_mutex_init(nlib_mutex *mutex)
Initializes a mutex.
mutex_type * mutex() const noexcept
Gets the pointer to the associated object like CriticalSection.
bool try_lock_until(const DateTime &abstime) noexcept
Attempts to lock with timeout the associated CriticalSection or other object.
native_handle_type native_handle() noexcept
Gets a pointer to a native type mutex.
errno_t nlib_mutex_recursive_timed_init(nlib_mutex *mutex)
Initializes a mutex that is recursive and can time out.
int errno_t
Indicates with an int-type typedef that a POSIX error value is returned as the return value...
Definition: NMalloc.h:24