bool LockManager::unlock(LockRequest* request) { invariant(request->lock); // Fast path for decrementing multiple references of the same lock. It is safe to do this // without locking, because 1) all calls for the same lock request must be done on the same // thread and 2) if there are lock requests hanging of a given LockHead, then this lock // will never disappear. request->recursiveCount--; if ((request->status == LockRequest::STATUS_GRANTED) && (request->recursiveCount > 0)) { return false; } LockHead* lock = request->lock; LockBucket* bucket = _getBucket(lock->resourceId); scoped_spinlock scopedLock(bucket->mutex); invariant(lock->grantedQueue != NULL); invariant(lock->grantedModes != 0); if (request->status == LockRequest::STATUS_WAITING) { // This cancels a pending lock request invariant(request->recursiveCount == 0); lock->removeFromConflictQueue(request); lock->changeConflictModeCount(request->mode, LockHead::Decrement); } else if (request->status == LockRequest::STATUS_CONVERTING) { // This cancels a pending convert request invariant(request->recursiveCount > 0); // Lock only goes from GRANTED to CONVERTING, so cancelling the conversion request // brings it back to the previous granted mode. request->status = LockRequest::STATUS_GRANTED; lock->conversionsCount--; lock->changeGrantedModeCount(request->convertMode, LockHead::Decrement); request->convertMode = MODE_NONE; _onLockModeChanged(lock, lock->grantedCounts[request->convertMode] == 0); } else if (request->status == LockRequest::STATUS_GRANTED) { // This releases a currently held lock and is the most common path, so it should be // as efficient as possible. invariant(request->recursiveCount == 0); // Remove from the granted list lock->removeFromGrantedQueue(request); lock->changeGrantedModeCount(request->mode, LockHead::Decrement); _onLockModeChanged(lock, lock->grantedCounts[request->mode] == 0); } else { // Invalid request status invariant(false); } return (request->recursiveCount == 0); }
void LockManager::downgrade(LockRequest* request, LockMode newMode) { invariant(request->lock); invariant(request->status == LockRequest::STATUS_GRANTED); invariant(request->recursiveCount > 0); // The conflict set of the newMode should be a subset of the conflict set of the old mode. // Can't downgrade from S -> IX for example. invariant((LockConflictsTable[request->mode] | LockConflictsTable[newMode]) == LockConflictsTable[request->mode]); LockHead* lock = request->lock; LockBucket* bucket = _getBucket(lock->resourceId); SimpleMutex::scoped_lock scopedLock(bucket->mutex); invariant(lock->grantedQueueBegin != NULL); invariant(lock->grantedQueueEnd != NULL); invariant(lock->grantedModes != 0); lock->changeGrantedModeCount(newMode, LockHead::Increment); lock->changeGrantedModeCount(request->mode, LockHead::Decrement); request->mode = newMode; _onLockModeChanged(lock, true); }
void OperationLatencyHistogram::increment(uint64_t latency, Command::ReadWriteType type) { int bucket = _getBucket(latency); switch (type) { case Command::ReadWriteType::kRead: _incrementData(latency, bucket, &_reads); break; case Command::ReadWriteType::kWrite: _incrementData(latency, bucket, &_writes); break; case Command::ReadWriteType::kCommand: _incrementData(latency, bucket, &_commands); break; default: MONGO_UNREACHABLE; } }
LockResult LockManager::lock(const ResourceId& resId, LockRequest* request, LockMode mode) { dassert(mode > MODE_NONE); // Fast path for acquiring the same lock multiple times in modes, which are already covered // by the current mode. It is safe to do this without locking, because 1) all calls for the // same lock request must be done on the same thread and 2) if there are lock requests // hanging off a given LockHead, then this lock will never disappear. if ((LockConflictsTable[request->mode] | LockConflictsTable[mode]) == LockConflictsTable[request->mode]) { request->recursiveCount++; return LOCK_OK; } // TODO: For the time being we do not need conversions between unrelated lock modes (i.e., // modes which both add and remove to the conflicts set), so these are not implemented yet // (e.g., S -> IX). invariant((LockConflictsTable[request->mode] | LockConflictsTable[mode]) == LockConflictsTable[mode]); LockBucket* bucket = _getBucket(resId); scoped_spinlock scopedLock(bucket->mutex); LockHead* lock; LockHeadMap::iterator it = bucket->data.find(resId); if (it == bucket->data.end()) { // Lock is free (not on the map) invariant(request->status == LockRequest::STATUS_NEW); lock = new LockHead(resId); bucket->data.insert(LockHeadPair(resId, lock)); } else { // Lock is not free lock = it->second; } // Sanity check if requests are being reused invariant(request->lock == NULL || request->lock == lock); request->lock = lock; request->recursiveCount++; if (request->status == LockRequest::STATUS_NEW) { invariant(request->recursiveCount == 1); // New lock request if (conflicts(mode, lock->grantedModes)) { request->status = LockRequest::STATUS_WAITING; request->mode = mode; request->convertMode = MODE_NONE; // Put it on the conflict queue. This is the place where various policies could be // applied for where in the wait queue does a request go. lock->addToConflictQueue(request); lock->changeConflictModeCount(mode, LockHead::Increment); return LOCK_WAITING; } else { // No conflict, new request request->status = LockRequest::STATUS_GRANTED; request->mode = mode; request->convertMode = MODE_NONE; lock->addToGrantedQueue(request); lock->changeGrantedModeCount(mode, LockHead::Increment); return LOCK_OK; } } else { // If we are here, we already hold the lock in some mode. In order to keep it simple, // we do not allow requesting a conversion while a lock is already waiting or pending // conversion, hence the assertion below. invariant(request->status == LockRequest::STATUS_GRANTED); invariant(request->recursiveCount > 1); invariant(request->mode != mode); // Construct granted mask without our current mode, so that it is not counted as // conflicting uint32_t grantedModesWithoutCurrentRequest = 0; // We start the counting at 1 below, because LockModesCount also includes MODE_NONE // at position 0, which can never be acquired/granted. for (uint32_t i = 1; i < LockModesCount; i++) { const uint32_t currentRequestHolds = (request->mode == static_cast<LockMode>(i) ? 1 : 0); if (lock->grantedCounts[i] > currentRequestHolds) { grantedModesWithoutCurrentRequest |= modeMask(static_cast<LockMode>(i)); } } // This check favours conversion requests over pending requests. For example: // // T1 requests lock L in IS // T2 requests lock L in X // T1 then upgrades L from IS -> S // // Because the check does not look into the conflict modes bitmap, it will grant L to // T1 in S mode, instead of block, which would otherwise cause deadlock. if (conflicts(mode, grantedModesWithoutCurrentRequest)) { request->status = LockRequest::STATUS_CONVERTING; request->convertMode = mode; lock->conversionsCount++; lock->changeGrantedModeCount(request->convertMode, LockHead::Increment); return LOCK_WAITING; } else { // No conflict, existing request lock->changeGrantedModeCount(mode, LockHead::Increment); lock->changeGrantedModeCount(request->mode, LockHead::Decrement); request->mode = mode; return LOCK_OK; } } }
void BtreeBuilder<V>::buildNextLevel(DiskLoc loc, bool mayInterrupt) { int levels = 1; while( 1 ) { if( _getBucket(loc)->tempNext().isNull() ) { // only 1 bucket at this level. we are done. _btreeState->setHead( loc ); break; } levels++; DiskLoc upLoc = BtreeBucket<V>::addBucket(_btreeState); DiskLoc upStart = upLoc; BtreeBucket<V> *up = _getModifiableBucket( upLoc ); DiskLoc xloc = loc; while( !xloc.isNull() ) { killCurrentOp.checkForInterrupt( !mayInterrupt ); if ( getDur().commitIfNeeded() ) { b = _getModifiableBucket( cur ); up = _getModifiableBucket( upLoc ); } BtreeBucket<V> *x = _getModifiableBucket( xloc ); Key k; DiskLoc r; x->popBack(r,k); bool keepX = ( x->n != 0 ); DiskLoc keepLoc = keepX ? xloc : x->nextChild; if ( ! up->_pushBack(r, k, _btreeState->ordering(), keepLoc) ) { // current bucket full DiskLoc n = BtreeBucket<V>::addBucket(_btreeState); up->setTempNext(n); upLoc = n; up = _getModifiableBucket( upLoc ); up->pushBack(r, k, _btreeState->ordering(), keepLoc); } DiskLoc nextLoc = x->tempNext(); // get next in chain at current level if ( keepX ) { x->parent = upLoc; } else { if ( !x->nextChild.isNull() ) { DiskLoc ll = x->nextChild; _getModifiableBucket(ll)->parent = upLoc; //(x->nextChild.btreemod<V>())->parent = upLoc; } x->deallocBucket( _btreeState, xloc ); } xloc = nextLoc; } loc = upStart; mayCommitProgressDurably(); } if( levels > 1 ) { LOG(2) << "btree levels: " << levels << endl; } }