dealloc stacks in destructor (allows stack reuse)

master
Daniel Kolesa 2017-03-11 17:57:04 +01:00
parent 0185fa7c12
commit 20f57dad9b
3 changed files with 51 additions and 31 deletions

View File

@ -37,7 +37,13 @@ int main() {
int val = 5;
for (int i: range(steps)) {
writeln(" calling into coroutine...");
auto v = f(val);
int v;
try {
v = f(val);
} catch (coroutine_error const &e) {
writefln("coroutine error: %s", e.what());
return 0;
}
writefln(" called into coroutine which yielded: %s", v);
writefln(" call loop iteration %s done", i + 1);
writefln(" coroutine dead: %s", !f);
@ -285,6 +291,5 @@ starting main...
call loop iteration 6 done
coroutine dead: true
calling into coroutine...
terminating with uncaught exception of type ostd::coroutine_error: dead coroutine
zsh: abort ./coro
coroutine error: dead coroutine
*/

View File

@ -267,12 +267,14 @@ namespace detail {
coro_args<A...> operator()(R &&ret) {
p_coro.p_result = std::forward<R>(ret);
p_coro.set_hold();
p_coro.yield_jump();
return p_coro.get_args(std::make_index_sequence<sizeof...(A)>{});
}
coro_args<A...> operator()(R const &ret) {
p_coro.p_result = ret;
p_coro.set_hold();
p_coro.yield_jump();
return p_coro.get_args(std::make_index_sequence<sizeof...(A)>{});
}
@ -287,6 +289,7 @@ namespace detail {
coro_args<A...> operator()(R &ret) {
p_coro.p_result = ret;
p_coro.set_hold();
p_coro.yield_jump();
return p_coro.get_args(std::make_index_sequence<sizeof...(A)>{});
}
@ -301,6 +304,7 @@ namespace detail {
coro_args<A...> operator()(R &&ret) {
p_coro.p_result = std::forward<R>(ret);
p_coro.set_hold();
p_coro.yield_jump();
return p_coro.get_args(std::make_index_sequence<sizeof...(A)>{});
}
@ -314,6 +318,7 @@ namespace detail {
coro_yielder(coro_base<void, A...> &coro): p_coro(coro) {}
coro_args<A...> operator()() {
p_coro.set_hold();
p_coro.yield_jump();
return p_coro.get_args(std::make_index_sequence<sizeof...(A)>{});
}
@ -419,7 +424,8 @@ private:
}
/* switch back, release stack */
release:
self.template finish<SA>();
self.set_dead();
self.yield_jump();
}
std::function<R(yield_type, A...)> p_func;
@ -446,11 +452,13 @@ private:
void operator()(T &&ret) {
p_gen.p_result = &ret;
p_gen.set_hold();
p_gen.yield_jump();
}
void operator()(T &ret) {
p_gen.p_result = &ret;
p_gen.set_hold();
p_gen.yield_jump();
}
private:
@ -566,7 +574,8 @@ private:
}
release:
self.p_result = nullptr;
self.template finish<SA>();
self.set_dead();
self.yield_jump();
}
std::function<void(yield_type)> p_func;

View File

@ -56,11 +56,12 @@ protected:
coroutine_context(coroutine_context &&c):
p_stack(std::move(c.p_stack)), p_coro(c.p_coro), p_orig(c.p_orig),
p_except(std::move(c.p_except)), p_state(std::move(c.p_state)),
p_sa(c.p_sa)
p_sa(c.p_sa), p_free(c.p_free)
{
c.p_coro = c.p_orig = nullptr;
c.p_stack = { nullptr, 0 };
c.p_sa = nullptr;
c.p_free = nullptr;
c.set_dead();
}
@ -79,37 +80,21 @@ protected:
}
void unwind() {
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
*/
coro_jump();
if (is_dead() || !p_orig) {
/* this coroutine is either done or never started */
free_stack();
return;
}
/* this coroutine is suspended, and we need to make sure all
* the destructors for its locals get called by forcing unwind
*/
ostd_ontop_fcontext(
std::exchange(p_coro, nullptr), nullptr,
[](transfer_t t) -> transfer_t {
throw forced_unwind{t.ctx};
}
);
}
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));
sa.deallocate(self.p_stack);
return { nullptr, nullptr };
});
free_stack();
}
void coro_jump() {
@ -117,7 +102,6 @@ protected:
}
void yield_jump() {
p_state = state::HOLD;
p_orig = ostd_jump_fcontext(p_orig, nullptr).ctx;
}
@ -129,6 +113,10 @@ protected:
return (p_state == state::TERM);
}
void set_hold() {
p_state = state::HOLD;
}
void set_dead() {
p_state = state::TERM;
}
@ -139,6 +127,9 @@ protected:
swap(p_coro, other.p_coro);
swap(p_orig, other.p_orig);
swap(p_except, other.p_except);
swap(p_state, other.p_state);
swap(p_sa, other.p_sa);
swap(p_free, other.p_free);
}
template<typename SA>
@ -155,6 +146,20 @@ protected:
p_coro = ostd_make_fcontext(sp, asize, callp);
p_sa = new (sp) SA(std::move(sa));
p_free = &free_stack_call<SA>;
}
template<typename SA>
static void free_stack_call(void *data) {
auto &self = *(static_cast<coroutine_context *>(data));
auto &sa = *(static_cast<SA *>(self.p_sa));
sa.deallocate(self.p_stack);
}
void free_stack() {
if (p_free) {
p_free(this);
}
}
stack_context p_stack;
@ -162,7 +167,8 @@ protected:
fcontext_t p_orig;
std::exception_ptr p_except;
state p_state = state::HOLD;
void *p_sa;
void *p_sa = nullptr;
void (*p_free)(void *) = nullptr;
};
} /* namespace detail */