0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-12-26 15:33:54 +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; }
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(); }
operator bool() const { return valid(); }
@ -72,7 +72,7 @@ struct ircd::ctx::future<void>
const shared_state<void> &state() const { 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(); }
operator bool() const { return valid(); }
@ -150,7 +150,7 @@ ircd::ctx::future<void>::future(promise<void> &promise)
inline
ircd::ctx::future<void>::future(already_t)
{
set_ready(state());
set(state(), future_state::READY);
}
template<class T>
@ -213,10 +213,10 @@ T
ircd::ctx::future<T>::get()
{
wait();
if(unlikely(retrieved(state())))
if(unlikely(is(state(), future_state::RETRIEVED)))
throw future_already_retrieved{};
set_retrieved(state());
set(state(), future_state::RETRIEVED);
if(bool(state().eptr))
std::rethrow_exception(state().eptr);
@ -327,7 +327,7 @@ const
const_cast<future<void> *>(this)->state()
};
set_retrieved(state);
set(state, future_state::RETRIEVED);
if(bool(state.eptr))
std::rethrow_exception(state.eptr);
}
@ -366,10 +366,10 @@ ircd::ctx::wait_until(const future<T> &f,
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{};
if(unlikely(!state.cond.wait_until(tp, wfun)))

View file

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

View file

@ -16,21 +16,32 @@ namespace ircd::ctx
struct shared_state_base;
template<class T = void> struct shared_state;
template<> struct shared_state<void>;
template<class T> struct promise;
template<> struct promise<void>;
enum class future_state;
template<class T> bool invalid(const shared_state<T> &);
template<class T> bool pending(const shared_state<T> &);
template<class T> bool retrieved(const shared_state<T> &);
template<class T> bool ready(const shared_state<T> &);
template<class T> future_state state(const shared_state<T> &);
template<class T> bool is(const shared_state<T> &, const future_state &);
template<class T> void set(shared_state<T> &, const future_state &);
template<> void set(shared_state<void> &, const future_state &);
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
{
mutable dock cond;
@ -38,6 +49,7 @@ struct ircd::ctx::shared_state_base
std::function<void (shared_state_base &)> then;
};
/// Internal shared state between future and promise appropos a future value.
template<class T>
struct ircd::ctx::shared_state
:shared_state_base
@ -56,6 +68,8 @@ struct ircd::ctx::shared_state
shared_state() = default;
};
/// Internal shared state between future and promise when there is no future
/// value, only a notification of completion or exception.
template<>
struct ircd::ctx::shared_state<void>
:shared_state_base
@ -71,6 +85,7 @@ struct ircd::ctx::shared_state<void>
shared_state() = default;
};
/// Internal use
template<class T>
void
ircd::ctx::notify(shared_state<T> &st)
@ -89,58 +104,69 @@ ircd::ctx::notify(shared_state<T> &st)
});
}
/// Internal use
template<class T>
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<>
inline void
ircd::ctx::set_observed(shared_state<void> &st)
ircd::ctx::set(shared_state<void> &st,
const future_state &state)
{
set_retrieved(st);
}
template<class T>
void
ircd::ctx::set_ready(shared_state<T> &st)
{
st.p = reinterpret_cast<promise<T> *>(uintptr_t(0x42));
}
template<class T>
void
ircd::ctx::set_retrieved(shared_state<T> &st)
{
st.p = reinterpret_cast<promise<T> *>(uintptr_t(0x84));
switch(state)
{
case future_state::INVALID: assert(0); return;
case future_state::PENDING: assert(0); return;
case future_state::READY:
reinterpret_cast<uintptr_t &>(st.p) = uintptr_t(0x42);
return;
case future_state::OBSERVED:
case future_state::RETRIEVED:
reinterpret_cast<uintptr_t &>(st.p) = uintptr_t(0x123);
return;
}
}
/// Internal; check if the current state is something; safe but unnecessary
/// for public use.
template<class T>
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>
bool
ircd::ctx::retrieved(const shared_state<T> &st)
ircd::ctx::future_state
ircd::ctx::state(const shared_state<T> &st)
{
return st.p == reinterpret_cast<const promise<T> *>(uintptr_t(0x84));
}
template<class T>
bool
ircd::ctx::pending(const shared_state<T> &st)
{
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;
switch(uintptr_t(st.p))
{
case 0x00: return future_state::INVALID;
case 0x42: return future_state::READY;
case 0x123: return future_state::RETRIEVED;
default: return future_state::PENDING;
}
}

View file

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