根据 Lewis Baker 的 C++ 协程系列文章,写了一些示例代码。
generator
https://lewissbaker.github.io/2018/09/05/understanding-the-promise-type
#include <atomic>
#include <chrono>
#include <coroutine>
#include <functional>
#include <mutex>
#include <print>
#include <source_location>
#include <sstream>
#include <string>
#include <thread>
int get_tid() {
static std::atomic<int> global_thread_counter{0};
thread_local int my_tid = ++global_thread_counter;
return my_tid;
}
#define MSG " ----- "
#define LOCKED " LOCKED"
#define BEGIN " IN"
#define END " OUT"
#define LOG_FMT "{} {:>{}}{}"
#define FUNCTION std::source_location::current().function_name()
#define _LOG_LEVEL_BASE(level, func, user_fmt, ...) \
std::println(LOG_FMT user_fmt, get_tid(), "", (level) * 2, \
func __VA_OPT__(, ) __VA_ARGS__)
#define _LOG_L_F(level, func, ...) _LOG_LEVEL_BASE(level, func, "" __VA_ARGS__)
#define LOG_L(level, ...) _LOG_LEVEL_BASE(level, FUNCTION, "" __VA_ARGS__)
struct generator {
struct promise_type {
int val{};
promise_type() { LOG_L(2, MSG "with val"); }
~promise_type() { LOG_L(2); }
generator get_return_object() {
LOG_L(2, BEGIN);
auto handle = std::coroutine_handle<promise_type>::from_promise(*this);
return generator{handle};
}
std::suspend_always initial_suspend() {
LOG_L(2);
return {};
}
std::suspend_always final_suspend() noexcept {
LOG_L(2);
return {};
}
std::suspend_always yield_value(int value) {
val = value;
LOG_L(2, MSG "val is {}", val);
return {};
}
void return_void() { LOG_L(2); }
void unhandled_exception() {
LOG_L(2);
std::terminate();
}
};
std::coroutine_handle<promise_type> handle;
generator(std::coroutine_handle<promise_type> h) : handle(h) {
LOG_L(3, MSG "with handle");
}
~generator() {
LOG_L(3, BEGIN);
if (handle) {
LOG_L(3, MSG "to handle.destroy()");
handle.destroy();
}
}
};
generator make_generator() {
LOG_L(1, BEGIN);
for (int i = 1; i <= 4; ++i) {
int val = i * 10;
LOG_L(1, MSG "to yield_value({})", val);
co_yield val;
}
LOG_L(1, END);
}
int main() {
LOG_L(0, BEGIN);
auto gen = make_generator();
std::mutex mtx_generator;
auto consumer = [&](std::string_view func) {
_LOG_L_F(0, func, BEGIN);
for (int i = 0; i < 2; ++i) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
{
std::lock_guard<std::mutex> resume_lock(mtx_generator);
std::println();
_LOG_L_F(0, func, MSG "resume" LOCKED);
gen.handle.resume();
_LOG_L_F(0, func, MSG "lookup val:{}" LOCKED, gen.handle.promise().val);
}
}
_LOG_L_F(0, func, END);
};
std::thread thrd2(consumer, "thrd 2");
std::thread thrd3(consumer, "thrd 3");
thrd2.join();
thrd3.join();
std::println();
LOG_L(0, MSG "resume safely");
gen.handle.resume();
LOG_L(0, END);
return 0;
}
// clang-format off
// mkdir -p output; clang++ -std=c++23 -Wall -Wextra -g generator.cpp -o output/generator; ./output/generator
asymmetric transfer
asymmetric
https://lewissbaker.github.io/2020/05/11/understanding_symmetric_transfer#the-stack-overflow-problem
#include <array>
#include <atomic>
#include <chrono>
#include <coroutine>
#include <print>
#include <source_location>
#include <thread>
thread_local int g_stack_depth = 0;
struct StackFrameTracker {
StackFrameTracker() { ++g_stack_depth; }
~StackFrameTracker() { --g_stack_depth; }
};
#define FUNCTION std::source_location::current().function_name()
#define LOG(fmt, ...) \
std::println("{:02} | {:>{}}{} " fmt, g_stack_depth, "", g_stack_depth * 2, \
FUNCTION __VA_OPT__(, ) __VA_ARGS__)
#define LOG_MSG() \
std::println("{:02} | {:>{}}{}", g_stack_depth, "", g_stack_depth * 2, \
FUNCTION)
#define LOG_F(func, fmt, ...) \
std::println("{:02} | {:>{}}{} " fmt, g_stack_depth, "", g_stack_depth * 2, \
func __VA_OPT__(, ) __VA_ARGS__)
#define LOG_LINE() \
std::println("{:02} | {:>{}}-------------------------------------------", \
g_stack_depth, "", g_stack_depth * 2)
#define TRACK_RESUME(resume_expr) \
do { \
LOG_LINE(); \
{ \
StackFrameTracker _tracker; \
resume_expr; \
} \
LOG_LINE(); \
} while (0)
// for make_loop()
struct Task {
struct promise_type {
promise_type() {}
~promise_type() {}
Task get_return_object() {
auto handle = std::coroutine_handle<promise_type>::from_promise(*this);
return Task{handle};
}
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_void() noexcept { LOG_MSG(); }
void unhandled_exception() { std::terminate(); }
};
std::coroutine_handle<promise_type> handle_;
Task(std::coroutine_handle<promise_type> h) : handle_(h) {}
~Task() {
LOG_MSG();
if (handle_) {
LOG("- destroying handle");
handle_.destroy();
}
}
};
// for make_async()
struct AsyncTask {
struct promise_type {
std::coroutine_handle<> continuation = nullptr;
promise_type() {}
~promise_type() {}
AsyncTask get_return_object() {
return AsyncTask{
std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_always initial_suspend() noexcept { return {}; }
struct final_awaiter {
bool await_ready() const noexcept { return false; }
void await_suspend(std::coroutine_handle<promise_type> h) noexcept {
if (h.promise().continuation) {
LOG("[D2] to continuation.resume()");
TRACK_RESUME(h.promise().continuation.resume());
}
}
void await_resume() const noexcept { LOG_MSG(); }
};
final_awaiter final_suspend() noexcept { return {}; }
void return_void() noexcept { LOG_MSG(); }
void unhandled_exception() { std::terminate(); }
};
std::coroutine_handle<promise_type> handle_;
AsyncTask(std::coroutine_handle<promise_type> h) : handle_(h) {}
~AsyncTask() {
LOG_MSG();
if (handle_)
handle_.destroy();
}
bool await_ready() const noexcept { return false; }
void await_suspend(std::coroutine_handle<> awaiting_coro) noexcept {
handle_.promise().continuation = awaiting_coro;
LOG("[D1] continuation recorded: {}", awaiting_coro.address());
std::thread([this]() {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
TRACK_RESUME(handle_.resume());
}).detach();
LOG("thread detached");
}
void await_resume() const noexcept { LOG_MSG(); }
};
AsyncTask make_async() { co_return; }
Task make_loop(int count) {
for (int i = 1; i <= count; ++i) {
LOG("to make_async [i:{}]", i);
auto async = make_async();
LOG("to co_await async");
co_await async;
LOG("co_await sync finished [i:{}]", i);
}
co_return;
}
int main() {
int count = 3;
LOG("to make_loop()");
auto loop = make_loop(count);
TRACK_RESUME(loop.handle_.resume());
std::this_thread::sleep_for(std::chrono::seconds(count));
LOG("All done.");
return 0;
}
symmetric
#include <atomic>
#include <coroutine>
#include <print>
#include <source_location>
#include <string>
#include <thread>
thread_local int g_stack_depth = 0;
struct StackFrameTracker {
StackFrameTracker() { ++g_stack_depth; }
~StackFrameTracker() { --g_stack_depth; }
};
#define FUNCTION std::source_location::current().function_name()
#define LOG(fmt, ...) \
std::println("{:02} | {:>{}}{} " fmt, g_stack_depth, "", g_stack_depth * 2, \
FUNCTION __VA_OPT__(, ) __VA_ARGS__)
#define LOG_MSG() \
std::println("{:02} | {:>{}}{}", g_stack_depth, "", g_stack_depth * 2, \
FUNCTION)
#define LOG_LINE() \
std::println("{:02} | {:>{}}-------------------------------------------", \
g_stack_depth, "", g_stack_depth * 2)
#define TRACK_RESUME(resume_expr) \
do { \
LOG_LINE(); \
{ \
StackFrameTracker _tracker; \
resume_expr; \
} \
LOG_LINE(); \
} while (0)
struct Task {
struct promise_type {
promise_type() {}
~promise_type() {}
Task get_return_object() {
auto handle = std::coroutine_handle<promise_type>::from_promise(*this);
return Task{handle};
}
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_void() noexcept { LOG_MSG(); }
void unhandled_exception() { std::terminate(); }
};
std::coroutine_handle<promise_type> handle_;
Task(std::coroutine_handle<promise_type> h) : handle_(h) {}
~Task() {
LOG_MSG();
if (handle_) {
LOG("- destroying handle");
handle_.destroy();
}
}
};
struct SyncTask {
struct promise_type {
std::coroutine_handle<> continuation = nullptr;
promise_type() {}
~promise_type() {}
SyncTask get_return_object() {
auto handle = std::coroutine_handle<promise_type>::from_promise(*this);
return SyncTask{handle};
}
std::suspend_always initial_suspend() noexcept { return {}; }
struct final_awaiter {
bool await_ready() const noexcept { return false; }
void await_suspend(std::coroutine_handle<promise_type> h) noexcept {
if (h.promise().continuation) {
LOG("[D2] to continuation.resume()");
TRACK_RESUME(h.promise().continuation.resume());
}
}
void await_resume() const noexcept { LOG_MSG(); }
};
final_awaiter final_suspend() noexcept { return {}; }
void return_void() noexcept { LOG_MSG(); }
void unhandled_exception() { std::terminate(); }
};
std::coroutine_handle<promise_type> handle_;
SyncTask(std::coroutine_handle<promise_type> h) : handle_(h) {}
~SyncTask() {
LOG_MSG();
if (handle_)
handle_.destroy();
}
bool await_ready() const noexcept { return false; }
void await_suspend(std::coroutine_handle<> awaiting_coro) noexcept {
handle_.promise().continuation = awaiting_coro;
LOG("[D1] continuation recorded: {}", awaiting_coro.address());
TRACK_RESUME(handle_.resume());
}
void await_resume() const noexcept { LOG_MSG(); }
};
SyncTask make_sync() { co_return; }
Task make_loop(int count) {
for (int i = 1; i <= count; ++i) {
LOG("to make_sync [i:{}]", i);
auto sync = make_sync();
LOG("to co_await sync");
co_await sync;
LOG("co_await sync finished [i:{}]", i);
}
co_return;
}
int main() {
LOG("to make_loop()");
auto loop = make_loop(3);
TRACK_RESUME(loop.handle_.resume());
LOG("All done.");
return 0;
}
浙公网安备 33010602011771号