// @file d_concurrency.h /** * 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 . */ // only used by mongod, thus the name ('d') // (also used by dbtests test binary, which is running mongod test code) #pragma once #include "mongo/base/string_data.h" #include "mongo/db/jsobj.h" #include "mongo/db/lockstat.h" #include "mongo/util/concurrency/mutex.h" #include "mongo/util/concurrency/rwlock.h" namespace mongo { class WrapperForRWLock; class LockState; class Lock : boost::noncopyable { public: enum Nestable { notnestable=0, local, admin }; static int isLocked(); // true if *anything* is locked (by us) static int isReadLocked(); // r or R static int somethingWriteLocked(); // w or W static bool isW(); // W static bool isR(); static bool isRW(); // R or W. i.e., we are write-exclusive static bool nested(); static bool isWriteLocked(const StringData& ns); static bool atLeastReadLocked(const StringData& ns); // true if this db is locked static void assertAtLeastReadLocked(const StringData& ns); static void assertWriteLocked(const StringData& ns); static bool dbLevelLockingEnabled(); static LockStat* globalLockStat(); static LockStat* nestableLockStat( Nestable db ); class ScopedLock; // note: avoid TempRelease when possible. not a good thing. struct TempRelease { TempRelease(); ~TempRelease(); const bool cant; // true if couldn't because of recursive locking ScopedLock *scopedLk; }; /** turn on "parallel batch writer mode". blocks all other threads. this mode is off by default. note only one thread creates a ParallelBatchWriterMode object; the rest just call iAmABatchParticipant(). Note that this lock is not released on a temprelease, just the normal lock things below. */ class ParallelBatchWriterMode : boost::noncopyable { RWLockRecursive::Exclusive _lk; public: ParallelBatchWriterMode() : _lk(_batchLock) {} static void iAmABatchParticipant(); static RWLockRecursive &_batchLock; }; private: class ParallelBatchWriterSupport : boost::noncopyable { public: ParallelBatchWriterSupport(); private: void tempRelease(); void relock(); scoped_ptr _lk; friend class ScopedLock; }; public: class ScopedLock : boost::noncopyable { public: virtual ~ScopedLock(); /** @return micros since we started acquiring */ long long acquireFinished( LockStat* stat ); // Accrue elapsed lock time since last we called reset void recordTime(); // Start recording a new period, starting now() void resetTime(); protected: explicit ScopedLock( char type ); private: friend struct TempRelease; void tempRelease(); // TempRelease class calls these void relock(); protected: virtual void _tempRelease() = 0; virtual void _relock() = 0; private: ParallelBatchWriterSupport _pbws_lk; void _recordTime( long long micros ); Timer _timer; char _type; // 'r','w','R','W' LockStat* _stat; // the stat for the relevant lock to increment when we're done }; // note that for these classes recursive locking is ok if the recursive locking "makes sense" // i.e. you could grab globalread after globalwrite. class GlobalWrite : public ScopedLock { bool noop; protected: void _tempRelease(); void _relock(); public: // stopGreed is removed and does NOT work // timeoutms is only for writelocktry -- deprecated -- do not use GlobalWrite(bool stopGreed = false, int timeoutms = -1 ); virtual ~GlobalWrite(); void downgrade(); // W -> R void upgrade(); // caution see notes }; class GlobalRead : public ScopedLock { // recursive is ok public: bool noop; protected: void _tempRelease(); void _relock(); public: // timeoutms is only for readlocktry -- deprecated -- do not use GlobalRead( int timeoutms = -1 ); virtual ~GlobalRead(); }; // lock this database. do not shared_lock globally first, that is handledin herein. class DBWrite : public ScopedLock { /** * flow * 1) lockDB * a) lockTop * b) lockNestable or lockOther * 2) unlockDB */ void lockTop(LockState&); void lockNestable(Nestable db); void lockOther(const StringData& db); void lockDB(const string& ns); void unlockDB(); protected: void _tempRelease(); void _relock(); public: DBWrite(const StringData& dbOrNs); virtual ~DBWrite(); class UpgradeToExclusive : private boost::noncopyable { public: UpgradeToExclusive(); ~UpgradeToExclusive(); bool gotUpgrade() const { return _gotUpgrade; } private: bool _gotUpgrade; }; private: bool _locked_w; bool _locked_W; WrapperForRWLock *_weLocked; const string _what; bool _nested; }; // lock this database for reading. do not shared_lock globally first, that is handledin herein. class DBRead : public ScopedLock { void lockTop(LockState&); void lockNestable(Nestable db); void lockOther(const StringData& db); void lockDB(const string& ns); void unlockDB(); protected: void _tempRelease(); void _relock(); public: DBRead(const StringData& dbOrNs); virtual ~DBRead(); private: bool _locked_r; WrapperForRWLock *_weLocked; string _what; bool _nested; }; }; class readlocktry : boost::noncopyable { bool _got; scoped_ptr _dbrlock; public: readlocktry( int tryms ); ~readlocktry(); bool got() const { return _got; } }; class writelocktry : boost::noncopyable { bool _got; scoped_ptr _dbwlock; public: writelocktry( int tryms ); ~writelocktry(); bool got() const { return _got; } }; /** a mutex, but reported in curop() - thus a "high level" (HL) one some overhead so we don't use this for everything. the externalobjsort mutex uses this, as it can be held for eons. implementation still needed. */ class HLMutex : public SimpleMutex { LockStat ls; public: HLMutex(const char *name); }; }