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:
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; }
|
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)))
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Reference in a new issue