Coroutines mostly working, although not nearly as fancy as cppcoro. I'll try them out in my code and if I like it I'll probably just go use cppcoro.

master
Zed A. Shaw 1 month ago
parent daf9a3cc07
commit 8f7235ade1
  1. 104
      coro.hpp
  2. 100
      corotest.cpp
  3. 4
      meson.build

@ -0,0 +1,104 @@
#pragma once
#include <concepts>
#include <coroutine>
#include <exception>
#include <assert.h>
using namespace std;
enum TaskStates {
STARTED, AWAIT, YIELD,
EXCEPTION, RETURN,
RETURN_VOID, DEAD
};
template<typename T>
struct Task {
struct promise_type;
using handle_type = std::coroutine_handle<promise_type>;
struct promise_type {
T value_;
std::exception_ptr exception_;
TaskStates state_{STARTED};
Task get_return_object() {
return Task(handle_type::from_promise(*this));
}
std::suspend_always initial_suspend() {
state_ = AWAIT;
return {};
}
std::suspend_always final_suspend() noexcept {
return {};
}
void unhandled_exception() {
state_ = EXCEPTION;
exception_ = std::current_exception();
}
template<std::convertible_to<T> From> // C++20 concept
void return_value(From &&from) {
state_ = RETURN;
value_ = std::forward<From>(from);
}
template<std::convertible_to<T> From> // C++20 concept
std::suspend_always yield_value(From &&from) {
state_ = YIELD;
value_ = std::forward<From>(from);
return {};
}
void return_void() {
state_ = RETURN_VOID;
}
};
handle_type h_;
Task() {
}
Task(handle_type h) : h_(h) {
}
Task(const Task &t) : h_(t.h_) {
}
void destroy() {
// this should make it safe to clal repeatedly
if(h_.promise().state_ != DEAD) {
h_.destroy();
h_.promise().state_ = DEAD;
}
}
T operator()() {
assert(!h_.done());
call();
return std::move(h_.promise().value_);
}
bool done() {
return h_.done();
}
TaskStates state() {
return h_.promise().state_;
}
private:
void call() {
h_();
if (h_.promise().exception_) {
std::rethrow_exception(h_.promise().exception_);
}
}
};

@ -1,94 +1,11 @@
#include <concepts> #include "coro.hpp"
#include <coroutine> #include <coroutine>
#include <exception>
#include <iostream>
#include <vector> #include <vector>
#include <assert.h> #include <iostream>
#include <chrono>
using namespace std;
using namespace std::chrono;
template<typename T>
struct Task {
struct promise_type;
using handle_type = std::coroutine_handle<promise_type>;
struct promise_type {
T value_;
std::exception_ptr exception_;
Task get_return_object() {
return Task(handle_type::from_promise(*this));
}
std::suspend_always initial_suspend() {
return {};
}
std::suspend_always final_suspend() noexcept {
return {};
}
void unhandled_exception() {
exception_ = std::current_exception();
}
template<std::convertible_to<T> From> // C++20 concept
void return_value(From &&from) {
value_ = std::forward<From>(from);
}
template<std::convertible_to<T> From> // C++20 concept
std::suspend_always yield_value(From &&from) {
value_ = std::forward<From>(from);
return {};
}
void return_void() {}
};
handle_type h_;
Task() {
}
Task(handle_type h) : h_(h) {
}
Task(const Task &t) : h_(t.h_) {
}
void destroy() {
h_.destroy();
}
T operator()() {
assert(!h_.done());
call();
return std::move(h_.promise().value_);
}
bool done() {
return h_.done();
}
private:
void call() {
h_();
if (h_.promise().exception_)
std::rethrow_exception(h_.promise().exception_);
}
};
#define pass() co_await std::suspend_always{}
Task<unsigned> task_test() Task<unsigned> task_test()
{ {
pass(); co_await std::suspend_always{};
for (unsigned i = 0; i < 3; ++i) for (unsigned i = 0; i < 3; ++i)
co_yield i; co_yield i;
@ -113,12 +30,21 @@ int main()
Task<unsigned> &t = tasks[i]; Task<unsigned> &t = tasks[i];
if(t.done()) { if(t.done()) {
// remove it from the tasks
// this cause crash I think?
t.destroy(); t.destroy();
done_count++; done_count++;
} else { } else {
auto res = t(); auto res = t();
if(t.state() == AWAIT) {
cout << "AWAIT! " << t.state() << endl;
} else if(t.state() != YIELD) {
cout << "NOT YIELD: " << t.state() << endl;
} else {
cout << "T# " << i << " result " cout << "T# " << i << " result "
<< res << endl; << res << " STATE " << t.state() << endl;
}
} }
} }
} }

@ -46,7 +46,9 @@ executable('jsontest', 'jsontest.cpp',
executable('threadtest', 'threadtest.cpp', executable('threadtest', 'threadtest.cpp',
dependencies: dependencies) dependencies: dependencies)
executable('corotest', 'corotest.cpp', executable('corotest', [
'corotest.cpp'
],
dependencies: dependencies, dependencies: dependencies,
cpp_args: '-fcoroutines') cpp_args: '-fcoroutines')

Loading…
Cancel
Save