280 lines · cpp
1// RUN: %check_clang_tidy -std=c++20 %s cppcoreguidelines-no-suspend-with-lock %t -- -- -fno-delayed-template-parsing -fexceptions2 3// NOLINTBEGIN4namespace std {5template <typename T, typename... Args>6struct coroutine_traits {7 using promise_type = typename T::promise_type;8};9template <typename T = void>10struct coroutine_handle;11template <>12struct coroutine_handle<void> {13 coroutine_handle() noexcept;14 coroutine_handle(decltype(nullptr)) noexcept;15 static constexpr coroutine_handle from_address(void*);16};17template <typename T>18struct coroutine_handle {19 coroutine_handle() noexcept;20 coroutine_handle(decltype(nullptr)) noexcept;21 static constexpr coroutine_handle from_address(void*);22 operator coroutine_handle<>() const noexcept;23};24 25template <class Mutex>26class unique_lock {27public:28 unique_lock() noexcept;29 explicit unique_lock(Mutex &m);30 unique_lock& operator=(unique_lock&&);31 void unlock();32 Mutex* release() noexcept;33 Mutex* mutex() const noexcept;34 void swap(unique_lock& other) noexcept;35};36 37class mutex {38public:39 mutex() noexcept;40 ~mutex();41 mutex(const mutex &) = delete;42 mutex &operator=(const mutex &) = delete;43 44 void lock();45 void unlock();46};47} // namespace std48 49class my_own_mutex {50public:51 void lock();52 void unlock();53};54 55struct Awaiter {56 bool await_ready() noexcept;57 void await_suspend(std::coroutine_handle<>) noexcept;58 void await_resume() noexcept;59};60 61struct Coro {62 struct promise_type {63 Awaiter initial_suspend();64 Awaiter final_suspend() noexcept;65 void return_void();66 Coro get_return_object();67 void unhandled_exception();68 Awaiter yield_value(int);69 };70};71// NOLINTEND72 73std::mutex mtx;74std::mutex mtx2;75 76Coro awaits_with_lock() {77 std::unique_lock<std::mutex> lock(mtx);78 79 co_await Awaiter{};80 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]81 82 co_await Awaiter{};83 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]84 85 if (true) {86 co_await Awaiter{};87 // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]88 }89 90 if (true) {91 std::unique_lock<std::mutex> lock2;92 lock2.unlock();93 co_await Awaiter{};94 // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock2' held [cppcoreguidelines-no-suspend-with-lock]95 }96}97 98Coro awaits_with_lock_in_try() try {99 std::unique_lock<std::mutex> lock(mtx);100 co_await Awaiter{};101 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]102} catch (...) {}103 104Coro lock_possibly_unlocked() {105 // CppCoreGuideline CP.52's enforcement strictly requires flagging106 // code that suspends while any lock guard is not destructed.107 108 {109 std::unique_lock<std::mutex> lock(mtx);110 lock.unlock();111 co_await Awaiter{};112 // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]113 }114 115 {116 std::unique_lock<std::mutex> lock(mtx);117 lock.release();118 co_await Awaiter{};119 // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]120 }121 122 {123 std::unique_lock<std::mutex> lock(mtx);124 std::unique_lock<std::mutex> lock2;125 lock.swap(lock2);126 co_await Awaiter{};127 // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]128 }129 130 {131 std::unique_lock<std::mutex> lock(mtx);132 std::unique_lock<std::mutex> lock2{mtx2};133 lock.swap(lock2);134 co_await Awaiter{};135 // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]136 }137 138 {139 std::unique_lock<std::mutex> lock(mtx);140 lock = std::unique_lock<std::mutex>{};141 co_await Awaiter{};142 // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]143 }144 145 {146 std::unique_lock<std::mutex> lock(mtx);147 lock = std::unique_lock<std::mutex>{mtx2};148 co_await Awaiter{};149 // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]150 }151}152 153Coro await_with_underlying_mutex_unlocked() {154 std::unique_lock<std::mutex> lock(mtx);155 156 // Even though we unlock the mutex here, 'lock' is still active unless157 // there is a call to lock.unlock(). This is a bug in the program since158 // it will result in locking the mutex twice. The check does not track159 // unlock calls on the underlying mutex held by a lock guard object.160 mtx.unlock();161 162 co_await Awaiter{};163 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]164}165 166Coro await_with_empty_lock() {167 std::unique_lock<std::mutex> lock;168 co_await Awaiter{};169 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]170}171 172Coro await_before_lock() {173 co_await Awaiter{};174 std::unique_lock<std::mutex> lock(mtx);175}176 177Coro await_with_lock_different_scope() {178 {179 std::unique_lock<std::mutex> lock(mtx);180 }181 co_await Awaiter{};182}183 184Coro await_with_goto() {185first:186 co_await Awaiter{};187 std::unique_lock<std::mutex> lock(mtx);188 goto first;189}190 191void await_in_lambda() {192 auto f1 = []() -> Coro {193 std::unique_lock<std::mutex> lock(mtx);194 co_await Awaiter{};195 // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]196 };197 198 auto f2 = [](auto& m) -> Coro {199 std::unique_lock<decltype(m)> lock(m);200 co_await Awaiter{};201 // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]202 };203}204 205void await_in_lambda_without_immediate_mutex() {206 std::unique_lock<std::mutex> lock(mtx);207 208 auto f1 = []() -> Coro {209 co_await Awaiter{};210 };211 212 // The check only finds suspension points where there is a lock held in the213 // immediate callable.214 f1();215}216 217Coro yields_with_lock() {218 std::unique_lock<std::mutex> lock(mtx);219 co_yield 0;220 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]221}222 223template <class Mutex>224Coro awaits_templated_type(Mutex& m) {225 std::unique_lock<Mutex> lock(m);226 co_await Awaiter{};227 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]228}229 230template <class T>231Coro awaits_in_template_function(T) {232 std::unique_lock<std::mutex> lock(mtx);233 co_await Awaiter{};234 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]235}236 237template <class Mutex>238Coro awaits_in_never_instantiated_template_of_mutex(Mutex& m) {239 // Nothing should instantiate this function240 std::unique_lock<Mutex> lock(m);241 co_await Awaiter{};242 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]243}244 245template <class T>246Coro awaits_in_never_instantiated_templated_function(T) {247 // Nothing should instantiate this function248 std::unique_lock<std::mutex> lock(mtx);249 co_await Awaiter{};250 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]251}252 253template <class T>254struct my_container {255 256 Coro push_back() {257 std::unique_lock<std::mutex> lock(mtx_);258 co_await Awaiter{};259 // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]260 }261 262 template <class... Args>263 Coro emplace_back(Args&&...) {264 std::unique_lock<std::mutex> lock(mtx_);265 co_await Awaiter{};266 // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]267 }268 269 std::mutex mtx_;270};271 272void calls_templated_functions() {273 my_own_mutex m2;274 awaits_templated_type(mtx);275 awaits_templated_type(m2);276 277 awaits_in_template_function(1);278 awaits_in_template_function(1.0);279}280