summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBen Caimano <ben.caimano@10gen.com>2020-01-29 18:34:54 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-01-30 00:07:42 +0000
commit28d4d9cb69b68c759a76851675390cec29855a28 (patch)
treec551c33cb8ebdd3fab9fe56b6366f18c8b5b0039 /src
parent84dc6fcc49eb679c7fe7a5614f496c65e95576fb (diff)
downloadmongo-28d4d9cb69b68c759a76851675390cec29855a28.tar.gz
Revert "SERVER-45793 Improve mongo::Mutex contract"
This reverts commit 695146e648e032e04d97bb0b4de873272c242f04.
Diffstat (limited to 'src')
-rw-r--r--src/mongo/platform/mutex.cpp31
-rw-r--r--src/mongo/platform/mutex.h153
-rw-r--r--src/mongo/util/diagnostic_info.cpp8
-rw-r--r--src/mongo/util/latch_analyzer.cpp13
-rw-r--r--src/mongo/util/latch_analyzer.h10
5 files changed, 101 insertions, 114 deletions
diff --git a/src/mongo/platform/mutex.cpp b/src/mongo/platform/mutex.cpp
index fd5a5dd212f..cd24a87a84b 100644
--- a/src/mongo/platform/mutex.cpp
+++ b/src/mongo/platform/mutex.cpp
@@ -77,10 +77,22 @@ 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 = latch_detail::getDiagnosticListenerState();
+ auto& state = _getListenerState();
if (!state.isFinalized.load()) {
return;
}
@@ -93,7 +105,7 @@ void Mutex::_onContendedLock() noexcept {
void Mutex::_onQuickLock() noexcept {
_data->counts().acquired.fetchAndAdd(1);
- auto& state = latch_detail::getDiagnosticListenerState();
+ auto& state = _getListenerState();
if (!state.isFinalized.load()) {
return;
}
@@ -106,7 +118,7 @@ void Mutex::_onQuickLock() noexcept {
void Mutex::_onSlowLock() noexcept {
_data->counts().acquired.fetchAndAdd(1);
- auto& state = latch_detail::getDiagnosticListenerState();
+ auto& state = _getListenerState();
if (!state.isFinalized.load()) {
return;
}
@@ -119,7 +131,7 @@ void Mutex::_onSlowLock() noexcept {
void Mutex::_onUnlock() noexcept {
_data->counts().released.fetchAndAdd(1);
- auto& state = latch_detail::getDiagnosticListenerState();
+ auto& state = _getListenerState();
if (!state.isFinalized.load()) {
return;
}
@@ -130,13 +142,12 @@ void Mutex::_onUnlock() noexcept {
}
/**
- * 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.
+ * 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.
*/
-MONGO_INITIALIZER(FinalizeDiagnosticListeners)(InitializerContext* context) {
- auto& state = latch_detail::getDiagnosticListenerState();
- state.isFinalized.store(true);
+MONGO_INITIALIZER(FinalizeLockListeners)(InitializerContext* context) {
+ Mutex::finalizeLockListeners();
return Status::OK();
}
diff --git a/src/mongo/platform/mutex.h b/src/mongo/platform/mutex.h
index 25ad8f04a7a..5165d239bdc 100644
--- a/src/mongo/platform/mutex.h
+++ b/src/mongo/platform/mutex.h
@@ -41,7 +41,6 @@
#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"
@@ -59,9 +58,6 @@ 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:
@@ -129,74 +125,6 @@ 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.
@@ -233,7 +161,7 @@ private:
};
/**
- * latch_details::Catalog holds a set of Data objects for use with Mutexes
+ * latch_details::Catalog holds a collection of Data objects for use with Mutexes
*
* All rules for LockFreeCollection apply:
* - Synchronization is provided internally
@@ -248,7 +176,7 @@ public:
};
/**
- * Simple registration object that takes an Identity and provides access to a Data
+ * Simple registration object that construct with an Identity and provides access to a Data
*
* This object actually owns the Data object to make lifetime management simpler.
*/
@@ -290,14 +218,6 @@ 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;
@@ -316,17 +236,11 @@ 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 final : public Latch {
+class Mutex : public Latch {
public:
+ class LockListener;
+
void lock() override;
void unlock() override;
bool try_lock() override;
@@ -337,7 +251,32 @@ 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;
@@ -349,6 +288,38 @@ 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
/**
diff --git a/src/mongo/util/diagnostic_info.cpp b/src/mongo/util/diagnostic_info.cpp
index f0b7bb8b1ce..0f66a6f96f4 100644
--- a/src/mongo/util/diagnostic_info.cpp
+++ b/src/mongo/util/diagnostic_info.cpp
@@ -174,9 +174,9 @@ struct DiagnosticInfoHandle {
};
const auto getDiagnosticInfoHandle = Client::declareDecoration<DiagnosticInfoHandle>();
-MONGO_INITIALIZER_GENERAL(DiagnosticInfo, (/* NO PREREQS */), ("FinalizeDiagnosticListeners"))
+MONGO_INITIALIZER_GENERAL(DiagnosticInfo, (/* NO PREREQS */), ("FinalizeLockListeners"))
(InitializerContext* context) {
- class DiagnosticListener : public latch_detail::DiagnosticListener {
+ class LockListener : public Mutex::LockListener {
void onContendedLock(const Identity& id) override {
if (auto client = Client::getCurrent()) {
auto& handle = getDiagnosticInfoHandle(client);
@@ -209,7 +209,9 @@ MONGO_INITIALIZER_GENERAL(DiagnosticInfo, (/* NO PREREQS */), ("FinalizeDiagnost
}
};
- latch_detail::installDiagnosticListener<DiagnosticListener>();
+ // Intentionally leaked, people use Latches in detached threads
+ static auto& listener = *new LockListener;
+ Mutex::addLockListener(&listener);
return Status::OK();
}
diff --git a/src/mongo/util/latch_analyzer.cpp b/src/mongo/util/latch_analyzer.cpp
index b60254886d2..2062daa3d9a 100644
--- a/src/mongo/util/latch_analyzer.cpp
+++ b/src/mongo/util/latch_analyzer.cpp
@@ -59,9 +59,9 @@ auto kLatchViolationKey = "hierarchicalAcquisitionLevelViolations"_sd;
const auto getLatchAnalyzer = ServiceContext::declareDecoration<LatchAnalyzer>();
/**
- * DiagnosticListener sub-class to implement updating set in LatchSetState
+ * LockListener sub-class to implement updating set in LatchSetState
*/
-class DiagnosticListener : public latch_detail::DiagnosticListener {
+class LockListener : public Mutex::LockListener {
public:
void onContendedLock(const Identity& id) override {
if (auto client = Client::getCurrent()) {
@@ -88,10 +88,13 @@ public:
}
};
-// Register our DiagnosticListener
-MONGO_INITIALIZER_GENERAL(LatchAnalysis, (/* NO PREREQS */), ("FinalizeDiagnosticListeners"))
+// Register our LockListener with the Mutex class
+MONGO_INITIALIZER_GENERAL(LatchAnalysis, (/* NO PREREQS */), ("FinalizeLockListeners"))
(InitializerContext* context) {
- latch_detail::installDiagnosticListener<DiagnosticListener>();
+
+ // Intentionally leaked, people use Latches in detached threads
+ static auto& listener = *new LockListener;
+ Mutex::addLockListener(&listener);
return Status::OK();
}
diff --git a/src/mongo/util/latch_analyzer.h b/src/mongo/util/latch_analyzer.h
index 328e5549695..41f41dca15f 100644
--- a/src/mongo/util/latch_analyzer.h
+++ b/src/mongo/util/latch_analyzer.h
@@ -44,11 +44,11 @@ namespace mongo {
* LatchAnalyzer is a ServiceContext decoration that aggregates latch events
*
* This class is intended to provide a platform for hierarchical analysis on latches. To that end,
- * onContention(), onAcquire(), and onRelease() are currently called by a
- * latch_detail::DiagnosticListener subclass defined in source. This class does much more work for
- * each event when the enableLatchAnalysis failpoint is set to "alwaysOn". This failpoint provides a
- * wealth of data for future analysis, but involves additional mutexes and mapping structures that
- * may prove too costly for production usage at the least.
+ * onContention(), onAcquire(), and onRelease() are currently called by a Mutex::LockListener
+ * subclass defined in source. This class does much more work for each event when the
+ * enableLatchAnalysis failpoint is set to "alwaysOn". This failpoint provides a wealth of data for
+ * future analysis, but involves additional mutexes and mapping structures that may prove too costly
+ * for production usage at the least.
*/
class LatchAnalyzer {
public: