Thread safety analysis: add two new attributes to the thread safety analysis:
assert_exclusive_lock and assert_shared_lock. These attributes are used to
mark functions that dynamically check (i.e. assert) that a lock is held.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@182170 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp
index 479d9a3..c12dc64 100644
--- a/lib/Analysis/ThreadSafety.cpp
+++ b/lib/Analysis/ThreadSafety.cpp
@@ -750,16 +750,18 @@
///
/// FIXME: add support for re-entrant locking and lock up/downgrading
LockKind LKind;
+ bool Asserted; // for asserted locks
bool Managed; // for ScopedLockable objects
SExpr UnderlyingMutex; // for ScopedLockable objects
- LockData(SourceLocation AcquireLoc, LockKind LKind, bool M = false)
- : AcquireLoc(AcquireLoc), LKind(LKind), Managed(M),
+ LockData(SourceLocation AcquireLoc, LockKind LKind, bool M=false,
+ bool Asrt=false)
+ : AcquireLoc(AcquireLoc), LKind(LKind), Asserted(Asrt), Managed(M),
UnderlyingMutex(Decl::EmptyShell())
{}
LockData(SourceLocation AcquireLoc, LockKind LKind, const SExpr &Mu)
- : AcquireLoc(AcquireLoc), LKind(LKind), Managed(false),
+ : AcquireLoc(AcquireLoc), LKind(LKind), Asserted(false), Managed(false),
UnderlyingMutex(Mu)
{}
@@ -1484,7 +1486,8 @@
return;
if (FSet.findLock(FactMan, Mutex)) {
- Handler.handleDoubleLock(Mutex.toString(), LDat.AcquireLoc);
+ if (!LDat.Asserted)
+ Handler.handleDoubleLock(Mutex.toString(), LDat.AcquireLoc);
} else {
FSet.addLock(FactMan, Mutex, LDat);
}
@@ -1909,6 +1912,7 @@
/// the same signature as const method calls can be also treated as reads.
///
void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
+ SourceLocation Loc = Exp->getExprLoc();
const AttrVec &ArgAttrs = D->getAttrs();
MutexIDList ExclusiveLocksToAdd;
MutexIDList SharedLocksToAdd;
@@ -1933,6 +1937,32 @@
break;
}
+ // An assert will add a lock to the lockset, but will not generate
+ // a warning if it is already there, and will not generate a warning
+ // if it is not removed.
+ case attr::AssertExclusiveLock: {
+ AssertExclusiveLockAttr *A = cast<AssertExclusiveLockAttr>(At);
+
+ MutexIDList AssertLocks;
+ Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
+ for (unsigned i=0,n=AssertLocks.size(); i<n; ++i) {
+ Analyzer->addLock(FSet, AssertLocks[i],
+ LockData(Loc, LK_Exclusive, false, true));
+ }
+ break;
+ }
+ case attr::AssertSharedLock: {
+ AssertSharedLockAttr *A = cast<AssertSharedLockAttr>(At);
+
+ MutexIDList AssertLocks;
+ Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
+ for (unsigned i=0,n=AssertLocks.size(); i<n; ++i) {
+ Analyzer->addLock(FSet, AssertLocks[i],
+ LockData(Loc, LK_Shared, false, true));
+ }
+ break;
+ }
+
// When we encounter an unlock function, we need to remove unlocked
// mutexes from the lockset, and flag a warning if they are not there.
case attr::UnlockFunction: {
@@ -1986,7 +2016,6 @@
}
// Add locks.
- SourceLocation Loc = Exp->getExprLoc();
for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) {
Analyzer->addLock(FSet, ExclusiveLocksToAdd[i],
LockData(Loc, LK_Exclusive, isScopedVar));
@@ -2165,7 +2194,7 @@
const SExpr &FSet2Mutex = FactMan[*I].MutID;
const LockData &LDat2 = FactMan[*I].LDat;
- if (const LockData *LDat1 = FSet1.findLock(FactMan, FSet2Mutex)) {
+ if (LockData *LDat1 = FSet1.findLock(FactMan, FSet2Mutex)) {
if (LDat1->LKind != LDat2.LKind) {
Handler.handleExclusiveAndShared(FSet2Mutex.toString(),
LDat2.AcquireLoc,
@@ -2175,6 +2204,10 @@
FSet1.addLock(FactMan, FSet2Mutex, LDat2);
}
}
+ if (LDat1->Asserted && !LDat2.Asserted) {
+ // The non-asserted lock is the one we want to track.
+ *LDat1 = LDat2;
+ }
} else {
if (LDat2.UnderlyingMutex.isValid()) {
if (FSet2.findLock(FactMan, LDat2.UnderlyingMutex)) {
@@ -2186,7 +2219,7 @@
JoinLoc, LEK1);
}
}
- else if (!LDat2.Managed && !FSet2Mutex.isUniversal())
+ else if (!LDat2.Managed && !FSet2Mutex.isUniversal() && !LDat2.Asserted)
Handler.handleMutexHeldEndOfScope(FSet2Mutex.toString(),
LDat2.AcquireLoc,
JoinLoc, LEK1);
@@ -2209,7 +2242,7 @@
JoinLoc, LEK1);
}
}
- else if (!LDat1.Managed && !FSet1Mutex.isUniversal())
+ else if (!LDat1.Managed && !FSet1Mutex.isUniversal() && !LDat1.Asserted)
Handler.handleMutexHeldEndOfScope(FSet1Mutex.toString(),
LDat1.AcquireLoc,
JoinLoc, LEK2);
diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp
index d9b8e16..c365bfc 100644
--- a/lib/Sema/SemaDeclAttr.cpp
+++ b/lib/Sema/SemaDeclAttr.cpp
@@ -788,6 +788,34 @@
Attr.getAttributeSpellingListIndex()));
}
+static void handleAssertSharedLockAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ SmallVector<Expr*, 1> Args;
+ if (!checkLockFunAttrCommon(S, D, Attr, Args))
+ return;
+
+ unsigned Size = Args.size();
+ Expr **StartArg = Size == 0 ? 0 : &Args[0];
+ D->addAttr(::new (S.Context)
+ AssertSharedLockAttr(Attr.getRange(), S.Context, StartArg, Size,
+ Attr.getAttributeSpellingListIndex()));
+}
+
+static void handleAssertExclusiveLockAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ SmallVector<Expr*, 1> Args;
+ if (!checkLockFunAttrCommon(S, D, Attr, Args))
+ return;
+
+ unsigned Size = Args.size();
+ Expr **StartArg = Size == 0 ? 0 : &Args[0];
+ D->addAttr(::new (S.Context)
+ AssertExclusiveLockAttr(Attr.getRange(), S.Context,
+ StartArg, Size,
+ Attr.getAttributeSpellingListIndex()));
+}
+
+
static bool checkTryLockFunAttrCommon(Sema &S, Decl *D,
const AttributeList &Attr,
SmallVector<Expr*, 2> &Args) {
@@ -4918,6 +4946,12 @@
break;
// Thread safety attributes:
+ case AttributeList::AT_AssertExclusiveLock:
+ handleAssertExclusiveLockAttr(S, D, Attr);
+ break;
+ case AttributeList::AT_AssertSharedLock:
+ handleAssertSharedLockAttr(S, D, Attr);
+ break;
case AttributeList::AT_GuardedVar:
handleGuardedVarAttr(S, D, Attr);
break;