Skip to content

Commit dfec26b

Browse files
committed
Thread safety analysis: Don't warn about managed locks on join points
We already did so for scoped locks acquired in the constructor, this change extends the treatment to deferred locks and scoped unlocking, so locks acquired outside of the constructor. Obviously this makes things more consistent. Originally I thought this was a bad idea, because obviously it introduces false negatives when it comes to double locking, but these are typically easily found in tests, and the primary goal of the Thread safety analysis is not to find double locks but race conditions. Since the scoped lock will release the mutex anyway when the scope ends, the inconsistent state is just temporary and probably fine. Reviewed By: delesley Differential Revision: https://reviews.llvm.org/D98747
1 parent 4bf8985 commit dfec26b

File tree

2 files changed

+24
-6
lines changed

2 files changed

+24
-6
lines changed

clang/lib/Analysis/ThreadSafety.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -983,7 +983,7 @@ class ScopedLockableFactEntry : public FactEntry {
983983
} else {
984984
FSet.removeLock(FactMan, !Cp);
985985
FSet.addLock(FactMan,
986-
std::make_unique<LockableFactEntry>(Cp, kind, loc));
986+
std::make_unique<LockableFactEntry>(Cp, kind, loc, true));
987987
}
988988
}
989989

clang/test/SemaCXX/warn-thread-safety-analysis.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2595,6 +2595,7 @@ void Foo::test2() {
25952595
if (c) { // test join point -- held/not held during release
25962596
rlock.Release();
25972597
}
2598+
// No warning on join point because the lock will be released by the scope object anyway.
25982599
}
25992600

26002601
void Foo::test3() {
@@ -2615,7 +2616,7 @@ void Foo::test5() {
26152616
if (c) {
26162617
rlock.Release();
26172618
}
2618-
// no warning on join point for managed lock.
2619+
// No warning on join point because the lock will be released by the scope object anyway.
26192620
rlock.Release(); // expected-warning {{releasing mutex 'mu_' that was not held}}
26202621
}
26212622

@@ -2659,6 +2660,7 @@ class SCOPED_LOCKABLE RelockableMutexLock {
26592660

26602661
Mutex mu;
26612662
int x GUARDED_BY(mu);
2663+
bool b;
26622664

26632665
void print(int);
26642666

@@ -2740,6 +2742,23 @@ void doubleLock2() {
27402742
scope.Lock(); // expected-warning {{acquiring mutex 'mu' that is already held}}
27412743
}
27422744

2745+
void lockJoin() {
2746+
RelockableMutexLock scope(&mu, DeferTraits{});
2747+
if (b)
2748+
scope.Lock();
2749+
// No warning on join point because the lock will be released by the scope object anyway.
2750+
x = 2; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
2751+
}
2752+
2753+
void unlockJoin() {
2754+
RelockableMutexLock scope(&mu, DeferTraits{});
2755+
scope.Lock();
2756+
if (b)
2757+
scope.Unlock();
2758+
// No warning on join point because the lock will be released by the scope object anyway.
2759+
x = 2; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
2760+
}
2761+
27432762
void directUnlock() {
27442763
RelockableExclusiveMutexLock scope(&mu);
27452764
mu.Unlock();
@@ -2871,10 +2890,9 @@ void manual() EXCLUSIVE_LOCKS_REQUIRED(mu) {
28712890

28722891
void join() EXCLUSIVE_LOCKS_REQUIRED(mu) {
28732892
MutexUnlock scope(&mu);
2874-
if (c) {
2875-
scope.Lock(); // expected-note{{mutex acquired here}}
2876-
}
2877-
// expected-warning@+1{{mutex 'mu' is not held on every path through here}}
2893+
if (c)
2894+
scope.Lock();
2895+
// No warning on join point because the lock will be released by the scope object anyway.
28782896
scope.Lock();
28792897
}
28802898

0 commit comments

Comments
 (0)