__chaz

Wake up, Neo... The Matrix has you...

导航

协程

Posted on 2026-07-06 19:13  __chaz  阅读(4)  评论(0)    收藏  举报

根据 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;
}