Last active
October 6, 2016 14:42
-
-
Save GorNishanov/86b0d235dd4187b766d6b87a47223ea9 to your computer and use it in GitHub Desktop.
CppCon 2016 Lean Future Example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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