forked from OctaForge/libostd
unify context_call between coroutines and generators
parent
de45dd2e80
commit
f1341b913c
|
@ -157,10 +157,9 @@ namespace detail {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Y, typename F, size_t ...I>
|
template<typename Y, typename F, size_t ...I>
|
||||||
void call_helper(Y &&yielder, F &func, std::index_sequence<I...>) {
|
void call_helper(F &func, std::index_sequence<I...>) {
|
||||||
p_result = std::forward<R>(func(
|
p_result = std::forward<R>(func(
|
||||||
std::forward<Y>(yielder),
|
Y{*this}, std::forward<A>(std::get<I>(p_args))...
|
||||||
std::forward<A>(std::get<I>(p_args))...
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,8 +190,8 @@ namespace detail {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Y, typename F, size_t ...I>
|
template<typename Y, typename F, size_t ...I>
|
||||||
void call_helper(Y &&yielder, F &func, std::index_sequence<I...>) {
|
void call_helper(F &func, std::index_sequence<I...>) {
|
||||||
p_result = std::forward<R>(func(std::forward<Y>(yielder)));
|
p_result = std::forward<R>(func(Y{*this}));
|
||||||
}
|
}
|
||||||
|
|
||||||
void swap(coro_base &other) {
|
void swap(coro_base &other) {
|
||||||
|
@ -222,11 +221,8 @@ namespace detail {
|
||||||
void get_result() {}
|
void get_result() {}
|
||||||
|
|
||||||
template<typename Y, typename F, size_t ...I>
|
template<typename Y, typename F, size_t ...I>
|
||||||
void call_helper(Y &&yielder, F &func, std::index_sequence<I...>) {
|
void call_helper(F &func, std::index_sequence<I...>) {
|
||||||
func(
|
func(Y{*this}, std::forward<A>(std::get<I>(p_args))...);
|
||||||
std::forward<Y>(yielder),
|
|
||||||
std::forward<A>(std::get<I>(p_args))...
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void swap(coro_base &other) {
|
void swap(coro_base &other) {
|
||||||
|
@ -252,8 +248,8 @@ namespace detail {
|
||||||
void get_result() {}
|
void get_result() {}
|
||||||
|
|
||||||
template<typename Y, typename F, size_t ...I>
|
template<typename Y, typename F, size_t ...I>
|
||||||
void call_helper(Y &&yielder, F &func, std::index_sequence<I...>) {
|
void call_helper(F &func, std::index_sequence<I...>) {
|
||||||
func(std::forward<Y>(yielder));
|
func(Y{*this});
|
||||||
}
|
}
|
||||||
|
|
||||||
void swap(coro_base &other) {
|
void swap(coro_base &other) {
|
||||||
|
@ -327,6 +323,8 @@ template<typename R, typename ...A>
|
||||||
struct coroutine<R(A...)>: detail::coro_base<R, A...> {
|
struct coroutine<R(A...)>: detail::coro_base<R, A...> {
|
||||||
private:
|
private:
|
||||||
using base_t = detail::coro_base<R, A...>;
|
using base_t = detail::coro_base<R, A...>;
|
||||||
|
/* necessary so that context callback can access privates */
|
||||||
|
friend struct detail::coroutine_context;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using yield_type = detail::coro_yielder<R, A...>;
|
using yield_type = detail::coro_yielder<R, A...>;
|
||||||
|
@ -344,7 +342,7 @@ public:
|
||||||
this->set_dead();
|
this->set_dead();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->make_context(sa, &context_call<SA>);
|
this->template make_context<coroutine<R(A...)>>(sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename SA = default_stack>
|
template<typename SA = default_stack>
|
||||||
|
@ -395,31 +393,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/* the main entry point of the coroutine */
|
void resume_call() {
|
||||||
template<typename SA>
|
base_t::template call_helper<yield_type>(
|
||||||
static void context_call(detail::transfer_t t) {
|
p_func, std::index_sequence_for<A...>{}
|
||||||
auto &self = *(static_cast<coroutine *>(t.data));
|
);
|
||||||
self.p_orig = t.ctx;
|
|
||||||
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 {
|
|
||||||
self.call_helper(
|
|
||||||
yield_type{self}, self.p_func, std::index_sequence_for<A...>{}
|
|
||||||
);
|
|
||||||
} catch (detail::coroutine_context::forced_unwind v) {
|
|
||||||
/* forced_unwind is unique */
|
|
||||||
self.p_orig = v.ctx;
|
|
||||||
} catch (...) {
|
|
||||||
/* some other exception, will be rethrown later */
|
|
||||||
self.p_except = std::current_exception();
|
|
||||||
}
|
|
||||||
/* switch back, release stack */
|
|
||||||
release:
|
|
||||||
self.template finish<SA>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::function<R(yield_type, A...)> p_func;
|
std::function<R(yield_type, A...)> p_func;
|
||||||
|
@ -440,6 +417,7 @@ template<typename T>
|
||||||
struct generator: detail::coroutine_context {
|
struct generator: detail::coroutine_context {
|
||||||
private:
|
private:
|
||||||
using base_t = detail::coroutine_context;
|
using base_t = detail::coroutine_context;
|
||||||
|
friend struct detail::coroutine_context;
|
||||||
|
|
||||||
struct yielder {
|
struct yielder {
|
||||||
yielder(generator<T> &g): p_gen(g) {}
|
yielder(generator<T> &g): p_gen(g) {}
|
||||||
|
@ -473,7 +451,7 @@ public:
|
||||||
this->set_dead();
|
this->set_dead();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->make_context(sa, &context_call<SA>);
|
this->template make_context<generator<T>>(sa);
|
||||||
/* generate an initial value */
|
/* generate an initial value */
|
||||||
resume();
|
resume();
|
||||||
}
|
}
|
||||||
|
@ -549,24 +527,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/* the main entry point of the generator */
|
void resume_call() {
|
||||||
template<typename SA>
|
p_func(yield_type{*this});
|
||||||
static void context_call(detail::transfer_t t) {
|
|
||||||
auto &self = *(static_cast<generator *>(t.data));
|
|
||||||
self.p_orig = t.ctx;
|
|
||||||
if (self.is_hold()) {
|
|
||||||
goto release;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
self.p_func(yield_type{self});
|
|
||||||
} catch (detail::coroutine_context::forced_unwind v) {
|
|
||||||
self.p_orig = v.ctx;
|
|
||||||
} catch (...) {
|
|
||||||
self.p_except = std::current_exception();
|
|
||||||
}
|
|
||||||
release:
|
|
||||||
self.p_result = nullptr;
|
|
||||||
self.template finish<SA>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::function<void(yield_type)> p_func;
|
std::function<void(yield_type)> p_func;
|
||||||
|
|
|
@ -101,17 +101,6 @@ 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));
|
|
||||||
sa.deallocate(self.p_stack);
|
|
||||||
return { nullptr, nullptr };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void coro_jump() {
|
void coro_jump() {
|
||||||
p_coro = ostd_jump_fcontext(p_coro, this).ctx;
|
p_coro = ostd_jump_fcontext(p_coro, this).ctx;
|
||||||
}
|
}
|
||||||
|
@ -144,7 +133,18 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename SA>
|
template<typename SA>
|
||||||
void make_context(SA &sa, void (*callp)(transfer_t)) {
|
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 };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename C, typename SA>
|
||||||
|
void make_context(SA &sa) {
|
||||||
p_stack = sa.allocate();
|
p_stack = sa.allocate();
|
||||||
|
|
||||||
constexpr size_t salign = alignof(SA);
|
constexpr size_t salign = alignof(SA);
|
||||||
|
@ -155,10 +155,34 @@ protected:
|
||||||
size_t asize = p_stack.size -
|
size_t asize = p_stack.size -
|
||||||
(static_cast<byte *>(p_stack.ptr) - static_cast<byte *>(sp));
|
(static_cast<byte *>(p_stack.ptr) - static_cast<byte *>(sp));
|
||||||
|
|
||||||
p_coro = ostd_make_fcontext(sp, asize, callp);
|
p_coro = ostd_make_fcontext(sp, asize, &context_call<C, SA>);
|
||||||
p_sa = new (sp) SA(std::move(sa));
|
p_sa = new (sp) SA(std::move(sa));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename C, typename SA>
|
||||||
|
static void context_call(detail::transfer_t t) {
|
||||||
|
auto &self = *(static_cast<C *>(t.data));
|
||||||
|
self.p_orig = t.ctx;
|
||||||
|
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 {
|
||||||
|
self.resume_call();
|
||||||
|
} catch (detail::coroutine_context::forced_unwind v) {
|
||||||
|
/* forced_unwind is unique */
|
||||||
|
self.p_orig = v.ctx;
|
||||||
|
} catch (...) {
|
||||||
|
/* some other exception, will be rethrown later */
|
||||||
|
self.p_except = std::current_exception();
|
||||||
|
}
|
||||||
|
/* switch back, release stack */
|
||||||
|
release:
|
||||||
|
self.template finish<SA>();
|
||||||
|
}
|
||||||
|
|
||||||
stack_context p_stack;
|
stack_context p_stack;
|
||||||
fcontext_t p_coro;
|
fcontext_t p_coro;
|
||||||
fcontext_t p_orig;
|
fcontext_t p_orig;
|
||||||
|
|
Loading…
Reference in New Issue