Avoid double-checked locking with TSAN in parallel #23185
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Omit the first check of the double-checked locking pattern in
recordException()
in parallel.cpp whenCV_THREAD_SANITIZER
is defined. This should only slowrecordException()
down when the thread sanitizer is used, and avoids the TSAN data race warning.CV_THREAD_SANITIZER
is defined incvdef.h
which is included bycore.hpp
which is included byutility.hpp
which is included byprecomp.hpp
which is included byparallel.cpp
. If this is too indirect,#include "cvdef.h"
can be added inparallel.cpp
.Original report
opencv/modules/core/src/parallel.cpp
Lines 266 to 291 in 52855a3
Clang's thread sanitizer found the following when running an internal fuzz target:
I believe it is a false positive in this case. As long as
pException
is not accessed until all threads are joined (infinalize()
only), I do not see how the lack of order guarantee in the body of the second!hasException
condition is an issue (unlike the anti-pattern DCLP singleton initialization).In other words, I think the code at head works as intended as long as C++ guarantees the following, and I guess it does:
if (!hasException)
is not optimized out by the firstif (!hasException)
std::lock_guard
happens before the secondif (!hasException)
if (!hasException)
happens beforehasException = true;
if (!hasException)
happens beforeexception = std::current_exception();
TSAN may be triggered because it detects a read during a write, or maybe it sees a branching on the first
if (!hasException)
that differs depending on the run. But the data race here has no effect: it is the purpose of the double-checked locking pattern.Alternatives
The current solution is simple but I considered other options. Feel free to pick any or suggest another.
recordException()
is not performance-critical.std::call_once()
:Related
Pull Request Readiness Checklist
See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request