xref: /llvm-project/clang-tools-extra/docs/clang-tidy/checks/misc/coroutine-hostile-raii.rst (revision c94444390f4c8f4d898ac8a8444bc4bf3394609f)
1.. title:: clang-tidy - misc-coroutine-hostile-raii
2
3misc-coroutine-hostile-raii
4===========================
5
6Detects when objects of certain hostile RAII types persists across suspension
7points in a coroutine. Such hostile types include scoped-lockable types and
8types belonging to a configurable denylist.
9
10Some objects require that they be destroyed on the same thread that created them.
11Traditionally this requirement was often phrased as "must be a local variable",
12under the assumption that local variables always work this way. However this is
13incorrect with C++20 coroutines, since an intervening ``co_await`` may cause the
14coroutine to suspend and later be resumed on another thread.
15
16The lifetime of an object that requires being destroyed on the same thread must
17not encompass a ``co_await`` or ``co_yield`` point. If you create/destroy an object,
18you must do so without allowing the coroutine to suspend in the meantime.
19
20Following types are considered as hostile:
21
22 - Scoped-lockable types: A scoped-lockable object persisting across a suspension
23   point is problematic as the lock held by this object could be unlocked by a
24   different thread. This would be undefined behaviour.
25   This includes all types annotated with the ``scoped_lockable`` attribute.
26
27 - Types belonging to a configurable denylist.
28
29.. code-block:: c++
30
31  // Call some async API while holding a lock.
32  task coro() {
33    const std::lock_guard l(&mu_);
34
35    // Oops! The async Bar function may finish on a different
36    // thread from the one that created the lock_guard (and called
37    // Mutex::Lock). After suspension, Mutex::Unlock will be called on the wrong thread.
38    co_await Bar();
39  }
40
41Options
42-------
43
44.. option:: RAIITypesList
45
46    A semicolon-separated list of qualified types which should not be allowed to
47    persist across suspension points.
48    Eg: ``my::lockable; a::b;::my::other::lockable;``
49    The default value of this option is `"std::lock_guard;std::scoped_lock"`.
50
51.. option:: AllowedAwaitablesList
52
53    A semicolon-separated list of qualified types of awaitables types which can
54    be safely awaited while having hostile RAII objects in scope.
55
56    ``co_await``-ing an expression of ``awaitable`` type is considered
57    safe if the ``awaitable`` type is part of this list.
58    RAII objects persisting across such a ``co_await`` expression are
59    considered safe and hence are not flagged.
60
61    Example usage:
62
63    .. code-block:: c++
64
65      // Consider option AllowedAwaitablesList = "safe_awaitable"
66      struct safe_awaitable {
67        bool await_ready() noexcept { return false; }
68        void await_suspend(std::coroutine_handle<>) noexcept {}
69        void await_resume() noexcept {}
70      };
71      auto wait() { return safe_awaitable{}; }
72
73      task coro() {
74        // This persists across both the co_await's but is not flagged
75        // because the awaitable is considered safe to await on.
76        const std::lock_guard l(&mu_);
77        co_await safe_awaitable{};
78        co_await wait();
79      }
80
81    Eg: ``my::safe::awaitable;other::awaitable``
82    The default value of this option is empty string `""`.
83
84