// Matrix Construct // // Copyright (C) Matrix Construct Developers, Authors & Contributors // Copyright (C) 2016-2018 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. The // full license for this software is available in the LICENSE file. #pragma once #define HAVE_IRCD_CTX_FUTURE_H namespace ircd::ctx { IRCD_OVERLOAD(use_future) template class future; template<> class future; template struct scoped_future; enum class future_status; } enum class ircd::ctx::future_status { ready, timeout, deferred, }; template struct ircd::ctx::future { mutable shared_state 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 !invalid(st); } bool operator!() const { return !valid(); } operator bool() const { return valid(); } template friend future_status wait_until(const future &, const time_point &, std::nothrow_t); template friend future_status wait_until(const future &, const time_point &); template future_status wait_until(const time_point &, std::nothrow_t) const; template future_status wait_until(const time_point &) const; template future_status wait(const duration &d, std::nothrow_t) const; template future_status wait(const duration &d) const; void wait() const; T get(); operator T() { return get(); } future() = default; future(promise &promise); future(future &&) noexcept; future(const future &) = delete; future &operator=(future &&) noexcept; future &operator=(const future &) = delete; ~future() noexcept; }; template<> struct ircd::ctx::future { mutable shared_state st; public: using value_type = typename shared_state::value_type; bool valid() const { return !invalid(st); } bool operator!() const { return !valid(); } operator bool() const { return valid(); } template friend future_status wait_until(const future &, const time_point &, std::nothrow_t); template friend future_status wait_until(const future &, const time_point &); template future_status wait_until(const time_point &, std::nothrow_t) const; template future_status wait_until(const time_point &) const; template future_status wait(const duration &d, std::nothrow_t) const; template future_status wait(const duration &d) const; void wait() const; future() = default; future(promise &promise); future(future &&) noexcept; future(const future &) = delete; future &operator=(future &&) noexcept; future &operator=(const future &) = delete; ~future() noexcept; }; namespace ircd::ctx { template future_status wait_until(const future &, const time_point &, std::nothrow_t); } template struct ircd::ctx::scoped_future :future { template scoped_future(Args&&... args); ~scoped_future() noexcept; }; template template ircd::ctx::scoped_future::scoped_future(Args&&... args) :future{std::forward(args)...} { } template ircd::ctx::scoped_future::~scoped_future() noexcept { if(std::uncaught_exception()) return; if(this->valid()) this->wait(); } template ircd::ctx::future::future(promise &promise) { assert(!promise.st); st.p = &promise; update(st); assert(promise.st); } inline ircd::ctx::future::future(promise &promise) { assert(!promise.st); st.p = &promise; update(st); assert(promise.st); } template ircd::ctx::future::future(future &&o) noexcept :st{std::move(o.st)} { update(st); o.st.p = nullptr; } inline ircd::ctx::future::future(future &&o) noexcept :st{std::move(o.st)} { update(st); o.st.p = nullptr; } template ircd::ctx::future & ircd::ctx::future::operator=(future &&o) noexcept { this->~future(); st = std::move(o.st); update(st); o.st.p = nullptr; return *this; } inline ircd::ctx::future & ircd::ctx::future::operator=(future &&o) noexcept { this->~future(); st = std::move(o.st); update(st); o.st.p = nullptr; return *this; } template ircd::ctx::future::~future() noexcept { invalidate(st); } inline ircd::ctx::future::~future() noexcept { invalidate(st); } template T ircd::ctx::future::get() { wait(); if(unlikely(retrieved(st))) throw future_already_retrieved{}; set_retrieved(st); if(bool(st.eptr)) std::rethrow_exception(st.eptr); return st.val; } template void ircd::ctx::future::wait() const { this->wait_until(steady_clock::time_point::max()); } inline void ircd::ctx::future::wait() const { this->wait_until(steady_clock::time_point::max()); } template template ircd::ctx::future_status ircd::ctx::future::wait(const duration &d) const { return this->wait_until(steady_clock::now() + d); } template ircd::ctx::future_status ircd::ctx::future::wait(const duration &d) const { return this->wait_until(steady_clock::now() + d); } template template ircd::ctx::future_status ircd::ctx::future::wait(const duration &d, std::nothrow_t) const { return this->wait_until(steady_clock::now() + d, std::nothrow); } template ircd::ctx::future_status ircd::ctx::future::wait(const duration &d, std::nothrow_t) const { return this->wait_until(steady_clock::now() + d, std::nothrow); } template template ircd::ctx::future_status ircd::ctx::future::wait_until(const time_point &tp) const { return ircd::ctx::wait_until(*this, tp); } template ircd::ctx::future_status ircd::ctx::future::wait_until(const time_point &tp) const { const auto status { this->wait_until(tp, std::nothrow) }; if(status == future_status::timeout) throw timeout{}; return status; } template template ircd::ctx::future_status ircd::ctx::future::wait_until(const time_point &tp, std::nothrow_t) const { return ircd::ctx::wait_until(*this, tp, std::nothrow); } template ircd::ctx::future_status ircd::ctx::future::wait_until(const time_point &tp, std::nothrow_t) const { const auto status { ircd::ctx::wait_until(*this, tp, std::nothrow) }; if(status == future_status::ready) { set_retrieved(st); if(bool(st.eptr)) std::rethrow_exception(st.eptr); } return status; } template ircd::ctx::future_status ircd::ctx::wait_until(const future &f, const time_point &tp) { const auto ret { wait_until(f, tp, std::nothrow) }; if(ret == future_status::timeout) throw timeout{}; return ret; } template ircd::ctx::future_status ircd::ctx::wait_until(const future &f, const time_point &tp, std::nothrow_t) { const auto wfun([&f]() -> bool { return !pending(f.st); }); if(unlikely(invalid(f.st))) throw no_state{}; if(unlikely(!f.st.cond.wait_until(tp, wfun))) return future_status::timeout; return likely(wfun())? future_status::ready: future_status::deferred; }