diff options
author | Daniel Gottlieb <daniel.gottlieb@10gen.com> | 2017-02-26 12:01:02 -0500 |
---|---|---|
committer | Daniel Gottlieb <daniel.gottlieb@10gen.com> | 2017-02-26 12:01:02 -0500 |
commit | b94dd91adb6d8859ab5e5f91731be2e8571eed0c (patch) | |
tree | ef3116315d0f79c9e4f690fffc5bdabb93f4751c | |
parent | ce23378926659bc50604032782485c2f962c37ac (diff) | |
download | mongo-b94dd91adb6d8859ab5e5f91731be2e8571eed0c.tar.gz |
revert "SERVER-18410: Replace RWLock with OperationContext/LockManager"
This reverts commit 9c9bbf3318113cfdd4d9b72f493b6ebd23f1837e.
51 files changed, 1824 insertions, 474 deletions
diff --git a/src/mongo/client/examples/mongoperf.cpp b/src/mongo/client/examples/mongoperf.cpp index 04f83044955..0e45db2ff4d 100644 --- a/src/mongo/client/examples/mongoperf.cpp +++ b/src/mongo/client/examples/mongoperf.cpp @@ -52,7 +52,6 @@ #include "mongo/util/allocator.h" #include "mongo/util/mongoutils/str.h" #include "mongo/util/processinfo.h" -#include "mongo/util/scopeguard.h" #include "mongo/util/time_support.h" #include "mongo/util/timer.h" @@ -173,13 +172,7 @@ void go() { recSizeKB = 4; verify(recSizeKB <= 64000 && recSizeKB > 0); - auto txn = cc().makeOperationContext(); - MemoryMappedFile f(txn.get()); - ON_BLOCK_EXIT([&f, &txn] { - LockMongoFilesExclusive lock(txn.get()); - f.close(txn.get()); - }); - + MemoryMappedFile f; cout << "creating test file size:"; len = options["fileSizeMB"].numberLong(); if (len == 0) @@ -216,8 +209,8 @@ void go() { if (o["mmf"].trueValue()) { delete lf; lf = 0; - mmfFile = new MemoryMappedFile(txn.get()); - mmf = (char*)mmfFile->map(txn.get(), fname); + mmfFile = new MemoryMappedFile(); + mmf = (char*)mmfFile->map(fname); verify(mmf); syncDelaySecs = options["syncDelay"].numberInt(); diff --git a/src/mongo/db/commands/fsync.cpp b/src/mongo/db/commands/fsync.cpp index 01d922cec02..a2d4c3c2651 100644 --- a/src/mongo/db/commands/fsync.cpp +++ b/src/mongo/db/commands/fsync.cpp @@ -150,7 +150,7 @@ public: // Take a global IS lock to ensure the storage engine is not shutdown Lock::GlobalLock global(txn->lockState(), MODE_IS, UINT_MAX); StorageEngine* storageEngine = getGlobalServiceContext()->getGlobalStorageEngine(); - result.append("numFiles", storageEngine->flushAllFiles(txn, sync)); + result.append("numFiles", storageEngine->flushAllFiles(sync)); return true; } @@ -362,7 +362,7 @@ void FSyncLockThread::run() { StorageEngine* storageEngine = getGlobalServiceContext()->getGlobalStorageEngine(); try { - storageEngine->flushAllFiles(&txn, true); + storageEngine->flushAllFiles(true); } catch (const std::exception& e) { error() << "error doing flushAll: " << e.what(); fsyncCmd.threadStatus = Status(ErrorCodes::CommandFailed, e.what()); diff --git a/src/mongo/db/concurrency/d_concurrency.cpp b/src/mongo/db/concurrency/d_concurrency.cpp index 4a11ecdba2b..a2aead7cdb4 100644 --- a/src/mongo/db/concurrency/d_concurrency.cpp +++ b/src/mongo/db/concurrency/d_concurrency.cpp @@ -127,14 +127,6 @@ std::string Lock::ResourceMutex::getName(ResourceId resourceId) { return ResourceIdFactory::nameForId(resourceId); } -bool Lock::ResourceMutex::isExclusivelyLocked(Locker* locker) { - return locker->isLockHeldForMode(_rid, MODE_X); -} - -bool Lock::ResourceMutex::isAtLeastReadLocked(Locker* locker) { - return locker->isLockHeldForMode(_rid, MODE_IS); -} - Lock::GlobalLock::GlobalLock(Locker* locker, LockMode lockMode, unsigned timeoutMs) : GlobalLock(locker, lockMode, EnqueueOnly()) { waitForLock(timeoutMs); diff --git a/src/mongo/db/concurrency/d_concurrency.h b/src/mongo/db/concurrency/d_concurrency.h index fe8ac0f49a1..9617bc985a0 100644 --- a/src/mongo/db/concurrency/d_concurrency.h +++ b/src/mongo/db/concurrency/d_concurrency.h @@ -119,10 +119,6 @@ public: static std::string getName(ResourceId resourceId); - bool isExclusivelyLocked(Locker* locker); - - bool isAtLeastReadLocked(Locker* locker); - private: friend class Lock::SharedLock; friend class Lock::ExclusiveLock; diff --git a/src/mongo/db/query/query_yield.cpp b/src/mongo/db/query/query_yield.cpp index 6548a662a17..893184fb14f 100644 --- a/src/mongo/db/query/query_yield.cpp +++ b/src/mongo/db/query/query_yield.cpp @@ -60,7 +60,7 @@ void QueryYield::yieldAllLocks(OperationContext* txn, Locker::LockSnapshot snapshot; if (fetcher) { - fetcher->setup(txn); + fetcher->setup(); } // Nothing was unlocked, just return, yielding is pointless. diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp index 908febd4c76..1cb67f64a53 100644 --- a/src/mongo/db/repl/oplog.cpp +++ b/src/mongo/db/repl/oplog.cpp @@ -534,7 +534,7 @@ void createOplog(OperationContext* txn, const std::string& oplogCollectionName, /* sync here so we don't get any surprising lag later when we try to sync */ StorageEngine* storageEngine = getGlobalServiceContext()->getGlobalStorageEngine(); - storageEngine->flushAllFiles(txn, true); + storageEngine->flushAllFiles(true); log() << "******" << endl; } diff --git a/src/mongo/db/storage/kv/kv_engine.h b/src/mongo/db/storage/kv/kv_engine.h index 04c5acb1dfa..e6dd1f8cbbe 100644 --- a/src/mongo/db/storage/kv/kv_engine.h +++ b/src/mongo/db/storage/kv/kv_engine.h @@ -91,7 +91,7 @@ public: virtual Status dropIdent(OperationContext* opCtx, StringData ident) = 0; // optional - virtual int flushAllFiles(OperationContext* txn, bool sync) { + virtual int flushAllFiles(bool sync) { return 0; } diff --git a/src/mongo/db/storage/kv/kv_storage_engine.cpp b/src/mongo/db/storage/kv/kv_storage_engine.cpp index ee02c447bdf..f825cb3a1c2 100644 --- a/src/mongo/db/storage/kv/kv_storage_engine.cpp +++ b/src/mongo/db/storage/kv/kv_storage_engine.cpp @@ -251,8 +251,8 @@ Status KVStorageEngine::dropDatabase(OperationContext* txn, StringData db) { return Status::OK(); } -int KVStorageEngine::flushAllFiles(OperationContext* txn, bool sync) { - return _engine->flushAllFiles(txn, sync); +int KVStorageEngine::flushAllFiles(bool sync) { + return _engine->flushAllFiles(sync); } Status KVStorageEngine::beginBackup(OperationContext* txn) { diff --git a/src/mongo/db/storage/kv/kv_storage_engine.h b/src/mongo/db/storage/kv/kv_storage_engine.h index ba656ae85c1..32bab2ee252 100644 --- a/src/mongo/db/storage/kv/kv_storage_engine.h +++ b/src/mongo/db/storage/kv/kv_storage_engine.h @@ -93,7 +93,7 @@ public: virtual Status dropDatabase(OperationContext* txn, StringData db); - virtual int flushAllFiles(OperationContext* txn, bool sync); + virtual int flushAllFiles(bool sync); virtual Status beginBackup(OperationContext* txn); diff --git a/src/mongo/db/storage/mmap_v1/SConscript b/src/mongo/db/storage/mmap_v1/SConscript index ba8fd96c097..4fa273fe408 100644 --- a/src/mongo/db/storage/mmap_v1/SConscript +++ b/src/mongo/db/storage/mmap_v1/SConscript @@ -115,11 +115,9 @@ env.Library( 'mmap_${TARGET_OS_FAMILY}.cpp', ], LIBDEPS=[ - '$BUILD_DIR/mongo/db/concurrency/lock_manager', - '$BUILD_DIR/mongo/db/service_context', - '$BUILD_DIR/mongo/db/storage/storage_options', - '$BUILD_DIR/mongo/util/progress_meter', 'file_allocator', + '$BUILD_DIR/mongo/util/progress_meter', + '$BUILD_DIR/mongo/db/storage/storage_options' ], ) diff --git a/src/mongo/db/storage/mmap_v1/catalog/namespace_index.cpp b/src/mongo/db/storage/mmap_v1/catalog/namespace_index.cpp index 173c2afceca..ffca8474db3 100644 --- a/src/mongo/db/storage/mmap_v1/catalog/namespace_index.cpp +++ b/src/mongo/db/storage/mmap_v1/catalog/namespace_index.cpp @@ -51,10 +51,8 @@ using std::endl; using std::list; using std::string; -NamespaceIndex::NamespaceIndex(OperationContext* txn, - const std::string& dir, - const std::string& database) - : _dir(dir), _database(database), _f(txn, MongoFile::Options::SEQUENTIAL), _ht(nullptr) {} +NamespaceIndex::NamespaceIndex(const std::string& dir, const std::string& database) + : _dir(dir), _database(database), _ht(nullptr) {} NamespaceIndex::~NamespaceIndex() {} @@ -158,7 +156,7 @@ void NamespaceIndex::init(OperationContext* txn) { void* p = 0; if (boost::filesystem::exists(nsPath)) { - if (_f.open(txn, pathString)) { + if (_f.open(pathString)) { len = _f.length(); if (len % (1024 * 1024) != 0) { @@ -217,7 +215,7 @@ void NamespaceIndex::init(OperationContext* txn) { massert(18826, str::stream() << "failure writing file " << pathString, !file.bad()); } - if (_f.create(txn, pathString, l)) { + if (_f.create(pathString, l)) { // The writes done in this function must not be rolled back. This will leave the // file empty, but available for future use. That is why we go directly to the // global dur dirty list rather than going through the OperationContext. diff --git a/src/mongo/db/storage/mmap_v1/catalog/namespace_index.h b/src/mongo/db/storage/mmap_v1/catalog/namespace_index.h index 51aae08ea61..8bcf836518c 100644 --- a/src/mongo/db/storage/mmap_v1/catalog/namespace_index.h +++ b/src/mongo/db/storage/mmap_v1/catalog/namespace_index.h @@ -51,17 +51,9 @@ class NamespaceIndex { MONGO_DISALLOW_COPYING(NamespaceIndex); public: - NamespaceIndex(OperationContext* txn, const std::string& dir, const std::string& database); + NamespaceIndex(const std::string& dir, const std::string& database); ~NamespaceIndex(); - /** - * Must be called before destruction. - */ - void close(OperationContext* txn) { - LockMongoFilesExclusive lock(txn); - _f.close(txn); - } - /* returns true if the file represented by this file exists on disk */ bool pathExists() const; @@ -94,7 +86,7 @@ private: const std::string _dir; const std::string _database; - DurableMappedFile _f; + DurableMappedFile _f{MongoFile::Options::SEQUENTIAL}; std::unique_ptr<NamespaceHashTable> _ht; }; } diff --git a/src/mongo/db/storage/mmap_v1/data_file.cpp b/src/mongo/db/storage/mmap_v1/data_file.cpp index d81aa591817..c90ff1f74da 100644 --- a/src/mongo/db/storage/mmap_v1/data_file.cpp +++ b/src/mongo/db/storage/mmap_v1/data_file.cpp @@ -104,14 +104,14 @@ int DataFile::_defaultSize() const { } /** @return true if found and opened. if uninitialized (prealloc only) does not open. */ -Status DataFile::openExisting(OperationContext* txn, const char* filename) { +Status DataFile::openExisting(const char* filename) { invariant(_mb == 0); if (!boost::filesystem::exists(filename)) { return Status(ErrorCodes::InvalidPath, "DataFile::openExisting - file does not exist"); } - if (!mmf.open(txn, filename)) { + if (!mmf.open(filename)) { return Status(ErrorCodes::InternalError, "DataFile::openExisting - mmf.open failed"); } @@ -170,7 +170,7 @@ void DataFile::open(OperationContext* txn, { invariant(_mb == 0); unsigned long long sz = size; - if (mmf.create(txn, filename, sz)) { + if (mmf.create(filename, sz)) { _mb = mmf.getView(); } diff --git a/src/mongo/db/storage/mmap_v1/data_file.h b/src/mongo/db/storage/mmap_v1/data_file.h index 57b5fb223f9..e28a5ebcc6e 100644 --- a/src/mongo/db/storage/mmap_v1/data_file.h +++ b/src/mongo/db/storage/mmap_v1/data_file.h @@ -195,10 +195,10 @@ public: class DataFile { public: - DataFile(OperationContext* txn, int fn) : _fileNo(fn), mmf(txn), _mb(NULL) {} + DataFile(int fn) : _fileNo(fn), _mb(NULL) {} /** @return true if found and opened. if uninitialized (prealloc only) does not open. */ - Status openExisting(OperationContext* txn, const char* filename); + Status openExisting(const char* filename); /** creates if DNE */ void open(OperationContext* txn, @@ -206,14 +206,6 @@ public: int requestedDataSize = 0, bool preallocateOnly = false); - /** - * Must be called before destruction. - */ - void close(OperationContext* txn) { - LockMongoFilesExclusive lock(txn); - mmf.close(txn); - } - DiskLoc allocExtentArea(OperationContext* txn, int size); DataFileHeader* getHeader() { diff --git a/src/mongo/db/storage/mmap_v1/data_file_sync.cpp b/src/mongo/db/storage/mmap_v1/data_file_sync.cpp index e1bc51d29f3..24be15045f0 100644 --- a/src/mongo/db/storage/mmap_v1/data_file_sync.cpp +++ b/src/mongo/db/storage/mmap_v1/data_file_sync.cpp @@ -35,7 +35,6 @@ #include "mongo/db/client.h" #include "mongo/db/commands/server_status_metric.h" #include "mongo/db/diag_log.h" -#include "mongo/db/operation_context.h" #include "mongo/db/service_context.h" #include "mongo/db/storage/mmap_v1/dur_journal.h" #include "mongo/db/storage/mmap_v1/mmap.h" @@ -81,12 +80,11 @@ void DataFileSync::run() { break; } - auto txn = cc().makeOperationContext(); Date_t start = jsTime(); StorageEngine* storageEngine = getGlobalServiceContext()->getGlobalStorageEngine(); dur::notifyPreDataFileFlush(); - int numFiles = storageEngine->flushAllFiles(txn.get(), true); + int numFiles = storageEngine->flushAllFiles(true); dur::notifyPostDataFileFlush(); time_flushing = durationCount<Milliseconds>(jsTime() - start); @@ -127,7 +125,7 @@ class MemJournalServerStatusMetric : public ServerStatusMetric { public: MemJournalServerStatusMetric() : ServerStatusMetric(".mem.mapped") {} virtual void appendAtLeaf(BSONObjBuilder& b) const { - int m = MemoryMappedFile::totalMappedLengthInMB(); + int m = static_cast<int>(MemoryMappedFile::totalMappedLength() / (1024 * 1024)); b.appendNumber("mapped", m); if (storageGlobalParams.dur) { @@ -135,5 +133,6 @@ public: b.appendNumber("mappedWithJournal", m); } } + } memJournalServerStatusMetric; } diff --git a/src/mongo/db/storage/mmap_v1/dur.cpp b/src/mongo/db/storage/mmap_v1/dur.cpp index 1ed496c3a6d..ab1d1988cba 100644 --- a/src/mongo/db/storage/mmap_v1/dur.cpp +++ b/src/mongo/db/storage/mmap_v1/dur.cpp @@ -204,7 +204,7 @@ public: return false; } virtual void closingFileNotification() {} - virtual void commitAndStopDurThread(OperationContext* txn) {} + virtual void commitAndStopDurThread() {} }; @@ -226,7 +226,7 @@ public: return true; } virtual void closingFileNotification(); - virtual void commitAndStopDurThread(OperationContext* txn); + virtual void commitAndStopDurThread(); void start(ClockSource* cs, int64_t serverStartMs); @@ -318,7 +318,7 @@ void debugValidateFileMapsMatch(const DurableMappedFile* mmf) { /** * Main code of the remap private view function. */ -void remapPrivateViewImpl(OperationContext* txn, double fraction) { +void remapPrivateViewImpl(double fraction) { LOG(4) << "journal REMAPPRIVATEVIEW" << endl; // There is no way that the set of files can change while we are in this method, because @@ -335,9 +335,9 @@ void remapPrivateViewImpl(OperationContext* txn, double fraction) { // See SERVER-5680 to see why this code is necessary on Windows. // See SERVER-8795 to see why this code is necessary on Solaris. #if defined(_WIN32) || defined(__sun) - LockMongoFilesExclusive lk(txn); + LockMongoFilesExclusive lk; #else - LockMongoFilesShared lk(txn); + LockMongoFilesShared lk; #endif std::set<MongoFile*>& files = MongoFile::getAllFiles(); @@ -381,7 +381,7 @@ void remapPrivateViewImpl(OperationContext* txn, double fraction) { } if (mmf->willNeedRemap()) { - mmf->remapThePrivateView(txn); + mmf->remapThePrivateView(); } i++; @@ -570,7 +570,7 @@ void DurableImpl::syncDataAndTruncateJournal(OperationContext* txn) { commitNow(txn); // Flush the shared view to disk. - MongoFile::flushAll(txn, true); + MongoFile::flushAll(true); // Once the shared view has been flushed, we do not need the journal files anymore. journalCleanup(true); @@ -588,7 +588,7 @@ void DurableImpl::closingFileNotification() { } } -void DurableImpl::commitAndStopDurThread(OperationContext* txn) { +void DurableImpl::commitAndStopDurThread() { CommitNotifier::When when = commitNotify.now(); // There is always just one waiting anyways @@ -600,7 +600,7 @@ void DurableImpl::commitAndStopDurThread(OperationContext* txn) { applyToDataFilesNotify.waitFor(when); // Flush the shared view to disk. - MongoFile::flushAll(txn, true); + MongoFile::flushAll(true); // Once the shared view has been flushed, we do not need the journal files anymore. journalCleanup(true); @@ -630,14 +630,14 @@ void DurableImpl::start(ClockSource* cs, int64_t serverStartMs) { * @param fraction Value between (0, 1] indicating what fraction of the memory to remap. * Remapping too much or too frequently incurs copy-on-write page fault cost. */ -static void remapPrivateView(OperationContext* txn, double fraction) { +static void remapPrivateView(double fraction) { // Remapping private views must occur after WRITETODATAFILES otherwise we wouldn't see any // newly written data on reads. invariant(!commitJob.hasWritten()); try { Timer t; - remapPrivateViewImpl(txn, fraction); + remapPrivateViewImpl(fraction); stats.curr()->_remapPrivateViewMicros += t.micros(); LOG(4) << "remapPrivateView end"; @@ -828,7 +828,7 @@ static void durThread(ClockSource* cs, int64_t serverStartMs) { // accessing it. Technically this step could be avoided on systems, which // support atomic remap. autoFlushLock.upgradeFlushLockToExclusive(); - remapPrivateView(txnPtr.get(), remapFraction); + remapPrivateView(remapFraction); autoFlushLock.release(); diff --git a/src/mongo/db/storage/mmap_v1/dur.h b/src/mongo/db/storage/mmap_v1/dur.h index e4aec954749..cf5e4ec2b14 100644 --- a/src/mongo/db/storage/mmap_v1/dur.h +++ b/src/mongo/db/storage/mmap_v1/dur.h @@ -112,7 +112,7 @@ public: * * Must be called under the global X lock. */ - virtual void commitAndStopDurThread(OperationContext* txn) = 0; + virtual void commitAndStopDurThread() = 0; /** * Commits pending changes, flushes all changes to main data files, then removes the diff --git a/src/mongo/db/storage/mmap_v1/dur_journal_writer.cpp b/src/mongo/db/storage/mmap_v1/dur_journal_writer.cpp index 5c9fe117d52..4eb0b4ea16b 100644 --- a/src/mongo/db/storage/mmap_v1/dur_journal_writer.cpp +++ b/src/mongo/db/storage/mmap_v1/dur_journal_writer.cpp @@ -56,14 +56,12 @@ namespace { * (2) TODO should we do this using N threads? Would be quite easy see Hackenberg paper table * 5 and 6. 2 threads might be a good balance. */ -void WRITETODATAFILES(OperationContext* txn, - const JSectHeader& h, - const AlignedBuilder& uncompressed) { +void WRITETODATAFILES(const JSectHeader& h, const AlignedBuilder& uncompressed) { Timer t; LOG(4) << "WRITETODATAFILES BEGIN"; - RecoveryJob::get().processSection(txn, &h, uncompressed.buf(), uncompressed.len(), NULL); + RecoveryJob::get().processSection(&h, uncompressed.buf(), uncompressed.len(), NULL); const long long m = t.micros(); stats.curr()->_writeToDataFilesMicros += m; @@ -246,7 +244,7 @@ void JournalWriter::_journalWriterThread() { // Apply the journal entries on top of the shared view so that when flush is // requested it would write the latest. - WRITETODATAFILES(cc().makeOperationContext().get(), buffer->_header, buffer->_builder); + WRITETODATAFILES(buffer->_header, buffer->_builder); // Data is now persisted on the shared view, so notify any potential journal file // cleanup waiters. diff --git a/src/mongo/db/storage/mmap_v1/dur_recover.cpp b/src/mongo/db/storage/mmap_v1/dur_recover.cpp index cdd3d4e3db2..ec6b945455e 100644 --- a/src/mongo/db/storage/mmap_v1/dur_recover.cpp +++ b/src/mongo/db/storage/mmap_v1/dur_recover.cpp @@ -58,7 +58,6 @@ #include "mongo/util/hex.h" #include "mongo/util/log.h" #include "mongo/util/mongoutils/str.h" -#include "mongo/util/scopeguard.h" #include "mongo/util/startup_test.h" namespace mongo { @@ -265,26 +264,22 @@ RecoveryJob::RecoveryJob() _appliedAnySections(false) {} RecoveryJob::~RecoveryJob() { - invariant(!"RecoveryJob is intentionally leaked with a bare call to operator new()"); + DESTRUCTOR_GUARD(if (!_mmfs.empty()) {} close();) } -void RecoveryJob::close(OperationContext* txn) { +void RecoveryJob::close() { stdx::lock_guard<stdx::mutex> lk(_mx); - _close(txn); + _close(); } -void RecoveryJob::_close(OperationContext* txn) { - MongoFile::flushAll(txn, true); - LockMongoFilesExclusive lock(txn); - for (auto& durFile : _mmfs) { - durFile->close(txn); - } +void RecoveryJob::_close() { + MongoFile::flushAll(true); _mmfs.clear(); } -RecoveryJob::Last::Last(OperationContext* txn) : _txn(txn), mmf(NULL), fileNo(-1) { +RecoveryJob::Last::Last() : mmf(NULL), fileNo(-1) { // Make sure the files list does not change from underneath - LockMongoFilesShared::assertAtLeastReadLocked(txn); + LockMongoFilesShared::assertAtLeastReadLocked(); } DurableMappedFile* RecoveryJob::Last::newEntry(const dur::ParsedJournalEntry& entry, @@ -296,7 +291,7 @@ DurableMappedFile* RecoveryJob::Last::newEntry(const dur::ParsedJournalEntry& en string fn = fileName(entry.dbName, num); MongoFile* file; { - MongoFileFinder finder(_txn); // must release lock before creating new DurableMappedFile + MongoFileFinder finder; // must release lock before creating new DurableMappedFile file = finder.findByPath(fn); } @@ -308,8 +303,8 @@ DurableMappedFile* RecoveryJob::Last::newEntry(const dur::ParsedJournalEntry& en log() << "journal error applying writes, file " << fn << " is not open" << endl; verify(false); } - std::shared_ptr<DurableMappedFile> sp(new DurableMappedFile(_txn)); - verify(sp->open(_txn, fn)); + std::shared_ptr<DurableMappedFile> sp(new DurableMappedFile); + verify(sp->open(fn)); rj._mmfs.push_back(sp); mmf = sp.get(); } @@ -363,14 +358,14 @@ void RecoveryJob::applyEntry(Last& last, const ParsedJournalEntry& entry, bool a } if (apply) { if (entry.op->needFilesClosed()) { - _close(last.txn()); // locked in processSection + _close(); // locked in processSection } entry.op->replay(); } } } -void RecoveryJob::applyEntries(OperationContext* txn, const vector<ParsedJournalEntry>& entries) { +void RecoveryJob::applyEntries(const vector<ParsedJournalEntry>& entries) { const bool apply = (mmapv1GlobalOptions.journalOptions & MMAPV1Options::JournalScanOnly) == 0; const bool dump = (mmapv1GlobalOptions.journalOptions & MMAPV1Options::JournalDumpJournal); @@ -378,7 +373,7 @@ void RecoveryJob::applyEntries(OperationContext* txn, const vector<ParsedJournal log() << "BEGIN section" << endl; } - Last last(txn); + Last last; for (vector<ParsedJournalEntry>::const_iterator i = entries.begin(); i != entries.end(); ++i) { applyEntry(last, *i, apply, dump); } @@ -388,12 +383,11 @@ void RecoveryJob::applyEntries(OperationContext* txn, const vector<ParsedJournal } } -void RecoveryJob::processSection(OperationContext* txn, - const JSectHeader* h, +void RecoveryJob::processSection(const JSectHeader* h, const void* p, unsigned len, const JSectFooter* f) { - LockMongoFilesShared lkFiles(txn); // for RecoveryJob::Last + LockMongoFilesShared lkFiles; // for RecoveryJob::Last stdx::lock_guard<stdx::mutex> lk(_mx); if (_recovering) { @@ -467,14 +461,14 @@ void RecoveryJob::processSection(OperationContext* txn, } // got all the entries for one group commit. apply them: - applyEntries(txn, entries); + applyEntries(entries); } /** apply a specific journal file, that is already mmap'd @param p start of the memory mapped file @return true if this is detected to be the last file (ends abruptly) */ -bool RecoveryJob::processFileBuffer(OperationContext* txn, const void* p, unsigned len) { +bool RecoveryJob::processFileBuffer(const void* p, unsigned len) { try { unsigned long long fileId; BufReader br(p, len); @@ -529,7 +523,7 @@ bool RecoveryJob::processFileBuffer(OperationContext* txn, const void* p, unsign const char* hdr = (const char*)br.skip(h.sectionLenWithPadding()); const char* data = hdr + sizeof(JSectHeader); const char* footer = data + dataLen; - processSection(txn, (const JSectHeader*)hdr, data, dataLen, (const JSectFooter*)footer); + processSection((const JSectHeader*)hdr, data, dataLen, (const JSectFooter*)footer); // ctrl c check uassert(ErrorCodes::Interrupted, @@ -550,7 +544,7 @@ bool RecoveryJob::processFileBuffer(OperationContext* txn, const void* p, unsign } /** apply a specific journal file */ -bool RecoveryJob::processFile(OperationContext* txn, boost::filesystem::path journalfile) { +bool RecoveryJob::processFile(boost::filesystem::path journalfile) { log() << "recover " << journalfile.string() << endl; try { @@ -564,20 +558,16 @@ bool RecoveryJob::processFile(OperationContext* txn, boost::filesystem::path jou log() << "recover exception checking filesize" << endl; } - MemoryMappedFile f{txn, MongoFile::Options::READONLY | MongoFile::Options::SEQUENTIAL}; - ON_BLOCK_EXIT([&f, &txn] { - LockMongoFilesExclusive lock(txn); - f.close(txn); - }); - void* p = f.map(txn, journalfile.string().c_str()); + MemoryMappedFile f{MongoFile::Options::READONLY | MongoFile::Options::SEQUENTIAL}; + void* p = f.map(journalfile.string().c_str()); massert(13544, str::stream() << "recover error couldn't open " << journalfile.string(), p); - return processFileBuffer(txn, p, (unsigned)f.length()); + return processFileBuffer(p, (unsigned)f.length()); } /** @param files all the j._0 style files we need to apply for recovery */ -void RecoveryJob::go(OperationContext* txn, vector<boost::filesystem::path>& files) { +void RecoveryJob::go(vector<boost::filesystem::path>& files) { log() << "recover begin" << endl; - LockMongoFilesExclusive lkFiles(txn); // for RecoveryJob::Last + LockMongoFilesExclusive lkFiles; // for RecoveryJob::Last _recovering = true; // load the last sequence number synced to the datafiles on disk before the last crash @@ -585,11 +575,11 @@ void RecoveryJob::go(OperationContext* txn, vector<boost::filesystem::path>& fil log() << "recover lsn: " << _lastDataSyncedFromLastRun << endl; for (unsigned i = 0; i != files.size(); ++i) { - bool abruptEnd = processFile(txn, files[i]); + bool abruptEnd = processFile(files[i]); if (abruptEnd && i + 1 < files.size()) { log() << "recover error: abrupt end to file " << files[i].string() << ", yet it isn't the last journal file" << endl; - close(txn); + close(); uasserted(13535, "recover abrupt journal file end"); } } @@ -600,7 +590,7 @@ void RecoveryJob::go(OperationContext* txn, vector<boost::filesystem::path>& fil << "Last skipped sections had sequence number " << _lastSeqSkipped; } - close(txn); + close(); if (mmapv1GlobalOptions.journalOptions & MMAPV1Options::JournalScanOnly) { uasserted(13545, @@ -615,7 +605,7 @@ void RecoveryJob::go(OperationContext* txn, vector<boost::filesystem::path>& fil _recovering = false; } -void _recover(OperationContext* txn) { +void _recover() { verify(storageGlobalParams.dur); boost::filesystem::path p = getJournalDir(); @@ -635,7 +625,7 @@ void _recover(OperationContext* txn) { return; } - RecoveryJob::get().go(txn, journalFiles); + RecoveryJob::get().go(journalFiles); } /** recover from a crash @@ -645,18 +635,18 @@ void _recover(OperationContext* txn) { void replayJournalFilesAtStartup() { // we use a lock so that exitCleanly will wait for us // to finish (or at least to notice what is up and stop) - auto txn = cc().makeOperationContext(); - ScopedTransaction transaction(txn.get(), MODE_X); - Lock::GlobalWrite lk(txn->lockState()); + const ServiceContext::UniqueOperationContext txnPtr = cc().makeOperationContext(); + OperationContext& txn = *txnPtr; + ScopedTransaction transaction(&txn, MODE_X); + Lock::GlobalWrite lk(txn.lockState()); - _recover(txn.get()); // throws on interruption + _recover(); // throws on interruption } struct BufReaderY { int a, b; }; class BufReaderUnitTest : public StartupTest { - public: void run() { BufReader r((void*)"abcdabcdabcd", 12); diff --git a/src/mongo/db/storage/mmap_v1/dur_recover.h b/src/mongo/db/storage/mmap_v1/dur_recover.h index 9447044b607..b9b7c5ddfdb 100644 --- a/src/mongo/db/storage/mmap_v1/dur_recover.h +++ b/src/mongo/db/storage/mmap_v1/dur_recover.h @@ -34,7 +34,6 @@ #include <list> #include <memory> -#include "mongo/db/service_context.h" #include "mongo/db/storage/mmap_v1/dur_journalformat.h" #include "mongo/stdx/mutex.h" #include "mongo/util/concurrency/mutex.h" @@ -56,17 +55,13 @@ public: RecoveryJob(); ~RecoveryJob(); - void go(OperationContext* txn, std::vector<boost::filesystem::path>& files); + void go(std::vector<boost::filesystem::path>& files); /** @param data data between header and footer. compressed if recovering. */ - void processSection(OperationContext* txn, - const JSectHeader* h, - const void* data, - unsigned len, - const JSectFooter* f); + void processSection(const JSectHeader* h, const void* data, unsigned len, const JSectFooter* f); // locks and calls _close() - void close(OperationContext* txn); + void close(); static RecoveryJob& get() { return _instance; @@ -75,16 +70,10 @@ public: private: class Last { public: - Last(OperationContext* txn); - + Last(); DurableMappedFile* newEntry(const ParsedJournalEntry&, RecoveryJob&); - OperationContext* txn() { - return _txn; - } - private: - OperationContext* _txn; DurableMappedFile* mmf; std::string dbName; int fileNo; @@ -93,10 +82,11 @@ private: void write(Last& last, const ParsedJournalEntry& entry); // actually writes to the file void applyEntry(Last& last, const ParsedJournalEntry& entry, bool apply, bool dump); - void applyEntries(OperationContext* txn, const std::vector<ParsedJournalEntry>& entries); - bool processFileBuffer(OperationContext* txn, const void*, unsigned len); - bool processFile(OperationContext* txn, boost::filesystem::path journalfile); - void _close(OperationContext* txn); // doesn't lock + void applyEntries(const std::vector<ParsedJournalEntry>& entries); + bool processFileBuffer(const void*, unsigned len); + bool processFile(boost::filesystem::path journalfile); + void _close(); // doesn't lock + // Set of memory mapped files and a mutex to protect them stdx::mutex _mx; diff --git a/src/mongo/db/storage/mmap_v1/durable_mapped_file.cpp b/src/mongo/db/storage/mmap_v1/durable_mapped_file.cpp index 548cb8c9f05..a7e8e2e429f 100644 --- a/src/mongo/db/storage/mmap_v1/durable_mapped_file.cpp +++ b/src/mongo/db/storage/mmap_v1/durable_mapped_file.cpp @@ -61,7 +61,7 @@ using std::map; using std::pair; using std::string; -void DurableMappedFile::remapThePrivateView(OperationContext* txn) { +void DurableMappedFile::remapThePrivateView() { verify(storageGlobalParams.dur); _willNeedRemap = false; @@ -70,7 +70,7 @@ void DurableMappedFile::remapThePrivateView(OperationContext* txn) { // so the remove / add isn't necessary and can be removed? void* old = _view_private; // privateViews.remove(_view_private); - _view_private = remapPrivateView(txn, _view_private); + _view_private = remapPrivateView(_view_private); // privateViews.add(_view_private, this); fassert(16112, _view_private == old); } @@ -241,24 +241,22 @@ void DurableMappedFile::setPath(const std::string& f) { _p = RelativePath::fromFullPath(storageGlobalParams.dbpath, prefix); } -bool DurableMappedFile::open(OperationContext* txn, const std::string& fname) { +bool DurableMappedFile::open(const std::string& fname) { LOG(3) << "mmf open " << fname; invariant(!_view_write); setPath(fname); - _view_write = map(txn, fname.c_str()); + _view_write = map(fname.c_str()); fassert(16333, _view_write); return finishOpening(); } -bool DurableMappedFile::create(OperationContext* txn, - const std::string& fname, - unsigned long long& len) { +bool DurableMappedFile::create(const std::string& fname, unsigned long long& len) { LOG(3) << "mmf create " << fname; invariant(!_view_write); setPath(fname); - _view_write = map(txn, fname.c_str(), len); + _view_write = map(fname.c_str(), len); fassert(16332, _view_write); return finishOpening(); } @@ -285,7 +283,12 @@ bool DurableMappedFile::finishOpening() { return false; } -void DurableMappedFile::close(OperationContext* txn) { +DurableMappedFile::DurableMappedFile(OptionSet options) + : MemoryMappedFile(options), _willNeedRemap(false) { + _view_write = _view_private = 0; +} + +DurableMappedFile::~DurableMappedFile() { try { LOG(3) << "mmf close " << filename(); @@ -296,20 +299,12 @@ void DurableMappedFile::close(OperationContext* txn) { getDur().closingFileNotification(); } + LockMongoFilesExclusive lk; privateViews.remove(_view_private, length()); - MemoryMappedFile::close(txn); + MemoryMappedFile::close(); } catch (...) { - error() << "exception in DurableMappedFile::close"; + error() << "exception in ~DurableMappedFile"; } } - -DurableMappedFile::DurableMappedFile(OperationContext* txn, OptionSet options) - : MemoryMappedFile(txn, options), _willNeedRemap(false) { - _view_write = _view_private = 0; -} - -DurableMappedFile::~DurableMappedFile() { - invariant(isClosed()); -} } diff --git a/src/mongo/db/storage/mmap_v1/durable_mapped_file.h b/src/mongo/db/storage/mmap_v1/durable_mapped_file.h index 7c6e99bfc7e..3175fa8fa73 100644 --- a/src/mongo/db/storage/mmap_v1/durable_mapped_file.h +++ b/src/mongo/db/storage/mmap_v1/durable_mapped_file.h @@ -32,7 +32,6 @@ #pragma once #include "mongo/base/static_assert.h" -#include "mongo/db/operation_context.h" #include "mongo/db/storage/mmap_v1/mmap.h" #include "mongo/db/storage/paths.h" #include "mongo/stdx/mutex.h" @@ -51,16 +50,11 @@ protected: } public: - explicit DurableMappedFile(OperationContext* txn, OptionSet options = NONE); + DurableMappedFile(OptionSet options = NONE); virtual ~DurableMappedFile(); - /** - * Callers must be holding a `LockMongoFilesExclusive`. - */ - virtual void close(OperationContext* txn); - /** @return true if opened ok. */ - bool open(OperationContext* txn, const std::string& fname); + bool open(const std::string& fname); /** @return file length */ unsigned long long length() const { @@ -79,7 +73,7 @@ public: passed length. @return true for ok */ - bool create(OperationContext* txn, const std::string& fname, unsigned long long& len); + bool create(const std::string& fname, unsigned long long& len); /* Get the "standard" view (which is the private one). @return the private view. @@ -123,7 +117,7 @@ public: _willNeedRemap = true; } - void remapThePrivateView(OperationContext* txn); + void remapThePrivateView(); virtual bool isDurableMappedFile() { return true; diff --git a/src/mongo/db/storage/mmap_v1/extent_manager.h b/src/mongo/db/storage/mmap_v1/extent_manager.h index 1ca0ab7b9f1..4ce623ffc8a 100644 --- a/src/mongo/db/storage/mmap_v1/extent_manager.h +++ b/src/mongo/db/storage/mmap_v1/extent_manager.h @@ -77,8 +77,6 @@ public: virtual ~ExtentManager() {} - virtual void close(OperationContext* txn) = 0; - /** * opens all current files */ diff --git a/src/mongo/db/storage/mmap_v1/mmap.cpp b/src/mongo/db/storage/mmap_v1/mmap.cpp index bdce2bd6468..c8eaacd2f63 100644 --- a/src/mongo/db/storage/mmap_v1/mmap.cpp +++ b/src/mongo/db/storage/mmap_v1/mmap.cpp @@ -36,10 +36,8 @@ #include <boost/filesystem/operations.hpp> #include "mongo/base/owned_pointer_vector.h" -#include "mongo/db/client.h" -#include "mongo/db/concurrency/locker.h" -#include "mongo/db/operation_context.h" #include "mongo/db/storage/storage_options.h" +#include "mongo/util/concurrency/rwlock.h" #include "mongo/util/log.h" #include "mongo/util/map_util.h" #include "mongo/util/mongoutils/str.h" @@ -69,33 +67,16 @@ map<string, MongoFile*> pathToFile; mongo::AtomicUInt64 mmfNextId(0); } // namespace -MemoryMappedFile::MemoryMappedFile(OperationContext* txn, OptionSet options) +MemoryMappedFile::MemoryMappedFile(OptionSet options) : MongoFile(options), _uniqueId(mmfNextId.fetchAndAdd(1)) { - created(txn); + created(); } -MemoryMappedFile::~MemoryMappedFile() { - invariant(isClosed()); - - auto txn = cc().getOperationContext(); - invariant(txn); - - LockMongoFilesShared lock(txn); - for (std::set<MongoFile*>::const_iterator it = mmfiles.begin(); it != mmfiles.end(); it++) { - invariant(*it != this); - } -} - -/*static*/ AtomicUInt64 MemoryMappedFile::totalMappedLength; - -void* MemoryMappedFile::create(OperationContext* txn, - const std::string& filename, - unsigned long long len, - bool zero) { +void* MemoryMappedFile::create(const std::string& filename, unsigned long long len, bool zero) { uassert(13468, string("can't create file already exists ") + filename, !boost::filesystem::exists(filename)); - void* p = map(txn, filename.c_str(), len); + void* p = map(filename.c_str(), len); fassert(16331, p); if (zero) { size_t sz = (size_t)len; @@ -113,7 +94,7 @@ void* MemoryMappedFile::create(OperationContext* txn, length = l; } -void* MemoryMappedFile::map(OperationContext* txn, const char* filename) { +void* MemoryMappedFile::map(const char* filename) { unsigned long long l; try { l = boost::filesystem::file_size(filename); @@ -125,7 +106,7 @@ void* MemoryMappedFile::map(OperationContext* txn, const char* filename) { << e.what()); } - void* ret = map(txn, filename, l); + void* ret = map(filename, l); fassert(16334, ret); return ret; } @@ -138,7 +119,7 @@ MongoFile::MongoFile(OptionSet options) : _options(storageGlobalParams.readOnly ? (options | READONLY) : options) {} -Lock::ResourceMutex LockMongoFilesShared::mmmutex("MMapMutex"); +RWLockRecursiveNongreedy LockMongoFilesShared::mmmutex("mmmutex", 10 * 60 * 1000 /* 10 minutes */); unsigned LockMongoFilesShared::era = 99; // note this rolls over set<MongoFile*>& MongoFile::getAllFiles() { @@ -150,14 +131,14 @@ set<MongoFile*>& MongoFile::getAllFiles() { safe to call more than once, albeit might be wasted work ideal to call close to the close, if the close is well before object destruction */ -void MongoFile::destroyed(OperationContext* txn) { - LockMongoFilesShared::assertExclusivelyLocked(txn); +void MongoFile::destroyed() { + LockMongoFilesShared::assertExclusivelyLocked(); mmfiles.erase(this); pathToFile.erase(filename()); } /*static*/ -void MongoFile::closeAllFiles(OperationContext* txn, stringstream& message) { +void MongoFile::closeAllFiles(stringstream& message) { static int closingAllFiles = 0; if (closingAllFiles) { message << "warning closingAllFiles=" << closingAllFiles << endl; @@ -165,26 +146,37 @@ void MongoFile::closeAllFiles(OperationContext* txn, stringstream& message) { } ++closingAllFiles; - LockMongoFilesExclusive lk(txn); + LockMongoFilesExclusive lk; ProgressMeter pm(mmfiles.size(), 2, 1, "files", "File Closing Progress"); set<MongoFile*> temp = mmfiles; for (set<MongoFile*>::iterator i = temp.begin(); i != temp.end(); i++) { - (*i)->close(txn); // close() now removes from mmfiles + (*i)->close(); // close() now removes from mmfiles pm.hit(); } message << "closeAllFiles() finished"; --closingAllFiles; } -/*static*/ int MongoFile::flushAll(OperationContext* txn, bool sync) { - return _flushAll(txn, sync); +/*static*/ long long MongoFile::totalMappedLength() { + unsigned long long total = 0; + + LockMongoFilesShared lk; + + for (set<MongoFile*>::iterator i = mmfiles.begin(); i != mmfiles.end(); i++) + total += (*i)->length(); + + return total; +} + +/*static*/ int MongoFile::flushAll(bool sync) { + return _flushAll(sync); } -/*static*/ int MongoFile::_flushAll(OperationContext* txn, bool sync) { +/*static*/ int MongoFile::_flushAll(bool sync) { if (!sync) { int num = 0; - LockMongoFilesShared lk(txn); + LockMongoFilesShared lk; for (set<MongoFile*>::iterator i = mmfiles.begin(); i != mmfiles.end(); i++) { num++; MongoFile* mmf = *i; @@ -204,7 +196,7 @@ void MongoFile::closeAllFiles(OperationContext* txn, stringstream& message) { OwnedPointerVector<Flushable> thingsToFlushWrapper; vector<Flushable*>& thingsToFlush = thingsToFlushWrapper.mutableVector(); { - LockMongoFilesShared lk(txn); + LockMongoFilesShared lk; for (set<MongoFile*>::iterator i = mmfiles.begin(); i != mmfiles.end(); i++) { MongoFile* mmf = *i; if (!mmf) @@ -214,22 +206,22 @@ void MongoFile::closeAllFiles(OperationContext* txn, stringstream& message) { } for (size_t i = 0; i < thingsToFlush.size(); i++) { - thingsToFlush[i]->flush(txn); + thingsToFlush[i]->flush(); } return thingsToFlush.size(); } -void MongoFile::created(OperationContext* txn) { +void MongoFile::created() { // If we're a READONLY mapping, we don't want to ever flush. if (!isOptionSet(READONLY)) { - LockMongoFilesExclusive lk(txn); + LockMongoFilesExclusive lk; mmfiles.insert(this); } } -void MongoFile::setFilename(OperationContext* txn, const std::string& fn) { - LockMongoFilesExclusive lk(txn); +void MongoFile::setFilename(const std::string& fn) { + LockMongoFilesExclusive lk; verify(_filename.empty()); _filename = boost::filesystem::absolute(fn).generic_string(); MongoFile*& ptf = pathToFile[_filename]; @@ -243,6 +235,23 @@ MongoFile* MongoFileFinder::findByPath(const std::string& path) const { static_cast<MongoFile*>(NULL)); } + +void printMemInfo(const char* where) { + LogstreamBuilder out = log(); + out << "mem info: "; + if (where) + out << where << " "; + + ProcessInfo pi; + if (!pi.supported()) { + out << " not supported"; + return; + } + + out << "vsize: " << pi.getVirtualMemorySize() << " resident: " << pi.getResidentSize() + << " mapped: " << (MemoryMappedFile::totalMappedLength() / (1024 * 1024)); +} + void dataSyncFailedHandler() { log() << "error syncing data to disk, probably a disk error"; log() << " shutting down immediately to avoid corruption"; diff --git a/src/mongo/db/storage/mmap_v1/mmap.h b/src/mongo/db/storage/mmap_v1/mmap.h index fc28d56e1d9..af559c7db63 100644 --- a/src/mongo/db/storage/mmap_v1/mmap.h +++ b/src/mongo/db/storage/mmap_v1/mmap.h @@ -33,10 +33,7 @@ #include <sstream> #include <vector> -#include "mongo/base/disallow_copying.h" -#include "mongo/db/client.h" -#include "mongo/db/concurrency/d_concurrency.h" -#include "mongo/db/operation_context.h" +#include "mongo/util/concurrency/rwlock.h" namespace mongo { @@ -65,24 +62,12 @@ private: // lock order: lock dbMutex before this if you lock both class LockMongoFilesShared { friend class LockMongoFilesExclusive; - static Lock::ResourceMutex mmmutex; + static RWLockRecursiveNongreedy mmmutex; static unsigned era; - - Lock::SharedLock lk; + RWLockRecursive::Shared lk; public: - explicit LockMongoFilesShared(OperationContext* txn) : lk(txn->lockState(), mmmutex) { - // JS worker threads may not have cc() setup, as they work on behalf of other clients - dassert(txn == cc().getOperationContext() || !cc().getOperationContext()); - } - - static void assertExclusivelyLocked(OperationContext* txn) { - invariant(mmmutex.isExclusivelyLocked(txn->lockState())); - } - - static void assertAtLeastReadLocked(OperationContext* txn) { - invariant(mmmutex.isAtLeastReadLocked(txn->lockState())); - } + LockMongoFilesShared() : lk(mmmutex) {} /** era changes anytime memory maps come and go. thus you can use this as a cheap way to check if nothing has changed since the last time you locked. Of course you must be shared locked @@ -93,16 +78,20 @@ public: static unsigned getEra() { return era; } + + static void assertExclusivelyLocked() { + mmmutex.assertExclusivelyLocked(); + } + static void assertAtLeastReadLocked() { + mmmutex.assertAtLeastReadLocked(); + } }; class LockMongoFilesExclusive { - Lock::ExclusiveLock lk; + RWLockRecursive::Exclusive lk; public: - explicit LockMongoFilesExclusive(OperationContext* txn) - : lk(txn->lockState(), LockMongoFilesShared::mmmutex) { - // JS worker threads may not have cc() setup, as they work on behalf of other clients - dassert(txn == cc().getOperationContext() || !cc().getOperationContext()); + LockMongoFilesExclusive() : lk(LockMongoFilesShared::mmmutex) { LockMongoFilesShared::era++; } }; @@ -116,7 +105,7 @@ public: class Flushable { public: virtual ~Flushable() {} - virtual void flush(OperationContext* txn) = 0; + virtual void flush() = 0; }; enum Options { @@ -135,7 +124,7 @@ public: called from within a mutex that MongoFile uses. so be careful not to deadlock. */ template <class F> - static void forEach(OperationContext* txn, F fun); + static void forEach(F fun); /** * note: you need to be in mmmutex when using this. forEach (above) handles that for you @@ -143,8 +132,9 @@ public: */ static std::set<MongoFile*>& getAllFiles(); - static int flushAll(OperationContext* txn, bool sync); // returns n flushed - static void closeAllFiles(OperationContext* txn, std::stringstream& message); + static int flushAll(bool sync); // returns n flushed + static long long totalMappedLength(); + static void closeAllFiles(std::stringstream& message); virtual bool isDurableMappedFile() { return false; @@ -153,20 +143,17 @@ public: std::string filename() const { return _filename; } - void setFilename(OperationContext* txn, const std::string& fn); + void setFilename(const std::string& fn); virtual uint64_t getUniqueId() const = 0; private: std::string _filename; - static int _flushAll(OperationContext* txn, bool sync); // returns n flushed + static int _flushAll(bool sync); // returns n flushed const OptionSet _options; protected: - /** - * Implementations may assume this is called from within `LockMongoFilesExclusive`. - */ - virtual void close(OperationContext* txn) = 0; + virtual void close() = 0; virtual void flush(bool sync) = 0; /** * returns a thread safe object that you can call flush on @@ -174,22 +161,14 @@ protected: */ virtual Flushable* prepareFlush() = 0; - /** - * Returns true iff the file is closed. - */ - virtual bool isClosed() = 0; - - void created(OperationContext* txn); /* subclass must call after create */ + void created(); /* subclass must call after create */ - /** - * Implementations may assume this is called from within `LockMongoFilesExclusive`. - * - * subclass must call in destructor (or at close). - * removes this from pathToFile and other maps - * safe to call more than once, albeit might be wasted work - * ideal to call close to the close, if the close is well before object destruction - */ - void destroyed(OperationContext* txn); + /* subclass must call in destructor (or at close). + removes this from pathToFile and other maps + safe to call more than once, albeit might be wasted work + ideal to call close to the close, if the close is well before object destruction + */ + void destroyed(); virtual unsigned long long length() const = 0; @@ -208,7 +187,7 @@ class MongoFileFinder { MONGO_DISALLOW_COPYING(MongoFileFinder); public: - MongoFileFinder(OperationContext* txn) : _lk(txn) {} + MongoFileFinder() {} /** @return The MongoFile object associated with the specified file name. If no file is open with the specified name, returns null. @@ -229,33 +208,27 @@ protected: } public: - MemoryMappedFile(OperationContext* txn, OptionSet options = NONE); + MemoryMappedFile(OptionSet options = NONE); - virtual ~MemoryMappedFile(); + virtual ~MemoryMappedFile() { + LockMongoFilesExclusive lk; + close(); + } - /** - * Callers must be holding a `LockMongoFilesExclusive`. - */ - virtual void close(OperationContext* txn); + virtual void close(); /** * uasserts if file doesn't exist. fasserts on mmap error. */ - void* map(OperationContext* txn, const char* filename); + void* map(const char* filename); /** * uasserts if file exists. fasserts on mmap error. * @param zero fill file with zeros when true */ - void* create(OperationContext* txn, - const std::string& filename, - unsigned long long len, - bool zero); + void* create(const std::string& filename, unsigned long long len, bool zero); void flush(bool sync); - - virtual bool isClosed(); - virtual Flushable* prepareFlush(); long shortLength() const { @@ -278,10 +251,6 @@ public: return _uniqueId; } - static int totalMappedLengthInMB() { - return static_cast<int>(totalMappedLength.load() / 1024 / 1024); - } - private: static void updateLength(const char* filename, unsigned long long& length); @@ -289,7 +258,6 @@ private: HANDLE maphandle = 0; std::vector<void*> views; unsigned long long len = 0u; - static AtomicUInt64 totalMappedLength; const uint64_t _uniqueId; #ifdef _WIN32 // flush Mutex @@ -307,18 +275,18 @@ protected: * Creates with length if DNE, otherwise validates input length. Returns nullptr on mmap * error. */ - void* map(OperationContext* txn, const char* filename, unsigned long long& length); + void* map(const char* filename, unsigned long long& length); /** * Close the current private view and open a new replacement. Returns nullptr on mmap error. */ - void* remapPrivateView(OperationContext* txn, void* oldPrivateAddr); + void* remapPrivateView(void* oldPrivateAddr); }; /** p is called from within a mutex that MongoFile uses. so be careful not to deadlock. */ template <class F> -inline void MongoFile::forEach(OperationContext* txn, F p) { - LockMongoFilesShared lklk(txn); +inline void MongoFile::forEach(F p) { + LockMongoFilesShared lklk; const std::set<MongoFile*>& mmfiles = MongoFile::getAllFiles(); for (std::set<MongoFile*>::const_iterator i = mmfiles.begin(); i != mmfiles.end(); i++) p(*i); diff --git a/src/mongo/db/storage/mmap_v1/mmap_posix.cpp b/src/mongo/db/storage/mmap_v1/mmap_posix.cpp index 02589421b44..e63e74923a7 100644 --- a/src/mongo/db/storage/mmap_v1/mmap_posix.cpp +++ b/src/mongo/db/storage/mmap_v1/mmap_posix.cpp @@ -54,23 +54,6 @@ using namespace mongoutils; namespace mongo { - -namespace { -void printMemInfo() { - LogstreamBuilder out = log(); - out << "mem info: "; - - ProcessInfo pi; - if (!pi.supported()) { - out << " not supported"; - return; - } - - out << "vsize: " << pi.getVirtualMemorySize() << " resident: " << pi.getResidentSize() - << " mapped: " << MemoryMappedFile::totalMappedLengthInMB(); -} -} // namespace - static size_t fetchMinOSPageSizeBytes() { size_t minOSPageSizeBytes = sysconf(_SC_PAGESIZE); minOSPageSizeBytesTest(minOSPageSizeBytes); @@ -79,19 +62,17 @@ static size_t fetchMinOSPageSizeBytes() { const size_t g_minOSPageSizeBytes = fetchMinOSPageSizeBytes(); -void MemoryMappedFile::close(OperationContext* txn) { +void MemoryMappedFile::close() { + LockMongoFilesShared::assertExclusivelyLocked(); for (vector<void*>::iterator i = views.begin(); i != views.end(); i++) { munmap(*i, len); } views.clear(); - totalMappedLength.fetchAndSubtract(len); - len = 0; - if (fd) { + if (fd) ::close(fd); - fd = 0; - } - destroyed(txn); // cleans up from the master list of mmaps + fd = 0; + destroyed(); // cleans up from the master list of mmaps } #ifndef O_NOATIME @@ -159,12 +140,11 @@ MAdvise::~MAdvise() { } #endif -void* MemoryMappedFile::map(OperationContext* txn, - const char* filename, - unsigned long long& length) { +void* MemoryMappedFile::map(const char* filename, unsigned long long& length) { // length may be updated by callee. - setFilename(txn, filename); + setFilename(filename); FileAllocator::get()->allocateAsap(filename, length); + len = length; const bool readOnly = isOptionSet(READONLY); @@ -213,10 +193,6 @@ void* MemoryMappedFile::map(OperationContext* txn, } #endif - // MemoryMappedFile successfully created, now update state. - len = length; - MemoryMappedFile::totalMappedLength.fetchAndAdd(len); - views.push_back(view); return view; @@ -243,9 +219,9 @@ void* MemoryMappedFile::createPrivateMap() { return x; } -void* MemoryMappedFile::remapPrivateView(OperationContext* txn, void* oldPrivateAddr) { +void* MemoryMappedFile::remapPrivateView(void* oldPrivateAddr) { #if defined(__sun) // SERVER-8795 - LockMongoFilesExclusive lockMongoFiles(txn); + LockMongoFilesExclusive lockMongoFiles; #endif // don't unmap, just mmap over the old region @@ -279,16 +255,12 @@ void MemoryMappedFile::flush(bool sync) { } } -bool MemoryMappedFile::isClosed() { - return !len && !fd && !views.size(); -} - class PosixFlushable : public MemoryMappedFile::Flushable { public: PosixFlushable(MemoryMappedFile* theFile, void* view, HANDLE fd, long len) : _theFile(theFile), _view(view), _fd(fd), _len(len), _id(_theFile->getUniqueId()) {} - void flush(OperationContext* txn) { + void flush() { if (_view == NULL || _fd == 0) return; @@ -303,7 +275,7 @@ public: } // some error, lets see if we're supposed to exist - LockMongoFilesShared mmfilesLock(txn); + LockMongoFilesShared mmfilesLock; std::set<MongoFile*> mmfs = MongoFile::getAllFiles(); std::set<MongoFile*>::const_iterator it = mmfs.find(_theFile); if ((it == mmfs.end()) || ((*it)->getUniqueId() != _id)) { @@ -329,4 +301,5 @@ MemoryMappedFile::Flushable* MemoryMappedFile::prepareFlush() { return new PosixFlushable(this, viewForFlushing(), fd, len); } + } // namespace mongo diff --git a/src/mongo/db/storage/mmap_v1/mmap_v1_database_catalog_entry.cpp b/src/mongo/db/storage/mmap_v1/mmap_v1_database_catalog_entry.cpp index 69978fb4b53..f27dcc935e8 100644 --- a/src/mongo/db/storage/mmap_v1/mmap_v1_database_catalog_entry.cpp +++ b/src/mongo/db/storage/mmap_v1/mmap_v1_database_catalog_entry.cpp @@ -56,7 +56,6 @@ #include "mongo/db/storage/mmap_v1/record_store_v1_simple.h" #include "mongo/db/storage/record_data.h" #include "mongo/util/log.h" -#include "mongo/util/scopeguard.h" namespace mongo { @@ -163,12 +162,8 @@ MMAPV1DatabaseCatalogEntry::MMAPV1DatabaseCatalogEntry(OperationContext* txn, std::unique_ptr<ExtentManager> extentManager) : DatabaseCatalogEntry(name), _path(path.toString()), - _namespaceIndex(txn, _path, name.toString()), + _namespaceIndex(_path, name.toString()), _extentManager(std::move(extentManager)) { - ScopeGuard onErrorClose = MakeGuard([&] { - _namespaceIndex.close(txn); - _extentManager->close(txn); - }); massert(34469, str::stream() << name << " is not a valid database name", NamespaceString::validDBName(name)); @@ -198,8 +193,6 @@ MMAPV1DatabaseCatalogEntry::MMAPV1DatabaseCatalogEntry(OperationContext* txn, warning() << "database " << path << " " << name << " could not be opened " << e.what(); throw; } - - onErrorClose.Dismiss(); } MMAPV1DatabaseCatalogEntry::~MMAPV1DatabaseCatalogEntry() { diff --git a/src/mongo/db/storage/mmap_v1/mmap_v1_database_catalog_entry.h b/src/mongo/db/storage/mmap_v1/mmap_v1_database_catalog_entry.h index ea4342bb868..5934ded8a11 100644 --- a/src/mongo/db/storage/mmap_v1/mmap_v1_database_catalog_entry.h +++ b/src/mongo/db/storage/mmap_v1/mmap_v1_database_catalog_entry.h @@ -62,14 +62,6 @@ public: virtual ~MMAPV1DatabaseCatalogEntry(); - /** - * Must be called before destruction. - */ - virtual void close(OperationContext* txn) { - _extentManager->close(txn); - _namespaceIndex.close(txn); - } - // these two seem the same and yet different // TODO(ERH): consolidate into one ideally virtual bool exists() const { diff --git a/src/mongo/db/storage/mmap_v1/mmap_v1_engine.cpp b/src/mongo/db/storage/mmap_v1/mmap_v1_engine.cpp index 36af8f3f06a..e185afe03c7 100644 --- a/src/mongo/db/storage/mmap_v1/mmap_v1_engine.cpp +++ b/src/mongo/db/storage/mmap_v1/mmap_v1_engine.cpp @@ -36,9 +36,7 @@ #include <boost/filesystem/path.hpp> #include <fstream> -#include "mongo/db/client.h" #include "mongo/db/mongod_options.h" -#include "mongo/db/operation_context.h" #include "mongo/db/storage/mmap_v1/data_file_sync.h" #include "mongo/db/storage/mmap_v1/dur.h" #include "mongo/db/storage/mmap_v1/dur_journal.h" @@ -313,9 +311,6 @@ Status MMAPV1Engine::closeDatabase(OperationContext* txn, StringData db) { stdx::lock_guard<stdx::mutex> lk(_entryMapMutex); MMAPV1DatabaseCatalogEntry* entry = _entryMap[db.toString()]; - if (entry) { - entry->close(txn); - } delete entry; _entryMap.erase(db.toString()); return Status::OK(); @@ -350,8 +345,8 @@ void MMAPV1Engine::_listDatabases(const std::string& directory, std::vector<std: } } -int MMAPV1Engine::flushAllFiles(OperationContext* txn, bool sync) { - return MongoFile::flushAll(txn, sync); +int MMAPV1Engine::flushAllFiles(bool sync) { + return MongoFile::flushAll(sync); } Status MMAPV1Engine::beginBackup(OperationContext* txn) { @@ -379,32 +374,21 @@ void MMAPV1Engine::cleanShutdown() { // we would only hang here if the file_allocator code generates a // synchronous signal, which we don't expect log() << "shutdown: waiting for fs preallocator..." << endl; - auto txn = cc().getOperationContext(); - - // In some cases we may shutdown early before we have any operation context yet, but we need - // one for synchronization purposes. - ServiceContext::UniqueOperationContext newTxn; - if (!txn) { - newTxn = cc().makeOperationContext(); - txn = newTxn.get(); - invariant(txn); - } - FileAllocator::get()->waitUntilFinished(); if (storageGlobalParams.dur) { log() << "shutdown: final commit..." << endl; - getDur().commitAndStopDurThread(txn); + getDur().commitAndStopDurThread(); } log() << "shutdown: closing all files..." << endl; stringstream ss3; - MemoryMappedFile::closeAllFiles(txn, ss3); + MemoryMappedFile::closeAllFiles(ss3); log() << ss3.str() << endl; } void MMAPV1Engine::setJournalListener(JournalListener* jl) { dur::setJournalListener(jl); } -} // namespace +} diff --git a/src/mongo/db/storage/mmap_v1/mmap_v1_engine.h b/src/mongo/db/storage/mmap_v1/mmap_v1_engine.h index b5d19950d7b..54e8594e053 100644 --- a/src/mongo/db/storage/mmap_v1/mmap_v1_engine.h +++ b/src/mongo/db/storage/mmap_v1/mmap_v1_engine.h @@ -57,7 +57,7 @@ public: RecoveryUnit* newRecoveryUnit(); void listDatabases(std::vector<std::string>* out) const; - int flushAllFiles(OperationContext* txn, bool sync); + int flushAllFiles(bool sync); Status beginBackup(OperationContext* txn); void endBackup(OperationContext* txn); diff --git a/src/mongo/db/storage/mmap_v1/mmap_v1_extent_manager.cpp b/src/mongo/db/storage/mmap_v1/mmap_v1_extent_manager.cpp index 3f9b6019802..761fecfb075 100644 --- a/src/mongo/db/storage/mmap_v1/mmap_v1_extent_manager.cpp +++ b/src/mongo/db/storage/mmap_v1/mmap_v1_extent_manager.cpp @@ -79,9 +79,9 @@ class MmapV1RecordFetcher : public RecordFetcher { public: explicit MmapV1RecordFetcher(const MmapV1RecordHeader* record) : _record(record) {} - virtual void setup(OperationContext* txn) { + virtual void setup() { invariant(!_filesLock.get()); - _filesLock.reset(new LockMongoFilesShared(txn)); + _filesLock.reset(new LockMongoFilesShared()); } virtual void fetch() { @@ -172,11 +172,10 @@ Status MmapV1ExtentManager::init(OperationContext* txn) { } } - unique_ptr<DataFile> df(new DataFile(txn, n)); + unique_ptr<DataFile> df(new DataFile(n)); - Status s = df->openExisting(txn, fullNameString.c_str()); + Status s = df->openExisting(fullNameString.c_str()); if (!s.isOK()) { - df->close(txn); return s; } @@ -241,17 +240,12 @@ DataFile* MmapV1ExtentManager::_addAFile(OperationContext* txn, } { - unique_ptr<DataFile> allocFile(new DataFile(txn, allocFileId)); + unique_ptr<DataFile> allocFile(new DataFile(allocFileId)); const string allocFileName = _fileName(allocFileId).string(); Timer t; - try { - allocFile->open(txn, allocFileName.c_str(), minSize, false); - } catch (...) { - allocFile->close(txn); - throw; - } + allocFile->open(txn, allocFileName.c_str(), minSize, false); if (t.seconds() > 1) { log() << "MmapV1ExtentManager took " << t.seconds() << " seconds to open: " << allocFileName; @@ -263,15 +257,10 @@ DataFile* MmapV1ExtentManager::_addAFile(OperationContext* txn, // Preallocate is asynchronous if (preallocateNextFile) { - unique_ptr<DataFile> nextFile(new DataFile(txn, allocFileId + 1)); + unique_ptr<DataFile> nextFile(new DataFile(allocFileId + 1)); const string nextFileName = _fileName(allocFileId + 1).string(); - try { - nextFile->open(txn, nextFileName.c_str(), minSize, false); - } catch (...) { - nextFile->close(txn); - throw; - } + nextFile->open(txn, nextFileName.c_str(), minSize, false); } // Returns the last file added @@ -644,12 +633,6 @@ MmapV1ExtentManager::FilesArray::~FilesArray() { } } -void MmapV1ExtentManager::FilesArray::close(OperationContext* txn) { - for (int i = 0; i < size(); i++) { - _files[i]->close(txn); - } -} - void MmapV1ExtentManager::FilesArray::push_back(DataFile* val) { stdx::lock_guard<stdx::mutex> lk(_writersMutex); const int n = _size.load(); diff --git a/src/mongo/db/storage/mmap_v1/mmap_v1_extent_manager.h b/src/mongo/db/storage/mmap_v1/mmap_v1_extent_manager.h index fb891ee8227..573396f76af 100644 --- a/src/mongo/db/storage/mmap_v1/mmap_v1_extent_manager.h +++ b/src/mongo/db/storage/mmap_v1/mmap_v1_extent_manager.h @@ -90,13 +90,6 @@ public: MmapV1ExtentManager(StringData dbname, StringData path, bool directoryPerDB); /** - * Must be called before destruction. - */ - void close(OperationContext* txn) { - _files.close(txn); - } - - /** * opens all current files, not thread safe */ Status init(OperationContext* txn); @@ -217,11 +210,6 @@ private: ~FilesArray(); /** - * Must be called before destruction. - */ - void close(OperationContext* txn); - - /** * Returns file at location 'n' in the array, with 'n' less than number of files added. * Will always return the same pointer for a given file. */ diff --git a/src/mongo/db/storage/mmap_v1/mmap_windows.cpp b/src/mongo/db/storage/mmap_v1/mmap_windows.cpp index d8e8d61e624..0922e5de7ea 100644 --- a/src/mongo/db/storage/mmap_v1/mmap_windows.cpp +++ b/src/mongo/db/storage/mmap_v1/mmap_windows.cpp @@ -148,8 +148,8 @@ static void* getNextMemoryMappedFileLocation(unsigned long long mmfSize) { return reinterpret_cast<void*>(static_cast<uintptr_t>(thisMemoryMappedFileLocation)); } -void MemoryMappedFile::close(OperationContext* txn) { - LockMongoFilesShared::assertExclusivelyLocked(txn); +void MemoryMappedFile::close() { + LockMongoFilesShared::assertExclusivelyLocked(); // Prevent flush and close from concurrently running stdx::lock_guard<stdx::mutex> lk(_flushMutex); @@ -163,29 +163,20 @@ void MemoryMappedFile::close(OperationContext* txn) { } views.clear(); - totalMappedLength.fetchAndSubtract(len); - len = 0; - if (maphandle) CloseHandle(maphandle); maphandle = 0; - if (fd) { + if (fd) CloseHandle(fd); - fd = 0; - } - - destroyed(txn); // cleans up from the master list of mmaps + fd = 0; + destroyed(); // cleans up from the master list of mmaps } -bool MemoryMappedFile::isClosed() { - return !len && !fd && !views.size(); -} +unsigned long long mapped = 0; -void* MemoryMappedFile::map(OperationContext* txn, - const char* filenameIn, - unsigned long long& length) { +void* MemoryMappedFile::map(const char* filenameIn, unsigned long long& length) { verify(fd == 0 && len == 0); // can't open more than once - setFilename(txn, filenameIn); + setFilename(filenameIn); FileAllocator::get()->allocateAsap(filenameIn, length); /* big hack here: Babble uses db names with colons. doesn't seem to work on windows. temporary * perhaps. */ @@ -231,6 +222,8 @@ void* MemoryMappedFile::map(OperationContext* txn, } } + mapped += length; + { DWORD flProtect = readOnly ? PAGE_READONLY : PAGE_READWRITE; maphandle = CreateFileMappingW(fd, @@ -244,8 +237,7 @@ void* MemoryMappedFile::map(OperationContext* txn, severe() << "CreateFileMappingW for " << filename << " failed with " << errnoWithDescription(dosError) << " (file size is " << length << ")" << " in MemoryMappedFile::map" << endl; - LockMongoFilesExclusive lock(txn); - close(txn); + close(); fassertFailed(16225); } } @@ -296,8 +288,7 @@ void* MemoryMappedFile::map(OperationContext* txn, << length << ")" << " in MemoryMappedFile::map" << endl; - LockMongoFilesExclusive lock(txn); - close(txn); + close(); fassertFailed(16166); } @@ -305,12 +296,8 @@ void* MemoryMappedFile::map(OperationContext* txn, } } - // MemoryMappedFile successfully created, now update state. - len = length; - totalMappedLength.fetchAndAdd(len); - views.push_back(view); - + len = length; return view; } @@ -359,8 +346,8 @@ void* MemoryMappedFile::createPrivateMap() { return privateMapAddress; } -void* MemoryMappedFile::remapPrivateView(OperationContext* txn, void* oldPrivateAddr) { - LockMongoFilesExclusive lockMongoFiles(txn); +void* MemoryMappedFile::remapPrivateView(void* oldPrivateAddr) { + LockMongoFilesExclusive lockMongoFiles; privateViews.clearWritableBits(oldPrivateAddr, len); @@ -406,12 +393,12 @@ public: _filename(filename), _flushMutex(flushMutex) {} - void flush(OperationContext* txn) { + void flush() { if (!_view || !_fd) return; { - LockMongoFilesShared mmfilesLock(txn); + LockMongoFilesShared mmfilesLock; std::set<MongoFile*> mmfs = MongoFile::getAllFiles(); std::set<MongoFile*>::const_iterator it = mmfs.find(_theFile); @@ -475,9 +462,7 @@ void MemoryMappedFile::flush(bool sync) { uassert(13056, "Async flushing not supported on windows", sync); if (!views.empty()) { WindowsFlushable f(this, viewForFlushing(), fd, _uniqueId, filename(), _flushMutex); - auto txn = cc().getOperationContext(); - invariant(txn); - f.flush(txn); + f.flush(); } } diff --git a/src/mongo/db/storage/mmap_v1/record_store_v1_test_help.cpp b/src/mongo/db/storage/mmap_v1/record_store_v1_test_help.cpp index 6f4d3993cbe..0c56ef9e6f1 100644 --- a/src/mongo/db/storage/mmap_v1/record_store_v1_test_help.cpp +++ b/src/mongo/db/storage/mmap_v1/record_store_v1_test_help.cpp @@ -204,8 +204,6 @@ DummyExtentManager::~DummyExtentManager() { } } -void DummyExtentManager::close(OperationContext* txn) {} - Status DummyExtentManager::init(OperationContext* txn) { return Status::OK(); } diff --git a/src/mongo/db/storage/mmap_v1/record_store_v1_test_help.h b/src/mongo/db/storage/mmap_v1/record_store_v1_test_help.h index eac135dd24a..1f29b59334c 100644 --- a/src/mongo/db/storage/mmap_v1/record_store_v1_test_help.h +++ b/src/mongo/db/storage/mmap_v1/record_store_v1_test_help.h @@ -113,8 +113,6 @@ class DummyExtentManager : public ExtentManager { public: virtual ~DummyExtentManager(); - virtual void close(OperationContext* txn); - virtual Status init(OperationContext* txn); virtual int numFiles() const; diff --git a/src/mongo/db/storage/mmap_v1/repair_database.cpp b/src/mongo/db/storage/mmap_v1/repair_database.cpp index 3ae8e9d7fdc..d82a89031d6 100644 --- a/src/mongo/db/storage/mmap_v1/repair_database.cpp +++ b/src/mongo/db/storage/mmap_v1/repair_database.cpp @@ -253,7 +253,7 @@ public: getDur().syncDataAndTruncateJournal(_txn); // need both in case journaling is disabled - MongoFile::flushAll(_txn, true); + MongoFile::flushAll(true); MONGO_ASSERT_ON_EXCEPTION(boost::filesystem::remove_all(_path)); } catch (DBException& e) { @@ -320,7 +320,6 @@ Status MMAPV1Engine::repairDatabase(OperationContext* txn, // Must call this before MMAPV1DatabaseCatalogEntry's destructor closes the DB files ON_BLOCK_EXIT(&dur::DurableInterface::syncDataAndTruncateJournal, &getDur(), txn); - ON_BLOCK_EXIT([&dbEntry, &txn] { dbEntry->close(txn); }); { dbEntry.reset(new MMAPV1DatabaseCatalogEntry( @@ -432,7 +431,7 @@ Status MMAPV1Engine::repairDatabase(OperationContext* txn, getDur().syncDataAndTruncateJournal(txn); // need both in case journaling is disabled - MongoFile::flushAll(txn, true); + MongoFile::flushAll(true); txn->checkForInterrupt(); } diff --git a/src/mongo/db/storage/record_fetcher.h b/src/mongo/db/storage/record_fetcher.h index e133e28bdf0..66c626ea4d5 100644 --- a/src/mongo/db/storage/record_fetcher.h +++ b/src/mongo/db/storage/record_fetcher.h @@ -30,8 +30,6 @@ namespace mongo { -class OperationContext; - /** * Used for yielding while data is fetched from disk. * @@ -44,7 +42,7 @@ public: /** * Performs any setup which is needed prior to yielding locks. */ - virtual void setup(OperationContext* txn) = 0; + virtual void setup() = 0; /** * Called after locks are yielded in order to bring data into memory. diff --git a/src/mongo/db/storage/storage_engine.h b/src/mongo/db/storage/storage_engine.h index 02e9c1ff7aa..f3dc6b1bf0f 100644 --- a/src/mongo/db/storage/storage_engine.h +++ b/src/mongo/db/storage/storage_engine.h @@ -207,7 +207,7 @@ public: /** * @return number of files flushed */ - virtual int flushAllFiles(OperationContext* txn, bool sync) = 0; + virtual int flushAllFiles(bool sync) = 0; /** * Transitions the storage engine into backup mode. diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.cpp index 682fd842aaf..0c3c006a6fa 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.cpp @@ -401,7 +401,7 @@ Status WiredTigerKVEngine::_salvageIfNeeded(const char* uri) { return wtRCToStatus(session->salvage(session, uri, NULL), "Salvage failed:"); } -int WiredTigerKVEngine::flushAllFiles(OperationContext* txn, bool sync) { +int WiredTigerKVEngine::flushAllFiles(bool sync) { LOG(1) << "WiredTigerKVEngine::flushAllFiles"; if (_ephemeral) { return 0; diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.h b/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.h index 8f632ef537c..f46b677f218 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.h +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.h @@ -108,7 +108,7 @@ public: StringData ident, const RecordStore* originalRecordStore) const; - virtual int flushAllFiles(OperationContext* txn, bool sync); + virtual int flushAllFiles(bool sync); virtual Status beginBackup(OperationContext* txn); diff --git a/src/mongo/db/write_concern.cpp b/src/mongo/db/write_concern.cpp index 77a90f0e396..6886bed64ca 100644 --- a/src/mongo/db/write_concern.cpp +++ b/src/mongo/db/write_concern.cpp @@ -199,7 +199,7 @@ Status waitForWriteConcern(OperationContext* txn, case WriteConcernOptions::SyncMode::FSYNC: { StorageEngine* storageEngine = getGlobalServiceContext()->getGlobalStorageEngine(); if (!storageEngine->isDurable()) { - result->fsyncFiles = storageEngine->flushAllFiles(txn, true); + result->fsyncFiles = storageEngine->flushAllFiles(true); } else { // We only need to commit the journal if we're durable txn->recoveryUnit()->waitUntilDurable(); diff --git a/src/mongo/dbtests/SConscript b/src/mongo/dbtests/SConscript index 558b61d316a..5db9ef95dec 100644 --- a/src/mongo/dbtests/SConscript +++ b/src/mongo/dbtests/SConscript @@ -124,6 +124,7 @@ dbtest = env.Program( "$BUILD_DIR/mongo/db/serveronly", "$BUILD_DIR/mongo/db/logical_clock", "$BUILD_DIR/mongo/db/storage/paths", + "$BUILD_DIR/mongo/util/concurrency/rwlock", "$BUILD_DIR/mongo/util/net/network", "$BUILD_DIR/mongo/util/progress_meter", "$BUILD_DIR/mongo/util/version_impl", diff --git a/src/mongo/dbtests/mmaptests.cpp b/src/mongo/dbtests/mmaptests.cpp index ab6766b72c3..2090e66d4b5 100644 --- a/src/mongo/dbtests/mmaptests.cpp +++ b/src/mongo/dbtests/mmaptests.cpp @@ -44,7 +44,6 @@ #include "mongo/db/storage/mmap_v1/mmap_v1_options.h" #include "mongo/db/storage/storage_options.h" #include "mongo/dbtests/dbtests.h" -#include "mongo/util/scopeguard.h" #include "mongo/util/timer.h" namespace MMapTests { @@ -77,16 +76,11 @@ public: MMAPV1LockerImpl lockState; Lock::GlobalWrite lk(&lockState); - auto txn = cc().makeOperationContext(); { - DurableMappedFile f(txn.get()); - ON_BLOCK_EXIT([&f, &txn] { - LockMongoFilesExclusive lock(txn.get()); - f.close(txn.get()); - }); + DurableMappedFile f; unsigned long long len = 256 * 1024 * 1024; - verify(f.create(txn.get(), fn, len)); + verify(f.create(fn, len)); { char* p = (char*)f.getView(); verify(p); @@ -99,12 +93,12 @@ public: char* w = (char*)f.view_write(); strcpy(w + 6, "world"); } - MongoFileFinder ff(txn.get()); + MongoFileFinder ff; ASSERT(ff.findByPath(fn)); ASSERT(ff.findByPath("asdf") == 0); } { - MongoFileFinder ff(txn.get()); + MongoFileFinder ff; ASSERT(ff.findByPath(fn) == 0); } @@ -118,13 +112,9 @@ public: Timer t; for (int i = 0; i < N; i++) { // Every 4 iterations we pass the sequential hint. - DurableMappedFile f{ - txn.get(), i % 4 == 1 ? MongoFile::Options::SEQUENTIAL : MongoFile::Options::NONE}; - ON_BLOCK_EXIT([&f, &txn] { - LockMongoFilesExclusive lock(txn.get()); - f.close(txn.get()); - }); - verify(f.open(txn.get(), fn)); + DurableMappedFile f{i % 4 == 1 ? MongoFile::Options::SEQUENTIAL + : MongoFile::Options::NONE}; + verify(f.open(fn)); { char* p = (char*)f.getView(); verify(p); diff --git a/src/mongo/dbtests/threadedtests.cpp b/src/mongo/dbtests/threadedtests.cpp index d73267ce7ce..498e39f71e7 100644 --- a/src/mongo/dbtests/threadedtests.cpp +++ b/src/mongo/dbtests/threadedtests.cpp @@ -45,6 +45,7 @@ #include "mongo/stdx/functional.h" #include "mongo/stdx/thread.h" #include "mongo/util/concurrency/old_thread_pool.h" +#include "mongo/util/concurrency/rwlock.h" #include "mongo/util/concurrency/ticketholder.h" #include "mongo/util/log.h" #include "mongo/util/timer.h" @@ -139,6 +140,237 @@ public: } }; +class RWLockTest1 { +public: + void run() { + RWLock lk("eliot"); + { rwlock r(lk, true, 1000); } + } +}; + +class RWLockTest2 { +public: + static void worker1(RWLockRecursiveNongreedy* lk, AtomicUInt32* x) { + x->fetchAndAdd(1); // 1 + RWLockRecursiveNongreedy::Exclusive b(*lk); + x->fetchAndAdd(1); // 2 + } + static void worker2(RWLockRecursiveNongreedy* lk, AtomicUInt32* x) { + RWLockRecursiveNongreedy::Shared c(*lk); + x->fetchAndAdd(1); + } + void run() { + /** + * note: this test will deadlock if the code breaks + */ + RWLockRecursiveNongreedy lk("eliot2", 120 * 1000); + cout << "RWLock impl: " << lk.implType() << endl; + unique_ptr<RWLockRecursiveNongreedy::Shared> a(new RWLockRecursiveNongreedy::Shared(lk)); + AtomicUInt32 x1(0); + cout << "A : " << &x1 << endl; + stdx::thread t1(stdx::bind(worker1, &lk, &x1)); + while (!x1.load()) + ; + verify(x1.load() == 1); + sleepmillis(500); + verify(x1.load() == 1); + AtomicUInt32 x2(0); + stdx::thread t2(stdx::bind(worker2, &lk, &x2)); + t2.join(); + verify(x2.load() == 1); + a.reset(); + for (int i = 0; i < 2000; i++) { + if (x1.load() == 2) + break; + sleepmillis(1); + } + verify(x1.load() == 2); + t1.join(); + } +}; + +class RWLockTest3 { +public: + static void worker2(RWLockRecursiveNongreedy* lk, AtomicUInt32* x) { + verify(!lk->__lock_try(0)); + RWLockRecursiveNongreedy::Shared c(*lk); + x->fetchAndAdd(1); + } + + void run() { + /** + * note: this test will deadlock if the code breaks + */ + + RWLockRecursiveNongreedy lk("eliot2", 120 * 1000); + + unique_ptr<RWLockRecursiveNongreedy::Shared> a(new RWLockRecursiveNongreedy::Shared(lk)); + + AtomicUInt32 x2(0); + + stdx::thread t2(stdx::bind(worker2, &lk, &x2)); + t2.join(); + verify(x2.load() == 1); + + a.reset(); + } +}; + +class RWLockTest4 { +public: +#if defined(__linux__) || defined(__APPLE__) + static void worker1(pthread_rwlock_t* lk, AtomicUInt32* x) { + x->fetchAndAdd(1); // 1 + cout << "lock b try" << endl; + while (1) { + if (pthread_rwlock_trywrlock(lk) == 0) + break; + sleepmillis(10); + } + cout << "lock b got" << endl; + x->fetchAndAdd(1); // 2 + pthread_rwlock_unlock(lk); + } + + static void worker2(pthread_rwlock_t* lk, AtomicUInt32* x) { + cout << "lock c try" << endl; + pthread_rwlock_rdlock(lk); + x->fetchAndAdd(1); + cout << "lock c got" << endl; + pthread_rwlock_unlock(lk); + } +#endif + void run() { +/** + * note: this test will deadlock if the code breaks + */ + +#if defined(__linux__) || defined(__APPLE__) + + // create + pthread_rwlock_t lk; + verify(pthread_rwlock_init(&lk, 0) == 0); + + // read lock + verify(pthread_rwlock_rdlock(&lk) == 0); + + AtomicUInt32 x1(0); + stdx::thread t1(stdx::bind(worker1, &lk, &x1)); + while (!x1.load()) + ; + verify(x1.load() == 1); + sleepmillis(500); + verify(x1.load() == 1); + + AtomicUInt32 x2(0); + + stdx::thread t2(stdx::bind(worker2, &lk, &x2)); + t2.join(); + verify(x2.load() == 1); + + pthread_rwlock_unlock(&lk); + + for (int i = 0; i < 2000; i++) { + if (x1.load() == 2) + break; + sleepmillis(1); + } + + verify(x1.load() == 2); + t1.join(); +#endif + } +}; + +// we don't use upgrade so that part is not important currently but the other aspects of this test +// are interesting; it would be nice to do analogous tests for SimpleRWLock and QLock +class UpgradableTest : public ThreadedTest<7> { + RWLock m; + +public: + UpgradableTest() : m("utest") {} + +private: + virtual void validate() {} + virtual void subthread(int x) { + Client::initThread("utest"); + + /* r = get a read lock + R = get a read lock and we expect it to be fast + u = get upgradable + U = get upgradable and we expect it to be fast + w = get a write lock + */ + // /-- verify upgrade can be done instantly while in a read lock already + // | /-- verify upgrade acquisition isn't greedy + // | | /-- verify writes aren't greedy while in upgradable(or are they?) + // v v v + const char* what = " RURuRwR"; + + sleepmillis(100 * x); + + int Z = 1; + LOG(Z) << x << ' ' << what[x] << " request" << endl; + char ch = what[x]; + switch (ch) { + case 'w': { + m.lock(); + LOG(Z) << x << " w got" << endl; + sleepmillis(100); + LOG(Z) << x << " w unlock" << endl; + m.unlock(); + } break; + case 'u': + case 'U': { + Timer t; + RWLock::Upgradable u(m); + LOG(Z) << x << ' ' << ch << " got" << endl; + if (ch == 'U') { +#if defined(NTDDI_VERSION) && defined(NTDDI_WIN7) && (NTDDI_VERSION >= NTDDI_WIN7) + // SRW locks are neither fair nor FIFO, as per docs + if (t.millis() > 2000) { +#else + if (t.millis() > 20) { +#endif + DEV { + // a debug buildbot might be slow, try to avoid false positives + mongo::unittest::log() << "warning lock upgrade was slow " << t.millis() + << endl; + } + else { + mongo::unittest::log() + << "assertion failure: lock upgrade was too slow: " << t.millis() + << endl; + ASSERT(false); + } + } + } + sleepsecs(1); + LOG(Z) << x << ' ' << ch << " unlock" << endl; + } break; + case 'r': + case 'R': { + Timer t; + m.lock_shared(); + LOG(Z) << x << ' ' << ch << " got " << endl; + if (what[x] == 'R') { + if (t.millis() > 15) { + // commented out for less chatter, we aren't using upgradeable anyway right + // now: + // log() << x << " info: when in upgradable, write locks are still greedy " + // "on this platform" << endl; + } + } + sleepmillis(200); + LOG(Z) << x << ' ' << ch << " unlock" << endl; + m.unlock_shared(); + } break; + default: + ASSERT(false); + } + } +}; + void sleepalittle() { Timer t; while (1) { @@ -219,6 +451,44 @@ private: } }; +const int WriteLocksAreGreedy_ThreadCount = 3; +class WriteLocksAreGreedy : public ThreadedTest<WriteLocksAreGreedy_ThreadCount> { +public: + WriteLocksAreGreedy() : m("gtest"), _barrier(WriteLocksAreGreedy_ThreadCount) {} + +private: + RWLock m; + boost::barrier _barrier; + virtual void validate() {} + virtual void subthread(int x) { + _barrier.wait(); + int Z = 0; + Client::initThread("utest"); + if (x == 1) { + LOG(Z) << mongo::curTimeMillis64() % 10000 << " 1" << endl; + rwlock_shared lk(m); + sleepmillis(400); + LOG(Z) << mongo::curTimeMillis64() % 10000 << " 1x" << endl; + } + if (x == 2) { + sleepmillis(100); + LOG(Z) << mongo::curTimeMillis64() % 10000 << " 2" << endl; + rwlock lk(m, true); + LOG(Z) << mongo::curTimeMillis64() % 10000 << " 2x" << endl; + } + if (x == 3) { + sleepmillis(200); + Timer t; + LOG(Z) << mongo::curTimeMillis64() % 10000 << " 3" << endl; + rwlock_shared lk(m); + LOG(Z) << mongo::curTimeMillis64() % 10000 << " 3x" << endl; + LOG(Z) << t.millis() << endl; + ASSERT(t.millis() > 50); + } + } +}; + + // Tests waiting on the TicketHolder by running many more threads than can fit into the "hotel", but // only max _nRooms threads should ever get in at once class TicketHolderWaits : public ThreadedTest<10> { @@ -289,15 +559,25 @@ public: All() : Suite("threading") {} void setupTests() { + add<WriteLocksAreGreedy>(); + // Slack is a test to see how long it takes for another thread to pick up // and begin work after another relinquishes the lock. e.g. a spin lock // would have very little slack. add<Slack<SimpleMutex, stdx::lock_guard<SimpleMutex>>>(); + add<Slack<SimpleRWLock, SimpleRWLock::Exclusive>>(); + + add<UpgradableTest>(); add<IsAtomicWordAtomic<AtomicUInt32>>(); add<IsAtomicWordAtomic<AtomicUInt64>>(); add<ThreadPoolTest>(); + add<RWLockTest1>(); + add<RWLockTest2>(); + add<RWLockTest3>(); + add<RWLockTest4>(); + add<TicketHolderWaits>(); } }; diff --git a/src/mongo/util/concurrency/SConscript b/src/mongo/util/concurrency/SConscript index df0ecc038c2..32dabcafa63 100644 --- a/src/mongo/util/concurrency/SConscript +++ b/src/mongo/util/concurrency/SConscript @@ -67,3 +67,13 @@ env.Library( '$BUILD_DIR/mongo/util/background_job', ], ) + +env.Library( + target='rwlock', + source=[ + 'rwlockimpl.cpp', + ], + LIBDEPS=[ + '$BUILD_DIR/third_party/shim_boost', + ], +) diff --git a/src/mongo/util/concurrency/rwlock.h b/src/mongo/util/concurrency/rwlock.h new file mode 100644 index 00000000000..b3988efa06b --- /dev/null +++ b/src/mongo/util/concurrency/rwlock.h @@ -0,0 +1,291 @@ +// @file rwlock.h generic reader-writer lock (cross platform support) + +/* + * Copyright (C) 2010 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 <http://www.gnu.org/licenses/>. + * + * 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. + */ + +#pragma once + +#include "mongo/util/concurrency/mutex.h" +#include "mongo/util/concurrency/rwlockimpl.h" +#include "mongo/util/concurrency/simplerwlock.h" +#include "mongo/util/concurrency/threadlocal.h" +#include "mongo/util/debug_util.h" +#include "mongo/util/time_support.h" + +namespace mongo { + +class RWLock : public RWLockBase { + enum { + NilState, + UpgradableState, + Exclusive + } x; // only bother to set when doing upgradable related things +public: + const char* const _name; + RWLock(const char* name) : _name(name) { + x = NilState; + } + void lock() { + RWLockBase::lock(); + } + void unlock() { + RWLockBase::unlock(); + } + + void lock_shared() { + RWLockBase::lock_shared(); + } + void unlock_shared() { + RWLockBase::unlock_shared(); + } + +private: + void lockAsUpgradable() { + RWLockBase::lockAsUpgradable(); + } + void unlockFromUpgradable() { // upgradable -> unlocked + RWLockBase::unlockFromUpgradable(); + } + +public: + void upgrade() { // upgradable -> exclusive lock + verify(x == UpgradableState); + RWLockBase::upgrade(); + x = Exclusive; + } + + bool lock_shared_try(int millis) { + return RWLockBase::lock_shared_try(millis); + } + + bool lock_try(int millis = 0) { + return RWLockBase::lock_try(millis); + } + + /** acquire upgradable state. You must be unlocked before creating. + unlocks on destruction, whether in upgradable state or upgraded to exclusive + in the interim. + */ + class Upgradable { + MONGO_DISALLOW_COPYING(Upgradable); + RWLock& _r; + + public: + Upgradable(RWLock& r) : _r(r) { + r.lockAsUpgradable(); + verify(_r.x == NilState); + _r.x = RWLock::UpgradableState; + } + ~Upgradable() { + if (_r.x == RWLock::UpgradableState) { + _r.x = NilState; + _r.unlockFromUpgradable(); + } else { + // TEMP verify( _r.x == Exclusive ); // has been upgraded + _r.x = NilState; + _r.unlock(); + } + } + }; +}; + +/** throws on failure to acquire in the specified time period. */ +class rwlock_try_write { + MONGO_DISALLOW_COPYING(rwlock_try_write); + +public: + struct exception {}; + rwlock_try_write(RWLock& l, int millis = 0) : _l(l) { + if (!l.lock_try(millis)) + throw exception(); + } + ~rwlock_try_write() { + _l.unlock(); + } + +private: + RWLock& _l; +}; + +class rwlock_shared { + MONGO_DISALLOW_COPYING(rwlock_shared); + +public: + rwlock_shared(RWLock& rwlock) : _r(rwlock) { + _r.lock_shared(); + } + ~rwlock_shared() { + _r.unlock_shared(); + } + +private: + RWLock& _r; +}; + +/* scoped lock for RWLock */ +class rwlock { + MONGO_DISALLOW_COPYING(rwlock); + +public: + /** + * @param write acquire write lock if true sharable if false + * @param lowPriority if > 0, will try to get the lock non-greedily for that many ms + */ + rwlock(const RWLock& lock, + bool write, + /* bool alreadyHaveLock = false , */ int lowPriorityWaitMS = 0) + : _lock((RWLock&)lock), _write(write) { + { + if (_write) { + _lock.lock(); + } else { + _lock.lock_shared(); + } + } + } + ~rwlock() { + if (_write) + _lock.unlock(); + else + _lock.unlock_shared(); + } + +private: + RWLock& _lock; + const bool _write; +}; + +// ---------------------------------------------------------------------------------------- + +/** recursive on shared locks is ok for this implementation */ +class RWLockRecursive : protected RWLockBase { +protected: + ThreadLocalValue<int> _state; + void + lock(); // not implemented - Lock() should be used; didn't overload this name to avoid mistakes + virtual void Lock() { + RWLockBase::lock(); + } + +public: + virtual ~RWLockRecursive() {} + const char* const _name; + RWLockRecursive(const char* name) : _name(name) {} + + void assertAtLeastReadLocked() { + verify(_state.get() != 0); + } + void assertExclusivelyLocked() { + verify(_state.get() < 0); + } + + class Exclusive { + MONGO_DISALLOW_COPYING(Exclusive); + RWLockRecursive& _r; + + public: + Exclusive(RWLockRecursive& r) : _r(r) { + int s = _r._state.get(); + dassert(s <= 0); + if (s == 0) + _r.Lock(); + _r._state.set(s - 1); + } + ~Exclusive() { + int s = _r._state.get(); + DEV wassert(s < 0); // wassert: don't throw from destructors + ++s; + _r._state.set(s); + if (s == 0) + _r.unlock(); + } + }; + + class Shared { + MONGO_DISALLOW_COPYING(Shared); + RWLockRecursive& _r; + bool _alreadyLockedExclusiveByUs; + + public: + Shared(RWLockRecursive& r) : _r(r) { + int s = _r._state.get(); + _alreadyLockedExclusiveByUs = s < 0; + if (!_alreadyLockedExclusiveByUs) { + dassert(s >= 0); // -1 would mean exclusive + if (s == 0) + _r.lock_shared(); + _r._state.set(s + 1); + } + } + ~Shared() { + if (_alreadyLockedExclusiveByUs) { + DEV wassert(_r._state.get() < 0); + } else { + int s = _r._state.get() - 1; + DEV wassert(s >= 0); + _r._state.set(s); + if (s == 0) + _r.unlock_shared(); + } + } + }; +}; + +class RWLockRecursiveNongreedy : public RWLockRecursive { + virtual void Lock() { + bool got = false; + for (int i = 0; i < lowPriorityWaitMS; i++) { + if (lock_try(0)) { + got = true; + break; + } + int sleep = 1; + if (i > (lowPriorityWaitMS / 20)) + sleep = 10; + sleepmillis(sleep); + i += (sleep - 1); + } + if (!got) { + RWLockBase::lock(); + } + } + +public: + const int lowPriorityWaitMS; + RWLockRecursiveNongreedy(const char* nm, int lpwaitms) + : RWLockRecursive(nm), lowPriorityWaitMS(lpwaitms) {} + const char* implType() const { + return RWLockRecursive::implType(); + } + + // just for testing: + bool __lock_try(int millis) { + return RWLockRecursive::lock_try(millis); + } +}; +} diff --git a/src/mongo/util/concurrency/rwlockimpl.cpp b/src/mongo/util/concurrency/rwlockimpl.cpp new file mode 100644 index 00000000000..755724cd3a5 --- /dev/null +++ b/src/mongo/util/concurrency/rwlockimpl.cpp @@ -0,0 +1,120 @@ +// @file rwlockimpl.cpp + +/** +* Copyright (C) 2012 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 <http://www.gnu.org/licenses/>. +* +* 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. +*/ + + +#if defined(_WIN32) +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include <windows.h> +#endif +#include <boost/version.hpp> +#include <map> +#include <set> + +#include "mongo/config.h" +#include "mongo/stdx/condition_variable.h" +#include "mongo/stdx/mutex.h" +#include "mongo/util/assert_util.h" +#include "mongo/util/concurrency/rwlockimpl.h" +#include "mongo/util/concurrency/simplerwlock.h" +#include "mongo/util/concurrency/threadlocal.h" +#include "mongo/util/time_support.h" + +using namespace std; + +namespace mongo { + +#if defined(NTDDI_VERSION) && defined(NTDDI_WIN7) && (NTDDI_VERSION >= NTDDI_WIN7) +SimpleRWLock::SimpleRWLock(StringData p) : name(p.toString()) { + InitializeSRWLock(&_lock); +} +#if defined(MONGO_CONFIG_DEBUG_BUILD) +// the code below in a debug build will check that we don't try to recursively lock, +// which is not supported by this class. also checks that you don't unlock without +// having locked +void SimpleRWLock::lock() { + unsigned me = GetCurrentThreadId(); + int& state = s.getRef(); + dassert(state == 0); + state--; + AcquireSRWLockExclusive(&_lock); + tid = me; // this is for use in the debugger to see who does have the lock +} +void SimpleRWLock::unlock() { + int& state = s.getRef(); + dassert(state == -1); + state++; + tid = 0xffffffff; + ReleaseSRWLockExclusive(&_lock); +} +void SimpleRWLock::lock_shared() { + int& state = s.getRef(); + dassert(state == 0); + state++; + AcquireSRWLockShared(&_lock); + shares.fetchAndAdd(1); +} +void SimpleRWLock::unlock_shared() { + int& state = s.getRef(); + dassert(state == 1); + state--; + shares.fetchAndSubtract(1); + ReleaseSRWLockShared(&_lock); +} +#else +void SimpleRWLock::lock() { + AcquireSRWLockExclusive(&_lock); +} +void SimpleRWLock::unlock() { + ReleaseSRWLockExclusive(&_lock); +} +void SimpleRWLock::lock_shared() { + AcquireSRWLockShared(&_lock); +} +void SimpleRWLock::unlock_shared() { + ReleaseSRWLockShared(&_lock); +} +#endif +#else +SimpleRWLock::SimpleRWLock(StringData p) : name(p.toString()) {} +void SimpleRWLock::lock() { + m.lock(); +} +void SimpleRWLock::unlock() { + m.unlock(); +} +void SimpleRWLock::lock_shared() { + m.lock_shared(); +} +void SimpleRWLock::unlock_shared() { + m.unlock_shared(); +} +#endif +} diff --git a/src/mongo/util/concurrency/rwlockimpl.h b/src/mongo/util/concurrency/rwlockimpl.h new file mode 100644 index 00000000000..40103080911 --- /dev/null +++ b/src/mongo/util/concurrency/rwlockimpl.h @@ -0,0 +1,185 @@ +// @file rwlockimpl.h + +/** +* Copyright (C) 2012 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 <http://www.gnu.org/licenses/>. +* +* 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. +*/ + +#pragma once + +#include "mongo/stdx/chrono.h" +#include "mongo/util/concurrency/mutex.h" + +#if defined(NTDDI_VERSION) && defined(NTDDI_WIN7) && (NTDDI_VERSION >= NTDDI_WIN7) + +// Windows slimreaderwriter version. Newer windows versions only. Under contention this is slower +// than boost::shared_mutex, but see https://jira.mongodb.org/browse/SERVER-2327 for why it cannot +// be used. + +namespace mongo { +unsigned long long curTimeMicros64(); + +class RWLockBase { + MONGO_DISALLOW_COPYING(RWLockBase); + friend class SimpleRWLock; + SRWLOCK _lock; + +protected: + RWLockBase() { + InitializeSRWLock(&_lock); + } + ~RWLockBase() { + // no special action needed to destroy a SRWLOCK + } + void lock() { + AcquireSRWLockExclusive(&_lock); + } + void unlock() { + ReleaseSRWLockExclusive(&_lock); + } + void lock_shared() { + AcquireSRWLockShared(&_lock); + } + void unlock_shared() { + ReleaseSRWLockShared(&_lock); + } + bool lock_shared_try(int millis) { + if (TryAcquireSRWLockShared(&_lock)) + return true; + if (millis == 0) + return false; + unsigned long long end = curTimeMicros64() + millis * 1000; + while (1) { + Sleep(1); + if (TryAcquireSRWLockShared(&_lock)) + return true; + if (curTimeMicros64() >= end) + break; + } + return false; + } + bool lock_try(int millis = 0) { + if (TryAcquireSRWLockExclusive( + &_lock)) // quick check to optimistically avoid calling curTimeMicros64 + return true; + if (millis == 0) + return false; + unsigned long long end = curTimeMicros64() + millis * 1000; + do { + Sleep(1); + if (TryAcquireSRWLockExclusive(&_lock)) + return true; + } while (curTimeMicros64() < end); + return false; + } + // no upgradable for this impl + void lockAsUpgradable() { + lock(); + } + void unlockFromUpgradable() { + unlock(); + } + void upgrade() {} + +public: + const char* implType() const { + return "WINSRW"; + } +}; +} + +#else + +#if defined(_WIN32) +#include "shared_mutex_win.hpp" +namespace mongo { +namespace detail { +using rwlock_underlying_shared_mutex = boost::modified_shared_mutex; +} // namespace detail +} // namespace mongo +#else +#include <boost/chrono.hpp> +#include <boost/thread/shared_mutex.hpp> +namespace mongo { +namespace detail { +using rwlock_underlying_shared_mutex = boost::shared_mutex; // NOLINT +} // namespace detail +} // namespace mongo +#endif + +namespace mongo { +class RWLockBase { + MONGO_DISALLOW_COPYING(RWLockBase); + friend class SimpleRWLock; + detail::rwlock_underlying_shared_mutex _m; + +protected: + RWLockBase() = default; + + void lock() { + _m.lock(); + } + void unlock() { + _m.unlock(); + } + void lockAsUpgradable() { + _m.lock_upgrade(); + } + void unlockFromUpgradable() { // upgradable -> unlocked + _m.unlock_upgrade(); + } + void upgrade() { // upgradable -> exclusive lock + _m.unlock_upgrade_and_lock(); + } + void lock_shared() { + _m.lock_shared(); + } + void unlock_shared() { + _m.unlock_shared(); + } + bool lock_shared_try(int millis) { +#if defined(_WIN32) + return _m.timed_lock_shared(boost::posix_time::milliseconds(millis)); +#else + return _m.try_lock_shared_for(boost::chrono::milliseconds(millis)); // NOLINT +#endif + } + bool lock_try(int millis = 0) { +#if defined(_WIN32) + return _m.timed_lock(boost::posix_time::milliseconds(millis)); +#else + return _m.try_lock_for(boost::chrono::milliseconds(millis)); // NOLINT +#endif + } + +public: + const char* implType() const { + return "boost"; + } +}; +} + +#endif diff --git a/src/mongo/util/concurrency/shared_mutex_win.hpp b/src/mongo/util/concurrency/shared_mutex_win.hpp new file mode 100644 index 00000000000..86ad659a6d8 --- /dev/null +++ b/src/mongo/util/concurrency/shared_mutex_win.hpp @@ -0,0 +1,593 @@ +#ifndef BOOST_THREAD_WIN32_SHARED_MUTEX_HPP_MODIFIED
+#define BOOST_THREAD_WIN32_SHARED_MUTEX_HPP_MODIFIED
+
+// (C) Copyright 2006-8 Anthony Williams
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+/* MongoDB :
+ Slightly modified boost file to not die above 127 pending writes
+ Here is what changed (from boost 1.42.0 shared_mutex.hpp):
+ 1,2c1,2
+ < #ifndef BOOST_THREAD_WIN32_SHARED_MUTEX_HPP
+ < #define BOOST_THREAD_WIN32_SHARED_MUTEX_HPP
+ ---
+ > #ifndef BOOST_THREAD_WIN32_SHARED_MUTEX_HPP_MODIFIED
+ > #define BOOST_THREAD_WIN32_SHARED_MUTEX_HPP_MODIFIED
+ 22c27
+ < class shared_mutex:
+ ---
+ > class modified_shared_mutex:
+ 73c78
+ < shared_mutex():
+ ---
+ > modified_shared_mutex():
+ 84c89
+ < ~shared_mutex()
+ ---
+ > ~modified_shared_mutex()
+ 283a289,290
+ > if( new_state.exclusive_waiting == 127 ) // the maximum already!
+ > break;
+*/
+
+#include <boost/assert.hpp>
+#include <boost/detail/interlocked.hpp>
+#include <boost/thread/win32/thread_primitives.hpp>
+#include <boost/static_assert.hpp>
+#include <limits.h>
+#include <boost/thread/thread_time.hpp>
+
+#include <boost/config/abi_prefix.hpp>
+
+namespace boost
+{
+ class modified_shared_mutex
+ {
+ MONGO_DISALLOW_COPYING(modified_shared_mutex);
+ private:
+ struct state_data
+ {
+ unsigned shared_count:11,
+ shared_waiting:11,
+ exclusive:1,
+ upgrade:1,
+ exclusive_waiting:7,
+ exclusive_waiting_blocked:1;
+
+ friend bool operator==(state_data const& lhs,state_data const& rhs)
+ {
+ return *reinterpret_cast<unsigned const*>(&lhs)==*reinterpret_cast<unsigned const*>(&rhs);
+ }
+ };
+
+
+ template<typename T>
+ T interlocked_compare_exchange(T* target,T new_value,T comparand)
+ {
+ BOOST_STATIC_ASSERT(sizeof(T)==sizeof(long));
+ long const res=BOOST_INTERLOCKED_COMPARE_EXCHANGE(reinterpret_cast<long*>(target),
+ *reinterpret_cast<long*>(&new_value),
+ *reinterpret_cast<long*>(&comparand));
+ return *reinterpret_cast<T const*>(&res);
+ }
+
+ state_data state;
+ detail::win32::handle semaphores[2];
+ detail::win32::handle &unlock_sem;
+ detail::win32::handle &exclusive_sem;
+ detail::win32::handle upgrade_sem;
+
+ void release_waiters(state_data old_state)
+ {
+ if(old_state.exclusive_waiting)
+ {
+ BOOST_VERIFY(detail::win32::ReleaseSemaphore(exclusive_sem,1,0)!=0);
+ }
+
+ if(old_state.shared_waiting || old_state.exclusive_waiting)
+ {
+ BOOST_VERIFY(detail::win32::ReleaseSemaphore(unlock_sem,old_state.shared_waiting + (old_state.exclusive_waiting?1:0),0)!=0);
+ }
+ }
+
+
+ public:
+ modified_shared_mutex():
+ unlock_sem(semaphores[0]),
+ exclusive_sem(semaphores[1])
+ {
+ unlock_sem=detail::win32::create_anonymous_semaphore(0,LONG_MAX);
+ exclusive_sem=detail::win32::create_anonymous_semaphore(0,LONG_MAX);
+ upgrade_sem=detail::win32::create_anonymous_semaphore(0,LONG_MAX);
+ state_data state_={0};
+ state=state_;
+ }
+
+ ~modified_shared_mutex()
+ {
+ detail::win32::CloseHandle(upgrade_sem);
+ detail::win32::CloseHandle(unlock_sem);
+ detail::win32::CloseHandle(exclusive_sem);
+ }
+
+ bool try_lock_shared()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ if(!new_state.exclusive && !new_state.exclusive_waiting_blocked)
+ {
+ ++new_state.shared_count;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+ return !(old_state.exclusive| old_state.exclusive_waiting_blocked);
+ }
+
+ void lock_shared()
+ {
+ BOOST_VERIFY(timed_lock_shared(::boost::detail::get_system_time_sentinel()));
+ }
+
+ template<typename TimeDuration>
+ bool timed_lock_shared(TimeDuration const & relative_time)
+ {
+ return timed_lock_shared(get_system_time()+relative_time);
+ }
+
+ bool timed_lock_shared(boost::system_time const& wait_until)
+ {
+ for(;;)
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ if(new_state.exclusive || new_state.exclusive_waiting_blocked)
+ {
+ ++new_state.shared_waiting;
+ }
+ else
+ {
+ ++new_state.shared_count;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+
+ if(!(old_state.exclusive| old_state.exclusive_waiting_blocked))
+ {
+ return true;
+ }
+
+ unsigned long const res=WaitForSingleObject(unlock_sem,::boost::detail::get_milliseconds_until(wait_until));
+ if(res==detail::win32::timeout)
+ {
+ for(;;)
+ {
+ state_data new_state=old_state;
+ if(new_state.exclusive || new_state.exclusive_waiting_blocked)
+ {
+ if(new_state.shared_waiting)
+ {
+ --new_state.shared_waiting;
+ }
+ }
+ else
+ {
+ ++new_state.shared_count;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+
+ if(!(old_state.exclusive| old_state.exclusive_waiting_blocked))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ BOOST_ASSERT(res==0);
+ }
+ }
+
+ void unlock_shared()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ bool const last_reader=!--new_state.shared_count;
+
+ if(last_reader)
+ {
+ if(new_state.upgrade)
+ {
+ new_state.upgrade=false;
+ new_state.exclusive=true;
+ }
+ else
+ {
+ if(new_state.exclusive_waiting)
+ {
+ --new_state.exclusive_waiting;
+ new_state.exclusive_waiting_blocked=false;
+ }
+ new_state.shared_waiting=0;
+ }
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ if(last_reader)
+ {
+ if(old_state.upgrade)
+ {
+ BOOST_VERIFY(detail::win32::ReleaseSemaphore(upgrade_sem,1,0)!=0);
+ }
+ else
+ {
+ release_waiters(old_state);
+ }
+ }
+ break;
+ }
+ old_state=current_state;
+ }
+ }
+
+ void lock()
+ {
+ BOOST_VERIFY(timed_lock(::boost::detail::get_system_time_sentinel()));
+ }
+
+ template<typename TimeDuration>
+ bool timed_lock(TimeDuration const & relative_time)
+ {
+ return timed_lock(get_system_time()+relative_time);
+ }
+
+ bool try_lock()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ if(new_state.shared_count || new_state.exclusive)
+ {
+ return false;
+ }
+ else
+ {
+ new_state.exclusive=true;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+ return true;
+ }
+
+
+ bool timed_lock(boost::system_time const& wait_until)
+ {
+ for(;;)
+ {
+ state_data old_state=state;
+
+ for(;;)
+ {
+ state_data new_state=old_state;
+ if(new_state.shared_count || new_state.exclusive)
+ {
+ if( new_state.exclusive_waiting == 127 ) // the maximum already!
+ break;
+ ++new_state.exclusive_waiting;
+ new_state.exclusive_waiting_blocked=true;
+ }
+ else
+ {
+ new_state.exclusive=true;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+
+ if(!old_state.shared_count && !old_state.exclusive)
+ {
+ return true;
+ }
+ unsigned long const wait_res=WaitForMultipleObjects(2,semaphores,true,::boost::detail::get_milliseconds_until(wait_until));
+ if(wait_res==detail::win32::timeout)
+ {
+ for(;;)
+ {
+ state_data new_state=old_state;
+ if(new_state.shared_count || new_state.exclusive)
+ {
+ if(new_state.exclusive_waiting)
+ {
+ if(!--new_state.exclusive_waiting)
+ {
+ new_state.exclusive_waiting_blocked=false;
+ }
+ }
+ }
+ else
+ {
+ new_state.exclusive=true;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+ if(!old_state.shared_count && !old_state.exclusive)
+ {
+ return true;
+ }
+ return false;
+ }
+ BOOST_ASSERT(wait_res<2);
+ }
+ }
+
+ void unlock()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ new_state.exclusive=false;
+ if(new_state.exclusive_waiting)
+ {
+ --new_state.exclusive_waiting;
+ new_state.exclusive_waiting_blocked=false;
+ }
+ new_state.shared_waiting=0;
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+ release_waiters(old_state);
+ }
+
+ void lock_upgrade()
+ {
+ for(;;)
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ if(new_state.exclusive || new_state.exclusive_waiting_blocked || new_state.upgrade)
+ {
+ ++new_state.shared_waiting;
+ }
+ else
+ {
+ ++new_state.shared_count;
+ new_state.upgrade=true;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+
+ if(!(old_state.exclusive|| old_state.exclusive_waiting_blocked|| old_state.upgrade))
+ {
+ return;
+ }
+
+ BOOST_VERIFY(!WaitForSingleObject(unlock_sem,detail::win32::infinite));
+ }
+ }
+
+ bool try_lock_upgrade()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ if(new_state.exclusive || new_state.exclusive_waiting_blocked || new_state.upgrade)
+ {
+ return false;
+ }
+ else
+ {
+ ++new_state.shared_count;
+ new_state.upgrade=true;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+ return true;
+ }
+
+ void unlock_upgrade()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ new_state.upgrade=false;
+ bool const last_reader=!--new_state.shared_count;
+
+ if(last_reader)
+ {
+ if(new_state.exclusive_waiting)
+ {
+ --new_state.exclusive_waiting;
+ new_state.exclusive_waiting_blocked=false;
+ }
+ new_state.shared_waiting=0;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ if(last_reader)
+ {
+ release_waiters(old_state);
+ }
+ break;
+ }
+ old_state=current_state;
+ }
+ }
+
+ void unlock_upgrade_and_lock()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ bool const last_reader=!--new_state.shared_count;
+
+ if(last_reader)
+ {
+ new_state.upgrade=false;
+ new_state.exclusive=true;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ if(!last_reader)
+ {
+ BOOST_VERIFY(!WaitForSingleObject(upgrade_sem,detail::win32::infinite));
+ }
+ break;
+ }
+ old_state=current_state;
+ }
+ }
+
+ void unlock_and_lock_upgrade()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ new_state.exclusive=false;
+ new_state.upgrade=true;
+ ++new_state.shared_count;
+ if(new_state.exclusive_waiting)
+ {
+ --new_state.exclusive_waiting;
+ new_state.exclusive_waiting_blocked=false;
+ }
+ new_state.shared_waiting=0;
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+ release_waiters(old_state);
+ }
+
+ void unlock_and_lock_shared()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ new_state.exclusive=false;
+ ++new_state.shared_count;
+ if(new_state.exclusive_waiting)
+ {
+ --new_state.exclusive_waiting;
+ new_state.exclusive_waiting_blocked=false;
+ }
+ new_state.shared_waiting=0;
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+ release_waiters(old_state);
+ }
+
+ void unlock_upgrade_and_lock_shared()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ new_state.upgrade=false;
+ if(new_state.exclusive_waiting)
+ {
+ --new_state.exclusive_waiting;
+ new_state.exclusive_waiting_blocked=false;
+ }
+ new_state.shared_waiting=0;
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+ release_waiters(old_state);
+ }
+
+ };
+}
+
+#include <boost/config/abi_suffix.hpp>
+
+#endif
diff --git a/src/mongo/util/concurrency/simplerwlock.h b/src/mongo/util/concurrency/simplerwlock.h new file mode 100644 index 00000000000..4673f799f8c --- /dev/null +++ b/src/mongo/util/concurrency/simplerwlock.h @@ -0,0 +1,85 @@ +/** +* Copyright (C) 2012 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 <http://www.gnu.org/licenses/>. +* +* 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. +*/ + +#pragma once + +#include "mongo/base/string_data.h" +#include "mongo/config.h" +#include "mongo/platform/atomic_word.h" +#include "mongo/util/concurrency/threadlocal.h" + +namespace mongo { + +/** separated out as later the implementation of this may be different than RWLock, + depending on OS, as there is no upgrade etc. facility herein. +*/ +class SimpleRWLock { + MONGO_DISALLOW_COPYING(SimpleRWLock); +#if defined(NTDDI_VERSION) && defined(NTDDI_WIN7) && (NTDDI_VERSION >= NTDDI_WIN7) + SRWLOCK _lock; +#else + RWLockBase m; +#endif +#if defined(_WIN32) && defined(MONGO_CONFIG_DEBUG_BUILD) + AtomicUInt32 shares; + ThreadLocalValue<int> s; + unsigned tid; +#endif +public: + const std::string name; + SimpleRWLock(StringData name = ""); + void lock(); + void unlock(); + void lock_shared(); + void unlock_shared(); + class Shared { + MONGO_DISALLOW_COPYING(Shared); + SimpleRWLock& _r; + + public: + Shared(SimpleRWLock& rwlock) : _r(rwlock) { + _r.lock_shared(); + } + ~Shared() { + _r.unlock_shared(); + } + }; + class Exclusive { + MONGO_DISALLOW_COPYING(Exclusive); + SimpleRWLock& _r; + + public: + Exclusive(SimpleRWLock& rwlock) : _r(rwlock) { + _r.lock(); + } + ~Exclusive() { + _r.unlock(); + } + }; +}; +} diff --git a/src/mongo/util/processinfo.h b/src/mongo/util/processinfo.h index f44ef37799a..b2e6fcf2852 100644 --- a/src/mongo/util/processinfo.h +++ b/src/mongo/util/processinfo.h @@ -245,4 +245,6 @@ public: }; bool writePidFile(const std::string& path); + +void printMemInfo(const char* whereContextStr = 0); } |