summaryrefslogtreecommitdiff
path: root/src/mongo/db/concurrency/lock_manager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/concurrency/lock_manager.cpp')
-rw-r--r--src/mongo/db/concurrency/lock_manager.cpp317
1 files changed, 115 insertions, 202 deletions
diff --git a/src/mongo/db/concurrency/lock_manager.cpp b/src/mongo/db/concurrency/lock_manager.cpp
index b18007877f8..0b93ae967fb 100644
--- a/src/mongo/db/concurrency/lock_manager.cpp
+++ b/src/mongo/db/concurrency/lock_manager.cpp
@@ -129,208 +129,6 @@ MONGO_STATIC_ASSERT((sizeof(LockRequestStatusNames) / sizeof(LockRequestStatusNa
} // namespace
/**
- * 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.
- */
-struct LockHead {
-
- /**
- * 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) {
- resourceId = resId;
-
- grantedList.reset();
- memset(grantedCounts, 0, sizeof(grantedCounts));
- grantedModes = 0;
-
- conflictList.reset();
- memset(conflictCounts, 0, sizeof(conflictCounts));
- conflictModes = 0;
-
- conversionsCount = 0;
- compatibleFirstCount = 0;
- }
-
- /**
- * True iff there may be partitions with granted requests for this resource.
- */
- bool partitioned() const {
- return !partitions.empty();
- }
-
- /**
- * 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 {
- // Check the granted queue first
- for (LockRequest* it = grantedList._front; it != nullptr; it = it->next) {
- if (it->locker->getId() == lockerId) {
- return it;
- }
- }
-
- // Check the conflict queue second
- for (LockRequest* it = conflictList._front; it != nullptr; it = it->next) {
- if (it->locker->getId() == lockerId) {
- return it;
- }
- }
-
- return nullptr;
- }
-
- /**
- * 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) {
- invariant(!request->partitionedLock);
- request->lock = this;
-
- // We cannot set request->partitioned to false, as this might be a migration, in which case
- // access to that field is not protected. The 'partitioned' member instead indicates if a
- // request was initially partitioned.
-
- // New lock request. Queue after all granted modes and after any already requested
- // conflicting modes
- if (conflicts(request->mode, grantedModes) ||
- (!compatibleFirstCount && conflicts(request->mode, conflictModes))) {
- request->status = LockRequest::STATUS_WAITING;
-
- // Put it on the conflict queue. Conflicts are granted front to back.
- if (request->enqueueAtFront) {
- conflictList.push_front(request);
- } else {
- conflictList.push_back(request);
- }
-
- incConflictModeCount(request->mode);
-
- return LOCK_WAITING;
- }
-
- // No conflict, new request
- request->status = LockRequest::STATUS_GRANTED;
-
- grantedList.push_back(request);
- incGrantedModeCount(request->mode);
-
- if (request->compatibleFirst) {
- compatibleFirstCount++;
- }
-
- return LOCK_OK;
- }
-
- /**
- * 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) {
- invariant(grantedCounts[mode] >= 0);
- if (++grantedCounts[mode] == 1) {
- invariant((grantedModes & modeMask(mode)) == 0);
- grantedModes |= modeMask(mode);
- }
- }
-
- void decGrantedModeCount(LockMode mode) {
- invariant(grantedCounts[mode] >= 1);
- if (--grantedCounts[mode] == 0) {
- invariant((grantedModes & modeMask(mode)) == modeMask(mode));
- grantedModes &= ~modeMask(mode);
- }
- }
-
- // Methods to maintain the conflict queue
- void incConflictModeCount(LockMode mode) {
- invariant(conflictCounts[mode] >= 0);
- if (++conflictCounts[mode] == 1) {
- invariant((conflictModes & modeMask(mode)) == 0);
- conflictModes |= modeMask(mode);
- }
- }
-
- void decConflictModeCount(LockMode mode) {
- invariant(conflictCounts[mode] >= 1);
- if (--conflictCounts[mode] == 0) {
- invariant((conflictModes & modeMask(mode)) == modeMask(mode));
- conflictModes &= ~modeMask(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;
-};
-
-/**
* The PartitionedLockHead allows optimizing the case where requests overwhelmingly use
* the intent lock modes MODE_IS and MODE_IX, which are compatible with each other.
* Having to use a single LockHead causes contention where none would be needed.
@@ -370,6 +168,85 @@ struct PartitionedLockHead {
LockRequestList grantedList;
};
+//
+// LockHead
+//
+void LockHead::initNew(ResourceId resId) {
+ resourceId = resId;
+
+ grantedList.reset();
+ memset(grantedCounts, 0, sizeof(grantedCounts));
+ grantedModes = 0;
+
+ conflictList.reset();
+ memset(conflictCounts, 0, sizeof(conflictCounts));
+ conflictModes = 0;
+
+ conversionsCount = 0;
+ compatibleFirstCount = 0;
+}
+
+bool LockHead::partitioned() const {
+ return !partitions.empty();
+}
+
+LockRequest* LockHead::findRequest(LockerId lockerId) const {
+ // Check the granted queue first
+ for (LockRequest* it = grantedList._front; it != nullptr; it = it->next) {
+ if (it->locker->getId() == lockerId) {
+ return it;
+ }
+ }
+
+ // Check the conflict queue second
+ for (LockRequest* it = conflictList._front; it != nullptr; it = it->next) {
+ if (it->locker->getId() == lockerId) {
+ return it;
+ }
+ }
+
+ return nullptr;
+}
+
+LockResult LockHead::newRequest(LockRequest* request) {
+ invariant(!request->partitionedLock);
+ request->lock = this;
+
+ // We cannot set request->partitioned to false, as this might be a migration, in which case
+ // access to that field is not protected. The 'partitioned' member instead indicates if a
+ // request was initially partitioned.
+
+ // New lock request. Queue after all granted modes and after any already requested
+ // conflicting modes
+ if (conflicts(request->mode, grantedModes) ||
+ (!compatibleFirstCount && conflicts(request->mode, conflictModes))) {
+ request->status = LockRequest::STATUS_WAITING;
+
+ // Put it on the conflict queue. Conflicts are granted front to back.
+ if (request->enqueueAtFront) {
+ conflictList.push_front(request);
+ } else {
+ conflictList.push_back(request);
+ }
+
+ incConflictModeCount(request->mode);
+
+ return LOCK_WAITING;
+ }
+
+ // No conflict, new request
+ request->status = LockRequest::STATUS_GRANTED;
+
+ grantedList.push_back(request);
+ incGrantedModeCount(request->mode);
+
+ if (request->compatibleFirst) {
+ compatibleFirstCount++;
+ }
+
+ return LOCK_OK;
+}
+
void LockHead::migratePartitionedLockHeads() {
invariant(partitioned());
@@ -403,6 +280,38 @@ void LockHead::migratePartitionedLockHeads() {
}
}
+void LockHead::incGrantedModeCount(LockMode mode) {
+ invariant(grantedCounts[mode] >= 0);
+ if (++grantedCounts[mode] == 1) {
+ invariant((grantedModes & modeMask(mode)) == 0);
+ grantedModes |= modeMask(mode);
+ }
+}
+
+void LockHead::decGrantedModeCount(LockMode mode) {
+ invariant(grantedCounts[mode] >= 1);
+ if (--grantedCounts[mode] == 0) {
+ invariant((grantedModes & modeMask(mode)) == modeMask(mode));
+ grantedModes &= ~modeMask(mode);
+ }
+}
+
+void LockHead::incConflictModeCount(LockMode mode) {
+ invariant(conflictCounts[mode] >= 0);
+ if (++conflictCounts[mode] == 1) {
+ invariant((conflictModes & modeMask(mode)) == 0);
+ conflictModes |= modeMask(mode);
+ }
+}
+
+void LockHead::decConflictModeCount(LockMode mode) {
+ invariant(conflictCounts[mode] >= 1);
+ if (--conflictCounts[mode] == 0) {
+ invariant((conflictModes & modeMask(mode)) == modeMask(mode));
+ conflictModes &= ~modeMask(mode);
+ }
+}
+
//
// LockManager
//
@@ -1204,4 +1113,8 @@ const char* lockRequestStatusName(LockRequest::Status status) {
return LockRequestStatusNames[status];
}
+LockManager::TemporaryResourceQueue::TemporaryResourceQueue(ResourceId resourceId) {
+ _lockHead.initNew(resourceId);
+}
+
} // namespace mongo