// 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 { template class future; template<> class future; template struct scoped_future; IRCD_EXCEPTION(future_error, future_already_retrieved) IRCD_OVERLOAD(use_future) } template struct ircd::ctx::future :private shared_state { using value_type = typename shared_state::value_type; using pointer_type = typename shared_state::pointer_type; using reference_type = typename shared_state::reference_type; const shared_state &state() const { return *this; } shared_state &state() { return *this; } bool valid() const { return !is(state(), future_state::INVALID); } bool operator!() const { return !valid(); } operator bool() const { return valid(); } template friend bool wait_until(const future &, const time_point &, std::nothrow_t); template friend void wait_until(const future &, const time_point &); template bool wait_until(const time_point &, std::nothrow_t) const; template void wait_until(const time_point &) const; template bool wait(const duration &d, std::nothrow_t) const; template void 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 :private shared_state { using value_type = typename shared_state::value_type; const shared_state &state() const { return *this; } shared_state &state() { return *this; } bool valid() const { return !is(state(), future_state::INVALID); } bool operator!() const { return !valid(); } operator bool() const { return valid(); } template friend bool wait_until(const future &, const time_point &, std::nothrow_t); template friend void wait_until(const future &, const time_point &); template bool wait_until(const time_point &, std::nothrow_t) const; template void wait_until(const time_point &) const; template bool wait(const duration &d, std::nothrow_t) const; template void wait(const duration &d) const; void wait() const; IRCD_OVERLOAD(already) future(promise &promise); future(already_t); // construct in ready state future() = default; future(future &&) noexcept; future(const future &) = delete; future &operator=(future &&) noexcept; future &operator=(const future &) = delete; ~future() noexcept; }; namespace ircd::ctx { template const shared_state &state(const future &); template shared_state &state(future &); template bool 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_exceptions()) return; if(this->valid()) this->wait(); } template ircd::ctx::future::future(promise &promise) :shared_state{promise} { assert(!promise.valid()); update(state()); assert(promise.valid()); } inline ircd::ctx::future::future(promise &promise) :shared_state{promise} { assert(!promise.valid()); update(state()); assert(promise.valid()); } inline ircd::ctx::future::future(already_t) { set(state(), future_state::READY); } template ircd::ctx::future::future(future &&o) noexcept :shared_state{std::move(o)} { update(state()); o.state().p = nullptr; } inline ircd::ctx::future::future(future &&o) noexcept :shared_state{std::move(o)} { update(state()); o.state().p = nullptr; } template ircd::ctx::future & ircd::ctx::future::operator=(future &&o) noexcept { this->~future(); static_cast &>(*this) = std::move(o); update(state()); o.state().p = nullptr; return *this; } inline ircd::ctx::future & ircd::ctx::future::operator=(future &&o) noexcept { this->~future(); static_cast &>(*this) = std::move(o); update(state()); o.state().p = nullptr; return *this; } template ircd::ctx::future::~future() noexcept { invalidate(state()); } inline ircd::ctx::future::~future() noexcept { invalidate(state()); } template T ircd::ctx::future::get() { wait(); if(unlikely(is(state(), future_state::RETRIEVED))) throw future_already_retrieved{}; set(state(), future_state::RETRIEVED); if(bool(state().eptr)) std::rethrow_exception(state().eptr); return state().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 void ircd::ctx::future::wait(const duration &d) const { this->wait_until(steady_clock::now() + d); } template void ircd::ctx::future::wait(const duration &d) const { this->wait_until(steady_clock::now() + d); } template template bool ircd::ctx::future::wait(const duration &d, std::nothrow_t) const { return this->wait_until(steady_clock::now() + d, std::nothrow); } template bool ircd::ctx::future::wait(const duration &d, std::nothrow_t) const { return this->wait_until(steady_clock::now() + d, std::nothrow); } template template void ircd::ctx::future::wait_until(const time_point &tp) const { ircd::ctx::wait_until(*this, tp); } template void ircd::ctx::future::wait_until(const time_point &tp) const { if(!this->wait_until(tp, std::nothrow)) throw timeout{}; } template template bool ircd::ctx::future::wait_until(const time_point &tp, std::nothrow_t) const { return ircd::ctx::wait_until(*this, tp, std::nothrow); } template bool ircd::ctx::future::wait_until(const time_point &tp, std::nothrow_t) const { if(ircd::ctx::wait_until(*this, tp, std::nothrow)) { auto &state { const_cast *>(this)->state() }; set(state, future_state::RETRIEVED); if(bool(state.eptr)) std::rethrow_exception(state.eptr); return true; } else return false; } template void ircd::ctx::wait_until(const future &f, const time_point &tp) { if(!wait_until(f, tp, std::nothrow)) throw timeout{}; } template bool ircd::ctx::wait_until(const future &f, const time_point &tp, std::nothrow_t) { auto &state { const_cast &>(f).state() }; const auto wfun([&state]() -> bool { return !is(state, future_state::PENDING); }); if(unlikely(is(state, future_state::INVALID))) throw no_state{}; if(unlikely(!state.cond.wait_until(tp, wfun))) return false; return wfun(); } template ircd::ctx::shared_state & ircd::ctx::state(future &future) { return future.state(); } template const ircd::ctx::shared_state & ircd::ctx::state(const future &future) { return future.state(); }