1 // RUN: %check_clang_tidy -std=c++20 %s cppcoreguidelines-no-suspend-with-lock %t -- -- -fno-delayed-template-parsing -fexceptions 2 3 // NOLINTBEGIN 4 namespace std { 5 template <typename T, typename... Args> 6 struct coroutine_traits { 7 using promise_type = typename T::promise_type; 8 }; 9 template <typename T = void> 10 struct coroutine_handle; 11 template <> 12 struct coroutine_handle<void> { 13 coroutine_handle() noexcept; 14 coroutine_handle(decltype(nullptr)) noexcept; 15 static constexpr coroutine_handle from_address(void*); 16 }; 17 template <typename T> 18 struct 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 25 template <class Mutex> 26 class unique_lock { 27 public: 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 37 class mutex { 38 public: 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 std 48 49 class my_own_mutex { 50 public: 51 void lock(); 52 void unlock(); 53 }; 54 55 struct Awaiter { 56 bool await_ready() noexcept; 57 void await_suspend(std::coroutine_handle<>) noexcept; 58 void await_resume() noexcept; 59 }; 60 61 struct 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 // NOLINTEND 72 73 std::mutex mtx; 74 std::mutex mtx2; 75 awaits_with_lock()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 awaits_with_lock_in_try()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 lock_possibly_unlocked()104Coro lock_possibly_unlocked() { 105 // CppCoreGuideline CP.52's enforcement strictly requires flagging 106 // 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 await_with_underlying_mutex_unlocked()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 unless 157 // there is a call to lock.unlock(). This is a bug in the program since 158 // it will result in locking the mutex twice. The check does not track 159 // 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 await_with_empty_lock()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 await_before_lock()172Coro await_before_lock() { 173 co_await Awaiter{}; 174 std::unique_lock<std::mutex> lock(mtx); 175 } 176 await_with_lock_different_scope()177Coro await_with_lock_different_scope() { 178 { 179 std::unique_lock<std::mutex> lock(mtx); 180 } 181 co_await Awaiter{}; 182 } 183 await_with_goto()184Coro await_with_goto() { 185 first: 186 co_await Awaiter{}; 187 std::unique_lock<std::mutex> lock(mtx); 188 goto first; 189 } 190 await_in_lambda()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 await_in_lambda_without_immediate_mutex()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 the 213 // immediate callable. 214 f1(); 215 } 216 yields_with_lock()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 223 template <class Mutex> awaits_templated_type(Mutex & m)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 230 template <class T> awaits_in_template_function(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 237 template <class Mutex> awaits_in_never_instantiated_template_of_mutex(Mutex & m)238Coro awaits_in_never_instantiated_template_of_mutex(Mutex& m) { 239 // Nothing should instantiate this function 240 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 245 template <class T> awaits_in_never_instantiated_templated_function(T)246Coro awaits_in_never_instantiated_templated_function(T) { 247 // Nothing should instantiate this function 248 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 253 template <class T> 254 struct my_container { 255 push_backmy_container256 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> emplace_backmy_container263 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 calls_templated_functions()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