See ThreadSanitizer, llvm Thread Safety annotations.
Some is possible, do at least something. There are commercial and open-source tools that try to address this problem, but be aware that solutions have costs and blind spots. Static tools often have many false positives and run-time tools often have a significant cost. We hope for better tools. Using multiple tools can catch more problems than a single one.
There are other ways you can mitigate the chance of data races:
- Avoid global data
- Avoid static variables
- More use of value types on the stack (and don't pass pointers around too much)
- More use of immutable data (literals, constexpr, and const)
no enforcement
- Flag calls of member lock() and unlock(). ???
no enforcement
- Detect the acquisition of multiple mutexes. This is undecidable in general, but catching common simple examples (like the one above) is easy.
no enforcement
- Flag calling a virtual function with a non-recursive mutex held
- Flag calling a callback with a non-recursive mutex held
no enforcement
- Ensure that joining_threads don't detach(). After that, the usual lifetime and ownership (for local objects) enforcement applies.
no enforcement
- Flag attempts to pass local variables to a thread that might detach().
no enforcement
Flag uses of std::thread:
- Suggest use of gsl::joining_thread.
- Suggest "exporting ownership" to an enclosing scope if it detaches.
- Seriously warn if it is not obvious whether if joins of detaches.
no enforcement
- Flag detach()
no enforcement
- Flag all waits without conditions.
no enforcement
- Flag all unnamed lock_guards and unique_locks.
core-check: C26441 NO_UNNAMED_GUARDS
- Have strong rules for re-testing in place that covers any change in hardware, operating system, compiler, and libraries.
use unittesting, buildbot/jenkins/travis/... for all your platforms
- Flag volatile T local and member variables; almost certainly you intended to use atomic instead.
no enforcement