MongoDB  2.7.0
rwlock.h
1 // @file rwlock.h generic reader-writer lock (cross platform support)
2 
3 /*
4  * Copyright (C) 2010 10gen Inc.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License, version 3,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU Affero General Public License for more details.
14  *
15  * You should have received a copy of the GNU Affero General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * As a special exception, the copyright holders give permission to link the
19  * code of portions of this program with the OpenSSL library under certain
20  * conditions as described in each individual source file and distribute
21  * linked combinations including the program with the OpenSSL library. You
22  * must comply with the GNU Affero General Public License in all respects
23  * for all of the code used other than as permitted herein. If you modify
24  * file(s) with this exception, you may extend this exception to your
25  * version of the file(s), but you are not obligated to do so. If you do not
26  * wish to do so, delete this exception statement from your version. If you
27  * delete this exception statement from all source files in the program,
28  * then also delete it in the license file.
29  */
30 
31 #pragma once
32 
33 #include "mongo/util/concurrency/mutex.h"
34 #include "mongo/util/concurrency/rwlockimpl.h"
35 #include "mongo/util/concurrency/simplerwlock.h"
36 #include "mongo/util/debug_util.h"
37 #include "mongo/util/log.h"
38 #include "mongo/util/time_support.h"
39 
40 #if defined(_DEBUG)
41 #include "mongo/util/concurrency/mutexdebugger.h"
42 #endif
43 
44 
45 
46 namespace mongo {
47 
48  class RWLock : public RWLockBase {
49  enum { NilState, UpgradableState, Exclusive } x; // only bother to set when doing upgradable related things
50  public:
51  const char * const _name;
52  RWLock(const char *name) : _name(name) {
53  x = NilState;
54  }
55  void lock() {
56  RWLockBase::lock();
57 #if defined(_DEBUG)
58  mutexDebugger.entering(_name);
59 #endif
60  }
61  void unlock() {
62 #if defined(_DEBUG)
63  mutexDebugger.leaving(_name);
64 #endif
65  RWLockBase::unlock();
66  }
67 
68  void lock_shared() { RWLockBase::lock_shared(); }
69  void unlock_shared() { RWLockBase::unlock_shared(); }
70  private:
71  void lockAsUpgradable() { RWLockBase::lockAsUpgradable(); }
72  void unlockFromUpgradable() { // upgradable -> unlocked
73  RWLockBase::unlockFromUpgradable();
74  }
75  public:
76  void upgrade() { // upgradable -> exclusive lock
77  verify( x == UpgradableState );
78  RWLockBase::upgrade();
79  x = Exclusive;
80  }
81 
82  bool lock_shared_try( int millis ) { return RWLockBase::lock_shared_try(millis); }
83 
84  bool lock_try( int millis = 0 ) {
85  if( RWLockBase::lock_try(millis) ) {
86 #if defined(_DEBUG)
87  mutexDebugger.entering(_name);
88 #endif
89  return true;
90  }
91  return false;
92  }
93 
98  class Upgradable : boost::noncopyable {
99  RWLock& _r;
100  public:
101  Upgradable(RWLock& r) : _r(r) {
102  r.lockAsUpgradable();
103  verify( _r.x == NilState );
104  _r.x = RWLock::UpgradableState;
105  }
106  ~Upgradable() {
107  if( _r.x == RWLock::UpgradableState ) {
108  _r.x = NilState;
109  _r.unlockFromUpgradable();
110  }
111  else {
112  //TEMP verify( _r.x == Exclusive ); // has been upgraded
113  _r.x = NilState;
114  _r.unlock();
115  }
116  }
117  };
118  };
119 
121  class rwlock_try_write : boost::noncopyable {
122  public:
123  struct exception { };
124  rwlock_try_write(RWLock& l, int millis = 0) : _l(l) {
125  if( !l.lock_try(millis) )
126  throw exception();
127  }
128  ~rwlock_try_write() { _l.unlock(); }
129  private:
130  RWLock& _l;
131  };
132 
133  class rwlock_shared : boost::noncopyable {
134  public:
135  rwlock_shared(RWLock& rwlock) : _r(rwlock) {_r.lock_shared(); }
136  ~rwlock_shared() { _r.unlock_shared(); }
137  private:
138  RWLock& _r;
139  };
140 
141  /* scoped lock for RWLock */
142  class rwlock : boost::noncopyable {
143  public:
148  rwlock( const RWLock& lock , bool write, /* bool alreadyHaveLock = false , */int lowPriorityWaitMS = 0 )
149  : _lock( (RWLock&)lock ) , _write( write ) {
150  {
151  if ( _write ) {
152  _lock.lock();
153  }
154  else {
155  _lock.lock_shared();
156  }
157  }
158  }
159  ~rwlock() {
160  if ( _write )
161  _lock.unlock();
162  else
163  _lock.unlock_shared();
164  }
165  private:
166  RWLock& _lock;
167  const bool _write;
168  };
169 
170  // ----------------------------------------------------------------------------------------
171 
173  class RWLockRecursive : protected RWLockBase {
174  protected:
175  ThreadLocalValue<int> _state;
176  void lock(); // not implemented - Lock() should be used; didn't overload this name to avoid mistakes
177  virtual void Lock() { RWLockBase::lock(); }
178  public:
179  virtual ~RWLockRecursive() { }
180  const char * const _name;
181  RWLockRecursive(const char *name) : _name(name) { }
182 
183  void assertAtLeastReadLocked() {
184  verify( _state.get() != 0 );
185  }
186  void assertExclusivelyLocked() {
187  verify( _state.get() < 0 );
188  }
189 
190  class Exclusive : boost::noncopyable {
191  RWLockRecursive& _r;
192  public:
193  Exclusive(RWLockRecursive& r) : _r(r) {
194  int s = _r._state.get();
195  dassert( s <= 0 );
196  if( s == 0 )
197  _r.Lock();
198  _r._state.set(s-1);
199  }
200  ~Exclusive() {
201  int s = _r._state.get();
202  DEV wassert( s < 0 ); // wassert: don't throw from destructors
203  ++s;
204  _r._state.set(s);
205  if ( s == 0 )
206  _r.unlock();
207  }
208  };
209 
210  class Shared : boost::noncopyable {
211  RWLockRecursive& _r;
212  bool _alreadyLockedExclusiveByUs;
213  public:
214  Shared(RWLockRecursive& r) : _r(r) {
215  int s = _r._state.get();
216  _alreadyLockedExclusiveByUs = s < 0;
217  if( !_alreadyLockedExclusiveByUs ) {
218  dassert( s >= 0 ); // -1 would mean exclusive
219  if( s == 0 )
220  _r.lock_shared();
221  _r._state.set(s+1);
222  }
223  }
224  ~Shared() {
225  if( _alreadyLockedExclusiveByUs ) {
226  DEV wassert( _r._state.get() < 0 );
227  }
228  else {
229  int s = _r._state.get() - 1;
230  DEV wassert( s >= 0 );
231  _r._state.set(s);
232  if( s == 0 )
233  _r.unlock_shared();
234  }
235  }
236  };
237  };
238 
240  virtual void Lock() {
241  bool got = false;
242  for ( int i=0; i<lowPriorityWaitMS; i++ ) {
243  if ( lock_try(0) ) {
244  got = true;
245  break;
246  }
247  int sleep = 1;
248  if ( i > ( lowPriorityWaitMS / 20 ) )
249  sleep = 10;
250  sleepmillis(sleep);
251  i += ( sleep - 1 );
252  }
253  if ( ! got ) {
254  log() << "couldn't lazily get rwlock";
255  RWLockBase::lock();
256  }
257  }
258 
259  public:
260  const int lowPriorityWaitMS;
261  RWLockRecursiveNongreedy(const char *nm, int lpwaitms) : RWLockRecursive(nm), lowPriorityWaitMS(lpwaitms) { }
262  const char * implType() const { return RWLockRecursive::implType(); }
263 
264  //just for testing:
265  bool __lock_try( int millis ) { return RWLockRecursive::lock_try(millis); }
266  };
267 
268 }
Definition: rwlock.h:48
acquire upgradable state.
Definition: rwlock.h:98
Definition: rwlock.h:210
rwlock(const RWLock &lock, bool write, int lowPriorityWaitMS=0)
Definition: rwlock.h:148
Definition: rwlock.h:239
throws on failure to acquire in the specified time period.
Definition: rwlock.h:121
Definition: rwlock.h:190
LogstreamBuilder log()
Returns a LogstreamBuilder for logging a message with LogSeverity::Log().
Definition: log.h:69
Definition: rwlock.h:123
Definition: rwlock.h:133
recursive on shared locks is ok for this implementation
Definition: rwlock.h:173
Definition: rwlock.h:142
Definition: rwlockimpl.h:104