brintos

brintos / llvm-project-archived public Read only

0
0
Text · 3.6 KiB · fc67634 Raw
106 lines · plain
1.. title:: clang-tidy - misc-coroutine-hostile-raii2 3misc-coroutine-hostile-raii4===========================5 6Detects when objects of certain hostile RAII types persists across suspension7points in a coroutine. Such hostile types include scoped-lockable types and8types belonging to a configurable denylist.9 10Some objects require that they be destroyed on the same thread that created11them. Traditionally this requirement was often phrased as "must be a local12variable", under the assumption that local variables always work this way.13However this is incorrect with C++20 coroutines, since an intervening14``co_await`` may cause the coroutine to suspend and later be resumed on15another thread.16 17The lifetime of an object that requires being destroyed on the same thread18must not encompass a ``co_await`` or ``co_yield`` point. If you create/destroy19an object, you must do so without allowing the coroutine to suspend in the20meantime.21 22Following types are considered as hostile:23 24 - Scoped-lockable types: A scoped-lockable object persisting across a25   suspension point is problematic as the lock held by this object could26   be unlocked by a different thread. This would be undefined behaviour.27   This includes all types annotated with the ``scoped_lockable`` attribute.28 29 - Types belonging to a configurable denylist.30 31.. code-block:: c++32 33  // Call some async API while holding a lock.34  task coro() {35    const std::lock_guard l(&mu_);36 37    // Oops! The async Bar function may finish on a different38    // thread from the one that created the lock_guard (and called39    // Mutex::Lock). After suspension, Mutex::Unlock will be called on the wrong thread.40    co_await Bar();41  }42 43Options44-------45 46.. option:: RAIITypesList47 48    A semicolon-separated list of qualified types which should not be allowed to49    persist across suspension points.50    Eg: `my::lockable;a::b;::my::other::lockable`51    The default value of this option is `std::lock_guard;std::scoped_lock`.52 53.. option:: AllowedAwaitablesList54 55    A semicolon-separated list of qualified types of awaitables types which can56    be safely awaited while having hostile RAII objects in scope.57 58    ``co_await``-ing an expression of ``awaitable`` type is considered59    safe if the ``awaitable`` type is part of this list.60    RAII objects persisting across such a ``co_await`` expression are61    considered safe and hence are not flagged.62 63    Example usage:64 65    .. code-block:: c++66 67      // Consider option AllowedAwaitablesList = "safe_awaitable"68      struct safe_awaitable {69        bool await_ready() noexcept { return false; }70        void await_suspend(std::coroutine_handle<>) noexcept {}71        void await_resume() noexcept {}72      };73      auto wait() { return safe_awaitable{}; }74 75      task coro() {76        // This persists across both the co_await's but is not flagged77        // because the awaitable is considered safe to await on.78        const std::lock_guard l(&mu_);79        co_await safe_awaitable{};80        co_await wait();81      }82 83    Eg: `my::safe::awaitable;other::awaitable`84    Default is an empty string.85 86.. option:: AllowedCallees87 88    A semicolon-separated list of callee function names which can89    be safely awaited while having hostile RAII objects in scope.90    Example usage:91 92    .. code-block:: c++93 94      // Consider option AllowedCallees = "noop"95      task noop() { co_return; }96 97      task coro() {98        // This persists across the co_await but is not flagged99        // because the awaitable is considered safe to await on.100        const std::lock_guard l(&mu_);101        co_await noop();102      }103 104    Eg: `my::safe::await;other::await`105    Default is an empty string.106