0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2025-02-18 09:40:12 +01:00

ircd::ctx: Cleanup/improve the shared_state states.

This commit is contained in:
Jason Volk 2018-03-15 11:45:01 -07:00
parent 150831cd83
commit fbb9cf0196
4 changed files with 100 additions and 74 deletions

View file

@ -39,7 +39,7 @@ struct ircd::ctx::future
const shared_state<T> &state() const { return *this; } const shared_state<T> &state() const { return *this; }
shared_state<T> &state() { return *this; } shared_state<T> &state() { return *this; }
bool valid() const { return !invalid(state()); } bool valid() const { return !is(state(), future_state::INVALID); }
bool operator!() const { return !valid(); } bool operator!() const { return !valid(); }
operator bool() const { return valid(); } operator bool() const { return valid(); }
@ -72,7 +72,7 @@ struct ircd::ctx::future<void>
const shared_state<void> &state() const { return *this; } const shared_state<void> &state() const { return *this; }
shared_state<void> &state() { return *this; } shared_state<void> &state() { return *this; }
bool valid() const { return !invalid(state()); } bool valid() const { return !is(state(), future_state::INVALID); }
bool operator!() const { return !valid(); } bool operator!() const { return !valid(); }
operator bool() const { return valid(); } operator bool() const { return valid(); }
@ -150,7 +150,7 @@ ircd::ctx::future<void>::future(promise<void> &promise)
inline inline
ircd::ctx::future<void>::future(already_t) ircd::ctx::future<void>::future(already_t)
{ {
set_ready(state()); set(state(), future_state::READY);
} }
template<class T> template<class T>
@ -213,10 +213,10 @@ T
ircd::ctx::future<T>::get() ircd::ctx::future<T>::get()
{ {
wait(); wait();
if(unlikely(retrieved(state()))) if(unlikely(is(state(), future_state::RETRIEVED)))
throw future_already_retrieved{}; throw future_already_retrieved{};
set_retrieved(state()); set(state(), future_state::RETRIEVED);
if(bool(state().eptr)) if(bool(state().eptr))
std::rethrow_exception(state().eptr); std::rethrow_exception(state().eptr);
@ -327,7 +327,7 @@ const
const_cast<future<void> *>(this)->state() const_cast<future<void> *>(this)->state()
}; };
set_retrieved(state); set(state, future_state::RETRIEVED);
if(bool(state.eptr)) if(bool(state.eptr))
std::rethrow_exception(state.eptr); std::rethrow_exception(state.eptr);
} }
@ -366,10 +366,10 @@ ircd::ctx::wait_until(const future<T> &f,
const auto wfun([&state]() -> bool const auto wfun([&state]() -> bool
{ {
return !pending(state); return !is(state, future_state::PENDING);
}); });
if(unlikely(invalid(state))) if(unlikely(is(state, future_state::INVALID)))
throw no_state{}; throw no_state{};
if(unlikely(!state.cond.wait_until(tp, wfun))) if(unlikely(!state.cond.wait_until(tp, wfun)))

View file

@ -198,13 +198,13 @@ void
ircd::ctx::promise<T>::set_value(T&& val) ircd::ctx::promise<T>::set_value(T&& val)
{ {
assert(valid()); assert(valid());
if(unlikely(ready(state()))) if(unlikely(!is(state(), future_state::PENDING)))
throw promise_already_satisfied{}; throw promise_already_satisfied{};
st->val = std::move(val); st->val = std::move(val);
auto *const st{this->st}; auto *const st{this->st};
invalidate(*st); invalidate(*st);
set_ready(*st); set(*st, future_state::READY);
notify(*st); notify(*st);
assert(!valid()); assert(!valid());
} }
@ -214,13 +214,13 @@ void
ircd::ctx::promise<T>::set_value(const T &val) ircd::ctx::promise<T>::set_value(const T &val)
{ {
assert(valid()); assert(valid());
if(unlikely(!pending(state()))) if(unlikely(!is(state(), future_state::PENDING)))
throw promise_already_satisfied{}; throw promise_already_satisfied{};
st->val = val; st->val = val;
auto *const st{this->st}; auto *const st{this->st};
invalidate(*st); invalidate(*st);
set_ready(*st); set(*st, future_state::READY);
notify(*st); notify(*st);
assert(!valid()); assert(!valid());
} }
@ -229,12 +229,12 @@ inline void
ircd::ctx::promise<void>::set_value() ircd::ctx::promise<void>::set_value()
{ {
assert(valid()); assert(valid());
if(unlikely(!pending(state()))) if(unlikely(!is(state(), future_state::PENDING)))
throw promise_already_satisfied{}; throw promise_already_satisfied{};
auto *const st{this->st}; auto *const st{this->st};
invalidate(*st); invalidate(*st);
set_ready(*st); set(*st, future_state::READY);
notify(*st); notify(*st);
assert(!valid()); assert(!valid());
} }
@ -244,13 +244,13 @@ void
ircd::ctx::promise<T>::set_exception(std::exception_ptr eptr) ircd::ctx::promise<T>::set_exception(std::exception_ptr eptr)
{ {
assert(valid()); assert(valid());
if(unlikely(!pending(state()))) if(unlikely(!is(state(), future_state::PENDING)))
throw promise_already_satisfied{}; throw promise_already_satisfied{};
st->eptr = std::move(eptr); st->eptr = std::move(eptr);
auto *const st{this->st}; auto *const st{this->st};
invalidate(*st); invalidate(*st);
set_ready(*st); set(*st, future_state::READY);
notify(*st); notify(*st);
assert(!valid()); assert(!valid());
} }
@ -259,13 +259,13 @@ inline void
ircd::ctx::promise<void>::set_exception(std::exception_ptr eptr) ircd::ctx::promise<void>::set_exception(std::exception_ptr eptr)
{ {
assert(valid()); assert(valid());
if(unlikely(!pending(state()))) if(unlikely(!is(state(), future_state::PENDING)))
throw promise_already_satisfied{}; throw promise_already_satisfied{};
st->eptr = std::move(eptr); st->eptr = std::move(eptr);
auto *const st{this->st}; auto *const st{this->st};
invalidate(*st); invalidate(*st);
set_ready(*st); set(*st, future_state::READY);
notify(*st); notify(*st);
assert(!valid()); assert(!valid());
} }
@ -293,7 +293,7 @@ ircd::ctx::update(promise<T> &new_,
{ {
assert(old.st); assert(old.st);
auto &st{*old.st}; auto &st{*old.st};
if(!pending(st)) if(!is(st, future_state::PENDING))
return; return;
if(st.p == &old) if(st.p == &old)
@ -316,7 +316,7 @@ void
ircd::ctx::remove(shared_state<T> &st, ircd::ctx::remove(shared_state<T> &st,
promise<T> &p) promise<T> &p)
{ {
if(!pending(st)) if(!is(st, future_state::PENDING))
return; return;
if(st.p == &p) if(st.p == &p)
@ -338,7 +338,7 @@ template<class T>
void void
ircd::ctx::invalidate(shared_state<T> &st) ircd::ctx::invalidate(shared_state<T> &st)
{ {
if(pending(st)) if(is(st, future_state::PENDING))
for(promise<T> *p{st.p}; p; p = p->next) for(promise<T> *p{st.p}; p; p = p->next)
p->st = nullptr; p->st = nullptr;
} }
@ -347,7 +347,7 @@ template<class T>
void void
ircd::ctx::update(shared_state<T> &st) ircd::ctx::update(shared_state<T> &st)
{ {
if(pending(st)) if(is(st, future_state::PENDING))
for(promise<T> *p{st.p}; p; p = p->next) for(promise<T> *p{st.p}; p; p = p->next)
p->st = &st; p->st = &st;
} }
@ -357,7 +357,7 @@ size_t
ircd::ctx::refcount(const shared_state<T> &st) ircd::ctx::refcount(const shared_state<T> &st)
{ {
size_t ret{0}; size_t ret{0};
if(pending(st)) if(is(st, future_state::PENDING))
for(const promise<T> *p{st.p}; p; p = p->next) for(const promise<T> *p{st.p}; p; p = p->next)
++ret; ++ret;

View file

@ -16,21 +16,32 @@ namespace ircd::ctx
struct shared_state_base; struct shared_state_base;
template<class T = void> struct shared_state; template<class T = void> struct shared_state;
template<> struct shared_state<void>; template<> struct shared_state<void>;
template<class T> struct promise; template<class T> struct promise;
template<> struct promise<void>; template<> struct promise<void>;
enum class future_state;
template<class T> bool invalid(const shared_state<T> &); template<class T> future_state state(const shared_state<T> &);
template<class T> bool pending(const shared_state<T> &); template<class T> bool is(const shared_state<T> &, const future_state &);
template<class T> bool retrieved(const shared_state<T> &); template<class T> void set(shared_state<T> &, const future_state &);
template<class T> bool ready(const shared_state<T> &); template<> void set(shared_state<void> &, const future_state &);
template<class T> void notify(shared_state<T> &); template<class T> void notify(shared_state<T> &);
template<class T> void set_retrieved(shared_state<T> &);
template<class T> void set_ready(shared_state<T> &);
template<class T> void set_observed(shared_state<T> &);
template<> void set_observed(shared_state<void> &);
} }
/// Internal state enumeration for the promise / future / related. These can
/// all be observed through state() or is(); only some can be set(). This is
/// not for public manipulation.
enum class ircd::ctx::future_state
{
INVALID, ///< Null.
PENDING, ///< Promise is attached and busy.
READY, ///< Result ready; promise is gone.
OBSERVED, ///< Special case for when_*(); not a state; promise is gone.
RETRIEVED, ///< User retrieved future value; promise is gone.
};
/// Internal Non-template base of the state object shared by promise and
/// future. It is extended by the appropriate template, and usually resides
/// in the future's instance, where the promise finds it.
struct ircd::ctx::shared_state_base struct ircd::ctx::shared_state_base
{ {
mutable dock cond; mutable dock cond;
@ -38,6 +49,7 @@ struct ircd::ctx::shared_state_base
std::function<void (shared_state_base &)> then; std::function<void (shared_state_base &)> then;
}; };
/// Internal shared state between future and promise appropos a future value.
template<class T> template<class T>
struct ircd::ctx::shared_state struct ircd::ctx::shared_state
:shared_state_base :shared_state_base
@ -56,6 +68,8 @@ struct ircd::ctx::shared_state
shared_state() = default; shared_state() = default;
}; };
/// Internal shared state between future and promise when there is no future
/// value, only a notification of completion or exception.
template<> template<>
struct ircd::ctx::shared_state<void> struct ircd::ctx::shared_state<void>
:shared_state_base :shared_state_base
@ -71,6 +85,7 @@ struct ircd::ctx::shared_state<void>
shared_state() = default; shared_state() = default;
}; };
/// Internal use
template<class T> template<class T>
void void
ircd::ctx::notify(shared_state<T> &st) ircd::ctx::notify(shared_state<T> &st)
@ -89,58 +104,69 @@ ircd::ctx::notify(shared_state<T> &st)
}); });
} }
/// Internal use
template<class T> template<class T>
void void
ircd::ctx::set_observed(shared_state<T> &st) ircd::ctx::set(shared_state<T> &st,
const future_state &state)
{ {
set_ready(st); switch(state)
{
case future_state::INVALID: assert(0); return;
case future_state::PENDING: assert(0); return;
case future_state::OBSERVED:
case future_state::READY:
reinterpret_cast<uintptr_t &>(st.p) = uintptr_t(0x42);
return;
case future_state::RETRIEVED:
reinterpret_cast<uintptr_t &>(st.p) = uintptr_t(0x123);
return;
}
} }
/// Internal use
template<> template<>
inline void inline void
ircd::ctx::set_observed(shared_state<void> &st) ircd::ctx::set(shared_state<void> &st,
const future_state &state)
{ {
set_retrieved(st); switch(state)
} {
case future_state::INVALID: assert(0); return;
template<class T> case future_state::PENDING: assert(0); return;
void case future_state::READY:
ircd::ctx::set_ready(shared_state<T> &st) reinterpret_cast<uintptr_t &>(st.p) = uintptr_t(0x42);
{ return;
st.p = reinterpret_cast<promise<T> *>(uintptr_t(0x42));
} case future_state::OBSERVED:
case future_state::RETRIEVED:
template<class T> reinterpret_cast<uintptr_t &>(st.p) = uintptr_t(0x123);
void return;
ircd::ctx::set_retrieved(shared_state<T> &st) }
{
st.p = reinterpret_cast<promise<T> *>(uintptr_t(0x84));
} }
/// Internal; check if the current state is something; safe but unnecessary
/// for public use.
template<class T> template<class T>
bool bool
ircd::ctx::ready(const shared_state<T> &st) ircd::ctx::is(const shared_state<T> &st,
const future_state &state_)
{ {
return st.p == reinterpret_cast<const promise<T> *>(uintptr_t(0x42)); return state(st) == state_;
} }
/// Internal; get the current state of the shared_state; safe but unnecessary
/// for public use.
template<class T> template<class T>
bool ircd::ctx::future_state
ircd::ctx::retrieved(const shared_state<T> &st) ircd::ctx::state(const shared_state<T> &st)
{ {
return st.p == reinterpret_cast<const promise<T> *>(uintptr_t(0x84)); switch(uintptr_t(st.p))
} {
case 0x00: return future_state::INVALID;
template<class T> case 0x42: return future_state::READY;
bool case 0x123: return future_state::RETRIEVED;
ircd::ctx::pending(const shared_state<T> &st) default: return future_state::PENDING;
{ }
return st.p > reinterpret_cast<const promise<T> *>(uintptr_t(0x1000));
}
template<class T>
bool
ircd::ctx::invalid(const shared_state<T> &st)
{
return st.p == nullptr;
} }

View file

@ -54,7 +54,7 @@ ircd::ctx::when_all(it first,
future<void> ret(p); future<void> ret(p);
for(; first != last; ++first) for(; first != last; ++first)
if(pending(first->state())) if(is(first->state(), future_state::PENDING))
set_then(*first); set_then(*first);
if(refcount(p.state()) <= 1) if(refcount(p.state()) <= 1)
@ -85,7 +85,7 @@ ircd::ctx::when_any(it first,
if(!p.valid()) if(!p.valid())
return; return;
set_observed(f->state()); set(f->state(), future_state::OBSERVED);
p.set_value(f); p.set_value(f);
} }
}; };
@ -105,15 +105,15 @@ ircd::ctx::when_any(it first,
future<it> ret(p); future<it> ret(p);
for(auto f(first); f != last; ++f) for(auto f(first); f != last; ++f)
if(ready(f->state())) if(is(f->state(), future_state::READY))
{ {
set_observed(f->state()); set(f->state(), future_state::OBSERVED);
p.set_value(f); p.set_value(f);
return ret; return ret;
} }
for(; first != last; ++first) for(; first != last; ++first)
if(pending(first->state())) if(is(first->state(), future_state::PENDING))
set_then(first); set_then(first);
if(refcount(p.state()) <= 1) if(refcount(p.state()) <= 1)