1 2 /** 3 * Copyright (C) 2018-present MongoDB, Inc. 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the Server Side Public License, version 1, 7 * as published by MongoDB, Inc. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * Server Side Public License for more details. 13 * 14 * You should have received a copy of the Server Side Public License 15 * along with this program. If not, see 16 * <http://www.mongodb.com/licensing/server-side-public-license>. 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 Server Side Public License in all respects for 23 * all of the code used other than as permitted herein. If you modify file(s) 24 * with this exception, you may extend this exception to your version of the 25 * file(s), but you are not obligated to do so. If you do not wish to do so, 26 * delete this exception statement from your version. If you delete this 27 * exception statement from all source files in the program, then also delete 28 * it in the license file. 29 */ 30 31 #pragma once 32 33 #include <climits> // For UINT_MAX 34 #include <vector> 35 36 #include "mongo/db/concurrency/lock_manager.h" 37 #include "mongo/db/concurrency/lock_stats.h" 38 #include "mongo/stdx/thread.h" 39 40 namespace mongo { 41 42 /** 43 * Interface for acquiring locks. One of those objects will have to be instantiated for each 44 * request (transaction). 45 * 46 * Lock/unlock methods must always be called from a single thread. 47 */ 48 class Locker { 49 MONGO_DISALLOW_COPYING(Locker); 50 51 public: ~Locker()52 virtual ~Locker() {} 53 54 /** 55 * Returns true if this is an instance of LockerNoop. Because LockerNoop doesn't implement many 56 * methods, some users may need to check this first to find out what is safe to call. LockerNoop 57 * is only used in unittests and for a brief period at startup, so you can assume you hold the 58 * equivalent of a MODE_X lock when using it. 59 * 60 * TODO get rid of this once we kill LockerNoop. 61 */ isNoop()62 virtual bool isNoop() const { 63 return false; 64 } 65 66 /** 67 * Require global lock attempts to obtain tickets from 'reading' (for MODE_S and MODE_IS), 68 * and from 'writing' (for MODE_IX), which must have static lifetimes. There is no throttling 69 * for MODE_X, as there can only ever be a single locker using this mode. The throttling is 70 * intended to defend against arge drops in throughput under high load due to too much 71 * concurrency. 72 */ 73 static void setGlobalThrottling(class TicketHolder* reading, class TicketHolder* writing); 74 75 /** 76 * State for reporting the number of active and queued reader and writer clients. 77 */ 78 enum ClientState { kInactive, kActiveReader, kActiveWriter, kQueuedReader, kQueuedWriter }; 79 80 /** 81 * Return whether client is holding any locks (active), or is queued on any locks or waiting 82 * for a ticket (throttled). 83 */ 84 virtual ClientState getClientState() const = 0; 85 86 virtual LockerId getId() const = 0; 87 88 /** 89 * Get a platform-specific thread identifier of the thread which owns the this locker for 90 * tracing purposes. 91 */ 92 virtual stdx::thread::id getThreadId() const = 0; 93 94 /** 95 * This should be the first method invoked for a particular Locker object. It acquires the 96 * Global lock in the specified mode and effectively indicates the mode of the operation. 97 * This is what the lock modes on the global lock mean: 98 * 99 * IX - Regular write operation 100 * IS - Regular read operation 101 * S - Stops all *write* activity. Used for administrative operations (repl, etc). 102 * X - Stops all activity. Used for administrative operations (repl state changes, 103 * shutdown, etc). 104 * 105 * This method can be called recursively, but each call to lockGlobal must be accompanied 106 * by a call to unlockGlobal. 107 * 108 * @param mode Mode in which the global lock should be acquired. Also indicates the intent 109 * of the operation. 110 * 111 * @return LOCK_OK, if the global lock (and the flush lock, for the MMAP V1 engine) were 112 * acquired within the specified time bound. Otherwise, the respective failure 113 * code and neither lock will be acquired. 114 */ 115 virtual LockResult lockGlobal(LockMode mode) = 0; 116 117 /** 118 * Requests the global lock to be acquired in the specified mode. 119 * 120 * See the comments for lockBegin/Complete for more information on the semantics. 121 * The timeout indicates how long to wait for the lock to be acquired. The lockGlobalBegin 122 * method has a timeout for use with the TicketHolder, if there is one. 123 */ 124 virtual LockResult lockGlobalBegin(LockMode mode, Milliseconds timeout) = 0; 125 virtual LockResult lockGlobalComplete(Milliseconds timeout) = 0; 126 127 /** 128 * This method is used only in the MMAP V1 storage engine, otherwise it is a no-op. See the 129 * comments in the implementation for more details on how MMAP V1 journaling works. 130 */ 131 virtual void lockMMAPV1Flush() = 0; 132 133 /** 134 * Decrements the reference count on the global lock. If the reference count on the 135 * global lock hits zero, the transaction is over, and unlockGlobal unlocks all other locks 136 * except for RESOURCE_MUTEX locks. 137 * 138 * @return true if this is the last endTransaction call (i.e., the global lock was 139 * released); false if there are still references on the global lock. This value 140 * should not be relied on and is only used for assertion purposes. 141 * 142 * @return false if the global lock is still held. 143 */ 144 virtual bool unlockGlobal() = 0; 145 146 /** 147 * This is only necessary for the MMAP V1 engine and in particular, the fsyncLock command 148 * which needs to first acquire the global lock in X-mode for truncating the journal and 149 * then downgrade to S before it blocks. 150 * 151 * The downgrade is necessary in order to be nice and not block readers while under 152 * fsyncLock. 153 */ 154 virtual void downgradeGlobalXtoSForMMAPV1() = 0; 155 156 /** 157 * beginWriteUnitOfWork/endWriteUnitOfWork must only be called by WriteUnitOfWork. See 158 * comments there for the semantics of units of work. 159 */ 160 virtual void beginWriteUnitOfWork() = 0; 161 virtual void endWriteUnitOfWork() = 0; 162 163 virtual bool inAWriteUnitOfWork() const = 0; 164 165 /** 166 * Acquires lock on the specified resource in the specified mode and returns the outcome 167 * of the operation. See the details for LockResult for more information on what the 168 * different results mean. 169 * 170 * Each successful acquisition of a lock on a given resource increments the reference count 171 * of the lock. Therefore, each call, which returns LOCK_OK must be matched with a 172 * corresponding call to unlock. 173 * 174 * @param resId Id of the resource to be locked. 175 * @param mode Mode in which the resource should be locked. Lock upgrades are allowed. 176 * @param timeout How long to wait for the lock to be granted, before 177 * returning LOCK_TIMEOUT. This parameter defaults to an infinite timeout. 178 * If Milliseconds(0) is passed, the request will return immediately, if 179 * the request could not be granted right away. 180 * @param checkDeadlock Whether to enable deadlock detection for this acquisition. This 181 * parameter is put in place until we can handle deadlocks at all places, 182 * which acquire locks. 183 * 184 * @return All LockResults except for LOCK_WAITING, because it blocks. 185 */ 186 virtual LockResult lock(ResourceId resId, 187 LockMode mode, 188 Milliseconds timeout = Milliseconds::max(), 189 bool checkDeadlock = false) = 0; 190 191 /** 192 * Downgrades the specified resource's lock mode without changing the reference count. 193 */ 194 virtual void downgrade(ResourceId resId, LockMode newMode) = 0; 195 196 /** 197 * Releases a lock previously acquired through a lock call. It is an error to try to 198 * release lock which has not been previously acquired (invariant violation). 199 * 200 * @return true if the lock was actually released; false if only the reference count was 201 * decremented, but the lock is still held. 202 */ 203 virtual bool unlock(ResourceId resId) = 0; 204 205 /** 206 * Retrieves the mode in which a lock is held or checks whether the lock held for a 207 * particular resource covers the specified mode. 208 * 209 * For example isLockHeldForMode will return true for MODE_S, if MODE_X is already held, 210 * because MODE_X covers MODE_S. 211 */ 212 virtual LockMode getLockMode(ResourceId resId) const = 0; 213 virtual bool isLockHeldForMode(ResourceId resId, LockMode mode) const = 0; 214 215 // These are shortcut methods for the above calls. They however check that the entire 216 // hierarchy is properly locked and because of this they are very expensive to call. 217 // Do not use them in performance critical code paths. 218 virtual bool isDbLockedForMode(StringData dbName, LockMode mode) const = 0; 219 virtual bool isCollectionLockedForMode(StringData ns, LockMode mode) const = 0; 220 221 /** 222 * Returns the resource that this locker is waiting/blocked on (if any). If the locker is 223 * not waiting for a resource the returned value will be invalid (isValid() == false). 224 */ 225 virtual ResourceId getWaitingResource() const = 0; 226 227 /** 228 * Describes a single lock acquisition for reporting/serialization purposes. 229 */ 230 struct OneLock { 231 // What lock resource is held? 232 ResourceId resourceId; 233 234 // In what mode is it held? 235 LockMode mode; 236 237 // Reporting/serialization order is by resourceId, which is the canonical locking order 238 bool operator<(const OneLock& rhs) const { 239 return resourceId < rhs.resourceId; 240 } 241 }; 242 243 /** 244 * Returns information and locking statistics for this instance of the locker. Used to 245 * support the db.currentOp view. This structure is not thread-safe and ideally should 246 * be used only for obtaining the necessary information and then discarded instead of 247 * reused. 248 */ 249 struct LockerInfo { 250 // List of high-level locks held by this locker, sorted by ResourceId 251 std::vector<OneLock> locks; 252 253 // If isValid(), then what lock this particular locker is sleeping on 254 ResourceId waitingResource; 255 256 // Lock timing statistics 257 SingleThreadedLockStats stats; 258 }; 259 260 /** 261 * lockStatsBase is the snapshot of the lock stats taken at the point when the operation starts. 262 * The precise lock stats of a sub-operation would be the stats from the locker info minus the 263 * lockStatsBase. 264 */ 265 virtual void getLockerInfo( 266 LockerInfo* lockerInfo, 267 const boost::optional<SingleThreadedLockStats> lockStatsBase) const = 0; 268 269 /** 270 * LockSnapshot captures the state of all resources that are locked, what modes they're 271 * locked in, and how many times they've been locked in that mode. 272 */ 273 struct LockSnapshot { 274 // The global lock is handled differently from all other locks. 275 LockMode globalMode; 276 277 // The non-global non-flush locks held, sorted by granularity. That is, locks[i] is 278 // coarser or as coarse as locks[i + 1]. 279 std::vector<OneLock> locks; 280 }; 281 282 /** 283 * Retrieves all locks held by this transaction, other than RESOURCE_MUTEX locks, and what mode 284 * they're held in. 285 * Stores these locks in 'stateOut', destroying any previous state. Unlocks all locks 286 * held by this transaction. This functionality is used for yielding, which is 287 * voluntary/cooperative lock release and reacquisition in order to allow for interleaving 288 * of otherwise conflicting long-running operations. 289 * 290 * This functionality is also used for releasing locks on databases and collections 291 * when cursors are dormant and waiting for a getMore request. 292 * 293 * Returns true if locks are released. It is expected that restoreLockerImpl will be called 294 * in the future. 295 * 296 * Returns false if locks are not released. restoreLockState(...) does not need to be 297 * called in this case. 298 */ 299 virtual bool saveLockStateAndUnlock(LockSnapshot* stateOut) = 0; 300 301 /** 302 * Re-locks all locks whose state was stored in 'stateToRestore'. 303 */ 304 virtual void restoreLockState(const LockSnapshot& stateToRestore) = 0; 305 306 // 307 // These methods are legacy from LockerImpl and will eventually go away or be converted to 308 // calls into the Locker methods 309 // 310 311 virtual void dump() const = 0; 312 313 virtual bool isW() const = 0; 314 virtual bool isR() const = 0; 315 316 virtual bool isLocked() const = 0; 317 virtual bool isWriteLocked() const = 0; 318 virtual bool isReadLocked() const = 0; 319 virtual bool isGlobalLockedRecursively() = 0; 320 321 /** 322 * Pending means we are currently trying to get a lock (could be the parallel batch writer 323 * lock). 324 */ 325 virtual bool hasLockPending() const = 0; 326 327 /** 328 * If set to false, this opts out of conflicting with replication's use of the 329 * ParallelBatchWriterMode lock. Code that opts-out must be ok with seeing an inconsistent view 330 * of data because within a batch, secondaries apply operations in a different order than on the 331 * primary. User operations should *never* opt out. 332 */ setShouldConflictWithSecondaryBatchApplication(bool newValue)333 void setShouldConflictWithSecondaryBatchApplication(bool newValue) { 334 _shouldConflictWithSecondaryBatchApplication = newValue; 335 } shouldConflictWithSecondaryBatchApplication()336 bool shouldConflictWithSecondaryBatchApplication() const { 337 return _shouldConflictWithSecondaryBatchApplication; 338 } 339 340 /** 341 * If set to false, this opts out of the ticket mechanism. This should be used sparingly 342 * for special purpose threads, such as FTDC. 343 */ setShouldAcquireTicket(bool newValue)344 void setShouldAcquireTicket(bool newValue) { 345 invariant(!isLocked()); 346 _shouldAcquireTicket = newValue; 347 } shouldAcquireTicket()348 bool shouldAcquireTicket() const { 349 return _shouldAcquireTicket; 350 } 351 352 353 protected: Locker()354 Locker() {} 355 356 private: 357 bool _shouldConflictWithSecondaryBatchApplication = true; 358 bool _shouldAcquireTicket = true; 359 }; 360 361 } // namespace mongo 362