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:
parent
150831cd83
commit
fbb9cf0196
4 changed files with 100 additions and 74 deletions
|
@ -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)))
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue