My Project
BlockReadWriteLock.h
1 #pragma once
2 #include <thread>
3 #include <mutex>
4 #include <condition_variable>
5 
6 namespace ParaEngine
7 {
19  {
20  public:
22 
23  public:
25  void BeginRead();
26  void EndRead();
27 
28  /* recursive calls with same id is supported. internally it uses a counter.
29  @param nWriterThreadId: usually thread id. if 0, it will be a hash value of the current thread id. */
30  void BeginWrite(size_t nWriterThreadId = 0);
31  void EndWrite();
32 
33  size_t GetCurrentThreadId();
37  bool HasWaitingWriters();
38  /* if one reader has priority over other readers, then it can call this function to decide whether to continue or not. */
39  bool HasWaitingWritersAndSingleReader();
40 
41  /* check if a writer lock is being held by any of the thread. */
42  bool HasWriterLock();
43 
46 
47  // we will allow recursive write lock on the same thread.
48  uint32 GetWritelockRecursiveDepth() const;
49  protected:
50  bool StartReadFromNewReader();
51  bool StartReadFromWaitingReader();
52  bool StartRead();
53  bool AllowReader();
54  protected:
55  bool StartWriteFromNewWriter(size_t nWriterId);
56  bool StartWrite(size_t nWriterId);
57  bool StartWriteFromWaitingWriter(size_t nWriterId);
58 
59  protected:
60  // current number of readers, must be 0 when writers can take control.
61  uint32 m_nActiveReaders;
62  // not useful, only used for debugging purposes
63  uint32 m_nWaitingReaders;
64  // number of writers waiting. this variable in introduced, because we wants to favor writer over reader.
65  uint32 m_nWaitingWriters;
66  // the current writer id: 0 means no writer. main thread has id==1, the light thread id is 2, IO thread id is 3.
67  size_t m_activeWriterId;
68 
69  // we will allow recursive write lock on the same thread.
70  uint32 m_writelock_recursive_depth;
71 
72  // only used with locks that is downgraded from write lock.
73  uint32 m_readlock_recursive_depth;
74  std::condition_variable m_reader_signal;
75  std::condition_variable m_writer_signal;
76 
77  // represent this object
78  std::mutex m_mutex;
79  };
80 
81  /* this is the reverse of Scoped_Writelock. It allows a writer thread to temporary unlock. */
82  template <typename Mutex = BlockReadWriteLock>
84  {
85  public:
86  bool IsValidLock() const { return m_bIsValidLock; }
88  : mutex_(m), m_bIsValidLock(false), m_nWriterLockRecursiveDepth(0)
89  {
90  if (mutex_.HasWriterLock())
91  {
92  if (mutex_.IsCurrentThreadHasWriterLock())
93  {
94  m_bIsValidLock = true;
95  m_nWriterLockRecursiveDepth = mutex_.GetWritelockRecursiveDepth();
96  for (int i = 0; i < m_nWriterLockRecursiveDepth; ++i)
97  {
98  mutex_.EndWrite();
99  }
100  }
101  }
102  }
104  {
105  if (IsValidLock())
106  {
107  for (int i = 0; i < m_nWriterLockRecursiveDepth; ++i)
108  {
109  mutex_.BeginWrite();
110  }
111  }
112  }
113  protected:
114  // The underlying mutex.
115  Mutex& mutex_;
116  bool m_bIsValidLock;
117  int32 m_nWriterLockRecursiveDepth;
118  };
119 
120 
122  template <typename Mutex = BlockReadWriteLock>
124  {
125  public:
126  // Constructor acquires the lock.
128  : mutex_(m)
129  {
130  mutex_.BeginRead();
131  locked_ = true;
132  }
133 
134  // Destructor releases the lock.
135  ~Scoped_ReadLock()
136  {
137  if (locked_)
138  mutex_.EndRead();
139  }
140 
141  // Explicitly acquire the lock.
142  void lock()
143  {
144  if (!locked_)
145  {
146  mutex_.BeginRead();
147  locked_ = true;
148  }
149  }
150 
151  // Explicitly release the lock.
152  void unlock()
153  {
154  if (locked_)
155  {
156  mutex_.EndRead();
157  locked_ = false;
158  }
159  }
160 
161  // Test whether the lock is held.
162  bool locked() const
163  {
164  return locked_;
165  }
166 
167  // Get the underlying mutex.
168  Mutex& mutex()
169  {
170  return mutex_;
171  }
172 
173  private:
174  // The underlying mutex.
175  Mutex& mutex_;
176 
177  // Whether the mutex is currently locked or unlocked.
178  bool locked_;
179  };
180 
182  template <typename Mutex = BlockReadWriteLock>
184  {
185  public:
186  // Constructor acquires the lock.
188  : mutex_(m)
189  {
190  mutex_.BeginWrite();
191  locked_ = true;
192  }
193 
194  // Destructor releases the lock.
196  {
197  if (locked_)
198  mutex_.EndWrite();
199  }
200 
201  // Explicitly acquire the lock.
202  void lock()
203  {
204  if (!locked_)
205  {
206  mutex_.BeginWrite();
207  locked_ = true;
208  }
209  }
210 
211  // Explicitly release the lock.
212  void unlock()
213  {
214  if (locked_)
215  {
216  mutex_.EndWrite();
217  locked_ = false;
218  }
219  }
220 
221  // Test whether the lock is held.
222  bool locked() const
223  {
224  return locked_;
225  }
226 
227  // Get the underlying mutex.
228  Mutex& mutex()
229  {
230  return mutex_;
231  }
232 
233  private:
234  // The underlying mutex.
235  Mutex& mutex_;
236 
237  // Whether the mutex is currently locked or unlocked.
238  bool locked_;
239  };
240 }
void BeginRead()
recursive calls is possible and will be regarded as new readers at minimum cost.
Definition: BlockReadWriteLock.cpp:20
simple scoped read lock function
Definition: BlockReadWriteLock.h:123
different physics engine has different winding order.
Definition: EventBinding.h:32
Definition: BlockReadWriteLock.h:83
bool HasWaitingWriters()
this function just check writer count WITHOUT using a lock.
Definition: BlockReadWriteLock.cpp:181
cross platform mutex
Definition: Mutex.hpp:88
multiple shared readers and multiple exclusive writers it favors writers over readers.
Definition: BlockReadWriteLock.h:18
cross platform mutex
Definition: mutex.h:95
simple scoped write lock function
Definition: BlockReadWriteLock.h:183
bool IsCurrentThreadHasWriterLock()
whether the current thread is holding the writer lock.
Definition: BlockReadWriteLock.cpp:196