diff options
Diffstat (limited to 'src/mongo/db/concurrency/lock_state.h')
-rw-r--r-- | src/mongo/db/concurrency/lock_state.h | 475 |
1 files changed, 241 insertions, 234 deletions
diff --git a/src/mongo/db/concurrency/lock_state.h b/src/mongo/db/concurrency/lock_state.h index ca16767eb49..f8c696b786c 100644 --- a/src/mongo/db/concurrency/lock_state.h +++ b/src/mongo/db/concurrency/lock_state.h @@ -36,286 +36,293 @@ namespace mongo { +/** + * Notfication callback, which stores the last notification result and signals a condition + * variable, which can be waited on. + */ +class CondVarLockGrantNotification : public LockGrantNotification { + MONGO_DISALLOW_COPYING(CondVarLockGrantNotification); + +public: + CondVarLockGrantNotification(); + + /** + * Clears the object so it can be reused. + */ + void clear(); + + /** + * Uninterruptible blocking method, which waits for the notification to fire. + * + * @param timeoutMs How many milliseconds to wait before returning LOCK_TIMEOUT. + */ + LockResult wait(unsigned timeoutMs); + +private: + virtual void notify(ResourceId resId, LockResult result); + + // These two go together to implement the conditional variable pattern. + stdx::mutex _mutex; + stdx::condition_variable _cond; + + // Result from the last call to notify + LockResult _result; +}; + + +/** + * Interface for acquiring locks. One of those objects will have to be instantiated for each + * request (transaction). + * + * Lock/unlock methods must always be called from a single thread. + * + * All instances reference a single global lock manager. + * + * @param IsForMMAPV1 Whether to compile-in the flush lock functionality, which is specific to + * the way the MMAP V1 (legacy) storag engine does commit concurrency control. + */ +template <bool IsForMMAPV1> +class LockerImpl : public Locker { +public: /** - * Notfication callback, which stores the last notification result and signals a condition - * variable, which can be waited on. + * Instantiates new locker. Must be given a unique identifier for disambiguation. Lockers + * having the same identifier will not conflict on lock acquisition. */ - class CondVarLockGrantNotification : public LockGrantNotification { - MONGO_DISALLOW_COPYING(CondVarLockGrantNotification); - public: - CondVarLockGrantNotification(); + LockerImpl(); + + virtual ~LockerImpl(); + + virtual LockerId getId() const { + return _id; + } + + virtual LockResult lockGlobal(LockMode mode, unsigned timeoutMs = UINT_MAX); + virtual LockResult lockGlobalBegin(LockMode mode); + virtual LockResult lockGlobalComplete(unsigned timeoutMs); + virtual void lockMMAPV1Flush(); + + virtual void downgradeGlobalXtoSForMMAPV1(); + virtual bool unlockAll(); - /** - * Clears the object so it can be reused. - */ - void clear(); + virtual void beginWriteUnitOfWork(); + virtual void endWriteUnitOfWork(); - /** - * Uninterruptible blocking method, which waits for the notification to fire. - * - * @param timeoutMs How many milliseconds to wait before returning LOCK_TIMEOUT. - */ - LockResult wait(unsigned timeoutMs); + virtual bool inAWriteUnitOfWork() const { + return _wuowNestingLevel > 0; + } - private: + virtual LockResult lock(ResourceId resId, + LockMode mode, + unsigned timeoutMs = UINT_MAX, + bool checkDeadlock = false); - virtual void notify(ResourceId resId, LockResult result); + virtual void downgrade(ResourceId resId, LockMode newMode); - // These two go together to implement the conditional variable pattern. - stdx::mutex _mutex; - stdx::condition_variable _cond; + virtual bool unlock(ResourceId resId); - // Result from the last call to notify - LockResult _result; - }; + virtual LockMode getLockMode(ResourceId resId) const; + virtual bool isLockHeldForMode(ResourceId resId, LockMode mode) const; + virtual bool isDbLockedForMode(StringData dbName, LockMode mode) const; + virtual bool isCollectionLockedForMode(StringData ns, LockMode mode) const; + virtual ResourceId getWaitingResource() const; + + virtual void getLockerInfo(LockerInfo* lockerInfo) const; + + virtual bool saveLockStateAndUnlock(LockSnapshot* stateOut); + + virtual void restoreLockState(const LockSnapshot& stateToRestore); /** - * Interface for acquiring locks. One of those objects will have to be instantiated for each - * request (transaction). + * Allows for lock requests to be requested in a non-blocking way. There can be only one + * outstanding pending lock request per locker object. + * + * lockBegin posts a request to the lock manager for the specified lock to be acquired, + * which either immediately grants the lock, or puts the requestor on the conflict queue + * and returns immediately with the result of the acquisition. The result can be one of: * - * Lock/unlock methods must always be called from a single thread. + * LOCK_OK - Nothing more needs to be done. The lock is granted. + * LOCK_WAITING - The request has been queued up and will be granted as soon as the lock + * is free. If this result is returned, typically lockComplete needs to be called in + * order to wait for the actual grant to occur. If the caller no longer needs to wait + * for the grant to happen, unlock needs to be called with the same resource passed + * to lockBegin. * - * All instances reference a single global lock manager. + * In other words for each call to lockBegin, which does not return LOCK_OK, there needs to + * be a corresponding call to either lockComplete or unlock. * - * @param IsForMMAPV1 Whether to compile-in the flush lock functionality, which is specific to - * the way the MMAP V1 (legacy) storag engine does commit concurrency control. + * NOTE: These methods are not public and should only be used inside the class + * implementation and for unit-tests and not called directly. */ - template<bool IsForMMAPV1> - class LockerImpl : public Locker { - public: - - /** - * Instantiates new locker. Must be given a unique identifier for disambiguation. Lockers - * having the same identifier will not conflict on lock acquisition. - */ - LockerImpl(); - - virtual ~LockerImpl(); - - virtual LockerId getId() const { return _id; } - - virtual LockResult lockGlobal(LockMode mode, unsigned timeoutMs = UINT_MAX); - virtual LockResult lockGlobalBegin(LockMode mode); - virtual LockResult lockGlobalComplete(unsigned timeoutMs); - virtual void lockMMAPV1Flush(); - - virtual void downgradeGlobalXtoSForMMAPV1(); - virtual bool unlockAll(); - - virtual void beginWriteUnitOfWork(); - virtual void endWriteUnitOfWork(); - - virtual bool inAWriteUnitOfWork() const { return _wuowNestingLevel > 0; } - - virtual LockResult lock(ResourceId resId, - LockMode mode, - unsigned timeoutMs = UINT_MAX, - bool checkDeadlock = false); - - virtual void downgrade(ResourceId resId, LockMode newMode); - - virtual bool unlock(ResourceId resId); - - virtual LockMode getLockMode(ResourceId resId) const; - virtual bool isLockHeldForMode(ResourceId resId, LockMode mode) const; - virtual bool isDbLockedForMode(StringData dbName, LockMode mode) const; - virtual bool isCollectionLockedForMode(StringData ns, LockMode mode) const; - - virtual ResourceId getWaitingResource() const; - - virtual void getLockerInfo(LockerInfo* lockerInfo) const; - - virtual bool saveLockStateAndUnlock(LockSnapshot* stateOut); - - virtual void restoreLockState(const LockSnapshot& stateToRestore); - - /** - * Allows for lock requests to be requested in a non-blocking way. There can be only one - * outstanding pending lock request per locker object. - * - * lockBegin posts a request to the lock manager for the specified lock to be acquired, - * which either immediately grants the lock, or puts the requestor on the conflict queue - * and returns immediately with the result of the acquisition. The result can be one of: - * - * LOCK_OK - Nothing more needs to be done. The lock is granted. - * LOCK_WAITING - The request has been queued up and will be granted as soon as the lock - * is free. If this result is returned, typically lockComplete needs to be called in - * order to wait for the actual grant to occur. If the caller no longer needs to wait - * for the grant to happen, unlock needs to be called with the same resource passed - * to lockBegin. - * - * In other words for each call to lockBegin, which does not return LOCK_OK, there needs to - * be a corresponding call to either lockComplete or unlock. - * - * NOTE: These methods are not public and should only be used inside the class - * implementation and for unit-tests and not called directly. - */ - LockResult lockBegin(ResourceId resId, LockMode mode); + LockResult lockBegin(ResourceId resId, LockMode mode); - /** - * Waits for the completion of a lock, previously requested through lockBegin or - * lockGlobalBegin. Must only be called, if lockBegin returned LOCK_WAITING. - * - * @param resId Resource id which was passed to an earlier lockBegin call. Must match. - * @param mode Mode which was passed to an earlier lockBegin call. Must match. - * @param timeoutMs How long to wait for the lock acquisition to complete. - * @param checkDeadlock whether to perform deadlock detection while waiting. - */ - LockResult lockComplete(ResourceId resId, - LockMode mode, - unsigned timeoutMs, - bool checkDeadlock); - - private: + /** + * Waits for the completion of a lock, previously requested through lockBegin or + * lockGlobalBegin. Must only be called, if lockBegin returned LOCK_WAITING. + * + * @param resId Resource id which was passed to an earlier lockBegin call. Must match. + * @param mode Mode which was passed to an earlier lockBegin call. Must match. + * @param timeoutMs How long to wait for the lock acquisition to complete. + * @param checkDeadlock whether to perform deadlock detection while waiting. + */ + LockResult lockComplete(ResourceId resId, + LockMode mode, + unsigned timeoutMs, + bool checkDeadlock); - friend class AutoYieldFlushLockForMMAPV1Commit; +private: + friend class AutoYieldFlushLockForMMAPV1Commit; - typedef FastMapNoAlloc<ResourceId, LockRequest, 16> LockRequestsMap; + typedef FastMapNoAlloc<ResourceId, LockRequest, 16> LockRequestsMap; - /** - * The main functionality of the unlock method, except accepts iterator in order to avoid - * additional lookups during unlockAll. - */ - bool _unlockImpl(LockRequestsMap::Iterator& it); + /** + * The main functionality of the unlock method, except accepts iterator in order to avoid + * additional lookups during unlockAll. + */ + bool _unlockImpl(LockRequestsMap::Iterator& it); - /** - * MMAP V1 locking code yields and re-acquires the flush lock occasionally in order to - * allow the flush thread proceed. This call returns in what mode the flush lock should be - * acquired. It is based on the type of the operation (IS for readers, IX for writers). - */ - LockMode _getModeForMMAPV1FlushLock() const; + /** + * MMAP V1 locking code yields and re-acquires the flush lock occasionally in order to + * allow the flush thread proceed. This call returns in what mode the flush lock should be + * acquired. It is based on the type of the operation (IS for readers, IX for writers). + */ + LockMode _getModeForMMAPV1FlushLock() const; - // Used to disambiguate different lockers - const LockerId _id; + // Used to disambiguate different lockers + const LockerId _id; - // The only reason we have this spin lock here is for the diagnostic tools, which could - // iterate through the LockRequestsMap on a separate thread and need it to be stable. - // Apart from that, all accesses to the LockerImpl are always from a single thread. - // - // This has to be locked inside const methods, hence the mutable. - mutable SpinLock _lock; - LockRequestsMap _requests; + // The only reason we have this spin lock here is for the diagnostic tools, which could + // iterate through the LockRequestsMap on a separate thread and need it to be stable. + // Apart from that, all accesses to the LockerImpl are always from a single thread. + // + // This has to be locked inside const methods, hence the mutable. + mutable SpinLock _lock; + LockRequestsMap _requests; - // Reuse the notification object across requests so we don't have to create a new mutex - // and condition variable every time. - CondVarLockGrantNotification _notify; + // Reuse the notification object across requests so we don't have to create a new mutex + // and condition variable every time. + CondVarLockGrantNotification _notify; - // Timer for measuring duration and timeouts. This value is set when lock acquisition is - // about to wait and is sampled at grant time. - uint64_t _requestStartTime; + // Timer for measuring duration and timeouts. This value is set when lock acquisition is + // about to wait and is sampled at grant time. + uint64_t _requestStartTime; - // Per-locker locking statistics. Reported in the slow-query log message and through - // db.currentOp. Complementary to the per-instance locking statistics. - SingleThreadedLockStats _stats; + // Per-locker locking statistics. Reported in the slow-query log message and through + // db.currentOp. Complementary to the per-instance locking statistics. + SingleThreadedLockStats _stats; - // Delays release of exclusive/intent-exclusive locked resources until the write unit of - // work completes. Value of 0 means we are not inside a write unit of work. - int _wuowNestingLevel; - std::queue<ResourceId> _resourcesToUnlockAtEndOfUnitOfWork; + // Delays release of exclusive/intent-exclusive locked resources until the write unit of + // work completes. Value of 0 means we are not inside a write unit of work. + int _wuowNestingLevel; + std::queue<ResourceId> _resourcesToUnlockAtEndOfUnitOfWork; - ////////////////////////////////////////////////////////////////////////////////////////// - // - // Methods merged from LockState, which should eventually be removed or changed to methods - // on the LockerImpl interface. - // + ////////////////////////////////////////////////////////////////////////////////////////// + // + // Methods merged from LockState, which should eventually be removed or changed to methods + // on the LockerImpl interface. + // - public: +public: + virtual void dump() const; - virtual void dump() const; - - virtual bool isW() const; - virtual bool isR() const; - - virtual bool isLocked() const; - virtual bool isWriteLocked() const; - virtual bool isReadLocked() const; - - virtual void assertEmptyAndReset(); - - virtual bool hasLockPending() const { return getWaitingResource().isValid(); } + virtual bool isW() const; + virtual bool isR() const; - virtual void setIsBatchWriter(bool newValue) { _batchWriter = newValue; } - virtual bool isBatchWriter() const { return _batchWriter; } + virtual bool isLocked() const; + virtual bool isWriteLocked() const; + virtual bool isReadLocked() const; - virtual bool hasStrongLocks() const; + virtual void assertEmptyAndReset(); - private: - bool _batchWriter; - }; + virtual bool hasLockPending() const { + return getWaitingResource().isValid(); + } - typedef LockerImpl<false> DefaultLockerImpl; - typedef LockerImpl<true> MMAPV1LockerImpl; + virtual void setIsBatchWriter(bool newValue) { + _batchWriter = newValue; + } + virtual bool isBatchWriter() const { + return _batchWriter; + } + virtual bool hasStrongLocks() const; - /** - * At global synchronization points, such as drop database we are running under a global - * exclusive lock and without an active write unit of work, doing changes which require global - * commit. This utility allows the flush lock to be temporarily dropped so the flush thread - * could run in such circumstances. Should not be used where write units of work are used, - * because these have different mechanism of yielding the flush lock. - */ - class AutoYieldFlushLockForMMAPV1Commit { - public: - AutoYieldFlushLockForMMAPV1Commit(Locker* locker); - ~AutoYieldFlushLockForMMAPV1Commit(); +private: + bool _batchWriter; +}; - private: - MMAPV1LockerImpl* const _locker; - }; +typedef LockerImpl<false> DefaultLockerImpl; +typedef LockerImpl<true> MMAPV1LockerImpl; - /** - * This explains how the MMAP V1 durability system is implemented. - * - * Every server operation (OperationContext), must call Locker::lockGlobal as the first lock - * action (it is illegal to acquire any other locks without calling this first). This action - * acquires the global and flush locks in the appropriate modes (IS for read operations, IX - * for write operations). Having the flush lock in one of these modes indicates to the flush - * thread that there is an active reader or writer. - * - * Whenever the flush thread(dur.cpp) activates, it goes through the following steps : - * - * Acquire the flush lock in S mode using AutoAcquireFlushLockForMMAPV1Commit. This waits until - * all current write activity on the system completes and does not allow any new operations to - * start. - * - * Once the S lock is granted, the flush thread writes the journal entries to disk (it is - * guaranteed that there will not be any modifications) and applies them to the shared view. - * - * After that, it upgrades the S lock to X and remaps the private view. - * - * NOTE: There should be only one usage of this class and this should be in dur.cpp - */ - class AutoAcquireFlushLockForMMAPV1Commit { - public: - AutoAcquireFlushLockForMMAPV1Commit(Locker* locker); - ~AutoAcquireFlushLockForMMAPV1Commit(); +/** + * At global synchronization points, such as drop database we are running under a global + * exclusive lock and without an active write unit of work, doing changes which require global + * commit. This utility allows the flush lock to be temporarily dropped so the flush thread + * could run in such circumstances. Should not be used where write units of work are used, + * because these have different mechanism of yielding the flush lock. + */ +class AutoYieldFlushLockForMMAPV1Commit { +public: + AutoYieldFlushLockForMMAPV1Commit(Locker* locker); + ~AutoYieldFlushLockForMMAPV1Commit(); - /** - * We need the exclusive lock in order to do the shared view remap. - */ - void upgradeFlushLockToExclusive(); +private: + MMAPV1LockerImpl* const _locker; +}; - /** - * Allows the acquired flush lock to be prematurely released. This is helpful for the case - * where we know that we won't be doing a remap after gathering the write intents, so the - * rest can be done outside of flush lock. - */ - void release(); - private: - Locker* const _locker; - bool _released; - }; +/** + * This explains how the MMAP V1 durability system is implemented. + * + * Every server operation (OperationContext), must call Locker::lockGlobal as the first lock + * action (it is illegal to acquire any other locks without calling this first). This action + * acquires the global and flush locks in the appropriate modes (IS for read operations, IX + * for write operations). Having the flush lock in one of these modes indicates to the flush + * thread that there is an active reader or writer. + * + * Whenever the flush thread(dur.cpp) activates, it goes through the following steps : + * + * Acquire the flush lock in S mode using AutoAcquireFlushLockForMMAPV1Commit. This waits until + * all current write activity on the system completes and does not allow any new operations to + * start. + * + * Once the S lock is granted, the flush thread writes the journal entries to disk (it is + * guaranteed that there will not be any modifications) and applies them to the shared view. + * + * After that, it upgrades the S lock to X and remaps the private view. + * + * NOTE: There should be only one usage of this class and this should be in dur.cpp + */ +class AutoAcquireFlushLockForMMAPV1Commit { +public: + AutoAcquireFlushLockForMMAPV1Commit(Locker* locker); + ~AutoAcquireFlushLockForMMAPV1Commit(); + /** + * We need the exclusive lock in order to do the shared view remap. + */ + void upgradeFlushLockToExclusive(); /** - * Retrieves the global lock manager instance. + * Allows the acquired flush lock to be prematurely released. This is helpful for the case + * where we know that we won't be doing a remap after gathering the write intents, so the + * rest can be done outside of flush lock. */ - LockManager* getGlobalLockManager(); + void release(); + +private: + Locker* const _locker; + bool _released; +}; + + +/** + * Retrieves the global lock manager instance. + */ +LockManager* getGlobalLockManager(); -} // namespace mongo +} // namespace mongo |