aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2019-07-01 23:35:27 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2019-07-02 16:43:57 +0300
commitf52b47eea4c5de553d202669f91e6f4f14668592 (patch)
tree1f7920725b5e878883caf570b233a72e42065f1b
parent1e71bf440efb037a7aa2bafd679cf988129fad7b (diff)
Add workaround for data race in libstdc++'s locale(const locale&, Facet*) constructor
-rw-r--r--build2/b.cxx2
-rw-r--r--build2/test/init.cxx10
-rw-r--r--build2/test/init.hxx3
-rw-r--r--build2/test/script/regex.cxx35
-rw-r--r--build2/test/script/regex.hxx7
-rw-r--r--build2/test/script/regex.test.cxx3
-rw-r--r--libbuild2/module.hxx14
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.
//