// lockstate.cpp /** * Copyright (C) 2008 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #include "mongo/db/lockstate.h" #include "mongo/db/namespace_string.h" #include "mongo/util/mongoutils/str.h" namespace mongo { LockState::LockState() : _batchWriter(false), _recursive(0), _threadState(0), _whichNestable( Lock::notnestable ), _nestableCount(0), _otherCount(0), _otherLock(NULL), _scopedLk(NULL), _lockPending(false), _lockPendingParallelWriter(false) { } bool LockState::isRW() const { return _threadState == 'R' || _threadState == 'W'; } bool LockState::isW() const { return _threadState == 'W'; } bool LockState::hasAnyReadLock() const { return _threadState == 'r' || _threadState == 'R'; } bool LockState::isLocked( const StringData& ns ) const { char db[MaxDatabaseNameLen]; nsToDatabase(ns, db); DEV verify( _otherName.find( '.' ) == string::npos ); // XXX this shouldn't be here, but somewhere if ( _otherCount && db == _otherName ) return true; if ( _nestableCount ) { if ( mongoutils::str::equals( db , "local" ) ) return _whichNestable == Lock::local; if ( mongoutils::str::equals( db , "admin" ) ) return _whichNestable == Lock::admin; } return false; } bool LockState::isLocked() const { return threadState() != 0; } bool LockState::isWriteLocked() const { return (threadState() == 'W' || threadState() == 'w'); } bool LockState::isWriteLocked(const StringData& ns) const { if (isWriteLocked()) { return true; } return isLocked(ns); } bool LockState::isAtLeastReadLocked(const StringData& ns) const { if (threadState() == 'R' || threadState() == 'W') return true; // global if (threadState() == 0) return false; return isLocked(ns); } bool LockState::isLockedForCommitting() const { return threadState() == 'R' || threadState() == 'W'; } bool LockState::isRecursive() const { return recursiveCount() > 1; } void LockState::assertWriteLocked(const StringData& ns) const { if (!isWriteLocked(ns)) { dump(); msgasserted( 16105, mongoutils::str::stream() << "expected to be write locked for " << ns); } } void LockState::assertAtLeastReadLocked(const StringData& ns) const { if (!isAtLeastReadLocked(ns)) { log() << "error expected " << ns << " to be locked " << endl; dump(); msgasserted( 16104, mongoutils::str::stream() << "expected to be read locked for " << ns); } } void LockState::lockedStart( char newState ) { _threadState = newState; } void LockState::unlocked() { _threadState = 0; } void LockState::changeLockState( char newState ) { fassert( 16169 , _threadState != 0 ); _threadState = newState; } static string kind(int n) { if( n > 0 ) return "W"; if( n < 0 ) return "R"; return "?"; } BSONObj LockState::reportState() { BSONObjBuilder b; reportState(&b); return b.obj(); } /** Note: this is is called by the currentOp command, which is a different thread. So be careful about thread safety here. For example reading this->otherName would not be safe as-is! */ void LockState::reportState(BSONObjBuilder* res) { BSONObjBuilder b; if( _threadState ) { char buf[2]; buf[0] = _threadState; buf[1] = 0; b.append("^", buf); } if( _nestableCount ) { string s = "?"; if( _whichNestable == Lock::local ) s = "^local"; else if( _whichNestable == Lock::admin ) s = "^admin"; b.append(s, kind(_nestableCount)); } if( _otherCount ) { WrapperForRWLock *k = _otherLock; if( k ) { string s = "^"; s += k->name(); b.append(s, kind(_otherCount)); } } BSONObj o = b.obj(); if (!o.isEmpty()) { res->append("locks", o); } res->append("waitingForLock", _lockPending); } void LockState::dump() const { char s = _threadState; stringstream ss; ss << "lock status: "; if( s == 0 ){ ss << "unlocked"; } else { ss << s; if( _recursive ) { ss << " recursive:" << _recursive; } ss << " otherCount:" << _otherCount; if( _otherCount ) { ss << " otherdb:" << _otherName; } if( _nestableCount ) { ss << " nestableCount:" << _nestableCount << " which:"; if( _whichNestable == Lock::local ) ss << "local"; else if( _whichNestable == Lock::admin ) ss << "admin"; else ss << (int)_whichNestable; } } log() << ss.str() << endl; } void LockState::enterScopedLock( Lock::ScopedLock* lock ) { _recursive++; if ( _recursive == 1 ) { fassert(16115, _scopedLk == 0); _scopedLk = lock; } } Lock::ScopedLock* LockState::leaveScopedLock() { _recursive--; dassert( _recursive < 10000 ); Lock::ScopedLock* temp = _scopedLk; if ( _recursive > 0 ) { return NULL; } _scopedLk = NULL; return temp; } void LockState::lockedNestable( Lock::Nestable what , int type) { verify( type ); _whichNestable = what; _nestableCount += type; } void LockState::unlockedNestable() { _whichNestable = Lock::notnestable; _nestableCount = 0; } void LockState::lockedOther( int type ) { fassert( 16231 , _otherCount == 0 ); _otherCount = type; } void LockState::lockedOther( const StringData& other , int type , WrapperForRWLock* lock ) { fassert( 16170 , _otherCount == 0 ); _otherName = other.toString(); _otherCount = type; _otherLock = lock; } void LockState::unlockedOther() { // we leave _otherName and _otherLock set as // _otherLock exists to cache a pointer _otherCount = 0; } LockStat* LockState::getRelevantLockStat() { if ( _whichNestable ) return Lock::nestableLockStat( _whichNestable ); if ( _otherCount && _otherLock ) return &_otherLock->getStats(); if ( isRW() ) return Lock::globalLockStat(); return 0; } Acquiring::Acquiring( Lock::ScopedLock* lock, LockState& ls ) : _lock( lock ), _ls( ls ){ _ls._lockPending = true; } Acquiring::~Acquiring() { _ls._lockPending = false; LockStat* stat = _ls.getRelevantLockStat(); if ( stat && _lock ) { // increment the global stats for this counter stat->recordAcquireTimeMicros( _ls.threadState(), _lock->acquireFinished( stat ) ); } } AcquiringParallelWriter::AcquiringParallelWriter( LockState& ls ) : _ls( ls ) { _ls._lockPendingParallelWriter = true; } AcquiringParallelWriter::~AcquiringParallelWriter() { _ls._lockPendingParallelWriter = false; } }