summaryrefslogtreecommitdiff
path: root/src/mongo/db/concurrency/lock_manager.h
diff options
context:
space:
mode:
authorSpencer T Brody <spencer@mongodb.com>2018-08-22 14:37:16 -0400
committerSpencer T Brody <spencer@mongodb.com>2018-09-18 13:20:18 -0400
commite65ff57e108ed69c46cc0b0ccbdd675663de2469 (patch)
tree010051703fd944884d99571e31f1dd45b28da8a1 /src/mongo/db/concurrency/lock_manager.h
parent157691ef0babb24cd1566446f5f88206f9607564 (diff)
downloadmongo-e65ff57e108ed69c46cc0b0ccbdd675663de2469.tar.gz
SERVER-36913 Add functionality to LockManager for repl state transitions with prepared transactions.
Diffstat (limited to 'src/mongo/db/concurrency/lock_manager.h')
-rw-r--r--src/mongo/db/concurrency/lock_manager.h150
1 files changed, 149 insertions, 1 deletions
diff --git a/src/mongo/db/concurrency/lock_manager.h b/src/mongo/db/concurrency/lock_manager.h
index 2956232f996..5f481b76911 100644
--- a/src/mongo/db/concurrency/lock_manager.h
+++ b/src/mongo/db/concurrency/lock_manager.h
@@ -57,6 +57,8 @@ public:
LockManager();
~LockManager();
+ class TemporaryResourceQueue;
+
/**
* Acquires lock on the specified resource in the specified mode and returns the outcome
* of the operation. See the details for LockResult for more information on what the
@@ -138,6 +140,10 @@ private:
// The lockheads need access to the partitions
friend struct LockHead;
+ // ReplicationLockManagerManipulator manipulates LockManager state directly in order to
+ // encapsulate specific logic for replication state transitions.
+ friend class ReplicationLockManagerManipulator;
+
// These types describe the locks hash table
struct LockBucket {
@@ -200,7 +206,7 @@ private:
*
* @param lock Lock whose grant state should be recalculated.
* @param checkConflictQueue Whether to go through the conflict queue. This is an
- * optimisation in that we only need to check the conflict queue if one of the
+ * optimization in that we only need to check the conflict queue if one of the
* granted modes, which was conflicting before became zero.
*/
void _onLockModeChanged(LockHead* lock, bool checkConflictQueue);
@@ -313,4 +319,146 @@ private:
bool _foundCycle;
};
+/**
+ * There is one of these objects for each resource that has a lock request. Empty objects (i.e.
+ * LockHead with no requests) are allowed to exist on the lock manager's hash table.
+ *
+ * The memory and lifetime is controlled entirely by the LockManager class.
+ *
+ * Not thread-safe and should only be accessed under the LockManager's bucket lock. Must be locked
+ * before locking a partition, not after.
+ *
+ * This struct *should* just be a private sub-class of LockManager since it is effectively an
+ * implementation detail of LockManager. The reason it isn't is because it is also used by
+ * ReplicationLockManagerManipulator and because it is forward-declared in lock_manager_defs.h.
+ * Nevertheless this struct should be thought of as an implementation detail of LockManager and
+ * should not be used directly except by LockManager and friend classes of LockManager that serve to
+ * extend LockManager functionality.
+ */
+struct LockHead {
+private:
+ friend class DeadlockDetector;
+ friend class LockManager;
+ friend class ReplicationLockManagerManipulator;
+
+ /**
+ * Used for initialization of a LockHead, which might have been retrieved from cache and also in
+ * order to keep the LockHead structure a POD.
+ */
+ void initNew(ResourceId resId);
+
+ /**
+ * True iff there may be partitions with granted requests for this resource.
+ */
+ bool partitioned() const;
+
+ /**
+ * Locates the request corresponding to the particular locker or returns nullptr. Must be called
+ * with the bucket holding this lock head locked.
+ */
+ LockRequest* findRequest(LockerId lockerId) const;
+
+ /**
+ * Finish creation of request and put it on the LockHead's conflict or granted queues. Returns
+ * LOCK_WAITING for conflict case and LOCK_OK otherwise.
+ */
+ LockResult newRequest(LockRequest* request);
+
+ /**
+ * Lock each partitioned LockHead in turn, and move any (granted) intent mode requests for
+ * lock->resourceId to lock, which must itself already be locked.
+ */
+ void migratePartitionedLockHeads();
+
+ // Methods to maintain the granted queue
+ void incGrantedModeCount(LockMode mode);
+
+ void decGrantedModeCount(LockMode mode);
+
+ // Methods to maintain the conflict queue
+ void incConflictModeCount(LockMode mode);
+
+ void decConflictModeCount(LockMode mode);
+
+ // Id of the resource which is protected by this lock. Initialized at construction time and does
+ // not change.
+ ResourceId resourceId;
+
+ //
+ // Granted queue
+ //
+
+ // Doubly-linked list of requests, which have been granted. Newly granted requests go to
+ // the end of the queue. Conversion requests are granted from the beginning forward.
+ LockRequestList grantedList;
+
+ // Counts the grants and conversion counts for each of the supported lock modes. These
+ // counts should exactly match the aggregated modes on the granted list.
+ uint32_t grantedCounts[LockModesCount];
+
+ // Bit-mask of the granted + converting modes on the granted queue. Maintained in lock-step
+ // with the grantedCounts array.
+ uint32_t grantedModes;
+
+ //
+ // Conflict queue
+ //
+
+ // Doubly-linked list of requests, which have not been granted yet because they conflict
+ // with the set of granted modes. Requests are queued at the end of the queue and are
+ // granted from the beginning forward, which gives these locks FIFO ordering. Exceptions
+ // are high-priority locks, such as the MMAP V1 flush lock.
+ LockRequestList conflictList;
+
+ // Counts the conflicting requests for each of the lock modes. These counts should exactly
+ // match the aggregated modes on the conflicts list.
+ uint32_t conflictCounts[LockModesCount];
+
+ // Bit-mask of the conflict modes on the conflict queue. Maintained in lock-step with the
+ // conflictCounts array.
+ uint32_t conflictModes;
+
+ // References partitions that may have PartitionedLockHeads for this LockHead.
+ // Non-empty implies the lock has no conflicts and only has intent modes as grantedModes.
+ // TODO: Remove this vector and make LockHead a POD
+ std::vector<LockManager::Partition*> partitions;
+
+ //
+ // Conversion
+ //
+
+ // Counts the number of requests on the granted queue, which have requested any kind of
+ // conflicting conversion and are blocked (i.e. all requests which are currently
+ // STATUS_CONVERTING). This is an optimization for unlocking in that we do not need to
+ // check the granted queue for requests in STATUS_CONVERTING if this count is zero. This
+ // saves cycles in the regular case and only burdens the less-frequent lock upgrade case.
+ uint32_t conversionsCount;
+
+ // Counts the number of requests on the granted queue, which have requested that the policy
+ // be switched to compatible-first. As long as this value is > 0, the policy will stay
+ // compatible-first.
+ uint32_t compatibleFirstCount;
+};
+
+/**
+ * This class wraps a LockHead and is used to represent a temporary state for a resource managed by
+ * the LockManager. This allows us to prepare lock requests against a resource without those
+ * requests actually being present in the "true" version of the resource which is actually being
+ * managed by the LockManager. We use this to avoid exposing LockHead, which is an implementation
+ * detail of the LockManager, while still providing a handle to state for a LockManager resource.
+ */
+class LockManager::TemporaryResourceQueue {
+public:
+ explicit TemporaryResourceQueue(ResourceId resourceId);
+
+ ResourceId getResourceId() const {
+ return _lockHead.resourceId;
+ }
+
+private:
+ friend class ReplicationLockManagerManipulator;
+
+ LockHead _lockHead;
+};
+
} // namespace mongo