com um clique
coroutine-audit
修改协程(co_await/co_return/co_spawn)、异步 I/O、定时器、多线程相关 C++ 代码时触发。
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Menu
修改协程(co_await/co_return/co_spawn)、异步 I/O、定时器、多线程相关 C++ 代码时触发。
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
写入新模块文档、重构模块、或用户要求分析模块时触发。
Bug 修复或对接问题排查确认有效后,将经验记录到知识库。
修改 PMR 分配器、内存池配置、热路径容器、对象生命周期管理代码后触发。
编写或修改性能基准测试、分析 benchmark 结果、优化热路径性能时触发。
新增或修改 enable_shared_from_this 类、co_spawn 调用、shared_ptr 捕获的 lambda、co_await 后的成员访问时触发。
新增或修改原子操作、异步定时器、无锁通道、跨线程状态同步、CAS 竞争相关代码时触发。
Baseado na classificação ocupacional SOC
| name | coroutine-audit |
| description | 修改协程(co_await/co_return/co_spawn)、异步 I/O、定时器、多线程相关 C++ 代码时触发。 |
采用 每线程一个 io_context 的纯协程架构。每个 worker 线程运行一个 io_context,所有 I/O 操作通过协程异步完成。任何阻塞调用都会卡住整个 worker 线程,导致该线程上所有会话停滞。
修改以下类型的 C++ 代码时必须检查:
co_await/co_return/co_yield)co_spawn、net::detached、回调 lambda 相关代码async_read/async_write/async_connect)net::steady_timer)| 禁止 | 替代方案 |
|---|---|
std::mutex / std::lock_guard / std::unique_lock | std::atomic(无锁)、strand(序列化)、net::post(跨线程派发) |
std::shared_mutex / std::shared_lock | 多线程只读无需锁;写操作用 strand 包装 |
std::condition_variable | net::steady_timer(异步等待) |
std::semaphore / std::latch / std::barrier | 仅限非协程的 stress test 代码中使用 |
例外:
std::pmr::synchronized_pool_resource(全局池,由 PMR 内部管理)trace/spdlog 子系统的日志初始化(非热路径)| 禁止 | 替代方案 |
|---|---|
std::this_thread::sleep_for() / sleep_until() | net::steady_timer::async_wait() |
::Sleep() / usleep() / nanosleep() | net::steady_timer::async_wait() |
阻塞 socket read/write(recv/send/read/write) | async_read_some/async_write_some/async_read/async_write |
::connect() 同步连接 | socket.async_connect() |
::getaddrinfo() / gethostbyname() 同步 DNS | resolver.async_resolve() 或自定义异步 DNS 管道 |
阻塞文件 I/O(fread/fwrite/ifstream) | net::io_context + boost::asio::streambuf 或异步文件库 |
std::future::get() / std::future::wait() | co_await 异步结果 |
| 禁止 | 替代方案 |
|---|---|
while (!flag) {} / while (flag.load()) {} | co_await 异步等待 + 通知机制 |
CAS 自旋循环(无退出条件) | CAS 带降级:先尝试 CAS,失败则 co_await 异步等待信号 |
SpinLock / 自定义自旋锁 | std::atomic + net::steady_timer |
| 禁止 | 替代方案 |
|---|---|
thread.join() 在协程中调用 | 使用 std::jthread(自动 join)或 net::post 异步通知 |
std::thread 创建后同步等待结果 | net::post + co_await |
在协程中调用 io_context.run()/run_one() | 所有操作通过 co_await 完成 |
以下函数在协程中禁止直接调用,必须使用异步替代:
| 禁止 | 原因 | 替代方案 |
|---|---|---|
OpenSSL::SSL_read() / SSL_write() | 阻塞 | ssl::stream::async_read_some/async_write_some |
getaddrinfo() | 阻塞 DNS | resolver::async_resolve() |
getsockname()/getpeername() | 通常非阻塞但依赖内核 | socket 已打开后调用无阻塞风险,但避免在热路径频繁调用 |
spdlog 同步 logger | 可能阻塞写文件 | 使用 spdlog::async_logger(项目已配置) |
// ❌ 禁止:阻塞当前线程
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// ✅ 正确:异步定时器
net::steady_timer timer(executor());
timer.expires_after(std::chrono::milliseconds(100));
co_await timer.async_wait(net::use_awaitable);
// ❌ 禁止:协程中使用互斥锁
std::lock_guard<std::mutex> lock(mutex_);
data_.push_back(item);
// ✅ 正确:使用原子标志 + 单线程保证
// worker 线程模型保证同一 io_context 上的协程不会并发
closed_.store(true, std::memory_order_release);
// ✅ 跨线程派发使用 net::post
net::post(target_ioc, [self = shared_from_this()]()
{
// 在目标 io_context 上执行
});
// ❌ 禁止:阻塞 DNS 解析
auto results = ::getaddrinfo(host.c_str(), nullptr, &hints, &result);
// ✅ 正确:异步 DNS 解析
auto results = co_await resolver.async_resolve(host, port);
CAS 失败的降级策略(重试 vs 异步等待 vs 放弃)详见 concurrency-audit Section 3(TOCTOU 与 CAS 防御)。本 skill 仅关注"禁止忙等待",CAS 的正确使用模式由 concurrency-audit 覆盖。
修改代码后逐项检查:
net::awaitable<T> 的函数内部不得包含阻塞调用co_spawn 的 completion handler)内也不得阻塞net::steady_timer// ✅ 使用 awaitable_operators::operator|| 竞速
// 注意:operator|| 必须通过 ADL 在 using 命名空间后才能用于 awaitable,
// 此处 using 仅限局部作用域且是 Boost.Asio 官方用法,不适用项目
// "禁止 using namespace" 规则的例外。
using namespace boost::asio::experimental::awaitable_operators;
auto result = co_await (
recognize(transport) || deadline_timer.async_wait(net::use_awaitable)
);
// recognize 超时后自动取消,协程正常返回
// ✅ 将 socket 从 listener 的 io_context 迁移到 worker 的 io_context
auto native_handle = sock.release();
tcp::socket migrated(target_ioc);
migrated.assign(protocol, native_handle, ec);
// 迁移后在新 io_context 上启动协程
net::post(target_ioc, start_session(std::move(migrated)));
// ✅ 带异常捕获的 co_spawn
net::co_spawn(ioc, std::move(process),
[](std::exception_ptr eptr)
{
try
{
if (eptr)
{
std::rethrow_exception(eptr);
}
}
catch (const psm::exception::deviant& e)
{
/* 业务异常 */
}
catch (const std::exception& e)
{
/* 系统异常 */
}
});
// ✅ 绑定取消槽,可从外部取消协程
auto cancel_signal = std::make_shared<net::cancellation_state>();
net::co_spawn(executor, std::move(relay_coro),
net::bind_cancellation_slot(cancel_signal->slot(), net::detached));
// 需要取消时
cancel_signal->emit(net::cancellation_type::all);
// ✅ 避免 timer 取消时抛异常
net::steady_timer timer(co_await net::this_coro::executor);
timer.expires_after(timeout);
boost::system::error_code ec;
co_await timer.async_wait(net::redirect_error(net::use_awaitable, ec));
if (ec == net::error::operation_aborted)
{
/* 正常取消 */
}
io_context,同一线程上的协程天然串行,无需互斥net::post(target_ioc, handler) 将任务派发到目标线程std::atomic 配合 memory_order 可以满足大部分跨协程状态同步需求async_logger,日志调用本身不阻塞(写入环形队列后返回)enforce-coding 覆盖了本 skill 未深入探讨的 C++ 编码规范、命名约定、代码结构约束维度co-lifecycle-audit 覆盖了协程对象生命周期悬挂、shared_ptr 捕获、co_await 后引用失效维度audit-memory 覆盖了本 skill 未深入探讨的 PMR 内存分配安全、容器生命周期维度concurrency-audit 覆盖了本 skill 未深入探讨的原子操作内存序、CAS 降级策略、定时器生命周期维度 — 本 skill 管"禁止什么",concurrency-audit 管"允许但要用对"