diff --git a/include/ircd/ctx/future.h b/include/ircd/ctx/future.h index 42ad58832..c68098f81 100644 --- a/include/ircd/ctx/future.h +++ b/include/ircd/ctx/future.h @@ -18,7 +18,6 @@ namespace ircd::ctx template class future; template<> class future; template struct scoped_future; - enum class future_status; template -class ircd::ctx::future +struct ircd::ctx::future { - std::shared_ptr> st; + 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 bool(st); } + bool valid() const { return !invalid(st); } bool operator!() const { return !valid(); } operator bool() const { return valid(); } @@ -58,19 +57,24 @@ class ircd::ctx::future T get(); operator T() { return get(); } - future(); + future() = default; future(promise &promise); + future(future &&) noexcept; + future(const future &) = delete; + future &operator=(future &&) noexcept; + future &operator=(const future &) = delete; + ~future() noexcept; }; template<> -class ircd::ctx::future +struct ircd::ctx::future { - std::shared_ptr> st; + shared_state st; public: using value_type = typename shared_state::value_type; - bool valid() const { return bool(st); } + bool valid() const { return !invalid(st); } bool operator!() const { return !valid(); } operator bool() const { return valid(); } @@ -82,8 +86,13 @@ class ircd::ctx::future template future_status wait(const duration &d) const; void wait() const; - future(); + future() = default; future(promise &promise); + future(future &&) noexcept; + future(const future &) = delete; + future &operator=(future &&) noexcept; + future &operator=(const future &) = delete; + ~future() noexcept; }; template @@ -112,28 +121,77 @@ noexcept this->wait(); } -inline -ircd::ctx::future::future() -:st{nullptr} -{ -} - template -ircd::ctx::future::future() -:st{nullptr} +ircd::ctx::future::future(promise &promise) { + assert(!promise.st); + st.p = &promise; + update(st); + assert(promise.st); } inline ircd::ctx::future::future(promise &promise) -:st{promise.get_state().share()} { + assert(!promise.st); + st.p = &promise; + update(st); + assert(promise.st); } template -ircd::ctx::future::future(promise &promise) -:st{promise.get_state().share()} +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 @@ -142,17 +200,10 @@ ircd::ctx::future::get() { wait(); - if(unlikely(bool(st->eptr))) - std::rethrow_exception(st->eptr); + if(unlikely(bool(st.eptr))) + std::rethrow_exception(st.eptr); - return st->val; -} - -inline void -ircd::ctx::future::wait() -const -{ - this->wait_until(steady_clock::time_point::max()); + return st.val; } template @@ -163,9 +214,17 @@ 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) +ircd::ctx::future::wait(const duration &d) const { return this->wait_until(steady_clock::now() + d); @@ -173,17 +232,7 @@ const 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(const duration &d) +ircd::ctx::future::wait(const duration &d) const { return this->wait_until(steady_clock::now() + d); @@ -199,6 +248,15 @@ 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 @@ -208,6 +266,14 @@ const return ircd::ctx::wait_until(*this, tp); } +template +ircd::ctx::future_status +ircd::ctx::future::wait_until(const time_point &tp) +const +{ + return ircd::ctx::wait_until(*this, tp); +} + template template ircd::ctx::future_status @@ -218,14 +284,6 @@ const return ircd::ctx::wait_until(*this, tp, std::nothrow); } -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, @@ -261,15 +319,16 @@ ircd::ctx::wait_until(const future &f, { const auto wfun([&f]() -> bool { - return f.st->finished; + return !pending(f.st); }); - if(unlikely(!f.valid())) - throw no_state(); + if(unlikely(invalid(f.st))) + throw no_state{}; - if(unlikely(!f.st->cond.wait_until(tp, wfun))) + if(unlikely(!f.st.cond.wait_until(tp, wfun))) return future_status::timeout; - return likely(wfun())? future_status::ready: - future_status::deferred; + return likely(wfun())? + future_status::ready: + future_status::deferred; } diff --git a/include/ircd/ctx/promise.h b/include/ircd/ctx/promise.h index c38ddb2fd..a3490961f 100644 --- a/include/ircd/ctx/promise.h +++ b/include/ircd/ctx/promise.h @@ -13,20 +13,31 @@ 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 class promise; - template<> class promise; + 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 -class ircd::ctx::promise +struct ircd::ctx::promise { - std::shared_ptr> st; + 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; @@ -34,108 +45,149 @@ class ircd::ctx::promise using reference_type = typename shared_state::reference_type; bool valid() const { return bool(st); } - bool finished() const { return !valid() || st->finished; } bool operator!() const { return !valid(); } operator bool() const { return valid(); } - const shared_state &get_state() const { return *st; } - shared_state &get_state() { return *st; } + const shared_state &get_state() const { assert(valid()); return *st; } + shared_state &get_state() { assert(valid()); return *st; } void set_exception(std::exception_ptr eptr); void set_value(const T &val); void set_value(T&& val); - promise(); - promise(promise &&o) noexcept = default; + promise() = default; + promise(promise &&o) noexcept; promise(const promise &); promise &operator=(const promise &) = delete; - promise &operator=(promise &&) noexcept = default; + promise &operator=(promise &&) noexcept; ~promise() noexcept; }; template<> -class ircd::ctx::promise +struct ircd::ctx::promise { - std::shared_ptr> st; + 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; bool valid() const { return bool(st); } - bool finished() const { return !valid() || st->finished; } bool operator!() const { return !valid(); } operator bool() const { return valid(); } - const shared_state &get_state() const { return *st; } - shared_state &get_state() { return *st; } + const shared_state &get_state() const { assert(valid()); return *st; } + shared_state &get_state() { assert(valid()); return *st; } void set_exception(std::exception_ptr eptr); void set_value(); - promise(); - promise(promise &&o) noexcept = default; + promise() = default; + promise(promise &&o) noexcept; promise(const promise &); promise &operator=(const promise &) = delete; - promise &operator=(promise &&) noexcept = default; + promise &operator=(promise &&) noexcept; ~promise() noexcept; }; -inline -ircd::ctx::promise::promise() -:st{std::make_shared>()} -{ - ++st->promise_refcnt; - assert(st->promise_refcnt == 1); -} - template -ircd::ctx::promise::promise() -:st{std::make_shared>()} +ircd::ctx::promise::promise(promise &&o) +noexcept +:st{std::move(o.st)} +,next{std::move(o.next)} { - ++st->promise_refcnt; - assert(st->promise_refcnt == 1); + if(st) + { + update(*this, o); + o.st = nullptr; + } } inline -ircd::ctx::promise::promise(const promise &o) -:st{o.st} +ircd::ctx::promise::promise(promise &&o) +noexcept +:st{std::move(o.st)} +,next{std::move(o.next)} { - if(valid()) + if(st) { - ++st->promise_refcnt; - assert(st->promise_refcnt > 1); + update(*this, o); + o.st = nullptr; } } template ircd::ctx::promise::promise(const promise &o) :st{o.st} +,next{nullptr} { - if(valid()) - { - ++st->promise_refcnt; - assert(st->promise_refcnt > 1); - } + append(*this, const_cast &>(o)); } inline -ircd::ctx::promise::~promise() +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 { - if(valid()) - if(!--st->promise_refcnt) - if(!st->finished && !st.unique()) - set_exception(std::make_exception_ptr(broken_promise())); + 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()) - if(!--st->promise_refcnt) - if(!st->finished && !st.unique()) - set_exception(std::make_exception_ptr(broken_promise())); + if(!valid()) + return; + + if(refcount(*st) == 1) + set_exception(std::make_exception_ptr(broken_promise())); + else + remove(*st, *this); +} + +inline +ircd::ctx::promise::~promise() +noexcept +{ + if(!valid()) + return; + + if(refcount(*st) == 1) + set_exception(std::make_exception_ptr(broken_promise())); + else + remove(*st, *this); } template @@ -143,18 +195,11 @@ void ircd::ctx::promise::set_value(T&& val) { assert(valid()); - assert(!finished()); + assert(pending(*st)); + auto *const st{this->st}; + invalidate(*st); + set_ready(*st); st->val = std::move(val); - st->finished = true; - st->cond.notify_all(); -} - -inline void -ircd::ctx::promise::set_value() -{ - assert(valid()); - assert(!finished()); - st->finished = true; st->cond.notify_all(); } @@ -163,19 +208,22 @@ void ircd::ctx::promise::set_value(const T &val) { assert(valid()); - assert(!finished()); + assert(pending(*st)); + auto *const st{this->st}; + invalidate(*st); + set_ready(*st); st->val = val; - st->finished = true; st->cond.notify_all(); } inline void -ircd::ctx::promise::set_exception(std::exception_ptr eptr) +ircd::ctx::promise::set_value() { assert(valid()); - assert(!finished()); - st->eptr = std::move(eptr); - st->finished = true; + assert(pending(*st)); + auto *const st{this->st}; + invalidate(*st); + set_ready(*st); st->cond.notify_all(); } @@ -184,8 +232,116 @@ void ircd::ctx::promise::set_exception(std::exception_ptr eptr) { assert(valid()); - assert(!finished()); + assert(pending(*st)); + auto *const st{this->st}; + invalidate(*st); + set_ready(*st); st->eptr = std::move(eptr); - st->finished = true; st->cond.notify_all(); } + +inline void +ircd::ctx::promise::set_exception(std::exception_ptr eptr) +{ + assert(valid()); + assert(pending(*st)); + auto *const st{this->st}; + invalidate(*st); + set_ready(*st); + st->eptr = std::move(eptr); + st->cond.notify_all(); +} + +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(!pending(st)) + 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(!pending(st)) + 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(pending(st)) + for(promise *p{st.p}; p; p = p->next) + p->st = nullptr; +} + +template +void +ircd::ctx::update(shared_state &st) +{ + if(pending(st)) + 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(pending(st)) + for(const promise *p{st.p}; p; p = p->next) + ++ret; + + return ret; +} diff --git a/include/ircd/ctx/shared_state.h b/include/ircd/ctx/shared_state.h index e9d422777..f2cf18b2a 100644 --- a/include/ircd/ctx/shared_state.h +++ b/include/ircd/ctx/shared_state.h @@ -16,66 +16,67 @@ namespace ircd::ctx struct shared_state_base; template struct shared_state; template<> struct shared_state; + + template struct promise; + template<> struct promise; + + template bool invalid(const shared_state &); + template bool pending(const shared_state &); + template bool ready(const shared_state &); + template void set_ready(shared_state &); } struct ircd::ctx::shared_state_base { - dock cond; + mutable dock cond; std::exception_ptr eptr; - uint promise_refcnt {0}; - bool finished {false}; }; template struct ircd::ctx::shared_state :shared_state_base -,std::enable_shared_from_this> { using value_type = T; using pointer_type = T *; using reference_type = T &; + promise *p {nullptr}; T val; - - std::shared_ptr> share() const; - std::shared_ptr> share(); }; template<> struct ircd::ctx::shared_state :shared_state_base -,std::enable_shared_from_this> { using value_type = void; - std::shared_ptr> share() const; - std::shared_ptr> share(); + promise *p {nullptr}; }; -inline std::shared_ptr> -ircd::ctx::shared_state::share() +template +void +ircd::ctx::set_ready(shared_state &st) { - return this->shared_from_this(); + st.p = reinterpret_cast *>(uintptr_t(0x42)); } template -std::shared_ptr> -ircd::ctx::shared_state::share() +bool +ircd::ctx::ready(const shared_state &st) { - return this->shared_from_this(); -} - -inline std::shared_ptr> -ircd::ctx::shared_state::share() -const -{ - return this->shared_from_this(); + return st.p == reinterpret_cast *>(uintptr_t(0x42)); } template -std::shared_ptr> -ircd::ctx::shared_state::share() -const +bool +ircd::ctx::pending(const shared_state &st) { - return this->shared_from_this(); + return st.p > reinterpret_cast *>(uintptr_t(0x42)); +} + +template +bool +ircd::ctx::invalid(const shared_state &st) +{ + return st.p == nullptr; } diff --git a/ircd/server.cc b/ircd/server.cc index 587796898..2c8394896 100644 --- a/ircd/server.cc +++ b/ircd/server.cc @@ -2331,6 +2331,9 @@ template void ircd::server::tag::set_exception(args&&... a) { + if(abandoned()) + return; + set_exception(std::make_exception_ptr(std::forward(a)...)); } @@ -2348,7 +2351,7 @@ bool ircd::server::tag::abandoned() const { - return p.finished(); + return !p.valid(); } bool