From d8d2fe1564640142b1055785c86863893852cd44 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Tue, 13 Sep 2016 12:39:13 -0700 Subject: [PATCH] ircd: Add scope 'life_guard' util for references in reentrant contexts. --- include/ircd/client.h | 2 + include/ircd/life_guard.h | 93 +++++++++++++++++++++++++++++++++++++++ include/ircd/stdinc.h | 1 + ircd/client.cc | 12 +++++ ircd/vm.cc | 6 +-- modules/m_host.cc | 2 + 6 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 include/ircd/life_guard.h diff --git a/include/ircd/client.h b/include/ircd/client.h index eecf9c318..5829d74a0 100644 --- a/include/ircd/client.h +++ b/include/ircd/client.h @@ -33,7 +33,9 @@ namespace ircd { struct sock; struct client; +std::shared_ptr shared_from(const client &); std::shared_ptr shared_from(client &); +std::weak_ptr weak_from(const client &); std::weak_ptr weak_from(client &); // Client socket addressing diff --git a/include/ircd/life_guard.h b/include/ircd/life_guard.h new file mode 100644 index 000000000..37064368e --- /dev/null +++ b/include/ircd/life_guard.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016 Charybdis Development Team + * Copyright (C) 2016 Jason Volk + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice is present in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once +#define HAVE_IRCD_LIFE_GUARD_H + +#ifdef __cplusplus +namespace ircd { + +// Tests if type inherits from std::enable_shared_from_this<> +template +constexpr typename std::enable_if::value, bool>::type +is_shared_from_this() +{ + return std::is_base_of, T>(); +} + +// Unconditional failure for fwd-declared incomplete types, which +// obviously don't inherit from std::enable_shared_from_this<> +template +constexpr typename std::enable_if::value, bool>::type +is_shared_from_this() +{ + return false; +} + +/* Use the life_guard to keep an object alive within a function running in a context. + * + * Example: + * + void foo(client &c) + { + const life_guard lg(c); + + c.call(); // This call was always safe with or w/o life_guard. + ctx::wait(); // The context has now yielded and another context might destroy client &c + c.call(); // The context continues and this would have made a call on a dead c. + } +*/ +template +struct life_guard +:std::shared_ptr +{ + // This constructor is used when the templated type inherits from std::enable_shared_from_this<> + template + life_guard(T &t, + typename std::enable_if(), void>::type * = 0) + :std::shared_ptr(t.shared_from_this()) + { + } + + // This constructor uses our convention for forward declaring objects that internally + // inherit from std::enable_shared_from_this<>. Our convention is to provide: + // + // std::shared_ptr shared_from(T &c); + // + template + life_guard(T &t, + typename std::enable_if(), void>::type * = 0) + :std::shared_ptr(shared_from(t)) + { + } + + // This constructor is used with a weak_ptr of the type. This throws an exception + // to abort the scope when the object already died before being able to guard at all. + life_guard(const std::weak_ptr &wp) + :std::shared_ptr(wp.lock()) + { + if(wp.expired()) + throw std::bad_weak_ptr(); + } +}; + +} // namespace ircd +#endif // __cplusplus diff --git a/include/ircd/stdinc.h b/include/ircd/stdinc.h index 20038a0a5..5bfe99098 100644 --- a/include/ircd/stdinc.h +++ b/include/ircd/stdinc.h @@ -70,6 +70,7 @@ namespace ircd #include "util.h" #include "util_timer.h" +#include "life_guard.h" #include "defaults.h" #include "exception.h" #include "getopt.h" diff --git a/ircd/client.cc b/ircd/client.cc index da2e745d4..6817182c5 100644 --- a/ircd/client.cc +++ b/ircd/client.cc @@ -325,8 +325,20 @@ ircd::weak_from(client &client) return shared_from(client); } +std::weak_ptr +ircd::weak_from(const client &client) +{ + return shared_from(client); +} + std::shared_ptr ircd::shared_from(client &client) { return client.shared_from_this(); } + +std::shared_ptr +ircd::shared_from(const client &client) +{ + return client.shared_from_this(); +} diff --git a/ircd/vm.cc b/ircd/vm.cc index fe5cabca8..4fdce87bc 100644 --- a/ircd/vm.cc +++ b/ircd/vm.cc @@ -45,10 +45,8 @@ ircd::vm::execute(client &client, { context([wp(weak_from(client)), &client, &reel] { - auto cp(wp.lock()); // Hold the client for the lifetime of this context - - if(!cp) // client already gone though - return; + // Hold the client for the lifetime of this context + const lifeguard lg(wp); while(!reel.empty()) try { diff --git a/modules/m_host.cc b/modules/m_host.cc index c45a6282f..b992f6e03 100644 --- a/modules/m_host.cc +++ b/modules/m_host.cc @@ -60,6 +60,8 @@ m_host::operator()(client &client, line line) try { + const lifeguard lg(client); + const auto &host(line[0]); const auto &port(has(line, 1)? line[1] : std::string{}); const ip::tcp::resolver::query query(host, port);