MongoDB  2.7.0
qlock.h
1 // @file qlock.h
2 
31 #pragma once
32 
33 #include <boost/noncopyable.hpp>
34 #include <boost/thread/mutex.hpp>
35 #include <boost/thread/condition.hpp>
36 #include "mongo/util/assert_util.h"
37 #include "mongo/util/time_support.h"
38 
39 namespace mongo {
40 
76  class QLock : boost::noncopyable {
77  struct Z {
78  Z() : n(0) { }
79  boost::condition c;
80  int n;
81  };
82  boost::mutex m;
83  Z r,w,R,W,U,X;
84  int numPendingGlobalWrites; // >0 if someone wants to acquire a write lock
85  long long generationX;
86  long long generationXExit;
87  void _lock_W();
88  void _unlock_R();
89  bool _areQueueJumpingGlobalWritesPending() const {
90  return numPendingGlobalWrites > 0;
91  }
92 
93  bool W_legal() const { return r.n + w.n + R.n + W.n + X.n == 0; }
94  bool R_legal_ignore_greed() const { return w.n + W.n + X.n == 0; }
95  bool r_legal_ignore_greed() const { return W.n + X.n == 0; }
96  bool w_legal_ignore_greed() const { return R.n + W.n + X.n == 0; }
97 
98  bool R_legal() const {
99  return !_areQueueJumpingGlobalWritesPending() && R_legal_ignore_greed();
100  }
101 
102  bool w_legal() const {
103  return !_areQueueJumpingGlobalWritesPending() && w_legal_ignore_greed();
104  }
105 
106  bool r_legal() const {
107  return !_areQueueJumpingGlobalWritesPending() && r_legal_ignore_greed();
108  }
109 
110  bool X_legal() const { return w.n + r.n + R.n + W.n == 0; }
111 
112  void notifyWeUnlocked(char me);
113  static bool i_block(char me, char them);
114  public:
115  QLock() :
116  numPendingGlobalWrites(0),
117  generationX(0),
118  generationXExit(0) {
119  }
120 
121  void lock_r();
122  void lock_w();
123  void lock_R();
124  bool lock_R_try(int millis);
125  void lock_W();
126  bool lock_W_try(int millis);
127  void unlock_r();
128  void unlock_w();
129  void unlock_R();
130  void unlock_W();
131  void W_to_R();
132  void R_to_W(); // caution see notes below
133  bool w_to_X();
134  void X_to_w();
135  };
136 
137  inline bool QLock::i_block(char me, char them) {
138  switch( me ) {
139  case 'W' : return true;
140  case 'R' : return them == 'W' || them == 'w' || them == 'X';
141  case 'w' : return them == 'W' || them == 'R' || them == 'X';
142  case 'r' : return them == 'W' || them == 'X';
143  case 'X' : return true;
144  default : fassertFailed(16200);
145  }
146  return false;
147  }
148 
149  inline void QLock::notifyWeUnlocked(char me) {
150  fassert(16201, W.n == 0);
151  if ( me == 'X' ) {
152  X.c.notify_all();
153  }
154  if( U.n ) {
155  // U is highest priority
156  if( (r.n + w.n + W.n + X.n == 0) && (R.n == 1) ) {
157  U.c.notify_one();
158  return;
159  }
160  }
161  if ( X_legal() && i_block(me, 'X') ) {
162  X.c.notify_one();
163  }
164  if ( W_legal() && i_block(me, 'W') ) {
165  W.c.notify_one();
166  if( _areQueueJumpingGlobalWritesPending() )
167  return;
168  }
169  if ( R_legal_ignore_greed() && i_block(me, 'R') ) {
170  R.c.notify_all();
171  }
172  if ( w_legal_ignore_greed() && i_block(me, 'w') ) {
173  w.c.notify_all();
174  }
175  if ( r_legal_ignore_greed() && i_block(me, 'r') ) {
176  r.c.notify_all();
177  }
178  }
179 
180  // "i will be reading. i promise to coordinate my activities with w's as i go with more
181  // granular locks."
182  inline void QLock::lock_r() {
183  boost::mutex::scoped_lock lk(m);
184  while( !r_legal() ) {
185  r.c.wait(m);
186  }
187  r.n++;
188  }
189 
190  // "i will be writing. i promise to coordinate my activities with w's and r's as i go with more
191  // granular locks."
192  inline void QLock::lock_w() {
193  boost::mutex::scoped_lock lk(m);
194  while( !w_legal() ) {
195  w.c.wait(m);
196  }
197  w.n++;
198  }
199 
200  // "i will be reading. i will coordinate with no one. you better stop them if they
201  // are writing."
202  inline void QLock::lock_R() {
203  boost::mutex::scoped_lock lk(m);
204  while( ! R_legal() ) {
205  R.c.wait(m);
206  }
207  R.n++;
208  }
209 
210  inline bool QLock::lock_R_try(int millis) {
211  unsigned long long end = curTimeMillis64() + millis;
212  boost::mutex::scoped_lock lk(m);
213  while( !R_legal() && curTimeMillis64() < end ) {
214  R.c.timed_wait(m, boost::posix_time::milliseconds(millis));
215  }
216  if ( R_legal() ) {
217  R.n++;
218  return true;
219  }
220  return false;
221  }
222 
223  inline bool QLock::lock_W_try(int millis) {
224  unsigned long long end = curTimeMillis64() + millis;
225  boost::mutex::scoped_lock lk(m);
226 
227  ++numPendingGlobalWrites;
228  while (!W_legal() && curTimeMillis64() < end) {
229  W.c.timed_wait(m, boost::posix_time::milliseconds(millis));
230  }
231  --numPendingGlobalWrites;
232 
233  if (W_legal()) {
234  W.n++;
235  fassert( 16202, W.n == 1 );
236  return true;
237  }
238 
239  return false;
240  }
241 
242 
243  // downgrade from W state to R state
244  inline void QLock::W_to_R() {
245  boost::mutex::scoped_lock lk(m);
246  fassert(16203, W.n == 1);
247  fassert(16204, R.n == 0);
248  fassert(16205, U.n == 0);
249  W.n = 0;
250  R.n = 1;
251  notifyWeUnlocked('W');
252  }
253 
254  // upgrade from R to W state.
255  //
256  // This transition takes precedence over all pending requests by threads to enter
257  // any state other than '\0'.
258  //
259  // there is no "upgradable" state so this is NOT a classic upgrade -
260  // if two threads try to do this you will deadlock.
261  //
262  // NOTE: ONLY CALL THIS FUNCTION ON A THREAD THAT GOT TO R BY CALLING W_to_R(), OR
263  // YOU MAY DEADLOCK WITH THREADS LEAVING THE X STATE.
264  inline void QLock::R_to_W() {
265  boost::mutex::scoped_lock lk(m);
266  fassert(16206, R.n > 0);
267  fassert(16207, W.n == 0);
268  fassert(16208, U.n == 0);
269 
270  U.n = 1;
271 
272  ++numPendingGlobalWrites;
273 
274  while( W.n + R.n + w.n + r.n > 1 ) {
275  U.c.wait(m);
276  }
277  --numPendingGlobalWrites;
278 
279  fassert(16209, R.n == 1);
280  fassert(16210, W.n == 0);
281  fassert(16211, U.n == 1);
282 
283  R.n = 0;
284  W.n = 1;
285  U.n = 0;
286  }
287 
288  inline bool QLock::w_to_X() {
289  boost::mutex::scoped_lock lk(m);
290 
291  fassert( 16212, w.n > 0 );
292 
293  ++X.n;
294  --w.n;
295 
296  long long myGeneration = generationX;
297 
298  while ( !X_legal() && (myGeneration == generationX) )
299  X.c.wait(m);
300 
301  if ( myGeneration == generationX ) {
302  fassert( 16214, X_legal() );
303  fassert( 16215, w.n == 0 );
304  ++generationX;
305  notifyWeUnlocked('w');
306  return true;
307  }
308 
309  while ( myGeneration == generationXExit )
310  X.c.wait(m);
311 
312  fassert( 16216, R.n == 0 );
313  fassert( 16217, w.n > 0 );
314  return false;
315  }
316 
317  inline void QLock::X_to_w() {
318  boost::mutex::scoped_lock lk(m);
319 
320  fassert( 16219, W.n == 0 );
321  fassert( 16220, R.n == 0 );
322  fassert( 16221, w.n == 0 );
323  fassert( 16222, X.n > 0 );
324 
325  w.n = X.n;
326  X.n = 0;
327  ++generationXExit;
328  notifyWeUnlocked('X');
329  }
330 
331  // "i will be writing. i will coordinate with no one. you better stop them all"
332  inline void QLock::_lock_W() {
333  ++numPendingGlobalWrites;
334  while( !W_legal() ) {
335  W.c.wait(m);
336  }
337  --numPendingGlobalWrites;
338  W.n++;
339  }
340  inline void QLock::lock_W() {
341  boost::mutex::scoped_lock lk(m);
342  _lock_W();
343  }
344 
345  inline void QLock::unlock_r() {
346  boost::mutex::scoped_lock lk(m);
347  fassert(16137, r.n > 0);
348  --r.n;
349  notifyWeUnlocked('r');
350  }
351  inline void QLock::unlock_w() {
352  boost::mutex::scoped_lock lk(m);
353  fassert(16138, w.n > 0);
354  --w.n;
355  notifyWeUnlocked('w');
356  }
357 
358  inline void QLock::unlock_R() {
359  boost::mutex::scoped_lock lk(m);
360  _unlock_R();
361  }
362 
363  inline void QLock::_unlock_R() {
364  fassert(16139, R.n > 0);
365  --R.n;
366  notifyWeUnlocked('R');
367  }
368 
369  inline void QLock::unlock_W() {
370  boost::mutex::scoped_lock lk(m);
371  fassert(16140, W.n == 1);
372  --W.n;
373  notifyWeUnlocked('W');
374  }
375 
376 }
MONGO_CLIENT_API void fassert(int msgid, bool testOK)
aborts on condition failure
Definition: assert_util.h:195
"Quad Lock" we want to be able to do semi-granular locking now, and read/write style locking for that...
Definition: qlock.h:76