use csched_task for both coroutine schedulers

master
Daniel Kolesa 2017-03-22 18:44:06 +01:00
parent 18cfe5119f
commit cd5bc965bd
1 changed files with 101 additions and 79 deletions

View File

@ -80,6 +80,62 @@ private:
std::mutex p_lock; std::mutex p_lock;
}; };
namespace detail {
struct csched_task;
OSTD_EXPORT extern thread_local csched_task *current_csched_task;
struct csched_task: coroutine_context {
friend struct coroutine_context;
csched_task() = delete;
csched_task(csched_task const &) = delete;
csched_task(csched_task &&) = delete;
csched_task &operator=(csched_task const &) = delete;
csched_task &operator=(csched_task &&) = delete;
template<typename F, typename SA>
csched_task(F &&f, SA &&sa): p_func(std::forward<F>(f)) {
if (!p_func) {
this->set_dead();
return;
}
this->make_context<csched_task>(sa);
}
void operator()() {
this->set_exec();
csched_task *curr = std::exchange(current_csched_task, this);
this->coro_jump();
current_csched_task = curr;
this->rethrow();
}
void yield() {
/* we'll yield back to the thread we were scheduled to, which
* will appropriately notify one or all other waiting threads
* so we either get re-scheduled or the whole scheduler dies
*/
this->yield_jump();
}
bool dead() const {
return this->is_dead();
}
static csched_task *current() {
return current_csched_task;
}
private:
void resume_call() {
p_func();
}
std::function<void()> p_func;
};
}
template<typename TR, bool Protected> template<typename TR, bool Protected>
struct basic_simple_coroutine_scheduler { struct basic_simple_coroutine_scheduler {
private: private:
@ -126,9 +182,9 @@ public:
size_t cs = basic_stack_pool<TR, Protected>::DEFAULT_CHUNK_SIZE size_t cs = basic_stack_pool<TR, Protected>::DEFAULT_CHUNK_SIZE
): ):
p_stacks(ss, cs), p_stacks(ss, cs),
p_dispatcher([this](auto yield_main) { p_dispatcher([this]() {
this->dispatch(yield_main); dispatch();
}), }, basic_fixedsize_stack<TR, Protected>{ss}),
p_coros() p_coros()
{} {}
@ -148,32 +204,27 @@ public:
template<typename F, typename ...A> template<typename F, typename ...A>
void spawn(F func, A &&...args) { void spawn(F func, A &&...args) {
if constexpr(sizeof...(A) == 0) { if constexpr(sizeof...(A) == 0) {
p_coros.emplace_back([lfunc = std::move(func)](auto) { p_coros.emplace_back([lfunc = std::move(func)]() {
lfunc(); lfunc();
}, p_stacks.get_allocator()); }, p_stacks.get_allocator());
} else { } else {
p_coros.emplace_back([lfunc = std::bind( p_coros.emplace_back([lfunc = std::bind(
std::move(func), std::forward<A>(args)... std::move(func), std::forward<A>(args)...
)](auto) mutable { )]() mutable {
lfunc(); lfunc();
}, p_stacks.get_allocator()); }, p_stacks.get_allocator());
} }
} }
void yield() { void yield() {
auto ctx = coroutine_context::current(); auto ctx = detail::csched_task::current();
if (!ctx) { if (!ctx) {
/* yield from main means go to dispatcher and call first task */ /* yield from main means go to dispatcher and call first task */
p_idx = p_coros.begin(); p_idx = p_coros.begin();
p_dispatcher(); p_dispatcher();
return; return;
} }
coro *c = dynamic_cast<coro *>(ctx); ctx->yield();
if (c) {
typename coro::yield_type{*c}();
return;
}
throw std::runtime_error{"attempt to yield outside coroutine"};
} }
template<typename T> template<typename T>
@ -183,22 +234,18 @@ public:
}}; }};
} }
private: private:
struct coro: coroutine<void()> { void dispatch() {
using coroutine<void()>::coroutine;
};
void dispatch(typename coro::yield_type &yield_main) {
while (!p_coros.empty()) { while (!p_coros.empty()) {
if (p_idx == p_coros.end()) { if (p_idx == p_coros.end()) {
/* we're at the end; it's time to return to main and /* we're at the end; it's time to return to main and
* continue there (potential yield from main results * continue there (potential yield from main results
* in continuing from this point with the first task) * in continuing from this point with the first task)
*/ */
yield_main(); detail::csched_task::current()->yield();
continue; continue;
} }
(*p_idx)(); (*p_idx)();
if (!*p_idx) { if (p_idx->dead()) {
p_idx = p_coros.erase(p_idx); p_idx = p_coros.erase(p_idx);
} else { } else {
++p_idx; ++p_idx;
@ -217,9 +264,9 @@ private:
} }
basic_stack_pool<TR, Protected> p_stacks; basic_stack_pool<TR, Protected> p_stacks;
coro p_dispatcher; detail::csched_task p_dispatcher;
std::list<coro> p_coros; std::list<detail::csched_task> p_coros;
typename std::list<coro>::iterator p_idx = p_coros.end(); typename std::list<detail::csched_task>::iterator p_idx = p_coros.end();
}; };
using simple_coroutine_scheduler = using simple_coroutine_scheduler =
@ -228,71 +275,46 @@ using simple_coroutine_scheduler =
using protected_simple_coroutine_scheduler = using protected_simple_coroutine_scheduler =
basic_simple_coroutine_scheduler<stack_traits, true>; basic_simple_coroutine_scheduler<stack_traits, true>;
namespace detail {
struct csched_task;
OSTD_EXPORT extern thread_local csched_task *current_csched_task;
struct csched_task: coroutine_context {
std::function<void()> func;
void *waiting_on = nullptr;
csched_task *next_waiting = nullptr;
typename std::list<csched_task>::iterator pos;
csched_task() = delete;
csched_task(csched_task const &) = delete;
csched_task(csched_task &&) = delete;
csched_task &operator=(csched_task const &) = delete;
csched_task &operator=(csched_task &&) = delete;
template<typename F, typename SA>
csched_task(F &&f, SA &&sa): func(std::forward<F>(f)) {
if (!func) {
this->set_dead();
return;
}
this->make_context<csched_task>(sa);
}
void operator()() {
this->set_exec();
csched_task *curr = std::exchange(current_csched_task, this);
this->coro_jump();
current_csched_task = curr;
this->rethrow();
}
void yield() {
/* we'll yield back to the thread we were scheduled to, which
* will appropriately notify one or all other waiting threads
* so we either get re-scheduled or the whole scheduler dies
*/
this->yield_jump();
}
bool dead() const {
return this->is_dead();
}
static csched_task *current() {
return current_csched_task;
}
void resume_call() {
func();
}
};
}
template<typename TR, bool Protected> template<typename TR, bool Protected>
struct basic_coroutine_scheduler { struct basic_coroutine_scheduler {
private: private:
struct task_cond; struct task_cond;
using task = detail::csched_task; struct task;
using tlist = std::list<task>; using tlist = std::list<task>;
using titer = typename tlist::iterator; using titer = typename tlist::iterator;
struct task {
private:
detail::csched_task p_func;
public:
void *waiting_on = nullptr;
task *next_waiting = nullptr;
titer pos;
template<typename F, typename SA>
task(F &&f, SA &&sa):
p_func(std::forward<F>(f), std::forward<SA>(sa))
{}
void operator()() {
p_func();
}
void yield() {
p_func.yield();
}
bool dead() const {
return p_func.dead();
}
static task *current() {
return reinterpret_cast<task *>(detail::csched_task::current());
}
};
struct task_cond { struct task_cond {
task_cond() = delete; task_cond() = delete;
task_cond(task_cond const &) = delete; task_cond(task_cond const &) = delete;