-
Notifications
You must be signed in to change notification settings - Fork 38.6k
sync: use proper TSA attributes #19929
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
Use the proper attribute from Clang's thread safety analysis for `AssertLockHeld()`: * if `DEBUG_LOCKORDER` is defined then `AssertLockHeld()` will check if the caller owns the given mutex and will `abort()` if not. Clang has an attribute exactly for that - `ASSERT_EXCLUSIVE_LOCK()`, documented as: "These are attributes on a function or method that does a run-time test to see whether the calling thread holds the given capability. The function is assumed to fail (no return) if the capability is not held." [1] * if `DEBUG_LOCKORDER` is not defined, then `AssertLockHeld()` does nothing, thus don't tag it with any attributes (don't fool the compiler that we do something which we don't). Replace `LockAssertion` with `AssertLockHeld` and remove the former. On the places where Clang cannot deduce that a mutex is held, use `NO_THREAD_SAFETY_ANALYSIS`, intended to be used when a code is "too complicated for the analysis to understand" [2]. [1] https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#requires-requires-shared [2] https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#no-thread-safety-analysis
|
Tested with the following patch: --- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -1108,7 +1108,7 @@ bool CTxMemPool::IsLoaded() const
void CTxMemPool::SetIsLoaded(bool loaded)
{
- LOCK(cs);
+ AssertLockHeld(cs);
m_is_loaded = loaded;
}
Not sure if this is correct as in both cases the patch causes run time |
|
This is exactly how it is supposed to work: https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#assert-capability-and-assert-shared-capability
|
|
Just to elaborate - expecting a warning in the example above is similar to expecting a warning in: int Func(int x)
{
if (x == 1) {
return 10;
}
abort();
} // should we get "control reaches end of non-void function" here?The compiler does not emit a warning because it sees that What we are doing now in |
|
My testing patch makes the code wrong from the point of concurrency -- writing to On master compiler emits a warning about wrong code. With this PR we get know about wrong code only at run time. I believe that the former is strictly better then latter. |
|
@vasild, the textualist interpretation of the clang documentation here is very smart, but I think is misplaced because https://clang.llvm.org/docs/ThreadSafetyAnalysis.html is not a legal document or programming language standard, and this interpretation causes practical problems that will make the code less verifiably thread safe and harder to work with. Here are the practical problems with this PR:
On the textual disagreement, I think while the text of https://clang.llvm.org/docs/ThreadSafetyAnalysis.html could be improved, it is compatible with how #19865 and #19668 are using |
|
@hebasto, @ryanofsky, thanks for the review! I re-read your comments a few times and I see your points, but I disagree with some of the above. I see neither side is convinced and so I am closing this PR because for it to get merged an agreement is needed. It would have been too boring if everybody agreed on everything all the time! :) I still think that sticking to the documentation and accepting the limitations of the tools used is the right approach in the long term. OTOH telling the compiler that we do something while we actually do something else and misusing its directives is confusing and may backfire at some point. |
|
@vasild I can't speak for others, but I agree with you there is a potential problem. I just don't think this PR is an good solution to the problem due to bigger problems it creates. I think the best solution would be a documentation fix. It'd be helpful if you could take a look at https://reviews.llvm.org/D87629 and see if it resolves your concerns. |
The attribute is meant to model the standard
Technically right, but we'd appreciate if you treat it like a language standard. So if there are discrepancies between documentation and implementation we'd like to know them.
Agreed, conceptually it's independent of the build profile whether a mutex is held in this case. It's just that in some profiles you check that it is, but how you handle assertions (like if you drop them in non-debug builds) doesn't concern the Analysis.
To be clear, Thread Safety annotations have absolutely no effect on the generated code. They are part of the Clang AST, and are considered in the Analysis library, but CodeGen ignores them, so the IR emitted by the Frontend already contains no trace of the attributes. Meaning that whatever the middle end or backend do cannot possibly be influenced by them. No optimization will ever depend on them. |
Use the proper attribute from Clang's thread safety analysis for
AssertLockHeld():if
DEBUG_LOCKORDERis defined thenAssertLockHeld()will check ifthe caller owns the given mutex and will
abort()if not. Clang hasan attribute exactly for that -
ASSERT_EXCLUSIVE_LOCK(), documentedas: "These are attributes on a function or method that does a run-time
test to see whether the calling thread holds the given capability. The
function is assumed to fail (no return) if the capability is not
held." [1]
if
DEBUG_LOCKORDERis not defined, thenAssertLockHeld()doesnothing, thus don't tag it with any attributes (don't fool the
compiler that we do something which we don't).
Replace
LockAssertionwithAssertLockHeldand remove the former.On the places where Clang cannot deduce that a mutex is held, use
NO_THREAD_SAFETY_ANALYSIS, intended to be used when a code is"too complicated for the analysis to understand" [2].
[1] https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#assert-capability-and-assert-shared-capability
[2] https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#no-thread-safety-analysis
PR is compared with alternatives in https://github.com/bitcoin-core/bitcoin-devwiki/wiki/AssertLockHeld-PRs