// Matrix Construct // // Copyright (C) Matrix Construct Developers, Authors & Contributors // Copyright (C) 2016-2018 Jason Volk <jason@zemos.net> // // 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 T = void> class promise; template<> class promise<void>; template<class T> class future; template<> class future<void>; 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 T> struct ircd::ctx::promise { shared_state<T> *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<T>::value_type; using pointer_type = typename shared_state<T>::pointer_type; using reference_type = typename shared_state<T>::reference_type; const shared_state<T> &state() const { assert(valid()); return *st; } shared_state<T> &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<void> { shared_state<void> *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<void>::value_type; const shared_state<void> &state() const { assert(valid()); return *st; } shared_state<void> &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<class T> size_t refcount(const shared_state<T> &); template<class T> void update(shared_state<T> &s); template<class T> void invalidate(shared_state<T> &); template<class T> void remove(shared_state<T> &, promise<T> &); template<class T> void update(promise<T> &new_, promise<T> &old); template<class T> void append(promise<T> &new_, promise<T> &old); } template<class T> ircd::ctx::promise<T>::promise(promise<T> &&o) noexcept :st{std::move(o.st)} ,next{std::move(o.next)} { if(st) { update(*this, o); o.st = nullptr; } } inline ircd::ctx::promise<void>::promise(promise<void> &&o) noexcept :st{std::move(o.st)} ,next{std::move(o.next)} { if(st) { update(*this, o); o.st = nullptr; } } template<class T> ircd::ctx::promise<T>::promise(const promise<T> &o) :st{o.st} ,next{nullptr} { append(*this, const_cast<promise<T> &>(o)); } inline ircd::ctx::promise<void>::promise(const promise<void> &o) :st{o.st} ,next{nullptr} { append(*this, const_cast<promise<void> &>(o)); } template<class T> ircd::ctx::promise<T> & ircd::ctx::promise<T>::operator=(promise<T> &&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<void> & ircd::ctx::promise<void>::operator=(promise<void> &&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<class T> ircd::ctx::promise<T>::~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<void>::~promise() noexcept { if(!valid()) return; if(refcount(state()) == 1) set_exception(std::make_exception_ptr(broken_promise())); else remove(state(), *this); } template<class T> void ircd::ctx::promise<T>::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<class T> void ircd::ctx::promise<T>::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<void>::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<class T> void ircd::ctx::promise<T>::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<void>::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<class T> void ircd::ctx::append(promise<T> &new_, promise<T> &old) { if(!old.next) { old.next = &new_; return; } promise<T> *next{old.next}; for(; next->next; next = next->next); next->next = &new_; } template<class T> void ircd::ctx::update(promise<T> &new_, promise<T> &old) { assert(old.st); auto &st{*old.st}; if(!is(st, future_state::PENDING)) return; if(st.p == &old) { st.p = &new_; return; } promise<T> *last{st.p}; for(promise<T> *next{last->next}; next; last = next, next = last->next) if(next == &old) { last->next = &new_; break; } } template<class T> void ircd::ctx::remove(shared_state<T> &st, promise<T> &p) { if(!is(st, future_state::PENDING)) return; if(st.p == &p) { st.p = p.next; return; } promise<T> *last{st.p}; for(promise<T> *next{last->next}; next; last = next, next = last->next) if(next == &p) { last->next = p.next; break; } } template<class T> void ircd::ctx::invalidate(shared_state<T> &st) { if(is(st, future_state::PENDING)) for(promise<T> *p{st.p}; p; p = p->next) p->st = nullptr; } template<class T> void ircd::ctx::update(shared_state<T> &st) { if(is(st, future_state::PENDING)) for(promise<T> *p{st.p}; p; p = p->next) p->st = &st; } template<class T> size_t ircd::ctx::refcount(const shared_state<T> &st) { size_t ret{0}; if(is(st, future_state::PENDING)) for(const promise<T> *p{st.p}; p; p = p->next) ++ret; return ret; }