summaryrefslogtreecommitdiff
path: root/async
diff options
context:
space:
mode:
authorVegard Storheil Eriksen <zyp@jvnv.net>2021-09-15 23:18:10 +0200
committerVegard Storheil Eriksen <zyp@jvnv.net>2021-09-15 23:18:10 +0200
commit799c535a95094297a38d922a0e9903b2e8a1a4bd (patch)
treed286dc17cf052d0b5428827bc9059463cb228776 /async
parent121d1334a945a7c3f9400afc09fffbb542181229 (diff)
async: Add coroutine tasks and scheduler.
Diffstat (limited to 'async')
-rw-r--r--async/scheduler.h163
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) {}
+};