Skip to content

Instantly share code, notes, and snippets.

@ericniebler
Last active December 21, 2021 03:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ericniebler/736467eb69cb296c8265889b42d56f4a to your computer and use it in GitHub Desktop.
Save ericniebler/736467eb69cb296c8265889b42d56f4a to your computer and use it in GitHub Desktop.
sync_wait function for an awaitable task type
#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