r228051 - Thread Safety Analysis: add support for before/after annotations on mutexes.
DeLesley Hutchins
delesley at google.com
Tue Feb 3 14:11:05 PST 2015
Author: delesley
Date: Tue Feb 3 16:11:04 2015
New Revision: 228051
URL: http://llvm.org/viewvc/llvm-project?rev=228051&view=rev
Log:
Thread Safety Analysis: add support for before/after annotations on mutexes.
These checks detect potential deadlocks caused by inconsistent lock
ordering. The checks are implemented under the -Wthread-safety-beta flag.
This patch also replaces calls to getAttrs() with calls to attrs() throughout
ThreadSafety.cpp, which fixes the earlier issue that cause assert failures.
Modified:
cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h
cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
cfe/trunk/include/clang/Sema/Sema.h
cfe/trunk/lib/Analysis/ThreadSafety.cpp
cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
cfe/trunk/lib/Sema/Sema.cpp
cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp
Modified: cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h?rev=228051&r1=228050&r2=228051&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h Tue Feb 3 16:11:04 2015
@@ -26,6 +26,8 @@
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 {
@@ -183,6 +185,14 @@ 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) {}
@@ -203,7 +213,8 @@ 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);
+ ThreadSafetyHandler &Handler,
+ BeforeSet **Bset);
/// \brief Helper function that returns a LockKind required for the given level
/// of access.
Modified: cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h?rev=228051&r1=228050&r2=228051&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h Tue Feb 3 16:11:04 2015
@@ -286,6 +286,14 @@ public:
sx::partiallyMatches(CapExpr, other.CapExpr);
}
+ const ValueDecl* valueDecl() const {
+ if (Negated)
+ return nullptr;
+ if (auto *P = dyn_cast<til::Project>(CapExpr))
+ return P->clangDecl();
+ return nullptr;
+ }
+
std::string toString() const {
if (Negated)
return "!" + sx::toString(CapExpr);
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=228051&r1=228050&r2=228051&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Feb 3 16:11:04 2015
@@ -2394,6 +2394,13 @@ def warn_fun_excludes_mutex : Warning<
def warn_cannot_resolve_lock : Warning<
"cannot resolve lock expression">,
InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
+def warn_acquired_before : Warning<
+ "%0 '%1' must be acquired before '%2'">,
+ InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
+def warn_acquired_before_after_cycle : Warning<
+ "Cycle in acquired_before/after dependencies, starting with '%0'">,
+ InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
+
// Thread safety warnings negative capabilities
def warn_acquire_requires_negative_cap : Warning<
Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=228051&r1=228050&r2=228051&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Tue Feb 3 16:11:04 2015
@@ -199,6 +199,11 @@ 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<llvm::PointerUnion<const TemplateTypeParmType*, NamedDecl*>,
@@ -6708,6 +6713,7 @@ 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.
///
Modified: cfe/trunk/lib/Analysis/ThreadSafety.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ThreadSafety.cpp?rev=228051&r1=228050&r2=228051&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/ThreadSafety.cpp (original)
+++ cfe/trunk/lib/Analysis/ThreadSafety.cpp Tue Feb 3 16:11:04 2015
@@ -101,17 +101,22 @@ 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)
- : CapabilityExpr(CE), LKind(LK), AcquireLoc(Loc), Asserted(Asrt) {}
+ bool Asrt, bool Declrd = false)
+ : CapabilityExpr(CE), LKind(LK), AcquireLoc(Loc), Asserted(Asrt),
+ Declared(Declrd) {}
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,
@@ -231,14 +236,59 @@ public:
FactEntry *findPartialMatch(FactManager &FM,
const CapabilityExpr &CapE) const {
- auto I = std::find_if(begin(), end(), [&](FactID ID) {
+ auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool {
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<const ValueDecl*, 4> BeforeVect;
+
+ struct BeforeInfo {
+ BeforeInfo() : Vect(nullptr), Visited(false) { }
+ BeforeInfo(BeforeInfo &&O)
+ : Vect(std::move(O.Vect)), Visited(O.Visited)
+ {}
+
+ std::unique_ptr<BeforeVect> Vect;
+ int Visited;
+ };
+
+ typedef llvm::DenseMap<const ValueDecl*, BeforeInfo> BeforeMap;
+ typedef llvm::DenseMap<const ValueDecl*, bool> 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<const NamedDecl*, unsigned> LocalVarContext;
class LocalVariableMap;
@@ -853,6 +903,7 @@ 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;
@@ -864,9 +915,11 @@ class ThreadSafetyAnalyzer {
FactManager FactMan;
std::vector<CFGBlockInfo> BlockInfo;
+ BeforeSet* GlobalBeforeSet;
+
public:
- ThreadSafetyAnalyzer(ThreadSafetyHandler &H)
- : Arena(&Bpa), SxBuilder(Arena), Handler(H) {}
+ ThreadSafetyAnalyzer(ThreadSafetyHandler &H, BeforeSet* Bset)
+ : Arena(&Bpa), SxBuilder(Arena), Handler(H), GlobalBeforeSet(Bset) {}
bool inCurrentScope(const CapabilityExpr &CapE);
@@ -907,6 +960,135 @@ 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;
+
+ for (Attr* At : Vd->attrs()) {
+ switch (At->getKind()) {
+ case attr::AcquiredBefore: {
+ auto *A = cast<AcquiredBeforeAttr>(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<AcquiredAfterAttr>(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<BeforeInfo*, 8> InfoVect;
+
+ // Do a depth-first traversal of Vd.
+ // Return true if there are cycles.
+ std::function<bool (const ValueDecl*)> 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<ImplicitCastExpr>(Exp))
@@ -1020,7 +1202,13 @@ void ThreadSafetyAnalyzer::addLock(FactS
}
}
- // FIXME: deal with acquired before/after annotations.
+ // Check before/after constraints
+ if (Handler.issueBetaWarnings() &&
+ !Entry->asserted() && !Entry->declared()) {
+ GlobalBeforeSet->checkBeforeAfter(Entry->valueDecl(), FSet, *this,
+ Entry->loc(), DiagKind);
+ }
+
// FIXME: Don't always warn when we have support for reentrant locks.
if (FSet.findLock(FactMan, *Entry)) {
if (!Entry->asserted())
@@ -1230,7 +1418,7 @@ void ThreadSafetyAnalyzer::getEdgeLockse
CapExprSet SharedLocksToAdd;
// If the condition is a call to a Trylock function, then grab the attributes
- for (auto *Attr : FunDecl->getAttrs()) {
+ for (auto *Attr : FunDecl->attrs()) {
switch (Attr->getKind()) {
case attr::ExclusiveTrylockFunction: {
ExclusiveTrylockFunctionAttr *A =
@@ -1500,13 +1688,12 @@ void BuildLockset::checkPtAccess(const E
///
void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
SourceLocation Loc = Exp->getExprLoc();
- const AttrVec &ArgAttrs = D->getAttrs();
CapExprSet ExclusiveLocksToAdd, SharedLocksToAdd;
CapExprSet ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;
StringRef CapDiagKind = "mutex";
- for(unsigned i = 0; i < ArgAttrs.size(); ++i) {
- Attr *At = const_cast<Attr*>(ArgAttrs[i]);
+ for(Attr *Atconst : D->attrs()) {
+ Attr* At = const_cast<Attr*>(Atconst);
switch (At->getKind()) {
// When we encounter a lock function, we need to add the lock to our
// lockset.
@@ -1940,14 +2127,13 @@ void ThreadSafetyAnalyzer::runAnalysis(A
if (!SortedGraph->empty() && D->hasAttrs()) {
const CFGBlock *FirstBlock = *SortedGraph->begin();
FactSet &InitialLockset = BlockInfo[FirstBlock->getBlockID()].EntrySet;
- const AttrVec &ArgAttrs = D->getAttrs();
CapExprSet ExclusiveLocksToAdd;
CapExprSet SharedLocksToAdd;
StringRef CapDiagKind = "mutex";
SourceLocation Loc = D->getLocation();
- for (const auto *Attr : ArgAttrs) {
+ for (const auto *Attr : D->attrs()) {
Loc = Attr->getLocation();
if (const auto *A = dyn_cast<RequiresCapabilityAttr>(Attr)) {
getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
@@ -1979,14 +2165,16 @@ void ThreadSafetyAnalyzer::runAnalysis(A
}
// FIXME -- Loc can be wrong here.
- for (const auto &Mu : ExclusiveLocksToAdd)
- addLock(InitialLockset,
- llvm::make_unique<LockableFactEntry>(Mu, LK_Exclusive, Loc),
- CapDiagKind, true);
- for (const auto &Mu : SharedLocksToAdd)
- addLock(InitialLockset,
- llvm::make_unique<LockableFactEntry>(Mu, LK_Shared, Loc),
- CapDiagKind, true);
+ for (const auto &Mu : ExclusiveLocksToAdd) {
+ auto Entry = llvm::make_unique<LockableFactEntry>(Mu, LK_Exclusive, Loc);
+ Entry->setDeclared(true);
+ addLock(InitialLockset, std::move(Entry), CapDiagKind, true);
+ }
+ for (const auto &Mu : SharedLocksToAdd) {
+ auto Entry = llvm::make_unique<LockableFactEntry>(Mu, LK_Shared, Loc);
+ Entry->setDeclared(true);
+ addLock(InitialLockset, std::move(Entry), CapDiagKind, true);
+ }
}
for (const auto *CurrBlock : *SortedGraph) {
@@ -2180,11 +2368,20 @@ void ThreadSafetyAnalyzer::runAnalysis(A
/// 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) {
- ThreadSafetyAnalyzer Analyzer(Handler);
+ ThreadSafetyHandler &Handler,
+ BeforeSet **BSet) {
+ if (!*BSet)
+ *BSet = new BeforeSet;
+ ThreadSafetyAnalyzer Analyzer(Handler, *BSet);
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) {
Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=228051&r1=228050&r2=228051&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)
+++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Tue Feb 3 16:11:04 2015
@@ -1679,6 +1679,22 @@ class ThreadSafetyReporter : public clan
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;
}
@@ -1704,7 +1720,7 @@ class ConsumedWarningsHandler : public C
DiagList Warnings;
public:
-
+
ConsumedWarningsHandler(Sema &S) : S(S) {}
void emitDiagnostics() override {
@@ -1981,7 +1997,8 @@ AnalysisBasedWarnings::IssueWarnings(sem
if (!Diags.isIgnored(diag::warn_thread_safety_verbose, D->getLocStart()))
Reporter.setVerbose(true);
- threadSafety::runThreadSafetyAnalysis(AC, Reporter);
+ threadSafety::runThreadSafetyAnalysis(AC, Reporter,
+ &S.ThreadSafetyDeclCache);
Reporter.emitDiagnostics();
}
Modified: cfe/trunk/lib/Sema/Sema.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.cpp?rev=228051&r1=228050&r2=228051&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.cpp (original)
+++ cfe/trunk/lib/Sema/Sema.cpp Tue Feb 3 16:11:04 2015
@@ -102,7 +102,7 @@ Sema::Sema(Preprocessor &pp, ASTContext
AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false),
NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1),
CurrentInstantiationScope(nullptr), DisableTypoCorrection(false),
- TyposCorrected(0), AnalysisWarnings(*this),
+ TyposCorrected(0), AnalysisWarnings(*this), ThreadSafetyDeclCache(nullptr),
VarDataSharingAttributesStack(nullptr), CurScope(nullptr),
Ident_super(nullptr), Ident___float128(nullptr)
{
@@ -243,6 +243,8 @@ Sema::~Sema() {
if (isMultiplexExternalSource)
delete ExternalSource;
+ threadSafety::threadSafetyCleanup(ThreadSafetyDeclCache);
+
// Destroys data sharing attributes stack for OpenMP
DestroyDataSharingAttributesStack();
Modified: cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp?rev=228051&r1=228050&r2=228051&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp Tue Feb 3 16:11:04 2015
@@ -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 requires better smart pointer handling.
+ // TODO -- these require better smart pointer handling.
copy(*foosp.get());
write1(*foosp.get());
write2(10, *foosp.get());
@@ -4848,3 +4848,213 @@ 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
+
More information about the cfe-commits
mailing list