diff options
author | ADAM David Alan Martin <adam.martin@10gen.com> | 2016-11-16 14:08:01 -0500 |
---|---|---|
committer | ADAM David Alan Martin <adam.martin@10gen.com> | 2016-11-16 14:08:01 -0500 |
commit | 2fae4242b9e8256da203639895d1ecd3fe8e2794 (patch) | |
tree | d1120bb75a52c7b742d8bce0c411966d0b992535 | |
parent | ed347a4f92a3388ab0f690502a81132577361623 (diff) | |
download | mongo-r3.5.0.tar.gz |
SERVER-27048 Fix recursive lock issue in transport.r3.5.0
The LegacySession teardown code has a race where promoted weak
pointers would be the last owners of a type which needs to hold
a lock in destruction. That same lock is held by the LegacySession
teardown code, thus leading to a deadlock or detectable recursive
locking situation.
-rw-r--r-- | src/mongo/transport/transport_layer_legacy.cpp | 51 | ||||
-rw-r--r-- | src/mongo/transport/transport_layer_legacy.h | 8 |
2 files changed, 45 insertions, 14 deletions
diff --git a/src/mongo/transport/transport_layer_legacy.cpp b/src/mongo/transport/transport_layer_legacy.cpp index 789b41529fa..a3b9c7306b0 100644 --- a/src/mongo/transport/transport_layer_legacy.cpp +++ b/src/mongo/transport/transport_layer_legacy.cpp @@ -30,6 +30,8 @@ #include "mongo/platform/basic.h" +#include <algorithm> +#include <iterator> #include <memory> #include "mongo/transport/transport_layer_legacy.h" @@ -47,6 +49,14 @@ namespace mongo { namespace transport { +namespace { +struct lock_weak { + template <typename T> + std::shared_ptr<T> operator()(const std::weak_ptr<T>& p) const { + return p.lock(); + } +}; +} // namespace TransportLayerLegacy::ListenerLegacy::ListenerLegacy(const TransportLayerLegacy::Options& opts, NewConnectionCb callback) @@ -221,23 +231,40 @@ void TransportLayerLegacy::_closeConnection(Connection* conn) { Listener::globalTicketHolder.release(); } +// Capture all of the weak pointers behind the lock, to delay their expiry until we leave the +// locking context. This function requires proof of locking, by passing the lock guard. +auto TransportLayerLegacy::lockAllSessions(const stdx::unique_lock<stdx::mutex>&) const + -> std::vector<LegacySessionHandle> { + using std::begin; + using std::end; + std::vector<std::shared_ptr<LegacySession>> result; + std::transform(begin(_sessions), end(_sessions), std::back_inserter(result), lock_weak()); + // Skip expired weak pointers. + result.erase(std::remove(begin(result), end(result), nullptr), end(result)); + return result; +} + void TransportLayerLegacy::endAllSessions(Session::TagMask tags) { log() << "legacy transport layer closing all connections"; { - stdx::lock_guard<stdx::mutex> lk(_sessionsMutex); - for (auto&& it : _sessions) { - - // Attempt to make our weak_ptr into a shared_ptr - auto session = it.lock(); - if (session) { - if (session->getTags() & tags) { - log() << "Skip closing connection for connection # " - << session->conn()->connectionId; - } else { - _closeConnection(session->conn()); - } + stdx::unique_lock<stdx::mutex> lk(_sessionsMutex); + // We want to capture the shared_ptrs to our sessions in a way which lets us destroy them + // outside of the lock. + const auto sessions = lockAllSessions(lk); + + for (auto&& session : sessions) { + if (session->getTags() & tags) { + log() << "Skip closing connection for connection # " + << session->conn()->connectionId; + } else { + _closeConnection(session->conn()); } } + // TODO(SERVER-27069): Revamp this lock to not cover the loop. This unlock was put here + // specifically to minimize risk, just before the release of 3.4. The risk is that we would + // be in the loop without the lock, which most of our testing didn't do. We must unlock + // manually here, because the `sessions` vector must be destroyed *outside* of the lock. + lk.unlock(); } } diff --git a/src/mongo/transport/transport_layer_legacy.h b/src/mongo/transport/transport_layer_legacy.h index dab651bb86e..6ef7b73a7c1 100644 --- a/src/mongo/transport/transport_layer_legacy.h +++ b/src/mongo/transport/transport_layer_legacy.h @@ -28,6 +28,8 @@ #pragma once +#include <vector> + #include "mongo/stdx/list.h" #include "mongo/stdx/memory.h" #include "mongo/stdx/mutex.h" @@ -101,6 +103,8 @@ private: using NewConnectionCb = stdx::function<void(std::unique_ptr<AbstractMessagingPort>)>; using WorkHandle = stdx::function<Status(AbstractMessagingPort*)>; + std::vector<LegacySessionHandle> lockAllSessions(const stdx::unique_lock<stdx::mutex>&) const; + /** * Connection object, to associate Sessions with AbstractMessagingPorts. */ @@ -127,8 +131,8 @@ private: public: ~LegacySession(); - static std::shared_ptr<LegacySession> create(std::unique_ptr<AbstractMessagingPort> amp, - TransportLayerLegacy* tl); + static LegacySessionHandle create(std::unique_ptr<AbstractMessagingPort> amp, + TransportLayerLegacy* tl); TransportLayer* getTransportLayer() const override { return _tl; |