-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[clang][ThreadSafety] Handle mutex scope #157171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-clang-static-analyzer-1 @llvm/pr-subscribers-clang Author: Prabhu Rajasekaran (Prabhuk) ChangesBefore emitting warning about locks being held at the end of function Full diff: https://github.com/llvm/llvm-project/pull/157171.diff 1 Files Affected:
diff --git a/clang/lib/Analysis/ThreadSafety.cpp b/clang/lib/Analysis/ThreadSafety.cpp
index 131170df9976e..b215a6e6d74cc 100644
--- a/clang/lib/Analysis/ThreadSafety.cpp
+++ b/clang/lib/Analysis/ThreadSafety.cpp
@@ -2443,6 +2443,22 @@ void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &EntrySet,
if (join(FactMan[*EntryIt], ExitFact, JoinLoc, EntryLEK))
*EntryIt = Fact;
} else if (!ExitFact.managed() || EntryLEK == LEK_LockedAtEndOfFunction) {
+ if (EntryLEK == LEK_LockedAtEndOfFunction) {
+ const til::SExpr *Sexp = ExitFact.sexpr();
+ const VarDecl *Var = nullptr;
+
+ if (const auto *Proj = dyn_cast<til::Project>(Sexp)) {
+ if (const auto *Base = dyn_cast<til::LiteralPtr>(Proj->record()))
+ Var = dyn_cast_or_null<VarDecl>(Base->clangDecl());
+ } else if (const auto *LP = dyn_cast<til::LiteralPtr>(Sexp)) {
+ Var = dyn_cast_or_null<VarDecl>(LP->clangDecl());
+ }
+
+ if (Var && Var->getStorageDuration() == SD_Automatic &&
+ Var->getDeclContext() == CurrentFunction) {
+ continue;
+ }
+ }
ExitFact.handleRemovalFromIntersection(ExitSet, FactMan, JoinLoc,
EntryLEK, Handler);
}
|
@llvm/pr-subscribers-clang-analysis Author: Prabhu Rajasekaran (Prabhuk) ChangesBefore emitting warning about locks being held at the end of function Full diff: https://github.com/llvm/llvm-project/pull/157171.diff 1 Files Affected:
diff --git a/clang/lib/Analysis/ThreadSafety.cpp b/clang/lib/Analysis/ThreadSafety.cpp
index 131170df9976e..b215a6e6d74cc 100644
--- a/clang/lib/Analysis/ThreadSafety.cpp
+++ b/clang/lib/Analysis/ThreadSafety.cpp
@@ -2443,6 +2443,22 @@ void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &EntrySet,
if (join(FactMan[*EntryIt], ExitFact, JoinLoc, EntryLEK))
*EntryIt = Fact;
} else if (!ExitFact.managed() || EntryLEK == LEK_LockedAtEndOfFunction) {
+ if (EntryLEK == LEK_LockedAtEndOfFunction) {
+ const til::SExpr *Sexp = ExitFact.sexpr();
+ const VarDecl *Var = nullptr;
+
+ if (const auto *Proj = dyn_cast<til::Project>(Sexp)) {
+ if (const auto *Base = dyn_cast<til::LiteralPtr>(Proj->record()))
+ Var = dyn_cast_or_null<VarDecl>(Base->clangDecl());
+ } else if (const auto *LP = dyn_cast<til::LiteralPtr>(Sexp)) {
+ Var = dyn_cast_or_null<VarDecl>(LP->clangDecl());
+ }
+
+ if (Var && Var->getStorageDuration() == SD_Automatic &&
+ Var->getDeclContext() == CurrentFunction) {
+ continue;
+ }
+ }
ExitFact.handleRemovalFromIntersection(ExitSet, FactMan, JoinLoc,
EntryLEK, Handler);
}
|
Before emitting warning about locks being held at the end of function scope check if the underlying mutex is function scoped or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM and can confirm this fixes #156760. Could you add that repro as a regression test here.
Can you check if the regression tests added to |
That test passes mutexes to std::lock but the example in the bug has unique_locks passed to std::lock |
Is there anything in this that requires pch? Seems like the other thread safety tests would make more sense. |
Extracted regression tests into clang/test/Analysis directory into a separate file. PTAL. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like there is some confusion around the concepts here: unique_lock
is not a lockable
but a scoped_lockable
. The latter should be automatically released if you annotate the destructor. The former should in my view be manually released before they're destroyed. I'll comment on the original issue.
Mutex local_m0; | ||
Mutex local_m1; | ||
LockMutexes(local_m0, local_m1); | ||
} // No warnings expected at end of function scope as the mutexes are function local. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, not sure I agree here. I think mutexes should be properly released even if they go out of scope.
void no_local_unique_locks_held_warning() { | ||
unique_lock<Mutex> ul0(m0); | ||
unique_lock<Mutex> ul1(m1); | ||
LockMutexes(ul0, ul1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are several issues here:
unique_lock
isn't annotated as a mutex (lockable) or scoped lockable class. I guess we don't warn about it because it's a template, but I'd have to check.- Once you have a scoped lockable class with properly annotated destructor, this should already be handled by the destructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we're still not able to emit attribute warnings on template instantiation. The reason is that we're doing Sema on ParsedAttr
, but template instantiation has a regular attribute. I don't have a good idea how to extend the warning there.
const til::SExpr *Sexp = ExitFact.sexpr(); | ||
const VarDecl *MutexVar = nullptr; | ||
|
||
if (const auto *Proj = dyn_cast<til::Project>(Sexp)) { | ||
if (const auto *Base = dyn_cast<til::LiteralPtr>(Proj->record())) | ||
MutexVar = dyn_cast_or_null<VarDecl>(Base->clangDecl()); | ||
} else if (const auto *LP = dyn_cast<til::LiteralPtr>(Sexp)) { | ||
MutexVar = dyn_cast_or_null<VarDecl>(LP->clangDecl()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scoped lockables should be unlocked by the automatic destructor. I don't think we should add this code.
Before emitting warning about locks being held at the end of function
scope check if the underlying mutex is function scoped or not.
Issue: #156760