Last active
December 21, 2021 03:01
-
-
Save ericniebler/736467eb69cb296c8265889b42d56f4a to your computer and use it in GitHub Desktop.
sync_wait function for an awaitable task type
This file contains 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 <cassert> | |
#include <mutex> | |
#include <condition_variable> | |
#include <variant> | |
#include <utility> | |
#include <exception> | |
#if __has_include(<coroutine>) | |
#include <coroutine> | |
namespace coro = std; | |
#else | |
#include <experimental/coroutine> | |
namespace coro = std::experimental; | |
#endif | |
template <class T> | |
struct task; | |
template <class T> | |
T sync_wait(task<T> t) { | |
std::variant<std::monostate, T, std::exception_ptr> data; | |
std::mutex mtx; | |
std::condition_variable cv; | |
struct sync_wait_task { | |
struct promise_type; | |
struct final_awaitable { | |
bool await_ready() noexcept { return false; } | |
void await_suspend(coro::coroutine_handle<promise_type> h) noexcept { | |
std::unique_lock lk{*h.promise().mtx_}; | |
h.promise().cv_->notify_one(); | |
} | |
void await_resume() noexcept {} | |
}; | |
struct promise_type { | |
coro::suspend_never initial_suspend() noexcept { return {}; } | |
final_awaitable final_suspend() noexcept { return {}; } | |
sync_wait_task get_return_object() noexcept { | |
return sync_wait_task{coro::coroutine_handle<promise_type>::from_promise(*this)}; | |
} | |
[[noreturn]] void unhandled_exception() noexcept { std::terminate(); } | |
void return_void() noexcept {} | |
std::mutex* mtx_; | |
std::condition_variable* cv_; | |
}; | |
struct init_awaitable { | |
std::mutex& mtx_; | |
std::condition_variable& cv_; | |
bool await_ready() noexcept { return false; } | |
bool await_suspend(coro::coroutine_handle<promise_type> h) noexcept { | |
h.promise().mtx_ = &mtx_; | |
h.promise().cv_ = &cv_; | |
return false; | |
} | |
void await_resume() noexcept {} | |
}; | |
explicit sync_wait_task(coro::coroutine_handle<promise_type> coro) | |
: coro_(coro) | |
{} | |
sync_wait_task(sync_wait_task&& that) noexcept | |
: coro_(std::exchange(that.coro_, {})) | |
{} | |
~sync_wait_task() { | |
if (coro_) | |
coro_.destroy(); | |
} | |
coro::coroutine_handle<promise_type> coro_; | |
}; | |
using init_awaitable = typename sync_wait_task::init_awaitable; | |
{ | |
auto swt = [&]() -> sync_wait_task { | |
co_await init_awaitable{mtx, cv}; | |
std::unique_lock lk{mtx}; | |
try { | |
data.template emplace<1>(co_await std::move(t)); | |
} catch(...) { | |
data.template emplace<2>(std::current_exception()); | |
} | |
}(); | |
std::unique_lock lk{mtx}; | |
cv.wait(lk, [&]{ return data.index() != 0; }); | |
} | |
assert(data.index() != 0); | |
if (data.index() == 2) | |
std::rethrow_exception(std::get<2>(data)); | |
return std::get<1>(std::move(data)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment