misc-coroutine-hostile-raii

检测在协程中跨越挂起点的某些敌对 RAII 类型对象的持久化。 这些敌对类型包括可作用域锁定类型和属于可配置拒绝列表的类型。

某些对象要求它们在创建它们的同一线程上被销毁。 传统上,此要求通常被表述为“必须是局部变量”,在假设局部变量始终以这种方式工作的情况下。 但是对于 C++20 协程来说,这是不正确的,因为中间的 co_await 可能会导致协程挂起,然后在另一个线程上恢复。

需要在同一线程上销毁的对象的生命周期不应包含 co_awaitco_yield 点。 如果你创建/销毁一个对象,你必须在不允许多线程挂起的情况下进行。

以下类型被视为敌对类型

  • 可作用域锁定类型: 跨越挂起点的持久化的可作用域锁定对象是有问题的,因为此对象持有的锁可能被另一个线程解锁。 这将是未定义的行为。 这包括所有用 scoped_lockable 属性注释的类型。

  • 属于可配置拒绝列表的类型。

// Call some async API while holding a lock.
task coro() {
  const std::lock_guard l(&mu_);

  // Oops! The async Bar function may finish on a different
  // thread from the one that created the lock_guard (and called
  // Mutex::Lock). After suspension, Mutex::Unlock will be called on the wrong thread.
  co_await Bar();
}

选项

RAIITypesList

一个以分号分隔的限定类型的列表,这些类型不应允许跨越挂起点持久化。 例如:my::lockable; a::b;::my::other::lockable; 此选项的默认值为 “std::lock_guard;std::scoped_lock”

AllowedAwaitablesList

一个以分号分隔的限定类型的列表,这些类型表示可安全等待的类型,即使在作用域中存在敌对的 RAII 对象。

awaitable 类型的表达式属于此列表时,co_await-ing 该表达式被认为是安全的。 跨越此类 co_await 表达式的 RAII 对象被认为是安全的,因此不会被标记。

示例用法

// Consider option AllowedAwaitablesList = "safe_awaitable"
struct safe_awaitable {
  bool await_ready() noexcept { return false; }
  void await_suspend(std::coroutine_handle<>) noexcept {}
  void await_resume() noexcept {}
};
auto wait() { return safe_awaitable{}; }

task coro() {
  // This persists across both the co_await's but is not flagged
  // because the awaitable is considered safe to await on.
  const std::lock_guard l(&mu_);
  co_await safe_awaitable{};
  co_await wait();
}

例如:my::safe::awaitable;other::awaitable 此选项的默认值为空字符串 “”