diff options
author | Karen Arutyunov <karen@codesynthesis.com> | 2019-07-01 23:35:27 +0300 |
---|---|---|
committer | Karen Arutyunov <karen@codesynthesis.com> | 2019-07-02 16:43:57 +0300 |
commit | f52b47eea4c5de553d202669f91e6f4f14668592 (patch) | |
tree | 1f7920725b5e878883caf570b233a72e42065f1b | |
parent | 1e71bf440efb037a7aa2bafd679cf988129fad7b (diff) |
Add workaround for data race in libstdc++'s locale(const locale&, Facet*) constructor
-rw-r--r-- | build2/b.cxx | 2 | ||||
-rw-r--r-- | build2/test/init.cxx | 10 | ||||
-rw-r--r-- | build2/test/init.hxx | 3 | ||||
-rw-r--r-- | build2/test/script/regex.cxx | 35 | ||||
-rw-r--r-- | build2/test/script/regex.hxx | 7 | ||||
-rw-r--r-- | build2/test/script/regex.test.cxx | 3 | ||||
-rw-r--r-- | libbuild2/module.hxx | 14 |
7 files changed, 66 insertions, 8 deletions
diff --git a/build2/b.cxx b/build2/b.cxx index 5ea4f02..4a446ac 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -445,7 +445,7 @@ main (int argc, char* argv[]) config_preprocess_create = &config::preprocess_create; bm["dist"] = mf {&dist::boot, &dist::init}; - bm["test"] = mf {&test::boot, &test::init}; + bm["test"] = test::build2_test_load (); bm["install"] = mf {&install::boot, &install::init}; bm["version"] = mf {&version::boot, &version::init}; diff --git a/build2/test/init.cxx b/build2/test/init.cxx index 725d557..1f5a3ae 100644 --- a/build2/test/init.cxx +++ b/build2/test/init.cxx @@ -15,6 +15,8 @@ #include <build2/test/target.hxx> #include <build2/test/operation.hxx> +#include <build2/test/script/regex.hxx> // script::regex::init() + using namespace std; using namespace butl; @@ -217,5 +219,13 @@ namespace build2 return true; } + + module_functions + build2_test_load () + { + script::regex::init (); + + return module_functions {&boot, &init}; + } } } diff --git a/build2/test/init.hxx b/build2/test/init.hxx index f429645..5272a4d 100644 --- a/build2/test/init.hxx +++ b/build2/test/init.hxx @@ -25,6 +25,9 @@ namespace build2 bool, bool, const variable_map&); + + extern "C" module_functions + build2_test_load (); } } diff --git a/build2/test/script/regex.cxx b/build2/test/script/regex.cxx index 6b15266..bbd1738 100644 --- a/build2/test/script/regex.cxx +++ b/build2/test/script/regex.cxx @@ -2,6 +2,8 @@ // copyright : Copyright (c) 2014-2019 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file +#include <locale> + #include <build2/test/script/regex.hxx> using namespace std; @@ -163,11 +165,40 @@ namespace build2 // line_char_locale // + + // An exemplar locale with the std::ctype<line_char> facet. It is + // used for the subsequent line char locale objects creation (see + // below) which normally ends up with a shallow copy of a reference- + // counted object. + // + // Note that creating the line char locales from the exemplar is not + // merely an optimization: there is a data race in the libstdc++ (at + // least as of GCC 9.1) implementation of the locale(const locale&, + // Facet*) constructor (bug #91057). + // + // Also note that we install the facet in init() rather than during + // the object creation to avoid a race with the std::locale-related + // global variables initialization. + // + static locale line_char_locale_exemplar; + + void + init () + { + line_char_locale_exemplar = + locale (locale (), + new std::ctype<line_char> ()); // Hidden by ctype bitmask. + } + line_char_locale:: line_char_locale () - : locale (locale (), - new std::ctype<line_char> ()) // Hidden by ctype bitmask. + : locale (line_char_locale_exemplar) { + // Make sure init() has been called. + // + // Note: has_facet() is hidden by a private function in libc++. + // + assert (std::has_facet<std::ctype<line_char>> (*this)); } // char_regex diff --git a/build2/test/script/regex.hxx b/build2/test/script/regex.hxx index 500c21b..33a4cba 100644 --- a/build2/test/script/regex.hxx +++ b/build2/test/script/regex.hxx @@ -344,6 +344,13 @@ namespace build2 // line_char_locale (); }; + + // Initialize the testscript regex global state. Should be called once + // prior to creating objects of types from this namespace. Note: not + // thread-safe. + // + void + init (); } } } diff --git a/build2/test/script/regex.test.cxx b/build2/test/script/regex.test.cxx index 7b89e4d..1e48f97 100644 --- a/build2/test/script/regex.test.cxx +++ b/build2/test/script/regex.test.cxx @@ -19,6 +19,8 @@ main () using cf = char_flags; using cr = char_regex; + init (); // Initializes the testscript regex global state. + // Test line_char. // { @@ -183,7 +185,6 @@ main () using ct = ctype<lc>; line_char_locale l; - assert (has_facet<ct> (l)); // It is better not to create q facet on stack as it is // reference-countable. diff --git a/libbuild2/module.hxx b/libbuild2/module.hxx index 5fbed9c..7d94837 100644 --- a/libbuild2/module.hxx +++ b/libbuild2/module.hxx @@ -56,12 +56,18 @@ namespace build2 module_init_function* init; }; - // The register() function will be written in C++ and will be called from - // C++ but we need to suppress name mangling to be able to use dlsym() and - // equivalent. + // The build2_<modname>_load() function will be written in C++ and will be + // called from C++ but we need to suppress name mangling to be able to use + // dlsym() or equivalent. + // + // Note that the load() function is guaranteed to be called during serial + // execution (either from main() or during the load phase). + // + // @@ I wonder if returning a "C++ struct" (it contains pointer to functions + // with signatures containing C++ type) is kosher. // extern "C" - using module_register_function = module_functions (); + using module_load_function = module_functions (); // Loaded modules state. // |