From 6c5e36ae3be87fefcec9177eae3aacd115905332 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Tue, 3 Feb 2015 19:51:16 +0000 Subject: [PATCH] Revert "Thread Safety Analysis: add support for before/after annotations on mutexes." This reverts r227997, as well as r228009. It does not pass check-clang for me locally on Linux. llvm-svn: 228020 --- .../include/clang/Analysis/Analyses/ThreadSafety.h | 13 +- .../clang/Analysis/Analyses/ThreadSafetyCommon.h | 8 - clang/include/clang/Basic/DiagnosticSemaKinds.td | 7 - clang/include/clang/Sema/Sema.h | 6 - clang/lib/Analysis/ThreadSafety.cpp | 233 ++------------------- clang/lib/Sema/AnalysisBasedWarnings.cpp | 21 +- clang/lib/Sema/Sema.cpp | 4 +- clang/test/SemaCXX/warn-thread-safety-analysis.cpp | 212 +------------------ 8 files changed, 22 insertions(+), 482 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/ThreadSafety.h b/clang/include/clang/Analysis/Analyses/ThreadSafety.h index eeadb77..458bb57 100644 --- a/clang/include/clang/Analysis/Analyses/ThreadSafety.h +++ b/clang/include/clang/Analysis/Analyses/ThreadSafety.h @@ -26,8 +26,6 @@ namespace clang { namespace threadSafety { -class BeforeSet; - /// This enum distinguishes between different kinds of operations that may /// need to be protected by locks. We use this enum in error handling. enum ProtectedOperationKind { @@ -185,14 +183,6 @@ public: virtual void handleFunExcludesLock(StringRef Kind, Name FunName, Name LockName, SourceLocation Loc) {} - - /// Warn that L1 cannot be acquired before L2. - virtual void handleLockAcquiredBefore(StringRef Kind, Name L1Name, - Name L2Name, SourceLocation Loc) {} - - /// Warn that there is a cycle in acquired_before/after dependencies. - virtual void handleBeforeAfterCycle(Name L1Name, SourceLocation Loc) {} - /// Called by the analysis when starting analysis of a function. /// Used to issue suggestions for changes to annotations. virtual void enterFunction(const FunctionDecl *FD) {} @@ -213,8 +203,7 @@ private: /// at the end of each block, and issue warnings for thread safety violations. /// Each block in the CFG is traversed exactly once. void runThreadSafetyAnalysis(AnalysisDeclContext &AC, - ThreadSafetyHandler &Handler, - BeforeSet **Bset); + ThreadSafetyHandler &Handler); /// \brief Helper function that returns a LockKind required for the given level /// of access. diff --git a/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h b/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h index 1d983e1..be81121 100644 --- a/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h +++ b/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h @@ -286,14 +286,6 @@ public: sx::partiallyMatches(CapExpr, other.CapExpr); } - const ValueDecl* valueDecl() const { - if (Negated) - return nullptr; - if (auto *P = dyn_cast(CapExpr)) - return P->clangDecl(); - return nullptr; - } - std::string toString() const { if (Negated) return "!" + sx::toString(CapExpr); diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 1283baf..f9dd56f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2394,13 +2394,6 @@ def warn_fun_excludes_mutex : Warning< def warn_cannot_resolve_lock : Warning< "cannot resolve lock expression">, InGroup, DefaultIgnore; -def warn_acquired_before : Warning< - "%0 '%1' must be acquired before '%2'">, - InGroup, DefaultIgnore; -def warn_acquired_before_after_cycle : Warning< - "Cycle in acquired_before/after dependencies, starting with '%0'">, - InGroup, DefaultIgnore; - // Thread safety warnings negative capabilities def warn_acquire_requires_negative_cap : Warning< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 744ce21..f6d185e 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -199,11 +199,6 @@ namespace sema { class TemplateDeductionInfo; } -namespace threadSafety { - class BeforeSet; - void threadSafetyCleanup(BeforeSet* Cache); -} - // FIXME: No way to easily map from TemplateTypeParmTypes to // TemplateTypeParmDecls, so we have this horrible PointerUnion. typedef std::pair, @@ -6713,7 +6708,6 @@ public: /// \brief Worker object for performing CFG-based warnings. sema::AnalysisBasedWarnings AnalysisWarnings; - threadSafety::BeforeSet *ThreadSafetyDeclCache; /// \brief An entity for which implicit template instantiation is required. /// diff --git a/clang/lib/Analysis/ThreadSafety.cpp b/clang/lib/Analysis/ThreadSafety.cpp index 2bd2671..a986c58 100644 --- a/clang/lib/Analysis/ThreadSafety.cpp +++ b/clang/lib/Analysis/ThreadSafety.cpp @@ -101,22 +101,17 @@ private: LockKind LKind; ///< exclusive or shared SourceLocation AcquireLoc; ///< where it was acquired. bool Asserted; ///< true if the lock was asserted - bool Declared; ///< true if the lock was declared public: FactEntry(const CapabilityExpr &CE, LockKind LK, SourceLocation Loc, - bool Asrt, bool Declrd = false) - : CapabilityExpr(CE), LKind(LK), AcquireLoc(Loc), Asserted(Asrt), - Declared(Declrd) {} + bool Asrt) + : CapabilityExpr(CE), LKind(LK), AcquireLoc(Loc), Asserted(Asrt) {} virtual ~FactEntry() {} - LockKind kind() const { return LKind; } + LockKind kind() const { return LKind; } SourceLocation loc() const { return AcquireLoc; } bool asserted() const { return Asserted; } - bool declared() const { return Declared; } - - void setDeclared(bool D) { Declared = D; } virtual void handleRemovalFromIntersection(const FactSet &FSet, FactManager &FactMan, @@ -236,58 +231,14 @@ public: FactEntry *findPartialMatch(FactManager &FM, const CapabilityExpr &CapE) const { - auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool { + auto I = std::find_if(begin(), end(), [&](FactID ID) { return FM[ID].partiallyMatches(CapE); }); return I != end() ? &FM[*I] : nullptr; } - - bool containsMutexDecl(FactManager &FM, const ValueDecl* Vd) const { - auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool { - return FM[ID].valueDecl() == Vd; - }); - return I != end(); - } }; -class ThreadSafetyAnalyzer; - - -class BeforeSet { -private: - typedef SmallVector BeforeVect; - - struct BeforeInfo { - BeforeInfo() : Vect(nullptr), Visited(false) { } - - BeforeInfo(BeforeInfo &&O) : Vect(std::move(O.Vect)), Visited(O.Visited) {} - - std::unique_ptr Vect; - int Visited; - }; - - typedef llvm::DenseMap BeforeMap; - typedef llvm::DenseMap CycleMap; - -public: - BeforeSet() { } - - BeforeInfo* insertAttrExprs(const ValueDecl* Vd, - ThreadSafetyAnalyzer& Analyzer); - - void checkBeforeAfter(const ValueDecl* Vd, - const FactSet& FSet, - ThreadSafetyAnalyzer& Analyzer, - SourceLocation Loc, StringRef CapKind); - -private: - BeforeMap BMap; - CycleMap CycMap; -}; - - - typedef llvm::ImmutableMap LocalVarContext; class LocalVariableMap; @@ -902,7 +853,6 @@ public: /// \brief Class which implements the core thread safety analysis routines. class ThreadSafetyAnalyzer { friend class BuildLockset; - friend class BeforeSet; llvm::BumpPtrAllocator Bpa; threadSafety::til::MemRegionRef Arena; @@ -914,11 +864,9 @@ class ThreadSafetyAnalyzer { FactManager FactMan; std::vector BlockInfo; - BeforeSet* GlobalBeforeSet; - public: - ThreadSafetyAnalyzer(ThreadSafetyHandler &H, BeforeSet* Bset) - : Arena(&Bpa), SxBuilder(Arena), Handler(H), GlobalBeforeSet(Bset) {} + ThreadSafetyAnalyzer(ThreadSafetyHandler &H) + : Arena(&Bpa), SxBuilder(Arena), Handler(H) {} bool inCurrentScope(const CapabilityExpr &CapE); @@ -959,136 +907,6 @@ public: void runAnalysis(AnalysisDeclContext &AC); }; - - -/// Process acquired_before and acquired_after attributes on Vd. -BeforeSet::BeforeInfo* BeforeSet::insertAttrExprs(const ValueDecl* Vd, - ThreadSafetyAnalyzer& Analyzer) { - // Create a new entry for Vd. - auto& Entry = BMap.FindAndConstruct(Vd); - BeforeInfo* Info = &Entry.second; - BeforeVect* Bv = nullptr; - - const AttrVec &ArgAttrs = Vd->getAttrs(); - for (Attr* At : ArgAttrs) { - switch (At->getKind()) { - case attr::AcquiredBefore: { - auto *A = cast(At); - - // Create a new BeforeVect for Vd if necessary. - if (!Bv) { - Bv = new BeforeVect; - Info->Vect.reset(Bv); - } - // Read exprs from the attribute, and add them to BeforeVect. - for (const auto *Arg : A->args()) { - CapabilityExpr Cp = - Analyzer.SxBuilder.translateAttrExpr(Arg, nullptr); - if (const ValueDecl *Cpvd = Cp.valueDecl()) { - Bv->push_back(Cpvd); - auto It = BMap.find(Cpvd); - if (It == BMap.end()) - insertAttrExprs(Cpvd, Analyzer); - } - } - break; - } - case attr::AcquiredAfter: { - auto *A = cast(At); - - // Read exprs from the attribute, and add them to BeforeVect. - for (const auto *Arg : A->args()) { - CapabilityExpr Cp = - Analyzer.SxBuilder.translateAttrExpr(Arg, nullptr); - if (const ValueDecl *ArgVd = Cp.valueDecl()) { - // Get entry for mutex listed in attribute - BeforeInfo* ArgInfo; - auto It = BMap.find(ArgVd); - if (It == BMap.end()) - ArgInfo = insertAttrExprs(ArgVd, Analyzer); - else - ArgInfo = &It->second; - - // Create a new BeforeVect if necessary. - BeforeVect* ArgBv = ArgInfo->Vect.get(); - if (!ArgBv) { - ArgBv = new BeforeVect; - ArgInfo->Vect.reset(ArgBv); - } - ArgBv->push_back(Vd); - } - } - break; - } - default: - break; - } - } - - return Info; -} - - -/// Return true if any mutexes in FSet are in the acquired_before set of Vd. -void BeforeSet::checkBeforeAfter(const ValueDecl* StartVd, - const FactSet& FSet, - ThreadSafetyAnalyzer& Analyzer, - SourceLocation Loc, StringRef CapKind) { - SmallVector InfoVect; - - // Do a depth-first traversal of Vd. - // Return true if there are cycles. - std::function traverse = [&](const ValueDecl* Vd) { - if (!Vd) - return false; - - BeforeSet::BeforeInfo* Info; - auto It = BMap.find(Vd); - if (It == BMap.end()) - Info = insertAttrExprs(Vd, Analyzer); - else - Info = &It->second; - - if (Info->Visited == 1) - return true; - - if (Info->Visited == 2) - return false; - - BeforeVect* Bv = Info->Vect.get(); - if (!Bv) - return false; - - InfoVect.push_back(Info); - Info->Visited = 1; - for (auto *Vdb : *Bv) { - // Exclude mutexes in our immediate before set. - if (FSet.containsMutexDecl(Analyzer.FactMan, Vdb)) { - StringRef L1 = StartVd->getName(); - StringRef L2 = Vdb->getName(); - Analyzer.Handler.handleLockAcquiredBefore(CapKind, L1, L2, Loc); - } - // Transitively search other before sets, and warn on cycles. - if (traverse(Vdb)) { - if (CycMap.find(Vd) == CycMap.end()) { - CycMap.insert(std::make_pair(Vd, true)); - StringRef L1 = Vd->getName(); - Analyzer.Handler.handleBeforeAfterCycle(L1, Vd->getLocation()); - } - } - } - Info->Visited = 2; - return false; - }; - - traverse(StartVd); - - for (auto* Info : InfoVect) - Info->Visited = 0; -} - - - /// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs. static const ValueDecl *getValueDecl(const Expr *Exp) { if (const auto *CE = dyn_cast(Exp)) @@ -1202,13 +1020,7 @@ void ThreadSafetyAnalyzer::addLock(FactSet &FSet, } } - // Check before/after constraints - if (Handler.issueBetaWarnings() && - !Entry->asserted() && !Entry->declared()) { - GlobalBeforeSet->checkBeforeAfter(Entry->valueDecl(), FSet, *this, - Entry->loc(), DiagKind); - } - + // FIXME: deal with acquired before/after annotations. // FIXME: Don't always warn when we have support for reentrant locks. if (FSet.findLock(FactMan, *Entry)) { if (!Entry->asserted()) @@ -2167,16 +1979,14 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { } // FIXME -- Loc can be wrong here. - for (const auto &Mu : ExclusiveLocksToAdd) { - auto Entry = llvm::make_unique(Mu, LK_Exclusive, Loc); - Entry->setDeclared(true); - addLock(InitialLockset, std::move(Entry), CapDiagKind, true); - } - for (const auto &Mu : SharedLocksToAdd) { - auto Entry = llvm::make_unique(Mu, LK_Shared, Loc); - Entry->setDeclared(true); - addLock(InitialLockset, std::move(Entry), CapDiagKind, true); - } + for (const auto &Mu : ExclusiveLocksToAdd) + addLock(InitialLockset, + llvm::make_unique(Mu, LK_Exclusive, Loc), + CapDiagKind, true); + for (const auto &Mu : SharedLocksToAdd) + addLock(InitialLockset, + llvm::make_unique(Mu, LK_Shared, Loc), + CapDiagKind, true); } for (const auto *CurrBlock : *SortedGraph) { @@ -2370,20 +2180,11 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { /// at the end of each block, and issue warnings for thread safety violations. /// Each block in the CFG is traversed exactly once. void runThreadSafetyAnalysis(AnalysisDeclContext &AC, - ThreadSafetyHandler &Handler, - BeforeSet **BSet) { - if (!*BSet) - *BSet = new BeforeSet; - ThreadSafetyAnalyzer Analyzer(Handler, *BSet); + ThreadSafetyHandler &Handler) { + ThreadSafetyAnalyzer Analyzer(Handler); Analyzer.runAnalysis(AC); } - -void threadSafetyCleanup(BeforeSet* Cache) { - delete Cache; -} - - /// \brief Helper function that returns a LockKind required for the given level /// of access. LockKind getLockKindFromAccessKind(AccessKind AK) { diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index da044d0..f666a9b 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -1679,22 +1679,6 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { Warnings.push_back(DelayedDiag(Warning, getNotes())); } - - virtual void handleLockAcquiredBefore(StringRef Kind, Name L1Name, - Name L2Name, SourceLocation Loc) - override { - PartialDiagnosticAt Warning(Loc, - S.PDiag(diag::warn_acquired_before) << Kind << L1Name << L2Name); - Warnings.push_back(DelayedDiag(Warning, getNotes())); - } - - virtual void handleBeforeAfterCycle(Name L1Name, SourceLocation Loc) - override { - PartialDiagnosticAt Warning(Loc, - S.PDiag(diag::warn_acquired_before_after_cycle) << L1Name); - Warnings.push_back(DelayedDiag(Warning, getNotes())); - } - void enterFunction(const FunctionDecl* FD) override { CurrentFunction = FD; } @@ -1720,7 +1704,7 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { DiagList Warnings; public: - + ConsumedWarningsHandler(Sema &S) : S(S) {} void emitDiagnostics() override { @@ -1997,8 +1981,7 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P, if (!Diags.isIgnored(diag::warn_thread_safety_verbose, D->getLocStart())) Reporter.setVerbose(true); - threadSafety::runThreadSafetyAnalysis(AC, Reporter, - &S.ThreadSafetyDeclCache); + threadSafety::runThreadSafetyAnalysis(AC, Reporter); Reporter.emitDiagnostics(); } diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index f23d89e..ded6c30 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -102,7 +102,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1), CurrentInstantiationScope(nullptr), DisableTypoCorrection(false), - TyposCorrected(0), AnalysisWarnings(*this), ThreadSafetyDeclCache(nullptr), + TyposCorrected(0), AnalysisWarnings(*this), VarDataSharingAttributesStack(nullptr), CurScope(nullptr), Ident_super(nullptr), Ident___float128(nullptr) { @@ -243,8 +243,6 @@ Sema::~Sema() { if (isMultiplexExternalSource) delete ExternalSource; - threadSafety::threadSafetyCleanup(ThreadSafetyDeclCache); - // Destroys data sharing attributes stack for OpenMP DestroyDataSharingAttributesStack(); diff --git a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp index 8060787..091e473 100644 --- a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp +++ b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp @@ -4835,7 +4835,7 @@ public: read2(10, *foosp); // expected-warning {{reading the value pointed to by 'foosp' requires holding mutex 'mu'}} destroy(mymove(*foosp)); // expected-warning {{reading the value pointed to by 'foosp' requires holding mutex 'mu'}} - // TODO -- these require better smart pointer handling. + // TODO -- these requires better smart pointer handling. copy(*foosp.get()); write1(*foosp.get()); write2(10, *foosp.get()); @@ -4848,213 +4848,3 @@ public: } // end namespace PassByRefTest - -namespace AcquiredBeforeAfterText { - -class Foo { - Mutex mu1 ACQUIRED_BEFORE(mu2, mu3); - Mutex mu2; - Mutex mu3; - - void test1() { - mu1.Lock(); - mu2.Lock(); - mu3.Lock(); - - mu3.Unlock(); - mu2.Unlock(); - mu1.Unlock(); - } - - void test2() { - mu2.Lock(); - mu1.Lock(); // expected-warning {{mutex 'mu1' must be acquired before 'mu2'}} - mu1.Unlock(); - mu2.Unlock(); - } - - void test3() { - mu3.Lock(); - mu1.Lock(); // expected-warning {{mutex 'mu1' must be acquired before 'mu3'}} - mu1.Unlock(); - mu3.Unlock(); - } - - void test4() EXCLUSIVE_LOCKS_REQUIRED(mu1) { - mu2.Lock(); - mu2.Unlock(); - } - - void test5() EXCLUSIVE_LOCKS_REQUIRED(mu2) { - mu1.Lock(); // expected-warning {{mutex 'mu1' must be acquired before 'mu2'}} - mu1.Unlock(); - } - - void test6() EXCLUSIVE_LOCKS_REQUIRED(mu2) { - mu1.AssertHeld(); - } - - void test7() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2, mu3) { } - - void test8() EXCLUSIVE_LOCKS_REQUIRED(mu3, mu2, mu1) { } -}; - - -class Foo2 { - Mutex mu1; - Mutex mu2 ACQUIRED_AFTER(mu1); - Mutex mu3 ACQUIRED_AFTER(mu1); - - void test1() { - mu1.Lock(); - mu2.Lock(); - mu3.Lock(); - - mu3.Unlock(); - mu2.Unlock(); - mu1.Unlock(); - } - - void test2() { - mu2.Lock(); - mu1.Lock(); // expected-warning {{mutex 'mu1' must be acquired before 'mu2'}} - mu1.Unlock(); - mu2.Unlock(); - } - - void test3() { - mu3.Lock(); - mu1.Lock(); // expected-warning {{mutex 'mu1' must be acquired before 'mu3'}} - mu1.Unlock(); - mu3.Unlock(); - } -}; - - -class Foo3 { - Mutex mu1 ACQUIRED_BEFORE(mu2); - Mutex mu2; - Mutex mu3 ACQUIRED_AFTER(mu2) ACQUIRED_BEFORE(mu4); - Mutex mu4; - - void test1() { - mu1.Lock(); - mu2.Lock(); - mu3.Lock(); - mu4.Lock(); - - mu4.Unlock(); - mu3.Unlock(); - mu2.Unlock(); - mu1.Unlock(); - } - - void test2() { - mu4.Lock(); - mu2.Lock(); // expected-warning {{mutex 'mu2' must be acquired before 'mu4'}} - - mu2.Unlock(); - mu4.Unlock(); - } - - void test3() { - mu4.Lock(); - mu1.Lock(); // expected-warning {{mutex 'mu1' must be acquired before 'mu4'}} - - mu1.Unlock(); - mu4.Unlock(); - } - - void test4() { - mu3.Lock(); - mu1.Lock(); // expected-warning {{mutex 'mu1' must be acquired before 'mu3'}} - - mu1.Unlock(); - mu3.Unlock(); - } -}; - - -// Test transitive DAG traversal with AFTER -class Foo4 { - Mutex mu1; - Mutex mu2 ACQUIRED_AFTER(mu1); - Mutex mu3 ACQUIRED_AFTER(mu1); - Mutex mu4 ACQUIRED_AFTER(mu2, mu3); - Mutex mu5 ACQUIRED_AFTER(mu4); - Mutex mu6 ACQUIRED_AFTER(mu4); - Mutex mu7 ACQUIRED_AFTER(mu5, mu6); - Mutex mu8 ACQUIRED_AFTER(mu7); - - void test() { - mu8.Lock(); - mu1.Lock(); // expected-warning {{mutex 'mu1' must be acquired before 'mu8'}} - mu1.Unlock(); - mu8.Unlock(); - } -}; - - -// Test transitive DAG traversal with BEFORE -class Foo5 { - Mutex mu1 ACQUIRED_BEFORE(mu2, mu3); - Mutex mu2 ACQUIRED_BEFORE(mu4); - Mutex mu3 ACQUIRED_BEFORE(mu4); - Mutex mu4 ACQUIRED_BEFORE(mu5, mu6); - Mutex mu5 ACQUIRED_BEFORE(mu7); - Mutex mu6 ACQUIRED_BEFORE(mu7); - Mutex mu7 ACQUIRED_BEFORE(mu8); - Mutex mu8; - - void test() { - mu8.Lock(); - mu1.Lock(); // expected-warning {{mutex 'mu1' must be acquired before 'mu8'}} - mu1.Unlock(); - mu8.Unlock(); - } -}; - - -class Foo6 { - Mutex mu1 ACQUIRED_AFTER(mu3); // expected-warning {{Cycle in acquired_before/after dependencies, starting with 'mu1'}} - Mutex mu2 ACQUIRED_AFTER(mu1); // expected-warning {{Cycle in acquired_before/after dependencies, starting with 'mu2'}} - Mutex mu3 ACQUIRED_AFTER(mu2); // expected-warning {{Cycle in acquired_before/after dependencies, starting with 'mu3'}} - - Mutex mu_b ACQUIRED_BEFORE(mu_b); // expected-warning {{Cycle in acquired_before/after dependencies, starting with 'mu_b'}} - Mutex mu_a ACQUIRED_AFTER(mu_a); // expected-warning {{Cycle in acquired_before/after dependencies, starting with 'mu_a'}} - - void test0() { - mu_a.Lock(); - mu_b.Lock(); - mu_b.Unlock(); - mu_a.Unlock(); - } - - void test1a() { - mu1.Lock(); - mu1.Unlock(); - } - - void test1b() { - mu1.Lock(); - mu_a.Lock(); - mu_b.Lock(); - mu_b.Unlock(); - mu_a.Unlock(); - mu1.Unlock(); - } - - void test() { - mu2.Lock(); - mu2.Unlock(); - } - - void test3() { - mu3.Lock(); - mu3.Unlock(); - } -}; - - -} // end namespace AcquiredBeforeAfterTest - -- 2.7.4