// 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_PROMISE_H namespace ircd::ctx { template class promise; template<> class promise; template class future; template<> class future; 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 struct ircd::ctx::promise { shared_state *st {nullptr}; // Reference to the state resident in future mutable promise *next {nullptr}; // Promise fwdlist to support copy semantics 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; const shared_state &state() const { assert(valid()); return *st; } shared_state &state() { assert(valid()); return *st; } bool valid() const { return bool(st); } bool operator!() const { return !valid(); } operator bool() const { return valid(); } void set_exception(std::exception_ptr eptr); void set_value(const T &val); void set_value(T&& val); promise() = default; promise(promise &&o) noexcept; promise(const promise &); promise &operator=(const promise &) = delete; promise &operator=(promise &&) noexcept; ~promise() noexcept; }; template<> struct ircd::ctx::promise { shared_state *st {nullptr}; // Reference to the state resident in future mutable promise *next {nullptr}; // Promise fwdlist to support copy semantics public: using value_type = typename shared_state::value_type; const shared_state &state() const { assert(valid()); return *st; } shared_state &state() { assert(valid()); return *st; } bool valid() const { return bool(st); } bool operator!() const { return !valid(); } operator bool() const { return valid(); } void set_exception(std::exception_ptr eptr); void set_value(); promise() = default; promise(promise &&o) noexcept; promise(const promise &); promise &operator=(const promise &) = delete; promise &operator=(promise &&) noexcept; ~promise() noexcept; }; namespace ircd::ctx { template size_t refcount(const shared_state &); template void update(shared_state &s); template void invalidate(shared_state &); template void remove(shared_state &, promise &); template void update(promise &new_, promise &old); template void append(promise &new_, promise &old); } template ircd::ctx::promise::promise(promise &&o) noexcept :st{std::move(o.st)} ,next{std::move(o.next)} { if(st) { update(*this, o); o.st = nullptr; } } inline ircd::ctx::promise::promise(promise &&o) noexcept :st{std::move(o.st)} ,next{std::move(o.next)} { if(st) { update(*this, o); o.st = nullptr; } } template ircd::ctx::promise::promise(const promise &o) :st{o.st} ,next{nullptr} { append(*this, const_cast &>(o)); } inline ircd::ctx::promise::promise(const promise &o) :st{o.st} ,next{nullptr} { append(*this, const_cast &>(o)); } template ircd::ctx::promise & ircd::ctx::promise::operator=(promise &&o) noexcept { this->~promise(); st = std::move(o.st); next = std::move(o.next); if(!st) return *this; update(*this, o); o.st = nullptr; return *this; } inline ircd::ctx::promise & ircd::ctx::promise::operator=(promise &&o) noexcept { this->~promise(); st = std::move(o.st); next = std::move(o.next); if(!st) return *this; update(*this, o); o.st = nullptr; return *this; } template ircd::ctx::promise::~promise() noexcept { if(!valid()) return; if(refcount(state()) == 1) set_exception(std::make_exception_ptr(broken_promise())); else remove(state(), *this); } inline ircd::ctx::promise::~promise() noexcept { if(!valid()) return; if(refcount(state()) == 1) set_exception(std::make_exception_ptr(broken_promise())); else remove(state(), *this); } template void ircd::ctx::promise::set_value(T&& val) { assert(valid()); if(unlikely(!is(state(), future_state::PENDING))) throw promise_already_satisfied{}; st->val = std::move(val); auto *const st{this->st}; invalidate(*st); set(*st, future_state::READY); notify(*st); assert(!valid()); } template void ircd::ctx::promise::set_value(const T &val) { assert(valid()); if(unlikely(!is(state(), future_state::PENDING))) throw promise_already_satisfied{}; st->val = val; auto *const st{this->st}; invalidate(*st); set(*st, future_state::READY); notify(*st); assert(!valid()); } inline void ircd::ctx::promise::set_value() { assert(valid()); if(unlikely(!is(state(), future_state::PENDING))) throw promise_already_satisfied{}; auto *const st{this->st}; invalidate(*st); set(*st, future_state::READY); notify(*st); assert(!valid()); } template void ircd::ctx::promise::set_exception(std::exception_ptr eptr) { assert(valid()); if(unlikely(!is(state(), future_state::PENDING))) throw promise_already_satisfied{}; st->eptr = std::move(eptr); auto *const st{this->st}; invalidate(*st); set(*st, future_state::READY); notify(*st); assert(!valid()); } inline void ircd::ctx::promise::set_exception(std::exception_ptr eptr) { assert(valid()); if(unlikely(!is(state(), future_state::PENDING))) throw promise_already_satisfied{}; st->eptr = std::move(eptr); auto *const st{this->st}; invalidate(*st); set(*st, future_state::READY); notify(*st); assert(!valid()); } template void ircd::ctx::append(promise &new_, promise &old) { if(!old.next) { old.next = &new_; return; } promise *next{old.next}; for(; next->next; next = next->next); next->next = &new_; } template void ircd::ctx::update(promise &new_, promise &old) { assert(old.st); auto &st{*old.st}; if(!is(st, future_state::PENDING)) return; if(st.p == &old) { st.p = &new_; return; } promise *last{st.p}; for(promise *next{last->next}; next; last = next, next = last->next) if(next == &old) { last->next = &new_; break; } } template void ircd::ctx::remove(shared_state &st, promise &p) { if(!is(st, future_state::PENDING)) return; if(st.p == &p) { st.p = p.next; return; } promise *last{st.p}; for(promise *next{last->next}; next; last = next, next = last->next) if(next == &p) { last->next = p.next; break; } } template void ircd::ctx::invalidate(shared_state &st) { if(is(st, future_state::PENDING)) for(promise *p{st.p}; p; p = p->next) p->st = nullptr; } template void ircd::ctx::update(shared_state &st) { if(is(st, future_state::PENDING)) for(promise *p{st.p}; p; p = p->next) p->st = &st; } template size_t ircd::ctx::refcount(const shared_state &st) { size_t ret{0}; if(is(st, future_state::PENDING)) for(const promise *p{st.p}; p; p = p->next) ++ret; return ret; }