Skip to content

Instantly share code, notes, and snippets.

@GorNishanov
Last active October 6, 2016 14:42
Show Gist options
  • Save GorNishanov/86b0d235dd4187b766d6b87a47223ea9 to your computer and use it in GitHub Desktop.
Save GorNishanov/86b0d235dd4187b766d6b87a47223ea9 to your computer and use it in GitHub Desktop.
CppCon 2016 Lean Future Example
#include <stdio.h>
#include <exception>
#include <experimental/coroutine>
#include <tuple>
#include <mutex>
#include <condition_variable>
#include <cstdlib>
struct monostate {};
template <typename... T> struct variant : std::tuple<T...> {
size_t index_;
template <size_t I, typename... Args>
void emplace(Args&&... args){
index_ = I;
get<I>(*this) = { std::forward<Args>(args)... };
}
size_t index() { return index_; }
};
using namespace std;
using namespace std::experimental;
template <typename T> struct task {
struct promise_type {
variant<monostate, T, exception_ptr> result;
coroutine_handle<> waiter;
task get_return_object() { return{ this }; }
suspend_always initial_suspend() { return{}; }
auto final_suspend() {
struct Awaiter {
promise_type* me;
bool await_ready() { return false; }
void await_suspend(coroutine_handle<>) { me->waiter.resume(); }
void await_resume() {}
};
return Awaiter{ this };
}
template <typename U> void return_value(U && value) {
result.emplace<1>(std::forward<U>(value));
}
void set_exception(exception_ptr E) {
result.emplace<2>(std::move(E));
}
};
~task() { coro.destroy(); }
bool await_ready() { return false; }
void await_suspend(coroutine_handle<> CallerCoro) {
coro.promise().waiter = CallerCoro;
coro.resume();
}
T await_resume() {
if (coro.promise().result.index() == 2)
std::rethrow_exception(get<2>(coro.promise().result));
return get<1>(coro.promise().result);
}
private:
task(promise_type *p)
: coro(coroutine_handle<promise_type>::from_promise(*p)) {}
coroutine_handle<promise_type> coro;
};
task<int> f() {
co_return 5;
}
task<int> g() {
throw 42;
co_return 5;
}
struct sync_await_helper {
struct promise_type {
condition_variable c;
mutex m;
bool done = false;
sync_await_helper get_return_object() {
return {coroutine_handle<promise_type>::from_promise(*this)};
}
suspend_always initial_suspend() { return {}; }
auto final_suspend() {
struct Awaiter {
bool await_ready() { return false; }
void await_resume() {}
void await_suspend(coroutine_handle<promise_type> h) {
promise_type &me = h.promise();
{
lock_guard<mutex> lock(me.m);
me.done = true;
}
me.c.notify_all();
}
};
return Awaiter{};
}
void return_void() {}
};
~sync_await_helper() { handle.destroy(); }
sync_await_helper(coroutine_handle<promise_type> h) : handle(h) {}
coroutine_handle<promise_type> handle;
};
template <typename Awaitable> auto sync_await(Awaitable &&a) {
if (!a.await_ready()) {
auto coro = []() -> sync_await_helper { co_return; }();
a.await_suspend(coro.handle);
auto *p = &coro.handle.promise();
unique_lock<mutex> lock(p->m);
p->c.wait(lock, [p] { return p->done; });
}
return a.await_resume();
}
int main() {
auto f_result = sync_await(f());
printf("await_resume => %d\n", f_result);
try {
auto g_result = sync_await(g());
printf("await_resume => %d\n", g_result);
}
catch (int val) {
printf("await_resume => exception %d\n", val);
}
puts("Hello, world");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment