OpenClonk
StdSync.h
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2011-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 /* synchronization helper classes */
17 
18 #ifndef INC_StdSync
19 #define INC_StdSync
20 
21 #ifdef _WIN32
23 
24 class CStdCSec
25 {
26 public:
27  CStdCSec() { InitializeCriticalSection(&sec); }
28  virtual ~CStdCSec() { DeleteCriticalSection(&sec); }
29 
30 protected:
31  CRITICAL_SECTION sec;
32 
33 public:
34  virtual void Enter() { EnterCriticalSection(&sec); }
35  virtual void Leave() { LeaveCriticalSection(&sec); }
36 };
37 
38 class CStdEvent
39 {
40 public:
41  CStdEvent(bool fManualReset) { hEvent = CreateEvent(nullptr, fManualReset, false, nullptr); }
42  ~CStdEvent() { CloseHandle(hEvent); }
43 
44 protected:
45  HANDLE hEvent;
46 
47 public:
48  void Set() { SetEvent(hEvent); }
49  void Pulse() { PulseEvent(hEvent); }
50  void Reset() { ResetEvent(hEvent); }
51  bool WaitFor(int iMillis) { return WaitForSingleObject(hEvent, iMillis) == WAIT_OBJECT_0; }
52 
53  HANDLE GetEvent() { return hEvent; }
54 };
55 
56 #else
57 // Value to specify infinite wait.
58 #define INFINITE (~0u)
59 
60 #if defined(HAVE_PTHREAD)
61 #include <pthread.h>
62 
63 class CStdCSec
64 {
65 public:
66  CStdCSec()
67  {
68  pthread_mutexattr_t attr;
69  pthread_mutexattr_init(&attr);
70  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
71  pthread_mutex_init(&mutex, &attr);
72  }
73  virtual ~CStdCSec() { pthread_mutex_destroy(&mutex); }
74 
75 protected:
76  pthread_mutex_t mutex;
77 
78 public:
79  virtual void Enter() { pthread_mutex_lock(&mutex); }
80  virtual void Leave() { pthread_mutex_unlock(&mutex); }
81 };
82 
83 class CStdEvent
84 {
85 public:
86  CStdEvent(bool fManualReset) : fManualReset(fManualReset), fSet(false)
87  {
88  pthread_cond_init(&cond, nullptr);
89  pthread_mutex_init(&mutex, nullptr);
90  }
91  ~CStdEvent()
92  {
93  pthread_cond_destroy(&cond);
94  pthread_mutex_destroy(&mutex);
95  }
96 
97 protected:
98  pthread_cond_t cond;
99  pthread_mutex_t mutex;
100  bool fManualReset, fSet;
101 
102 public:
103  void Set()
104  {
105  pthread_mutex_lock(&mutex);
106  fSet = true;
107  pthread_cond_broadcast(&cond);
108  pthread_mutex_unlock(&mutex);
109  }
110  void Pulse()
111  {
112  pthread_cond_broadcast(&cond);
113  }
114  void Reset()
115  {
116  pthread_mutex_lock(&mutex);
117  fSet = false;
118  pthread_mutex_unlock(&mutex);
119  }
120  bool WaitFor(unsigned int iMillis)
121  {
122  pthread_mutex_lock(&mutex);
123  // Already set?
124  while (!fSet)
125  {
126  // Use pthread_cond_wait or pthread_cond_timedwait depending on wait length. Check return value.
127  // Note this will temporarily unlock the mutex, so no deadlock should occur.
128  timespec ts = { static_cast<time_t>(iMillis / 1000),
129  static_cast<long>((iMillis % 1000) * 1000000) };
130  if (0 != (iMillis != INFINITE ? pthread_cond_timedwait(&cond, &mutex, &ts) : pthread_cond_wait(&cond, &mutex)))
131  {
132  pthread_mutex_unlock(&mutex);
133  return false;
134  }
135  }
136  // Reset flag, release mutex, done.
137  if (!fManualReset) fSet = false;
138  pthread_mutex_unlock(&mutex);
139  return true;
140  }
141 };
142 
143 #else
144 // Some stubs to silence the compiler
145 class CStdCSec
146 {
147 public:
148  CStdCSec() { }
149  virtual ~CStdCSec() { }
150  virtual void Enter() { }
151  virtual void Leave() { }
152 };
154 {
155 public:
156  CStdEvent(bool) { }
158  void Set() { }
159  void Pulse() { }
160  void Reset() { }
161  bool WaitFor(int) { return false; }
162 };
163 #endif // HAVE_PTHREAD
164 #endif // _WIN32
165 
166 class CStdLock
167 {
168 public:
169  CStdLock(CStdCSec *pSec) : sec(pSec)
170  { sec->Enter(); }
172  { Clear(); }
173 
174 protected:
176 
177 public:
178  void Clear()
179  { if (sec) sec->Leave(); sec = nullptr; }
180 };
181 
183 {
184 public:
185  // is called with CSec exlusive locked!
186  virtual void OnShareFree(class CStdCSecEx *pCSec) = 0;
187  virtual ~CStdCSecExCallback() = default;
188 };
189 
190 class CStdCSecEx : public CStdCSec
191 {
192 public:
194  : ShareFreeEvent(false)
195  { }
197  : lShareCnt(0), ShareFreeEvent(false), pCallbClass(pCallb)
198  { }
199  ~CStdCSecEx() override = default;
200 
201 protected:
202  // share counter
203  long lShareCnt{0};
204  // event: exclusive access permitted
206  // callback
208 
209 public:
210 
211  // (cycles forever if shared locked by calling thread!)
212  void Enter() override
213  {
214  // lock
215  CStdCSec::Enter();
216  // wait for share-free
217  while (lShareCnt)
218  {
219  // reset event
221  // leave section for waiting
222  CStdCSec::Leave();
223  // wait
225  // reenter section
226  CStdCSec::Enter();
227  }
228  }
229 
230  void Leave() override
231  {
232  // set event
234  // unlock
235  CStdCSec::Leave();
236  }
237 
238  void EnterShared()
239  {
240  // lock
241  CStdCSec::Enter();
242  // add share
243  lShareCnt++;
244  // unlock
245  CStdCSec::Leave();
246  }
247 
248  void LeaveShared()
249  {
250  // lock
251  CStdCSec::Enter();
252  // remove share
253  if (!--lShareCnt)
254  {
255  // do callback
256  if (pCallbClass)
257  pCallbClass->OnShareFree(this);
258  // set event
260  }
261  // unlock
262  CStdCSec::Leave();
263  }
264 };
265 
267 {
268 public:
269  CStdShareLock(CStdCSecEx *pSec) : sec(pSec)
270  { sec->EnterShared(); }
272  { Clear(); }
273 
274 protected:
276 
277 public:
278  void Clear()
279  { if (sec) sec->LeaveShared(); sec = nullptr; }
280 };
281 
282 /* Debug helper class: Set current thread in Set(); assert that it's still the same thread in Check(); */
284 {
285 #if defined(_DEBUG) && defined(_WIN32)
286  DWORD idThread;
287 public:
288  StdThreadCheck() : idThread(0) {}
289 
290  inline void Set() { idThread = ::GetCurrentThreadId(); }
291  inline void Check() { assert(idThread == ::GetCurrentThreadId()); }
292 #else
293 public:
294  inline void Set() {}
295  inline void Check() { }
296 #endif
297 };
298 
299 #endif // INC_StdSync
uint32_t DWORD
#define INFINITE
Definition: StdSync.h:58
virtual void OnShareFree(class CStdCSecEx *pCSec)=0
virtual ~CStdCSecExCallback()=default
CStdEvent ShareFreeEvent
Definition: StdSync.h:205
CStdCSecExCallback * pCallbClass
Definition: StdSync.h:207
CStdCSecEx()
Definition: StdSync.h:193
~CStdCSecEx() override=default
CStdCSecEx(CStdCSecExCallback *pCallb)
Definition: StdSync.h:196
void Enter() override
Definition: StdSync.h:212
void Leave() override
Definition: StdSync.h:230
void EnterShared()
Definition: StdSync.h:238
void LeaveShared()
Definition: StdSync.h:248
long lShareCnt
Definition: StdSync.h:203
virtual void Leave()
Definition: StdSync.h:151
virtual ~CStdCSec()
Definition: StdSync.h:149
virtual void Enter()
Definition: StdSync.h:150
CStdCSec()
Definition: StdSync.h:148
~CStdEvent()
Definition: StdSync.h:157
void Set()
Definition: StdSync.h:158
void Pulse()
Definition: StdSync.h:159
bool WaitFor(int)
Definition: StdSync.h:161
void Reset()
Definition: StdSync.h:160
CStdEvent(bool)
Definition: StdSync.h:156
CStdCSec * sec
Definition: StdSync.h:175
CStdLock(CStdCSec *pSec)
Definition: StdSync.h:169
void Clear()
Definition: StdSync.h:178
~CStdLock()
Definition: StdSync.h:171
~CStdShareLock()
Definition: StdSync.h:271
CStdShareLock(CStdCSecEx *pSec)
Definition: StdSync.h:269
CStdCSecEx * sec
Definition: StdSync.h:275
void Clear()
Definition: StdSync.h:278
void Check()
Definition: StdSync.h:295
void Set()
Definition: StdSync.h:294