summaryrefslogtreecommitdiff
path: root/src/mongo/platform
diff options
context:
space:
mode:
authorBen Caimano <ben.caimano@10gen.com>2020-01-28 16:38:38 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-01-29 20:28:40 +0000
commit695146e648e032e04d97bb0b4de873272c242f04 (patch)
tree4faf400ea417b78c7a8da09c548c1b2ea1ebef24 /src/mongo/platform
parent6c6d91edeaecd066dca7fe4479298318e7e2ca77 (diff)
downloadmongo-695146e648e032e04d97bb0b4de873272c242f04.tar.gz
SERVER-45793 Improve mongo::Mutex contract
Diffstat (limited to 'src/mongo/platform')
-rw-r--r--src/mongo/platform/mutex.cpp31
-rw-r--r--src/mongo/platform/mutex.h153
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
/**