clang-tidy - misc-coroutine-hostile-raii — Extra Clang Tools 22.1.8 documentation

# Extra Clang Tools 22.1.8 documentation

## clang-tidy - misc-coroutine-hostile-raii

# misc-coroutine-hostile-raii

Detects when objects of certain hostile RAII types persists across suspension points in a coroutine. Such hostile types include scoped-lockable types and types belonging to a configurable denylist.

Some objects require that they be destroyed on the same thread that created them. Traditionally this requirement was often phrased as “must be a local variable”, under the assumption that local variables always work this way. However this is incorrect with C++20 coroutines, since an intervening `co_await` may cause the coroutine to suspend and later be resumed on another thread.

The lifetime of an object that requires being destroyed on the same thread must not encompass a `co_await` or `co_yield` point. If you create/destroy an object, you must do so without allowing the coroutine to suspend in the meantime.

Following types are considered as hostile:

> - Scoped-lockable types: A scoped-lockable object persisting across a suspension point is problematic as the lock held by this object could be unlocked by a different thread. This would be undefined behaviour. This includes all types annotated with the `scoped_lockable` attribute.

> - Types belonging to a configurable denylist.

    // 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();
    }

## Options

RAIITypesList

  A semicolon-separated list of qualified types which should not be allowed to persist across suspension points. Eg: my::lockable;a::b;::my::other::lockable The default value of this option is std::lock_guard;std::scoped_lock.

AllowedAwaitablesList

  A semicolon-separated list of qualified types of awaitables types which can be safely awaited while having hostile RAII objects in scope.

  `co_await`-ing an expression of `awaitable` type is considered safe if the `awaitable` type is part of this list. RAII objects persisting across such a `co_await` expression are considered safe and hence are not flagged.

  Example usage:

      // 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();
      }

  Eg: my::safe::awaitable;other::awaitable Default is an empty string.

AllowedCallees

  A semicolon-separated list of callee function names which can be safely awaited while having hostile RAII objects in scope. Example usage:

      // Consider option AllowedCallees = "noop"
      task noop() { co_return; }

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

  Eg: my::safe::await;other::await Default is an empty string.