coroutine fixes/cleanups, remove INIT state (can be checked better)
parent
8c6e7d8bf7
commit
0185fa7c12
|
@ -341,14 +341,22 @@ public:
|
|||
{
|
||||
/* that way there is no context creation/stack allocation */
|
||||
if (!p_func) {
|
||||
this->set_dead();
|
||||
return;
|
||||
}
|
||||
this->make_context(sa, &context_call<SA>);
|
||||
}
|
||||
|
||||
template<typename SA = default_stack>
|
||||
coroutine(std::nullptr_t, SA = SA{0}):
|
||||
base_t(), p_func(nullptr)
|
||||
{
|
||||
this->set_dead();
|
||||
}
|
||||
|
||||
coroutine(coroutine const &) = delete;
|
||||
coroutine(coroutine &&c):
|
||||
detail::coro_base<R, A...>(std::move(c)), p_func(std::move(c.p_func))
|
||||
base_t(std::move(c)), p_func(std::move(c.p_func))
|
||||
{
|
||||
c.p_func = nullptr;
|
||||
}
|
||||
|
@ -361,19 +369,15 @@ public:
|
|||
}
|
||||
|
||||
~coroutine() {
|
||||
if (this->p_state == detail::coroutine_context::state::TERM) {
|
||||
/* the stack has already unwound by a normal return */
|
||||
return;
|
||||
}
|
||||
this->unwind();
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return (this->p_state != detail::coroutine_context::state::TERM);
|
||||
return !this->is_dead();
|
||||
}
|
||||
|
||||
R resume(A ...args) {
|
||||
if (this->p_state == detail::coroutine_context::state::TERM) {
|
||||
if (this->is_dead()) {
|
||||
throw coroutine_error{"dead coroutine"};
|
||||
}
|
||||
this->set_args(std::forward<A>(args)...);
|
||||
|
@ -396,8 +400,10 @@ private:
|
|||
static void context_call(detail::transfer_t t) {
|
||||
auto &self = *(static_cast<coroutine *>(t.data));
|
||||
self.p_orig = t.ctx;
|
||||
if (self.p_state == detail::coroutine_context::state::INIT) {
|
||||
/* we never got to execute properly */
|
||||
if (self.is_hold()) {
|
||||
/* we never got to execute properly, we're HOLD because we
|
||||
* jumped here without setting the state to EXEC before that
|
||||
*/
|
||||
goto release;
|
||||
}
|
||||
try {
|
||||
|
@ -413,7 +419,6 @@ private:
|
|||
}
|
||||
/* switch back, release stack */
|
||||
release:
|
||||
self.p_state = detail::coroutine_context::state::TERM;
|
||||
self.template finish<SA>();
|
||||
}
|
||||
|
||||
|
@ -434,6 +439,8 @@ namespace detail {
|
|||
template<typename T>
|
||||
struct generator: detail::coroutine_context {
|
||||
private:
|
||||
using base_t = detail::coroutine_context;
|
||||
|
||||
struct yielder {
|
||||
yielder(generator<T> &g): p_gen(g) {}
|
||||
|
||||
|
@ -458,9 +465,12 @@ public:
|
|||
generator() = delete;
|
||||
|
||||
template<typename F, typename SA = default_stack>
|
||||
generator(F func, SA sa = SA{0}): p_func(std::move(func)) {
|
||||
generator(F func, SA sa = SA{0}):
|
||||
base_t(), p_func(std::move(func))
|
||||
{
|
||||
/* that way there is no context creation/stack allocation */
|
||||
if (!p_func) {
|
||||
this->set_dead();
|
||||
return;
|
||||
}
|
||||
this->make_context(sa, &context_call<SA>);
|
||||
|
@ -468,9 +478,16 @@ public:
|
|||
resume();
|
||||
}
|
||||
|
||||
template<typename SA = default_stack>
|
||||
generator(std::nullptr_t, SA = SA{0}):
|
||||
base_t(), p_func(nullptr)
|
||||
{
|
||||
this->set_dead();
|
||||
}
|
||||
|
||||
generator(generator const &) = delete;
|
||||
generator(generator &&c):
|
||||
p_func(std::move(c.p_func)), p_result(c.p_result)
|
||||
base_t(std::move(c)), p_func(std::move(c.p_func)), p_result(c.p_result)
|
||||
{
|
||||
c.p_func = nullptr;
|
||||
c.p_result = nullptr;
|
||||
|
@ -478,6 +495,7 @@ public:
|
|||
|
||||
generator &operator=(generator const &) = delete;
|
||||
generator &operator=(generator &&c) {
|
||||
base_t::operator=(std::move(c));
|
||||
p_func = std::move(c.p_func);
|
||||
p_result = c.p_result;
|
||||
c.p_func = nullptr;
|
||||
|
@ -485,18 +503,15 @@ public:
|
|||
}
|
||||
|
||||
~generator() {
|
||||
if (this->p_state == detail::coroutine_context::state::TERM) {
|
||||
return;
|
||||
}
|
||||
this->unwind();
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return (this->p_state != detail::coroutine_context::state::TERM);
|
||||
return !this->is_dead();
|
||||
}
|
||||
|
||||
void resume() {
|
||||
if (this->p_state == detail::coroutine_context::state::TERM) {
|
||||
if (this->is_dead()) {
|
||||
throw coroutine_error{"dead generator"};
|
||||
}
|
||||
detail::coroutine_context::call();
|
||||
|
@ -517,7 +532,7 @@ public:
|
|||
}
|
||||
|
||||
bool empty() const {
|
||||
return (!p_result || this->p_state == detail::coroutine_context::state::TERM);
|
||||
return (!p_result || this->is_dead());
|
||||
}
|
||||
|
||||
generator_range<T> iter();
|
||||
|
@ -539,7 +554,7 @@ private:
|
|||
static void context_call(detail::transfer_t t) {
|
||||
auto &self = *(static_cast<generator *>(t.data));
|
||||
self.p_orig = t.ctx;
|
||||
if (self.p_state == detail::coroutine_context::state::INIT) {
|
||||
if (self.is_hold()) {
|
||||
goto release;
|
||||
}
|
||||
try {
|
||||
|
@ -550,7 +565,6 @@ private:
|
|||
self.p_except = std::current_exception();
|
||||
}
|
||||
release:
|
||||
self.p_state = detail::coroutine_context::state::TERM;
|
||||
self.p_result = nullptr;
|
||||
self.template finish<SA>();
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ protected:
|
|||
};
|
||||
|
||||
enum class state {
|
||||
INIT = 0, HOLD, EXEC, TERM
|
||||
HOLD = 0, EXEC, TERM
|
||||
};
|
||||
|
||||
coroutine_context() {}
|
||||
|
@ -60,8 +60,8 @@ protected:
|
|||
{
|
||||
c.p_coro = c.p_orig = nullptr;
|
||||
c.p_stack = { nullptr, 0 };
|
||||
c.p_state = state::TERM;
|
||||
c.p_sa = nullptr;
|
||||
c.set_dead();
|
||||
}
|
||||
|
||||
coroutine_context &operator=(coroutine_context const &) = delete;
|
||||
|
@ -79,7 +79,13 @@ protected:
|
|||
}
|
||||
|
||||
void unwind() {
|
||||
if (p_state == state::INIT) {
|
||||
if (is_dead()) {
|
||||
/* this coroutine was either initialized with a null function or
|
||||
* it's already terminated and thus its stack has already unwound
|
||||
*/
|
||||
return;
|
||||
}
|
||||
if (!p_orig) {
|
||||
/* this coroutine never got to live :(
|
||||
* let it call the entry point at least this once...
|
||||
* this will kill the stack so we don't leak memory
|
||||
|
@ -97,6 +103,7 @@ protected:
|
|||
|
||||
template<typename SA>
|
||||
void finish() {
|
||||
set_dead();
|
||||
ostd_ontop_fcontext(p_orig, this, [](transfer_t t) -> transfer_t {
|
||||
auto &self = *(static_cast<coroutine_context *>(t.data));
|
||||
auto &sa = *(static_cast<SA *>(self.p_sa));
|
||||
|
@ -114,6 +121,18 @@ protected:
|
|||
p_orig = ostd_jump_fcontext(p_orig, nullptr).ctx;
|
||||
}
|
||||
|
||||
bool is_hold() const {
|
||||
return (p_state == state::HOLD);
|
||||
}
|
||||
|
||||
bool is_dead() const {
|
||||
return (p_state == state::TERM);
|
||||
}
|
||||
|
||||
void set_dead() {
|
||||
p_state = state::TERM;
|
||||
}
|
||||
|
||||
void swap(coroutine_context &other) noexcept {
|
||||
using std::swap;
|
||||
swap(p_stack, other.p_stack);
|
||||
|
@ -142,7 +161,7 @@ protected:
|
|||
fcontext_t p_coro;
|
||||
fcontext_t p_orig;
|
||||
std::exception_ptr p_except;
|
||||
state p_state = state::INIT;
|
||||
state p_state = state::HOLD;
|
||||
void *p_sa;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue