Skip to content

Instantly share code, notes, and snippets.

@Qix-
Created September 29, 2020 04:16
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Qix-/09532acd0f6c9a57c09bd9ce31b3023f to your computer and use it in GitHub Desktop.
Save Qix-/09532acd0f6c9a57c09bd9ce31b3023f to your computer and use it in GitHub Desktop.
C++20 coroutines + LibUV sample, v2
// Thank you to the folks at the C++ slack channel,
// along with @lewissbaker for the excellent literature
// (even though it took me a few days to be convinced
// it really was so).
#include <uv.h>
#include <iostream>
#include <experimental/coroutine>
#define DELAY 1000
int puv(int res, const char *message) {
if (res < 0) {
std::cerr << "error: " << message << ": " << uv_err_name(res) << ": " << uv_strerror(res) << std::endl;
exit(1);
}
return res;
}
struct sleep {
unsigned long _delay;
explicit sleep(unsigned long delay) : _delay(delay) {}
};
class service {
public:
struct awaiter;
struct promise_type;
using coro_handle = std::experimental::coroutine_handle<promise_type>;
coro_handle _co;
service(coro_handle co) : _co(co) {}
service(const service &) = delete;
service(service &&) = delete;
awaiter operator co_await();
};
void on_sleep_done(uv_timer_t *timer) {
auto co = std::experimental::coroutine_handle<>::from_address(timer->data);
delete timer;
co.resume();
}
struct final_awaitable {
std::experimental::coroutine_handle<> _co;
final_awaitable(std::experimental::coroutine_handle<> co) : _co(co) {}
bool await_ready() noexcept { return false; }
std::experimental::coroutine_handle<> await_suspend(std::experimental::coroutine_handle<>) noexcept {
if (_co) {
return _co;
} else {
return std::experimental::noop_coroutine();
}
}
void await_resume() noexcept {}
};
struct service::promise_type {
using coro_handle = std::experimental::coroutine_handle<promise_type>;
uv_loop_t *_loop;
std::experimental::coroutine_handle<> _continuation;
promise_type(uv_loop_t *loop) : _loop(loop) {}
auto get_return_object() { return coro_handle::from_promise(*this); }
std::experimental::suspend_never initial_suspend() { return {}; }
auto final_suspend() noexcept {
return final_awaitable{ _continuation };
}
void return_void() {}
template <typename T>
auto && await_transform(T &&obj) const noexcept {
return std::forward<T>(obj);
}
auto await_transform(sleep sleep_cmd) {
uv_timer_t *timer = new uv_timer_t{};
puv(uv_timer_init(_loop, timer), "failed to initialize timer");
timer->data = coro_handle::from_promise(*this).address();
puv(uv_timer_start(timer, &on_sleep_done, sleep_cmd._delay, 0), "failed to start timer");
return std::experimental::suspend_always{};
}
void unhandled_exception() {
std::terminate();
}
};
struct service::awaiter {
using coro_handle = std::experimental::coroutine_handle<service::promise_type>;
coro_handle _co;
awaiter(coro_handle co) : _co(co) {};
bool await_ready() {
return false;
}
auto await_suspend(std::experimental::coroutine_handle<> co_cont) {
_co.promise()._continuation = co_cont;
return true;
}
void await_resume() {}
};
service::awaiter service::operator co_await() {
return { _co };
}
service third(uv_loop_t *) {
std::cout << "4" << std::endl;
co_await sleep(DELAY);
std::cout << "5" << std::endl;
co_await sleep(DELAY);
std::cout << "6" << std::endl;
co_await sleep(DELAY);
}
service second(uv_loop_t *loop) {
std::cout << "2" << std::endl;
co_await sleep(DELAY);
std::cout << "3" << std::endl;
co_await sleep(DELAY);
for (int i = 0; i < 3; i++) {
co_await third(loop);
}
std::cout << "7" << std::endl;
co_await sleep(DELAY);
}
service service_main(uv_loop_t *loop) {
std::cout << "1" << std::endl;
co_await sleep(DELAY);
co_await second(loop);
std::cout << "8" << std::endl;
co_await sleep(DELAY);
std::cout << "9" << std::endl;
}
int main() {
uv_loop_t loop;
puv(uv_loop_init(&loop), "failed to initialize loop");
service_main(&loop);
int res = uv_run(&loop, UV_RUN_DEFAULT);
std::cerr << "uv run complete; returned " << res << std::endl;
return 0;
}
@ibsusu
Copy link

ibsusu commented May 19, 2021

You are a sexy beast. I hope you know that. This code requires bare minimum modifications to get working with g++-10.2.

g++ -fcoroutines -std=c++20 coro.cpp -I/usr/local/include -L/usr/local/lib -luv -o coro or something like it, and experimental must be removed.
for clang we've got
clang++ -std=c++20 -stdlib=libc++ -fcoroutines-ts coro.cpp -I/usr/local/include -L/usr/local/lib -luv -o coro

Thank you.

@Qix-
Copy link
Author

Qix- commented May 20, 2021

😂 Glad to know it's useful for somebody!

My pleasure :)

@KubaO
Copy link

KubaO commented Aug 24, 2021

A beauty and joy forever :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment