mirror of
https://github.com/intel/llvm.git
synced 2026-01-24 17:01:00 +08:00
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. llvm-svn: 228051
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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(FactSet &FSet,
|
||||
}
|
||||
}
|
||||
|
||||
// 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::getEdgeLockset(FactSet& Result,
|
||||
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 Expr *Exp, AccessKind AK,
|
||||
///
|
||||
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(AnalysisDeclContext &AC) {
|
||||
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(AnalysisDeclContext &AC) {
|
||||
}
|
||||
|
||||
// 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(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) {
|
||||
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) {
|
||||
|
||||
@@ -1679,6 +1679,22 @@ 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;
|
||||
}
|
||||
@@ -1704,7 +1720,7 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase {
|
||||
DiagList Warnings;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
ConsumedWarningsHandler(Sema &S) : S(S) {}
|
||||
|
||||
void emitDiagnostics() override {
|
||||
@@ -1981,7 +1997,8 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user