diff --git a/include/ircd/ctx_future.h b/include/ircd/ctx_future.h new file mode 100644 index 000000000..f8b897663 --- /dev/null +++ b/include/ircd/ctx_future.h @@ -0,0 +1,215 @@ +/* + * 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_CTX_FUTURE_H + +namespace ircd { +namespace ctx { + +enum class future_status +{ + ready, + timeout, + deferred, +}; + +template +class future +{ + std::shared_ptr> st; + + public: + using value_type = typename shared_state::value_type; + using pointer_type = typename shared_state::pointer_type; + using reference_type = typename shared_state::reference_type; + + bool valid() const { return bool(st); } + bool operator!() const { return !valid(); } + operator bool() const { return valid(); } + + template future_status wait_until(const time_point &) const; + template future_status wait(const duration &d) const; + void wait() const; + + T get(); + operator T() { return get(); } + + future(); + future(promise &promise); +}; + +template<> +class future +{ + std::shared_ptr> st; + + public: + using value_type = typename shared_state::value_type; + + bool valid() const { return bool(st); } + bool operator!() const { return !valid(); } + operator bool() const { return valid(); } + + template future_status wait_until(const time_point &) const; + template future_status wait(const duration &d) const; + void wait() const; + + future(); + future(promise &promise); +}; + +template +struct scoped_future : future +{ + template scoped_future(Args&&... args); + ~scoped_future() noexcept; +}; + +template +template +scoped_future::scoped_future(Args&&... args): +future{std::forward(args)...} +{ +} + +template +scoped_future::~scoped_future() +noexcept +{ + if(std::uncaught_exception()) + return; + + if(this->valid()) + this->wait(); +} + +inline +future::future(): +st(nullptr) +{ +} + +template +future::future(): +st(nullptr) +{ +} + +inline +future::future(promise &promise): +st(promise.get_state().share()) +{ +} + +template +future::future(promise &promise): +st(promise.get_state().share()) +{ +} + +template +T +future::get() +{ + wait(); + + if(unlikely(bool(st->eptr))) + std::rethrow_exception(st->eptr); + + return st->val; +} + +inline void +future::wait() +const +{ + this->wait_until(steady_clock::time_point::max()); +} + +template +void +future::wait() +const +{ + this->wait_until(steady_clock::time_point::max()); +} + +template +future_status +future::wait(const duration &d) +const +{ + return this->wait_until(steady_clock::now() + d); +} + +template +template +future_status +future::wait(const duration &d) +const +{ + return this->wait_until(steady_clock::now() + d); +} + +template +future_status +future::wait_until(const time_point &tp) +const +{ + const auto wfun([this]() -> bool + { + return st->finished; + }); + + if(unlikely(!valid())) + throw no_state(); + + if(unlikely(!st->cond.wait_until(tp, wfun))) + return future_status::timeout; + + return likely(wfun())? future_status::ready: + future_status::deferred; +} + +template +template +future_status +future::wait_until(const time_point &tp) +const +{ + const auto wfun([this] + { + return st->finished; + }); + + if(unlikely(!valid())) + throw no_state(); + + if(unlikely(!st->cond.wait_until(tp, wfun))) + return future_status::timeout; + + return likely(wfun())? future_status::ready: + future_status::deferred; +} + +} // namespace ctx +} // namespace ircd diff --git a/include/ircd/ctx_promise.h b/include/ircd/ctx_promise.h new file mode 100644 index 000000000..91ea13b2a --- /dev/null +++ b/include/ircd/ctx_promise.h @@ -0,0 +1,192 @@ +/* + * 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_CTX_PROMISE_H + +namespace ircd { +namespace ctx { + +IRCD_EXCEPTION(ircd::ctx::error, future_error) +IRCD_EXCEPTION(future_error, no_state) +IRCD_EXCEPTION(future_error, broken_promise) +IRCD_EXCEPTION(future_error, future_already_retrieved) +IRCD_EXCEPTION(future_error, promise_already_satisfied) + +template +class promise +{ + std::shared_ptr> st; + + public: + using value_type = typename shared_state::value_type; + using pointer_type = typename shared_state::pointer_type; + using reference_type = typename shared_state::reference_type; + + bool valid() const { return bool(st); } + bool operator!() const { return !valid(); } + operator bool() const { return valid(); } + + const shared_state &get_state() const { return *st; } + shared_state &get_state() { return *st; } + + void set_exception(std::exception_ptr eptr); + void set_value(const T &val); + void set_value(T&& val); + void reset(); + + promise(); + promise(promise &&o) noexcept; + promise(const promise &) = delete; + promise &operator=(promise &&o) noexcept; + promise &operator=(const promise &) = delete; + ~promise() noexcept; +}; + +template<> +class promise +{ + std::shared_ptr> st; + + public: + using value_type = typename shared_state::value_type; + + bool valid() const { return bool(st); } + bool operator!() const { return !valid(); } + operator bool() const { return valid(); } + + const shared_state &get_state() const { return *st; } + shared_state &get_state() { return *st; } + + void set_exception(std::exception_ptr eptr); + void set_value(); + void reset(); + + promise(); + promise(const promise &) = delete; + promise &operator=(const promise &) = delete; + ~promise() noexcept; +}; + + +inline +promise::promise(): +st(std::make_shared>()) +{ +} + +template +promise::promise(): +st(std::make_shared>()) +{ +} + +template +promise::promise(promise &&o) +noexcept: +st(std::move(o.st)) +{ +} + +template +promise & +promise::operator=(promise &&o) +noexcept +{ + st = std::move(o.st); + return *this; +} + +inline +promise::~promise() +noexcept +{ + if(valid() && !st->finished && !st.unique()) + set_exception(std::make_exception_ptr(broken_promise())); +} + +template +promise::~promise() +noexcept +{ + if(valid() && !st->finished && !st.unique()) + set_exception(std::make_exception_ptr(broken_promise())); +} + +inline void +promise::reset() +{ + if(valid()) + st->reset(); +} + +template +void +promise::reset() +{ + if(valid()) + st->reset(); +} + +template +void +promise::set_value(T&& val) +{ + st->val = std::move(val); + st->finished = true; + st->cond.notify_all(); +} + +inline void +promise::set_value() +{ + st->finished = true; + st->cond.notify_all(); +} + +template +void +promise::set_value(const T &val) +{ + st->val = val; + st->finished = true; + st->cond.notify_all(); +} + +inline void +promise::set_exception(std::exception_ptr eptr) +{ + st->eptr = std::move(eptr); + st->finished = true; + st->cond.notify_all(); +} + +template +void +promise::set_exception(std::exception_ptr eptr) +{ + st->eptr = std::move(eptr); + st->finished = true; + st->cond.notify_all(); +} + +} // namespace ctx +} // namespace ircd diff --git a/include/ircd/ctx_shared_state.h b/include/ircd/ctx_shared_state.h new file mode 100644 index 000000000..8d58faa41 --- /dev/null +++ b/include/ircd/ctx_shared_state.h @@ -0,0 +1,100 @@ +/* + * 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_CTX_SHARED_STATE_H + +namespace ircd { +namespace ctx { + +struct shared_state_base +{ + dock cond; + std::exception_ptr eptr; + bool finished = false; + + void reset(); +}; + +template +struct shared_state +:shared_state_base +,std::enable_shared_from_this> +{ + using value_type = T; + using pointer_type = T *; + using reference_type = T &; + + T val; + + std::shared_ptr> share() const; + std::shared_ptr> share(); +}; + +template<> +struct shared_state +:shared_state_base +,std::enable_shared_from_this> +{ + using value_type = void; + + std::shared_ptr> share() const; + std::shared_ptr> share(); +}; + + +inline std::shared_ptr> +shared_state::share() +{ + return this->shared_from_this(); +} + +template +std::shared_ptr> +shared_state::share() +{ + return this->shared_from_this(); +} + +inline std::shared_ptr> +shared_state::share() +const +{ + return this->shared_from_this(); +} + +template +std::shared_ptr> +shared_state::share() +const +{ + return this->shared_from_this(); +} + +inline void +shared_state_base::reset() +{ + eptr = nullptr; + finished = false; +} + +} // namespace ctx +} // namespace ircd diff --git a/include/ircd/stdinc.h b/include/ircd/stdinc.h index 40698aaf7..210745100 100644 --- a/include/ircd/stdinc.h +++ b/include/ircd/stdinc.h @@ -92,6 +92,9 @@ namespace ircd #include "ctx.h" #include "ctx_dock.h" #include "ctx_mutex.h" +#include "ctx_shared_state.h" +#include "ctx_promise.h" +#include "ctx_future.h" #include "line.h" #include "tape.h"