00001 // @file mongomutex.h 00002 00003 /* 00004 * Copyright (C) 2010 10gen Inc. 00005 * 00006 * This program is free software: you can redistribute it and/or modify 00007 * it under the terms of the GNU Affero General Public License, version 3, 00008 * as published by the Free Software Foundation. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU Affero General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Affero General Public License 00016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00017 */ 00018 00019 #pragma once 00020 00021 // note: include concurrency.h, not this. 00022 00023 namespace mongo { 00024 00033 class MongoMutex { 00034 public: 00035 MongoMutex(const char * name); 00036 00042 int getState() const { return _state.get(); } 00043 00044 bool atLeastReadLocked() const { return _state.get() != 0; } 00045 void assertAtLeastReadLocked() const { assert(atLeastReadLocked()); } 00046 bool isWriteLocked() const { return getState() > 0; } 00047 void assertWriteLocked() const { 00048 assert( getState() > 0 ); 00049 DEV assert( !_releasedEarly.get() ); 00050 } 00051 00052 // write lock. use the writelock scoped lock class, not this directly. 00053 void lock() { 00054 if ( _writeLockedAlready() ) 00055 return; 00056 00057 _state.set(1); 00058 00059 Client *c = curopWaitingForLock( 1 ); // stats 00060 _m.lock(); 00061 curopGotLock(c); 00062 00063 _minfo.entered(); 00064 00065 MongoFile::markAllWritable(); // for _DEBUG validation -- a no op for release build 00066 00067 _acquiredWriteLock(); 00068 } 00069 00070 // try write lock 00071 bool lock_try( int millis ) { 00072 if ( _writeLockedAlready() ) 00073 return true; 00074 00075 Client *c = curopWaitingForLock( 1 ); 00076 bool got = _m.lock_try( millis ); 00077 00078 if ( got ) { 00079 curopGotLock(c); 00080 _minfo.entered(); 00081 _state.set(1); 00082 MongoFile::markAllWritable(); // for _DEBUG validation -- a no op for release build 00083 _acquiredWriteLock(); 00084 } 00085 00086 return got; 00087 } 00088 00089 // un write lock 00090 void unlock() { 00091 int s = _state.get(); 00092 if( s > 1 ) { 00093 _state.set(s-1); // recursive lock case 00094 return; 00095 } 00096 if( s != 1 ) { 00097 if( _releasedEarly.get() ) { 00098 _releasedEarly.set(false); 00099 return; 00100 } 00101 massert( 12599, "internal error: attempt to unlock when wasn't in a write lock", false); 00102 } 00103 _releasingWriteLock(); 00104 MongoFile::unmarkAllWritable(); // _DEBUG validation 00105 _state.set(0); 00106 _minfo.leaving(); 00107 _m.unlock(); 00108 } 00109 00110 /* unlock (write lock), and when unlock() is called later, 00111 be smart then and don't unlock it again. 00112 */ 00113 void releaseEarly() { 00114 assert( getState() == 1 ); // must not be recursive 00115 assert( !_releasedEarly.get() ); 00116 _releasedEarly.set(true); 00117 unlock(); 00118 } 00119 00120 // read lock. don't call directly, use readlock. 00121 void lock_shared() { 00122 int s = _state.get(); 00123 if( s ) { 00124 if( s > 0 ) { 00125 // already in write lock - just be recursive and stay write locked 00126 _state.set(s+1); 00127 } 00128 else { 00129 // already in read lock - recurse 00130 _state.set(s-1); 00131 } 00132 } 00133 else { 00134 _state.set(-1); 00135 Client *c = curopWaitingForLock( -1 ); 00136 _m.lock_shared(); 00137 curopGotLock(c); 00138 } 00139 } 00140 00141 // try read lock 00142 bool lock_shared_try( int millis ) { 00143 int s = _state.get(); 00144 if ( s ) { 00145 // we already have a lock, so no need to try 00146 lock_shared(); 00147 return true; 00148 } 00149 00150 /* [dm] should there be 00151 Client *c = curopWaitingForLock( 1 ); 00152 here? i think so. seems to be missing. 00153 */ 00154 bool got = _m.lock_shared_try( millis ); 00155 if ( got ) 00156 _state.set(-1); 00157 return got; 00158 } 00159 00160 void unlock_shared() { 00161 int s = _state.get(); 00162 if( s > 0 ) { 00163 assert( s > 1 ); /* we must have done a lock write first to have s > 1 */ 00164 _state.set(s-1); 00165 return; 00166 } 00167 if( s < -1 ) { 00168 _state.set(s+1); 00169 return; 00170 } 00171 assert( s == -1 ); 00172 _state.set(0); 00173 _m.unlock_shared(); 00174 } 00175 00176 MutexInfo& info() { return _minfo; } 00177 00178 private: 00179 void _acquiredWriteLock(); 00180 void _releasingWriteLock(); 00181 00182 /* @return true if was already write locked. increments recursive lock count. */ 00183 bool _writeLockedAlready(); 00184 00185 RWLock _m; 00186 00187 /* > 0 write lock with recurse count 00188 < 0 read lock 00189 */ 00190 ThreadLocalValue<int> _state; 00191 00192 MutexInfo _minfo; 00193 00194 public: 00195 // indicates we need to call dur::REMAPPRIVATEVIEW on the next write lock 00196 bool _remapPrivateViewRequested; 00197 00198 private: 00199 /* See the releaseEarly() method. 00200 we use a separate TLS value for releasedEarly - that is ok as 00201 our normal/common code path, we never even touch it */ 00202 ThreadLocalValue<bool> _releasedEarly; 00203 00204 /* this is for fsyncAndLock command. otherwise write lock's greediness will 00205 make us block on any attempted write lock the the fsync's lock. 00206 */ 00207 //volatile bool _blockWrites; 00208 }; 00209 00210 extern MongoMutex &dbMutex; 00211 00212 namespace dur { 00213 void REMAPPRIVATEVIEW(); 00214 void releasingWriteLock(); // because it's hard to include dur.h here 00215 } 00216 00217 inline void MongoMutex::_releasingWriteLock() { 00218 dur::releasingWriteLock(); 00219 } 00220 00221 inline void MongoMutex::_acquiredWriteLock() { 00222 if( _remapPrivateViewRequested ) { 00223 dur::REMAPPRIVATEVIEW(); 00224 dassert( !_remapPrivateViewRequested ); 00225 } 00226 } 00227 00228 /* @return true if was already write locked. increments recursive lock count. */ 00229 inline bool MongoMutex::_writeLockedAlready() { 00230 int s = _state.get(); 00231 if( s > 0 ) { 00232 _state.set(s+1); 00233 return true; 00234 } 00235 massert( 10293 , string("internal error: locks are not upgradeable: ") + sayClientState() , s == 0 ); 00236 return false; 00237 } 00238 00239 }