diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp --- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -41,6 +41,7 @@ #include "SignedCharMisuseCheck.h" #include "SizeofContainerCheck.h" #include "SizeofExpressionCheck.h" +#include "SpuriouslyWakeUpFunctionsCheck.h" #include "StringConstructorCheck.h" #include "StringIntegerAssignmentCheck.h" #include "StringLiteralWithEmbeddedNulCheck.h" @@ -133,6 +134,8 @@ "bugprone-sizeof-container"); CheckFactories.registerCheck( "bugprone-sizeof-expression"); + CheckFactories.registerCheck( + "bugprone-spuriously-wake-up-functions"); CheckFactories.registerCheck( "bugprone-string-constructor"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -33,6 +33,7 @@ SignedCharMisuseCheck.cpp SizeofContainerCheck.cpp SizeofExpressionCheck.cpp + SpuriouslyWakeUpFunctionsCheck.cpp StringConstructorCheck.cpp StringIntegerAssignmentCheck.cpp StringLiteralWithEmbeddedNulCheck.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/SpuriouslyWakeUpFunctionsCheck.h b/clang-tools-extra/clang-tidy/bugprone/SpuriouslyWakeUpFunctionsCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/SpuriouslyWakeUpFunctionsCheck.h @@ -0,0 +1,37 @@ +//===--- SpuriouslyWakeUpFunctionsCheck.h - clang-tidy ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SPURIOUSLYWAKEUPFUNCTIONSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SPURIOUSLYWAKEUPFUNCTIONSCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace bugprone { + +/// Finds ``cnd_wait``, ``cnd_timedwait``, ``wait``, ``wait_for``, or +/// ``wait_until`` function calls when the function is not invoked from a loop +/// that checks whether a condition predicate holds or the function has a +/// condition parameter. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-spuriously-wake-up-functions.html +class SpuriouslyWakeUpFunctionsCheck : public ClangTidyCheck { +public: + SpuriouslyWakeUpFunctionsCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace bugprone +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SPURIOUSLYWAKEUPFUNCTIONSCHECK_H diff --git a/clang-tools-extra/clang-tidy/bugprone/SpuriouslyWakeUpFunctionsCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SpuriouslyWakeUpFunctionsCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/SpuriouslyWakeUpFunctionsCheck.cpp @@ -0,0 +1,108 @@ +//===--- SpuriouslyWakeUpFunctionsCheck.cpp - clang-tidy ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "SpuriouslyWakeUpFunctionsCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace bugprone { + +void SpuriouslyWakeUpFunctionsCheck::registerMatchers(MatchFinder *Finder) { + + auto hasUniqueLock = hasDescendant(declRefExpr( + hasDeclaration(varDecl(hasType(recordDecl(classTemplateSpecializationDecl( + hasName("::std::unique_lock"), + hasTemplateArgument( + 0, templateArgument(refersToType(qualType(hasDeclaration( + cxxRecordDecl(hasName("::std::mutex")))))))))))))); + + auto hasWaitDescendantCPP = hasDescendant( + cxxMemberCallExpr( + anyOf( + allOf(hasDescendant(memberExpr(hasDeclaration(functionDecl( + allOf(hasName("::std::condition_variable::wait"), + parameterCountIs(1)))))), + onImplicitObjectArgument( + declRefExpr(to(varDecl(hasType(references(recordDecl( + hasName("::std::condition_variable")))))))), + hasUniqueLock), + allOf(hasDescendant(memberExpr(hasDeclaration(functionDecl( + allOf(hasName("::std::condition_variable::wait_for"), + parameterCountIs(2)))))), + onImplicitObjectArgument( + declRefExpr(to(varDecl(hasType(references(recordDecl( + hasName("::std::condition_variable")))))))), + hasUniqueLock), + allOf(hasDescendant(memberExpr(hasDeclaration(functionDecl( + allOf(hasName("::std::condition_variable::wait_until"), + parameterCountIs(2)))))), + onImplicitObjectArgument( + declRefExpr(to(varDecl(hasType(references(recordDecl( + hasName("::std::condition_variable")))))))), + hasUniqueLock) + + )) + .bind("wait")); + + auto hasWaitDescendantC = hasDescendant( + callExpr(callee(functionDecl( + anyOf(hasName("cnd_wait"), hasName("cnd_timedwait"))))) + .bind("wait")); + if (getLangOpts().CPlusPlus) { + // Check for `CON54-CPP` + Finder->addMatcher( + ifStmt( + + allOf(hasWaitDescendantCPP, + unless(anyOf(hasDescendant(ifStmt(hasWaitDescendantCPP)), + hasDescendant(whileStmt(hasWaitDescendantCPP)), + hasDescendant(forStmt(hasWaitDescendantCPP)), + hasDescendant(doStmt(hasWaitDescendantCPP))))) + + ), + this); + } else { + // Check for `CON36-C` + Finder->addMatcher( + + ifStmt( + allOf(hasWaitDescendantC, + unless(anyOf(hasDescendant(ifStmt(hasWaitDescendantC)), + hasDescendant(whileStmt(hasWaitDescendantC)), + hasDescendant(forStmt(hasWaitDescendantC)), + hasDescendant(doStmt(hasWaitDescendantC)), + hasParent(whileStmt()), + hasParent(compoundStmt(hasParent(whileStmt()))), + hasParent(forStmt()), + hasParent(compoundStmt(hasParent(forStmt()))), + hasParent(doStmt()), + hasParent(compoundStmt(hasParent(doStmt()))))) + + )) + + , + this); + } +} + +void SpuriouslyWakeUpFunctionsCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *MatchedWait = Result.Nodes.getNodeAs("wait"); + StringRef WaitName = MatchedWait->getDirectCallee()->getName(); + diag(MatchedWait->getExprLoc(), + "'%0' should be placed inside a while statement %select{|or used with a " + "conditional parameter}1") + << WaitName << (WaitName != "cnd_wait" && WaitName != "cnd_timedwait"); +} +} // namespace bugprone +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp --- a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp @@ -11,6 +11,7 @@ #include "../ClangTidyModuleRegistry.h" #include "../bugprone/BadSignalToKillThreadCheck.h" #include "../bugprone/ReservedIdentifierCheck.h" +#include "../bugprone/SpuriouslyWakeUpFunctionsCheck.h" #include "../bugprone/UnhandledSelfAssignmentCheck.h" #include "../google/UnnamedNamespaceInHeaderCheck.h" #include "../misc/NewDeleteOverloadsCheck.h" @@ -42,6 +43,9 @@ public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { // C++ checkers + // CON + CheckFactories.registerCheck( + "cert-con54-cpp"); // DCL CheckFactories.registerCheck( "cert-dcl21-cpp"); @@ -80,6 +84,9 @@ "cert-oop58-cpp"); // C checkers + // CON + CheckFactories.registerCheck( + "cert-con36-c"); // DCL CheckFactories.registerCheck("cert-dcl03-c"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -87,6 +87,14 @@ result of a memory allocation function (``malloc()``, ``calloc()``, ``realloc()``, ``alloca()``) instead of its argument. +- New :doc:`bugprone-spuriously-wake-up-functions + ` check. + + Finds ``cnd_wait``, ``cnd_timedwait``, ``wait``, ``wait_for``, or + ``wait_until`` function calls when the function is not invoked from a loop + that checks whether a condition predicate holds or the function has a + condition parameter. + - New :doc:`bugprone-reserved-identifier ` check. @@ -124,6 +132,16 @@ New check aliases ^^^^^^^^^^^^^^^^^ +- New alias :doc:`cert-con36-c + ` to + :doc:`bugprone-spuriously-wake-up-functions + ` was added. + +- New alias :doc:`cert-con54-cpp + ` to + :doc:`bugprone-spuriously-wake-up-functions + ` was added. + - New alias :doc:`cert-dcl37-c ` to :doc:`bugprone-reserved-identifier diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone-spuriously-wake-up-functions.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone-spuriously-wake-up-functions.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone-spuriously-wake-up-functions.rst @@ -0,0 +1,29 @@ +.. title:: clang-tidy - bugprone-spuriously-wake-up-functions + +bugprone-spuriously-wake-up-functions +===================================== + +Finds ``cnd_wait``, ``cnd_timedwait``, ``wait``, ``wait_for``, or +``wait_until`` function calls when the function is not invoked from a loop +that checks whether a condition predicate holds or the function has a +condition parameter. + +.. code-block: c++ + + if (condition_predicate) { + condition.wait(lk); + } + +.. code-block: c + + if (condition_predicate) { + if (thrd_success != cnd_wait(&condition, &lock)) { + } + } + +This check corresponds to the CERT C++ Coding Standard rule +`CON54-CPP. Wrap functions that can spuriously wake up in a loop +`_. +and CERT C Coding Standard rule +`CON36-C. Wrap functions that can spuriously wake up in a loop +`_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/cert-con36-c.rst b/clang-tools-extra/docs/clang-tidy/checks/cert-con36-c.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/cert-con36-c.rst @@ -0,0 +1,10 @@ +.. title:: clang-tidy - cert-con36-c +.. meta:: +:http-equiv=refresh: 5;URL=bugprone-spuriously-wake-up-functions.html + +cert-con36-c +============ + +The cert-con36-c check is an alias, please see +`bugprone-spuriously-wake-up-functions `_ +for more information. diff --git a/clang-tools-extra/docs/clang-tidy/checks/cert-con54-cpp.rst b/clang-tools-extra/docs/clang-tidy/checks/cert-con54-cpp.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/cert-con54-cpp.rst @@ -0,0 +1,10 @@ +.. title:: clang-tidy - cert-con54-cpp +.. meta:: +:http-equiv=refresh: 5;URL=bugprone-spuriously-wake-up-functions.html + +cert-con54-cpp +============== + +The cert-con54-cpp check is an alias, please see +`bugprone-spuriously-wake-up-functions `_ +for more information. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -76,6 +76,7 @@ `bugprone-signed-char-misuse `_, `bugprone-sizeof-container `_, `bugprone-sizeof-expression `_, + `bugprone-spuriously-wake-up-functions `_, `bugprone-string-constructor `_, "Yes" `bugprone-string-integer-assignment `_, "Yes" `bugprone-string-literal-with-embedded-nul `_, @@ -300,6 +301,8 @@ .. csv-table:: Aliases.. :header: "Name", "Redirect", "Offers fixes" + `cert-con36-c `_, `bugprone-spuriously-wake-up-functions `_, + `cert-con54-cpp `_, `bugprone-spuriously-wake-up-functions `_, `cert-dcl03-c `_, `misc-static-assert `_, "Yes" `cert-dcl16-c `_, `readability-uppercase-literal-suffix `_, "Yes" `cert-dcl37-c `_, `bugprone-reserved-identifier `_, "Yes" diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-spuriously-wake-up-functions.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone-spuriously-wake-up-functions.c new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-spuriously-wake-up-functions.c @@ -0,0 +1,164 @@ +// RUN: %check_clang_tidy %s bugprone-spuriously-wake-up-functions %t -- -- +#define NULL 0 + +struct Node1 { + void *Node1; + struct Node1 *next; +}; + +typedef struct mtx_t { +} mtx_t; +typedef struct cnd_t { +} cnd_t; +struct timespec {}; + +int cnd_wait(cnd_t *cond, mtx_t *mutex){}; +int cnd_timedwait(cnd_t *cond, mtx_t *mutex, + const struct timespec *time_point){}; + +struct Node1 list_c; +static mtx_t lock; +static cnd_t condition_c; +struct timespec ts; + +void consume_list_element(void) { + + if (list_c.next == NULL) { + if (0 != cnd_wait(&condition_c, &lock)) { + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 'cnd_wait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions] + } + } + if (list_c.next == NULL) + if (0 != cnd_wait(&condition_c, &lock)) + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 'cnd_wait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions] + ; + if (list_c.next == NULL && 0 != cnd_wait(&condition_c, &lock)) + // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: 'cnd_wait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions] + ; + while (list_c.next == NULL) { + if (0 != cnd_wait(&condition_c, &lock)) { + } + } + while (list_c.next == NULL) + if (0 != cnd_wait(&condition_c, &lock)) { + } + while (list_c.next == NULL) + if (0 != cnd_wait(&condition_c, &lock)) + ; + if (list_c.next == NULL) { + cnd_wait(&condition_c, &lock); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'cnd_wait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions] + } + if (list_c.next == NULL) + cnd_wait(&condition_c, &lock); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'cnd_wait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions] + while (list_c.next == NULL) { + cnd_wait(&condition_c, &lock); + } + while (list_c.next == NULL) + cnd_wait(&condition_c, &lock); + + do { + if (0 != cnd_wait(&condition_c, &lock)) { + } + } while (list_c.next == NULL); + do + if (0 != cnd_wait(&condition_c, &lock)) { + } + while (list_c.next == NULL); + do + if (0 != cnd_wait(&condition_c, &lock)) + ; + while (list_c.next == NULL); + do { + cnd_wait(&condition_c, &lock); + } while (list_c.next == NULL); + do + cnd_wait(&condition_c, &lock); + while (list_c.next == NULL); + for (;; list_c.next == NULL) { + if (0 != cnd_wait(&condition_c, &lock)) { + } + } + for (;; list_c.next == NULL) + if (0 != cnd_wait(&condition_c, &lock)) { + } + for (;; list_c.next == NULL) + if (0 != cnd_wait(&condition_c, &lock)) + ; + for (;; list_c.next == NULL) { + cnd_wait(&condition_c, &lock); + } + for (;; list_c.next == NULL) + cnd_wait(&condition_c, &lock); + + if (list_c.next == NULL) { + if (0 != cnd_timedwait(&condition_c, &lock, &ts)) { + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 'cnd_timedwait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions] + } + } + if (list_c.next == NULL) + if (0 != cnd_timedwait(&condition_c, &lock, &ts)) + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 'cnd_timedwait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions] + ; + if (list_c.next == NULL && 0 != cnd_timedwait(&condition_c, &lock, &ts)) + // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: 'cnd_timedwait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions] + ; + while (list_c.next == NULL) { + if (0 != cnd_timedwait(&condition_c, &lock, &ts)) { + } + } + while (list_c.next == NULL) + if (0 != cnd_timedwait(&condition_c, &lock, &ts)) { + } + while (list_c.next == NULL) + if (0 != cnd_timedwait(&condition_c, &lock, &ts)) + ; + if (list_c.next == NULL) { + cnd_timedwait(&condition_c, &lock, &ts); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'cnd_timedwait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions] + } + if (list_c.next == NULL) + cnd_timedwait(&condition_c, &lock, &ts); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'cnd_timedwait' should be placed inside a while statement [bugprone-spuriously-wake-up-functions] + while (list_c.next == NULL) { + cnd_timedwait(&condition_c, &lock, &ts); + } + while (list_c.next == NULL) + cnd_timedwait(&condition_c, &lock, &ts); + + do { + if (0 != cnd_timedwait(&condition_c, &lock, &ts)) { + } + } while (list_c.next == NULL); + do + if (0 != cnd_timedwait(&condition_c, &lock, &ts)) { + } + while (list_c.next == NULL); + do + if (0 != cnd_timedwait(&condition_c, &lock, &ts)) + ; + while (list_c.next == NULL); + do { + cnd_timedwait(&condition_c, &lock, &ts); + } while (list_c.next == NULL); + do + cnd_timedwait(&condition_c, &lock, &ts); + while (list_c.next == NULL); + for (;; list_c.next == NULL) { + if (0 != cnd_timedwait(&condition_c, &lock, &ts)) { + } + } + for (;; list_c.next == NULL) + if (0 != cnd_timedwait(&condition_c, &lock, &ts)) { + } + for (;; list_c.next == NULL) + if (0 != cnd_timedwait(&condition_c, &lock, &ts)) + ; + for (;; list_c.next == NULL) { + cnd_timedwait(&condition_c, &lock, &ts); + } + for (;; list_c.next == NULL) + cnd_timedwait(&condition_c, &lock, &ts); +} +int main() { return 0; } diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-spuriously-wake-up-functions.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-spuriously-wake-up-functions.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-spuriously-wake-up-functions.cpp @@ -0,0 +1,191 @@ +// RUN: %check_clang_tidy %s bugprone-spuriously-wake-up-functions %t -- -- +#define NULL 0 + +namespace std { +using intmax_t = int; + +template +class ratio { +public: + static constexpr intmax_t num = 0; + static constexpr intmax_t den = 0; + typedef ratio type; +}; +typedef ratio<1, 1000> milli; +namespace chrono { + +template > +class duration { +public: + using rep = Rep; + using period = Period; + +public: + constexpr duration() = default; + template + constexpr explicit duration(const Rep2 &r); + template + constexpr duration(const duration &d); + ~duration() = default; + duration(const duration &) = default; +}; + +template +class time_point { +public: + using clock = Clock; + using duration = Duration; + +public: + constexpr time_point(); + constexpr explicit time_point(const duration &d); + template + constexpr time_point(const time_point &t); +}; + +using milliseconds = duration; + +class system_clock { +public: + typedef milliseconds duration; + typedef duration::rep rep; + typedef duration::period period; + typedef chrono::time_point time_point; + + static time_point now() noexcept; +}; +} // namespace chrono + +class mutex; +template +class unique_lock { +public: + typedef Mutex mutex_type; + + unique_lock() noexcept; + explicit unique_lock(mutex_type &m); +}; + +class mutex { +public: + constexpr mutex() noexcept; + ~mutex(); + mutex(const mutex &) = delete; + mutex &operator=(const mutex &) = delete; +}; + +enum class cv_status { + no_timeout, + timeout +}; + +class condition_variable { +public: + condition_variable(); + ~condition_variable(); + condition_variable(const condition_variable &) = delete; + + void wait(unique_lock &lock); + template + void wait(unique_lock &lock, Predicate pred); + template + cv_status wait_until(unique_lock &lock, + const chrono::time_point &abs_time){}; + template + bool wait_until(unique_lock &lock, + const chrono::time_point &abs_time, + Predicate pred){}; + template + cv_status wait_for(unique_lock &lock, + const chrono::duration &rel_time){}; + template + bool wait_for(unique_lock &lock, + const chrono::duration &rel_time, + Predicate pred){}; +}; + +} // namespace std + +struct Node1 { + void *Node1; + struct Node1 *next; +}; + +static Node1 list; +static std::mutex m; +static std::condition_variable condition; + +void consume_list_element(std::condition_variable &condition) { + std::unique_lock lk(m); + + if (list.next == nullptr) { + condition.wait(lk); + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 'wait' should be placed inside a while statement or used with a conditional parameter [bugprone-spuriously-wake-up-functions] + } + + while (list.next == nullptr) { + condition.wait(lk); + } + + do { + condition.wait(lk); + } while (list.next == nullptr); + + for (;; list.next == nullptr) { + condition.wait(lk); + } + + if (list.next == nullptr) { + while (list.next == nullptr) { + condition.wait(lk); + } + } + + if (list.next == nullptr) { + do { + condition.wait(lk); + } while (list.next == nullptr); + } + + if (list.next == nullptr) { + for (;; list.next == nullptr) { + condition.wait(lk); + } + } + using durtype = std::chrono::duration; + durtype dur = std::chrono::duration(); + if (list.next == nullptr) { + condition.wait_for(lk, dur); + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 'wait_for' should be placed inside a while statement or used with a conditional parameter [bugprone-spuriously-wake-up-functions] + } + if (list.next == nullptr) { + condition.wait_for(lk, dur, [] { return 1; }); + } + while (list.next == nullptr) { + condition.wait_for(lk, dur); + } + do { + condition.wait_for(lk, dur); + } while (list.next == nullptr); + for (;; list.next == nullptr) { + condition.wait_for(lk, dur); + } + + auto now = std::chrono::system_clock::now(); + if (list.next == nullptr) { + condition.wait_until(lk, now); + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 'wait_until' should be placed inside a while statement or used with a conditional parameter [bugprone-spuriously-wake-up-functions] + } + if (list.next == nullptr) { + condition.wait_until(lk, now, [] { return 1; }); + } + while (list.next == nullptr) { + condition.wait_until(lk, now); + } + do { + condition.wait_until(lk, now); + } while (list.next == nullptr); + for (;; list.next == nullptr) { + condition.wait_until(lk, now); + } +}