Gray C++ Libraries  0.0.2
A set of C++ libraries for MSVC, GNU on Windows, WinCE, Linux
cThreadLock.h
Go to the documentation of this file.
1 //
5 //
6 
7 #ifndef _INC_cThreadLock_H
8 #define _INC_cThreadLock_H
9 #ifndef NO_PRAGMA_ONCE
10 #pragma once
11 #endif
12 
13 #include "cNonCopyable.h"
14 #include "cTimeSys.h"
15 #include "cInterlockedVal.h"
16 #include "cOSHandle.h"
17 #include "cLocker.h"
18 #include "FileName.h"
19 
20 #if defined(__linux__)
21 #include <pthread.h>
22 #endif
23 
24 namespace Gray
25 {
26 #ifdef _WIN32
27  typedef DWORD THREADID_t;
28 #define _SIZEOF_THREADID 4 // sizeof(THREADID_t)
29 #elif defined(__linux__)
30  typedef pthread_t THREADID_t;
31 #define _SIZEOF_THREADID _SIZEOF_PTR
32 #else
33 #error NOOS
34 #endif
35 
36 #ifdef _WIN32
37  typedef DWORD THREAD_EXITCODE_t;
38  static constexpr THREAD_EXITCODE_t THREAD_EXITCODE_RUNNING = ((THREAD_EXITCODE_t)STILL_ACTIVE);
39  static constexpr THREAD_EXITCODE_t THREAD_EXITCODE_ERR = ((THREAD_EXITCODE_t)-1);
40 
41 #elif defined(__linux__)
42  typedef void* THREAD_EXITCODE_t;
43  static constexpr THREAD_EXITCODE_t THREAD_EXITCODE_RUNNING = ((THREAD_EXITCODE_t)2);
44  static constexpr THREAD_EXITCODE_t THREAD_EXITCODE_ERR = ((THREAD_EXITCODE_t)1);
45 
46 #else
47 #error NOOS
48 #endif
49 
50  static constexpr THREAD_EXITCODE_t THREAD_EXITCODE_OK = ((THREAD_EXITCODE_t)0);
51 
52  typedef THREAD_EXITCODE_t(_stdcall* THREAD_FUNC_t)(void*); // entry point for a thread. same as _WIN32 PTHREAD_START_ROUTINE. like FARPROC ?
53 
55  {
59 
60  protected:
61  THREADID_t m_dwThreadId;
62 
63  public:
64  static const THREADID_t k_NULL = 0;
65 
66  public:
67  cThreadId(THREADID_t nThreadId = k_NULL) noexcept
68  : m_dwThreadId(nThreadId)
69  {
70  }
71  THREADID_t GetThreadId() const noexcept
72  {
75  return m_dwThreadId;
76  }
77  THREADID_t get_HashCode() const noexcept
78  {
80  return m_dwThreadId;
81  }
82  bool isCurrentThread() const noexcept
83  {
85  return IsEqualId(m_dwThreadId, GetCurrentId());
86  }
87  bool isValidId() const noexcept
88  {
89  return IsValidId(m_dwThreadId);
90  }
91  void InitCurrentId() noexcept
92  {
94  m_dwThreadId = GetCurrentId();
95  }
96 
97  static inline THREADID_t GetCurrentId() noexcept
98  {
102 #ifdef _WIN32
103  return ::GetCurrentThreadId();
104 #else // __linux__
105  return ::pthread_self();
106 #endif
107  }
108  static inline bool IsValidId(THREADID_t id) noexcept
109  {
111  return id != cThreadId::k_NULL;
112  }
113  static inline bool IsEqualId(THREADID_t a, THREADID_t b) noexcept
114  {
116 #ifdef _WIN32
117  return a == b;
118 #else
119  return ::pthread_equal(a, b);
120 #endif
121  }
122  static inline void SleepCurrent(TIMESYS_t uMs = cTimeSys::k_FREQ) noexcept
123  {
126 #ifdef _WIN32
127  ::Sleep(uMs);
128 #else
129  ::usleep((uMs) * 1000); // Sleep current thread.
130 #endif
131  };
132 
133  UNITTEST_FRIEND(cThreadLock);
134  };
135 
137  {
141 
142  protected:
145 
146  public:
147  cThreadState() noexcept
148  : m_bThreadRunning(false)
149  , m_bThreadStopping(false)
150  {
151  }
152 
153  bool isThreadRunning() const noexcept
154  {
156  return m_bThreadRunning;
157  }
158  bool isThreadStopping() const noexcept
159  {
161  return m_bThreadStopping;
162  }
163 
164  virtual bool RequestStopThread(bool bWillWait = false) noexcept
165  {
166  UNREFERENCED_PARAMETER(bWillWait);
167  m_bThreadStopping = true;
168  return isThreadRunning();
169  }
170  };
171 
173  {
176 
177  protected:
178  THREADID_t __DECL_ALIGN(_SIZEOF_THREADID) m_nLockThreadID;
179  public:
180  cThreadLockBase() noexcept
181  : m_nLockThreadID(cThreadId::k_NULL)
182  {
183  }
184  inline bool isLocked() const noexcept
185  {
187  return m_nLockThreadID != cThreadId::k_NULL;
188  }
189  inline THREADID_t get_ThreadLockOwner() const
190  {
192  THREADID_t nTid1 = m_nLockThreadID;
193  ASSERT(nTid1 == cThreadId::k_NULL || cThreadId::IsValidId(nTid1));
194  return nTid1;
195  }
196  inline bool isThreadLockedByCurrent() const noexcept
197  {
198  THREADID_t nTid1 = m_nLockThreadID;
199  THREADID_t nTid2 = cThreadId::GetCurrentId();
200  return cThreadId::IsEqualId(nTid1, nTid2);
201  }
202  };
203 
205  {
210  typedef cThreadLockBase SUPER_t;
211  public:
212  cThreadLockFast() noexcept
213  {
214  }
215  cThreadLockFast(const cThreadLockFast& a) noexcept
216  {
221  }
222  ~cThreadLockFast() noexcept
223  {
224  }
225 
226  bool ClearThreadLockOwner(THREADID_t nTid)
227  {
230  THREADID_t nTidowner = InterlockedN::CompareExchange(&m_nLockThreadID, cThreadId::k_NULL, nTid);
231  return cThreadId::IsEqualId(nTidowner, nTid);
232  }
233 
234  void Lock();
235  bool LockTry(TIMESYSD_t dwDelayMS = 0);
236 
237  inline void Unlock()
238  {
241  ASSERT(isThreadLockedByCurrent());
242  if (SUPER_t::DecLockCount() == 0)
243  {
244  InterlockedN::Exchange(&m_nLockThreadID, cThreadId::k_NULL);
245  }
246  }
247  };
248 
250 
252  {
262 
263  typedef cThreadLockBase SUPER_t;
264  public:
265 #ifdef _WIN32
266  cOSHandle m_Mutex;
267 #else // __linux__
268  pthread_mutex_t m_Mutex; // PTHREAD_MUTEX_INITIALIZER
269  static const pthread_mutex_t k_MutexInit; // PTHREAD_MUTEX_INITIALIZER
270 #endif
271  protected:
273 
274  public:
276  : m_bInitialOwner(false)
277  {
282  InitLockMutex(nullptr, false);
283  }
284  cThreadLockMutex(const FILECHAR_t* pszMutexName = nullptr, bool bInitialOwner = false) noexcept
285  : m_bInitialOwner(bInitialOwner)
286  {
287  InitLockMutex(pszMutexName, bInitialOwner);
288  }
289  inline ~cThreadLockMutex() noexcept
290  {
291  // DEBUG_CHECK( ! IsLocked());
292  if (m_bInitialOwner && isLocked()) // m_nLockCount == 1
293  {
294  this->Unlock();
295  }
296 #if defined(__linux__)
297  ::pthread_mutex_destroy(&m_Mutex);
298 #endif
299  }
300 
301  protected:
302  void InitLockMutex(const FILECHAR_t* pszMutexName, bool bInitialOwner) noexcept
303  {
306 #ifdef _WIN32
307  m_Mutex.AttachHandle(_FNF(::CreateMutex)(nullptr, bInitialOwner, pszMutexName));
308  if (bInitialOwner)
309  {
310  LockInternal();
311  }
312 #else // __linux__
313  // Allow this mutex to be opened multiple times in the same thread.
314  m_Mutex = k_MutexInit; // same as ::pthread_mutex_init() ??
315  if (bInitialOwner)
316  {
317  Lock();
318  }
319 #endif
320  }
321 
322  inline void LockInternal()
323  {
325  m_nLockThreadID = cThreadId::GetCurrentId();
326  SUPER_t::IncLockCount();
327  ASSERT(isThreadLockedByCurrent()); // may have several locks on the same thread.
328  }
329 
330  public:
331  inline bool Lock()
332  {
336 #ifdef _WIN32
338  if (hRes != S_OK)
339  {
340  return false;
341  }
342 #else
343  // If the mutex is already locked (by diff thread), the calling thread blocks until the mutex becomes available
344  int iRet = ::pthread_mutex_lock(&m_Mutex);
345  if (iRet)
346  {
347  // error on lock. EINVAL
348  return false;
349  }
350 #endif
351  LockInternal(); // may have several locks on the same thread.
352  return true;
353  }
354  inline bool Unlock()
355  {
356  // Assume i own the lock. so thread safe isn't a worry on entry.
357  ASSERT(isThreadLockedByCurrent()); // may have several locks on the same thread.
358  if (SUPER_t::DecLockCount() <= 0)
359  {
360  m_nLockThreadID = cThreadId::k_NULL;
361  }
362 #ifdef _WIN32
363  return ::ReleaseMutex(m_Mutex) ? true : false;
364 #else
365  ::pthread_mutex_unlock(&m_Mutex);
366  return true;
367 #endif
368  }
369 
370  bool LockTry(TIMESYSD_t dwDelayMS = 0)
371  {
374 #ifdef _WIN32
375  HRESULT hRes = m_Mutex.WaitForSingleObject(dwDelayMS);
376  if (hRes != S_OK)
377  {
378  return false;
379  }
380 #elif defined(__USE_XOPEN2K) // __linux__
381  cTimeSpec tSpec(dwDelayMS);
382  int iRet = ::pthread_mutex_timedlock(&m_Mutex, &tSpec);
383  if (iRet != 0)
384  {
385  return false;
386  }
387 #else
388  // if pthread_mutex_timedlock() is not available.
389  TIMESYSD_t dwWaitTimeMS = 0;
390  for (;;)
391  {
392  if (::pthread_mutex_trylock(&m_Mutex) == 0)
393  break;
394  if (dwDelayMS <= 0) // EBUSY
395  {
396  return false; // FAILED to lock
397  }
398  cThreadId::SleepCurrent(dwWaitTimeMS); // wait for a tick.
399  if (dwWaitTimeMS == 0)
400  {
401  dwWaitTimeMS = 1;
402  }
403  else
404  {
405  dwDelayMS--;
406  }
407  }
408 #endif
409  LockInternal();
410  return true;
411  }
412  };
413 
415 
416 #ifdef _WIN32
418  {
427 
428  protected:
429  CRITICAL_SECTION m_CritSection; // RTL_CRITICAL_SECTION, more efficient than a MUTEX. but takes more memory to store.
430 
431  private:
432  void LockInternal()
433  {
434  m_nLockThreadID = cThreadId::GetCurrentId();
435  IncLockCount();
436  ASSERT(isThreadLockedByCurrent()); // may have several locks on the same thread.
437  }
438  void InitLockCrit()
439  {
441  ::InitializeCriticalSection(&m_CritSection);
442  }
443 
444  public:
446  {
451  InitLockCrit();
452  }
454  {
455  InitLockCrit();
456  }
457  inline ~cThreadLockCrit()
458  {
460  ::DeleteCriticalSection(&m_CritSection);
461  }
462 
463  public:
464  inline void Lock()
465  {
469  ::EnterCriticalSection(&m_CritSection);
470  LockInternal();
471  }
472  inline void Unlock()
473  {
474  ASSERT(isThreadLockedByCurrent()); // may have several locks on the same thread.
475  if (DecLockCount() <= 0)
476  {
477  m_nLockThreadID = cThreadId::k_NULL;
478  }
479  ::LeaveCriticalSection(&m_CritSection);
480  }
481 
482 #if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400)
483  bool LockTry()
484  {
486  if (::TryEnterCriticalSection(&m_CritSection) == 0)
487  return false;
488  LockInternal();
489  return true;
490  }
491 #endif
492  };
493 #else
494  typedef cThreadLockMutex cThreadLockCrit; // just substitute it if not _WIN32.
495 #endif // _WIN32
496 
498  {
501  public:
502  inline THREADID_t get_ThreadLockOwner() const
503  {
505  return (THREADID_t)isLocked() ? 1 : 0;
506  }
507  };
508 
509  //****************************************************************************
510 
511 #if defined(_MT) || defined(__linux__)
512  typedef cThreadLockFast cThreadLockCount;
513 #else
515 #endif
516  typedef cLockerT<cThreadLockCount> cThreadGuard; // instantiated locker.
517 };
518 
519 #endif // _INC_cThreadLock_H
#define _FNF(c)
_WIN32 name has a A or W for UTF8 or UNICODE
Definition: FileName.h:24
#define GRAYCORE_LINK
Definition: GrayCore.h:47
#define UNREFERENCED_PARAMETER(P)
< _WIN32 type thing. get rid of stupid warning.
Definition: SysTypes.h:299
#define UNREFERENCED_REFERENCE(x)
Definition: SysTypes.h:318
#define VOLATILE
Definition: SysTypes.h:429
INT32 HRESULT
_WIN32 style error codes. INT32
Definition: SysTypes.h:465
#define ASSERT(exp)
Definition: cDebugAssert.h:87
#define UNITTEST_FRIEND(n)
Define this in the class body to be unit tested. Allow the unit test to access private/protected stuf...
Definition: cUnitTestDecl.h:17
Definition: cLocker.h:18
bool isLocked() const
Definition: cLocker.h:41
Definition: cLocker.h:72
Definition: cNonCopyable.h:17
Definition: cOSHandle.h:59
HRESULT WaitForSingleObject(TIMESYSD_t dwMilliseconds) const
Definition: cOSHandle.cpp:27
void AttachHandle(HANDLE h) noexcept
Definition: cOSHandle.h:163
Definition: cThreadLock.h:55
THREADID_t get_HashCode() const noexcept
Definition: cThreadLock.h:77
void InitCurrentId() noexcept
Definition: cThreadLock.h:91
static void SleepCurrent(TIMESYS_t uMs=cTimeSys::k_FREQ) noexcept
Definition: cThreadLock.h:122
THREADID_t GetThreadId() const noexcept
Definition: cThreadLock.h:71
static THREADID_t GetCurrentId() noexcept
Definition: cThreadLock.h:97
static bool IsEqualId(THREADID_t a, THREADID_t b) noexcept
Definition: cThreadLock.h:113
bool isValidId() const noexcept
Definition: cThreadLock.h:87
cThreadId(THREADID_t nThreadId=k_NULL) noexcept
Definition: cThreadLock.h:67
bool isCurrentThread() const noexcept
Definition: cThreadLock.h:82
THREADID_t m_dwThreadId
unique thread id. i.e. stack base pointer. (Use the MFC name)
Definition: cThreadLock.h:61
static bool IsValidId(THREADID_t id) noexcept
Definition: cThreadLock.h:108
static const THREADID_t k_NULL
Not a valid thread Id.
Definition: cThreadLock.h:64
Definition: cThreadLock.h:173
THREADID_t get_ThreadLockOwner() const
Definition: cThreadLock.h:189
bool isThreadLockedByCurrent() const noexcept
Definition: cThreadLock.h:196
THREADID_t __DECL_ALIGN(_SIZEOF_THREADID) m_nLockThreadID
The thread that has the lock. cThreadId:k_NULL is not locked.
bool isLocked() const noexcept
Definition: cThreadLock.h:184
cThreadLockBase() noexcept
Definition: cThreadLock.h:180
Definition: cThreadLock.h:205
bool ClearThreadLockOwner(THREADID_t nTid)
Definition: cThreadLock.h:226
void Unlock()
Definition: cThreadLock.h:237
~cThreadLockFast() noexcept
Definition: cThreadLock.h:222
cThreadLockFast(const cThreadLockFast &a) noexcept
Definition: cThreadLock.h:215
cThreadLockFast() noexcept
Definition: cThreadLock.h:212
Definition: cThreadLock.h:252
void LockInternal()
Definition: cThreadLock.h:322
bool Unlock()
Definition: cThreadLock.h:354
cThreadLockMutex(const FILECHAR_t *pszMutexName=nullptr, bool bInitialOwner=false) noexcept
Definition: cThreadLock.h:284
static const pthread_mutex_t k_MutexInit
Definition: cThreadLock.h:269
bool m_bInitialOwner
I also lock this myself.
Definition: cThreadLock.h:272
void InitLockMutex(const FILECHAR_t *pszMutexName, bool bInitialOwner) noexcept
Definition: cThreadLock.h:302
~cThreadLockMutex() noexcept
Definition: cThreadLock.h:289
bool LockTry(TIMESYSD_t dwDelayMS=0)
Definition: cThreadLock.h:370
cThreadLockMutex(const cThreadLockMutex &a) noexcept
Definition: cThreadLock.h:275
bool Lock()
Definition: cThreadLock.h:331
pthread_mutex_t m_Mutex
Definition: cThreadLock.h:268
Definition: cThreadLock.h:498
THREADID_t get_ThreadLockOwner() const
Definition: cThreadLock.h:502
Definition: cThreadLock.h:137
volatile bool m_bThreadStopping
trying to stop the thread nicely. Do this before TerminateThread()
Definition: cThreadLock.h:144
bool isThreadRunning() const noexcept
Definition: cThreadLock.h:153
bool isThreadStopping() const noexcept
Definition: cThreadLock.h:158
virtual bool RequestStopThread(bool bWillWait=false) noexcept
Definition: cThreadLock.h:164
cThreadState() noexcept
Definition: cThreadLock.h:147
bool m_bThreadRunning
called CreateThread() onThreadCreate(), and inside Run(), until onThreadExit()
Definition: cThreadLock.h:143
static const TIMESYS_t k_INF
INFINITE in _WIN32. MAILSLOT_WAIT_FOREVER.
Definition: cTimeSys.h:101
static const TIMESYS_t k_FREQ
milliSec per Sec
Definition: cTimeSys.h:100
__DECL_IMPORT TYPE Exchange(TYPE volatile *pnValue, TYPE nValue) noexcept
__DECL_IMPORT TYPE CompareExchange(TYPE volatile *pnValue, TYPE nValue, TYPE lComparand) noexcept
< The main namespace for all Core functions.
Definition: GrayCore.cpp:14
cThreadLockStub cThreadLockCount
Definition: cThreadLock.h:514
INT32 TIMESYSD_t
Time delta. signed milli-Seconds Span. cTimeSys::k_DMAX, cTimeSys::k_INF = MAILSLOT_WAIT_FOREVER.
Definition: cTimeSys.h:28
cThreadLockMutex cThreadLockCrit
Definition: cThreadLock.h:494
cLockerT< cThreadLockFast > cThreadGuardFast
Definition: cThreadLock.h:249
cLockerT< cThreadLockMutex > cThreadGuardMutex
Definition: cThreadLock.h:414
char FILECHAR_t
a UTF8 char in a file name. like TCHAR
Definition: FileName.h:22
cLockerT< cThreadLockCount > cThreadGuard
Definition: cThreadLock.h:516
THREAD_EXITCODE_t(_stdcall * THREAD_FUNC_t)(void *)
Definition: cThreadLock.h:52
UINT32 TIMESYS_t
TIMESYS_t = The normal system tick timer. milli-seconds since start of system/app ?
Definition: cTimeSys.h:27