mirror of
https://github.com/FairRootGroup/FairMQ.git
synced 2026-06-15 08:17:05 +00:00
test: pre-fill libstdc++ ctype caches before threads exist
- std::ctype<char> caches narrow()/widen() results per character in plain char arrays of the global classic-locale facet, written without synchronization from header-inlined code (locale_facets.h); two threads exercising an uncached character concurrently (e.g. compiling a std::regex in Channel::Validate) constitute a true data race that ThreadSanitizer rightfully reports - the stores are real and unsynchronized, so a tsan-instrumented libstdc++ cannot help here; instead fill the caches before any thread is spawned, which turns every later access into a pure read - warm the lazily-installed num_put/num_get caches used by stream insertion/extraction as well, via a small format/parse round-trip - wire the warm-up into the gtest runner main() and, via a static initializer, into the test device runner
This commit is contained in:
committed by
Dennis Klein
parent
fc69b5e7ae
commit
2bd9a072a9
@@ -32,6 +32,7 @@ endif()
|
||||
add_testhelper(runTestDevice
|
||||
SOURCES
|
||||
helper/runTestDevice.cxx
|
||||
helper/LocaleWarmup.h
|
||||
helper/devices/TestPairLeft.h
|
||||
helper/devices/TestPairRight.h
|
||||
helper/devices/TestPollIn.h
|
||||
|
||||
63
test/helper/LocaleWarmup.h
Normal file
63
test/helper/LocaleWarmup.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2026 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef FAIR_MQ_TEST_LOCALEWARMUP_H
|
||||
#define FAIR_MQ_TEST_LOCALEWARMUP_H
|
||||
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
|
||||
namespace fair::mq::test {
|
||||
|
||||
/// libstdc++'s std::ctype<char> caches narrow()/widen() results per character
|
||||
/// in plain char arrays of the global classic-locale facet -- by design with
|
||||
/// unsynchronized writes, emitted from header-inlined code (locale_facets.h).
|
||||
/// Whenever two threads exercise an uncached character concurrently (e.g. by
|
||||
/// compiling a std::regex), ThreadSanitizer rightfully reports the write as a
|
||||
/// data race. The stores are real and unsynchronized, so instrumenting
|
||||
/// libstdc++ cannot help; instead, fill the caches before any thread exists,
|
||||
/// which turns every later access into a pure read.
|
||||
inline void WarmUpLocaleCaches()
|
||||
{
|
||||
auto const& ct = std::use_facet<std::ctype<char>>(std::locale::classic());
|
||||
for (int c = 1; c < 256; ++c) {
|
||||
auto const ch = static_cast<char>(c);
|
||||
ct.narrow(ch, '\0');
|
||||
ct.widen(ch);
|
||||
}
|
||||
|
||||
// Only the range overload runs _M_narrow_init(), which sets the
|
||||
// all-cached flag (_M_narrow_ok); the single-char loop above fills the
|
||||
// cache without it. (widen() needs no equivalent: the single-char
|
||||
// overload already runs _M_widen_init().)
|
||||
char from[256];
|
||||
char to[256];
|
||||
for (int c = 0; c < 256; ++c) {
|
||||
from[c] = static_cast<char>(c);
|
||||
}
|
||||
ct.narrow(from, from + 256, '?', to);
|
||||
|
||||
// num_put/num_get caches used by stream insertion/extraction
|
||||
std::ostringstream os;
|
||||
os << 4711 << ' ' << 3.14;
|
||||
std::istringstream is("42 3.14");
|
||||
int i = 0;
|
||||
double d = 0.;
|
||||
is >> i >> d;
|
||||
}
|
||||
|
||||
/// For binaries that do not own main(): define a namespace-scope instance to
|
||||
/// run the warm-up during static initialization, before main().
|
||||
struct LocaleWarmup
|
||||
{
|
||||
LocaleWarmup() { WarmUpLocaleCaches(); }
|
||||
};
|
||||
|
||||
} // namespace fair::mq::test
|
||||
|
||||
#endif /* FAIR_MQ_TEST_LOCALEWARMUP_H */
|
||||
@@ -6,6 +6,7 @@
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#include "LocaleWarmup.h"
|
||||
#include "devices/TestPairLeft.h"
|
||||
#include "devices/TestPairRight.h"
|
||||
#include "devices/TestPollIn.h"
|
||||
@@ -30,6 +31,10 @@
|
||||
|
||||
namespace bpo = boost::program_options;
|
||||
|
||||
namespace {
|
||||
[[maybe_unused]] fair::mq::test::LocaleWarmup const gLocaleWarmup{};
|
||||
}
|
||||
|
||||
auto addCustomOptions(bpo::options_description& options) -> void
|
||||
{
|
||||
options.add_options()
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <TestEnvironment.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <helper/LocaleWarmup.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -24,6 +25,7 @@ string runTestDevice = "@RUN_TEST_DEVICE@";
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
fair::mq::test::WarmUpLocaleCaches();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
::testing::FLAGS_gtest_death_test_style = "threadsafe";
|
||||
setenv("FAIRMQ_PATH", FAIRMQ_TEST_ENVIRONMENT, 0);
|
||||
|
||||
Reference in New Issue
Block a user