diff options
author | Ben Caimano <ben.caimano@10gen.com> | 2020-01-28 16:38:38 -0500 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-01-29 20:28:40 +0000 |
commit | 695146e648e032e04d97bb0b4de873272c242f04 (patch) | |
tree | 4faf400ea417b78c7a8da09c548c1b2ea1ebef24 /src/mongo/platform | |
parent | 6c6d91edeaecd066dca7fe4479298318e7e2ca77 (diff) | |
download | mongo-695146e648e032e04d97bb0b4de873272c242f04.tar.gz |
SERVER-45793 Improve mongo::Mutex contract
Diffstat (limited to 'src/mongo/platform')
-rw-r--r-- | src/mongo/platform/mutex.cpp | 31 | ||||
-rw-r--r-- | src/mongo/platform/mutex.h | 153 |
2 files changed, 101 insertions, 83 deletions
diff --git a/src/mongo/platform/mutex.cpp b/src/mongo/platform/mutex.cpp index cd24a87a84b..fd5a5dd212f 100644 --- a/src/mongo/platform/mutex.cpp +++ b/src/mongo/platform/mutex.cpp @@ -77,22 +77,10 @@ StringData Mutex::getName() const { return StringData(_data->identity().name()); } -void Mutex::addLockListener(LockListener* listener) { - auto& state = _getListenerState(); - - invariant(!state.isFinalized.load()); - state.listeners.push_back(listener); -} - -void Mutex::finalizeLockListeners() { - auto& state = _getListenerState(); - state.isFinalized.store(true); -} - void Mutex::_onContendedLock() noexcept { _data->counts().contended.fetchAndAdd(1); - auto& state = _getListenerState(); + auto& state = latch_detail::getDiagnosticListenerState(); if (!state.isFinalized.load()) { return; } @@ -105,7 +93,7 @@ void Mutex::_onContendedLock() noexcept { void Mutex::_onQuickLock() noexcept { _data->counts().acquired.fetchAndAdd(1); - auto& state = _getListenerState(); + auto& state = latch_detail::getDiagnosticListenerState(); if (!state.isFinalized.load()) { return; } @@ -118,7 +106,7 @@ void Mutex::_onQuickLock() noexcept { void Mutex::_onSlowLock() noexcept { _data->counts().acquired.fetchAndAdd(1); - auto& state = _getListenerState(); + auto& state = latch_detail::getDiagnosticListenerState(); if (!state.isFinalized.load()) { return; } @@ -131,7 +119,7 @@ void Mutex::_onSlowLock() noexcept { void Mutex::_onUnlock() noexcept { _data->counts().released.fetchAndAdd(1); - auto& state = _getListenerState(); + auto& state = latch_detail::getDiagnosticListenerState(); if (!state.isFinalized.load()) { return; } @@ -142,12 +130,13 @@ void Mutex::_onUnlock() noexcept { } /** - * Any MONGO_INITIALIZER that adds a LockListener will want to list FinalizeLockListeners as - * a dependent initializer. This means that all LockListeners are certified to be added before main - * and no LockListeners are ever invoked before main. + * Any MONGO_INITIALIZER that adds a DiagnosticListener will want to list + * FinalizeDiagnosticListeners as a dependent initializer. This means that all DiagnosticListeners + * are certified to be added before main and no DiagnosticListeners are ever invoked before main. */ -MONGO_INITIALIZER(FinalizeLockListeners)(InitializerContext* context) { - Mutex::finalizeLockListeners(); +MONGO_INITIALIZER(FinalizeDiagnosticListeners)(InitializerContext* context) { + auto& state = latch_detail::getDiagnosticListenerState(); + state.isFinalized.store(true); return Status::OK(); } diff --git a/src/mongo/platform/mutex.h b/src/mongo/platform/mutex.h index 5165d239bdc..25ad8f04a7a 100644 --- a/src/mongo/platform/mutex.h +++ b/src/mongo/platform/mutex.h @@ -41,6 +41,7 @@ #include "mongo/platform/source_location.h" #include "mongo/stdx/mutex.h" #include "mongo/util/assert_util.h" +#include "mongo/util/concepts.h" #include "mongo/util/decorable.h" #include "mongo/util/duration.h" #include "mongo/util/hierarchical_acquisition.h" @@ -58,6 +59,9 @@ static constexpr auto kAnonymousName = "AnonymousLatch"_sd; /** * An Identity encapsulates the context around a latch + * + * Identities are intended to be constructed rarely and utilized via constant reference or pointer. + * Once an Identity is visible in a multithreaded context, it will be effectively constant. */ class Identity { public: @@ -125,6 +129,74 @@ private: }; /** + * A set of actions to happen upon notable events on a Lockable-conceptualized type + * + * The event handlers on this type will be invoked extremely frequently and can substantially affect + * the efficiency and overall health of the process. As a general rule, avoid logging, disk io, and + * networking in any DiagnosticListener functions. System functions not related to those activities + * should be avoided as much as possible. For example, the overhead of taking an elementary stack + * trace voa backtrace_symbols_fd(3) proved too heavyweight to be used in a DiagnosticListener. + * Additionally, in parts of our system, Mutexes can outlive the invocation of main() and, indeed, + * certain process global variables. DiagnosticListeners usually need to be dynamically allocated + * and leaked. + * + * In short, HERE BE DRAGONS. DO NOT IMPLEMENT NON-DIAGNOSTIC FUNCTIONALITY USING THIS CLASS. + */ +class DiagnosticListener { + friend class Mutex; + +public: + using Identity = latch_detail::Identity; + + virtual ~DiagnosticListener() = default; + + /** + * Action to do when a lock cannot be immediately acquired + */ + virtual void onContendedLock(const Identity& id) = 0; + + /** + * Action to do when a lock was acquired without blocking + */ + virtual void onQuickLock(const Identity& id) = 0; + + /** + * Action to do when a lock was acquired after blocking + */ + virtual void onSlowLock(const Identity& id) = 0; + + /** + * Action to do when a lock is unlocked + */ + virtual void onUnlock(const Identity& id) = 0; +}; + + +inline auto& getDiagnosticListenerState() noexcept { + struct State { + AtomicWord<bool> isFinalized{false}; + std::vector<latch_detail::DiagnosticListener*> listeners; + }; + static State state; + return state; +} + +/** + * Creates a DiagnosticListener subclass and adds it to the triggers for certain actions. + * + * DiagnosticListeners can only be added and not removed. If you wish to deactivate a + * DiagnosticListeners subclass, please provide the switch on that subclass to noop its + * functions. It is only safe to add a DiagnosticListener during a MONGO_INITIALIZER. + */ +TEMPLATE(typename ListenerT) +REQUIRES(std::is_base_of_v<DiagnosticListener, ListenerT>) +void installDiagnosticListener() { + auto& state = getDiagnosticListenerState(); + state.listeners.push_back(new ListenerT()); + invariant(!state.isFinalized.load()); +} + +/** * This class holds working data for a latchable resource * * All member data is either i) synchronized or ii) constant. @@ -161,7 +233,7 @@ private: }; /** - * latch_details::Catalog holds a collection of Data objects for use with Mutexes + * latch_details::Catalog holds a set of Data objects for use with Mutexes * * All rules for LockFreeCollection apply: * - Synchronization is provided internally @@ -176,7 +248,7 @@ public: }; /** - * Simple registration object that construct with an Identity and provides access to a Data + * Simple registration object that takes an Identity and provides access to a Data * * This object actually owns the Data object to make lifetime management simpler. */ @@ -218,6 +290,14 @@ inline auto defaultData() { } } // namespace latch_detail +/** + * Latch is an abstract base class that implements the Lockable concept + * + * This class is useful for designing function APIs that take stdx::unique_lock around an + * ambiguously defined resource. A stdx::unique_lock<Latch> can be constructed from a Mutex. + * A stdx::unique_lock<Latch> cannot be constructed from a stdx::unique_lock<Mutex>. Sometimes, + * standard types are not as powerful as we would like them to be. + */ class Latch { public: virtual ~Latch() = default; @@ -236,11 +316,17 @@ public: * * This class is intended to be used wherever a stdx::mutex would previously be used. It provides * a generic event-listener interface for instrumenting around lock()/unlock()/try_lock(). + * Conceptually, this type is similar to most unique_lock and timed_mutex implementations. + * + * If you believe that you need syncronization with absolutely no additional nanosecond latency or + * need to exist at a very core level (e.g. code living in the base folder), please excuse yourself + * from the linter and use a stdx::mutex. + * + * If you believe that you need logical synchronization at a user-facing level, you may need + * a database Lock instead. Talk to Storage Execution. */ -class Mutex : public Latch { +class Mutex final : public Latch { public: - class LockListener; - void lock() override; void unlock() override; bool try_lock() override; @@ -251,32 +337,7 @@ public: ~Mutex(); - /** - * This function adds a LockListener subclass to the triggers for certain actions. - * - * LockListeners can only be added and not removed. If you wish to deactivate a LockListeners - * subclass, please provide the switch on that subclass to noop its functions. It is only safe - * to add a LockListener during a MONGO_INITIALIZER. - */ - static void addLockListener(LockListener* listener); - - /** - * This function finalizes the list of LockListener subclasses and prevents more from being - * added. - */ - static void finalizeLockListeners(); - private: - static auto& _getListenerState() noexcept { - struct State { - AtomicWord<bool> isFinalized{false}; - std::vector<LockListener*> listeners; - }; - - static State state; - return state; - } - void _onContendedLock() noexcept; void _onQuickLock() noexcept; void _onSlowLock() noexcept; @@ -288,38 +349,6 @@ private: bool _isLocked = false; }; -/** - * A set of actions to happen upon notable events on a Lockable-conceptualized type - */ -class Mutex::LockListener { - friend class Mutex; - -public: - using Identity = latch_detail::Identity; - - virtual ~LockListener() = default; - - /** - * Action to do when a lock cannot be immediately acquired - */ - virtual void onContendedLock(const Identity& id) = 0; - - /** - * Action to do when a lock was acquired without blocking - */ - virtual void onQuickLock(const Identity& id) = 0; - - /** - * Action to do when a lock was acquired after blocking - */ - virtual void onSlowLock(const Identity& id) = 0; - - /** - * Action to do when a lock is unlocked - */ - virtual void onUnlock(const Identity& id) = 0; -}; - } // namespace mongo /** |