From f9ad73598ba08ce26f236eae2691a5c60a6e807d Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 1 Oct 2014 20:26:38 +0200 Subject: lib/nfs/Manager: use boost::intrusive::map Reduce overhead for storing the key twice, and more overhead while looking up the connection to remove it after a failure. --- src/lib/nfs/Manager.cxx | 66 +++++++++++++++++++++++++++++++++++-------------- src/lib/nfs/Manager.hxx | 49 ++++++++++++++++++++++-------------- 2 files changed, 78 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/lib/nfs/Manager.cxx b/src/lib/nfs/Manager.cxx index 5c236552c..c5aecf48d 100644 --- a/src/lib/nfs/Manager.cxx +++ b/src/lib/nfs/Manager.cxx @@ -22,12 +22,48 @@ #include "event/Loop.hxx" #include "Log.hxx" +#include + void NfsManager::ManagedConnection::OnNfsConnectionError(Error &&error) { FormatError(error, "NFS error on %s:%s", GetServer(), GetExportName()); - manager.connections.erase(Key(GetServer(), GetExportName())); + manager.connections.erase(manager.connections.iterator_to(*this)); + delete this; +} + +inline bool +NfsManager::Compare::operator()(const LookupKey a, + const ManagedConnection &b) const +{ + int result = strcmp(a.server, b.GetServer()); + if (result != 0) + return result < 0; + + result = strcmp(a.export_name, b.GetExportName()); + return result < 0; +} + +inline bool +NfsManager::Compare::operator()(const ManagedConnection &a, + const LookupKey b) const +{ + int result = strcmp(a.GetServer(), b.server); + if (result != 0) + return result < 0; + + result = strcmp(a.GetExportName(), b.export_name); + return result < 0; +} + +NfsManager::~NfsManager() +{ + assert(loop.IsInside()); + + connections.clear_and_dispose([](ManagedConnection *c){ + delete c; + }); } NfsConnection & @@ -37,21 +73,15 @@ NfsManager::GetConnection(const char *server, const char *export_name) assert(export_name != nullptr); assert(loop.IsInside()); - const std::string key = Key(server, export_name); - -#if defined(__GNUC__) && !defined(__clang__) && !GCC_CHECK_VERSION(4,8) - /* std::map::emplace() not available; this hack uses the move - constructor */ - auto e = connections.insert(std::make_pair(key, - ManagedConnection(*this, loop, - server, - export_name))); -#else - auto e = connections.emplace(std::piecewise_construct, - std::forward_as_tuple(key), - std::forward_as_tuple(*this, loop, - server, - export_name)); -#endif - return e.first->second; + Map::insert_commit_data hint; + auto result = connections.insert_check(LookupKey{server, export_name}, + Compare(), hint); + if (result.second) { + auto c = new ManagedConnection(*this, loop, + server, export_name); + connections.insert_commit(*c, hint); + return *c; + } else { + return *result.first; + } } diff --git a/src/lib/nfs/Manager.hxx b/src/lib/nfs/Manager.hxx index 0e2c998fe..612b01f9c 100644 --- a/src/lib/nfs/Manager.hxx +++ b/src/lib/nfs/Manager.hxx @@ -24,15 +24,21 @@ #include "Connection.hxx" #include "Compiler.h" -#include -#include +#include /** * A manager for NFS connections. Handles multiple connections to * multiple NFS servers. */ class NfsManager { - class ManagedConnection final : public NfsConnection { + struct LookupKey { + const char *server; + const char *export_name; + }; + + class ManagedConnection final + : public NfsConnection, + public boost::intrusive::set_base_hook> { NfsManager &manager; public: @@ -42,39 +48,44 @@ class NfsManager { :NfsConnection(_loop, _server, _export_name), manager(_manager) {} -#if defined(__GNUC__) && !defined(__clang__) && !GCC_CHECK_VERSION(4,8) - /* needed due to lack of std::map::emplace() */ - ManagedConnection(ManagedConnection &&other) - :NfsConnection(std::move(other)), - manager(other.manager) {} -#endif - protected: /* virtual methods from NfsConnection */ void OnNfsConnectionError(Error &&error) override; }; + struct Compare { + gcc_pure + bool operator()(const LookupKey a, + const ManagedConnection &b) const; + + gcc_pure + bool operator()(const ManagedConnection &a, + const LookupKey b) const; + }; + EventLoop &loop; /** - * Maps server+":"+export_name (see method Key()) to - * #ManagedConnection. + * Maps server and export_name to #ManagedConnection. */ - std::map connections; + typedef boost::intrusive::set, + boost::intrusive::constant_time_size> Map; + + Map connections; public: NfsManager(EventLoop &_loop) :loop(_loop) {} + /** + * Must be run from EventLoop's thread. + */ + ~NfsManager(); + gcc_pure NfsConnection &GetConnection(const char *server, const char *export_name); - -private: - gcc_pure - static std::string Key(const char *server, const char *export_name) { - return std::string(server) + ':' + export_name; - } }; #endif -- cgit v1.2.3