coroutine fixes/cleanups, remove INIT state (can be checked better)

master
Daniel Kolesa 2017-03-11 17:15:39 +01:00
parent 8c6e7d8bf7
commit 0185fa7c12
2 changed files with 57 additions and 24 deletions

View File

@ -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>();
}

View File

@ -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;
};