diff options
author | Vegard Storheil Eriksen <zyp@jvnv.net> | 2021-09-15 23:18:10 +0200 |
---|---|---|
committer | Vegard Storheil Eriksen <zyp@jvnv.net> | 2021-09-15 23:18:10 +0200 |
commit | 799c535a95094297a38d922a0e9903b2e8a1a4bd (patch) | |
tree | d286dc17cf052d0b5428827bc9059463cb228776 | |
parent | 121d1334a945a7c3f9400afc09fffbb542181229 (diff) |
async: Add coroutine tasks and scheduler.
-rw-r--r-- | async/scheduler.h | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/async/scheduler.h b/async/scheduler.h new file mode 100644 index 0000000..0196aa5 --- /dev/null +++ b/async/scheduler.h @@ -0,0 +1,163 @@ +#pragma once + +#include <coroutine> + +struct schedulable { + schedulable* next = nullptr; + + std::coroutine_handle<> awaiter; + + schedulable() {} + schedulable(std::coroutine_handle<> h) : awaiter(h) {} +}; + +struct scheduler_t { + schedulable* first = nullptr; + schedulable** last_next_p = &first; + + void run() { + while(first) { + auto sch = pop_next(); + sch.awaiter.resume(); + } + } + + schedulable& pop_next() { + critical_section lock; + + auto sch = first; + first = first->next; + + if(!first) { + last_next_p = &first; + } + + return *sch; + } + + void schedule(schedulable& sch) { + critical_section lock; + + sch.next = nullptr; + *last_next_p = &sch; + last_next_p = &sch.next; + } +}; + +scheduler_t scheduler; + +struct yield : public schedulable { + bool await_ready() { + return false; + } + + bool await_suspend(std::coroutine_handle<> h) { + awaiter = h; + scheduler.schedule(*this); + return true; + } + + void await_resume() {} +}; + +template <typename T = void> +struct future : public schedulable { + std::optional<T> value; + + bool await_ready() { + return bool(value); + } + + bool await_suspend(std::coroutine_handle<> h) { + if(value) { + return false; + } else { + awaiter = h; + return true; + } + } + + T await_resume() { + return *value; // TODO: move? + } + + bool done() { + return bool(value); + } + + void set(T v) { + if(value) { + return; + } + + value = v; + + if(awaiter) { + scheduler.schedule(*this); + } + } + + void reset() { + value.reset(); + awaiter = nullptr; + } +}; + +template <> +struct future<void> : public schedulable { + bool ready; + + future() : ready(false) {} + + bool await_ready() { + return ready; + } + + bool await_suspend(std::coroutine_handle<> h) { + if(ready) { + return false; + } else { + awaiter = h; + return true; + } + } + + void await_resume() { + + } + + bool done() { + return ready; + } + + void set() { + if(ready) { + return; + } + + ready = true; + + if(awaiter) { + scheduler.schedule(*this); + } + } + + void reset() { + awaiter = nullptr; + ready = false; + } +}; + +struct task : public schedulable { + struct promise_type { + task get_return_object() { + auto handle = std::coroutine_handle<promise_type>::from_promise(*this); + return {handle}; + } + std::suspend_always initial_suspend() { return {}; } + std::suspend_never final_suspend() { return {}; } + void return_void() {} + }; + + task(std::coroutine_handle<promise_type> h) : schedulable(h) {} +}; |