From cc386cf23346d7cf78493390c4fe1e3ca4f171e6 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Wed, 9 Oct 2019 16:59:56 +0100 Subject: [PATCH] PR libstdc++/91057 set locale::id::_M_index atomically If two threads see _M_index==0 concurrently they will both try to set it, potentially storing the facet at two different indices in the array. Either set the _M_index data member using an atomic compare-exchange operation or while holding a mutex. Also move the LONG_DOUBLE_COMPAT code into a separate function to remove the visual noise it creates. PR libstdc++/91057 * src/c++98/locale.cc (locale::id::_M_id()) [__GTHREADS]: Use atomic compare-exchange or double-checked lock to ensure only one thread sets the _M_index variable. [_GLIBCXX_LONG_DOUBLE_COMPAT]: Call find_ldbl_sync_facet to detect facets that share another facet's ID. [_GLIBCXX_LONG_DOUBLE_COMPAT] (find_ldbl_sync_facet): New function. From-SVN: r276762 --- libstdc++-v3/ChangeLog | 8 ++++ libstdc++-v3/src/c++98/locale.cc | 72 ++++++++++++++++++++++++-------- 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 47f0ce86d96..045fbe3e918 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,5 +1,13 @@ 2019-10-09 Jonathan Wakely + PR libstdc++/91057 + * src/c++98/locale.cc (locale::id::_M_id()) [__GTHREADS]: Use atomic + compare-exchange or double-checked lock to ensure only one thread sets + the _M_index variable. + [_GLIBCXX_LONG_DOUBLE_COMPAT]: Call find_ldbl_sync_facet to detect + facets that share another facet's ID. + [_GLIBCXX_LONG_DOUBLE_COMPAT] (find_ldbl_sync_facet): New function. + PR libstdc++/78552 * src/c++98/locale_init.cc (locale::classic()): Do not construct a new locale object for every call. diff --git a/libstdc++-v3/src/c++98/locale.cc b/libstdc++-v3/src/c++98/locale.cc index 8652f8559c2..1d00edc6f51 100644 --- a/libstdc++-v3/src/c++98/locale.cc +++ b/libstdc++-v3/src/c++98/locale.cc @@ -474,6 +474,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Definitions for static const data members of locale::id _Atomic_word locale::id::_S_refcount; // init'd to 0 by linker + // XXX GLIBCXX_ABI Deprecated +#ifdef _GLIBCXX_LONG_DOUBLE_COMPAT +namespace { + inline locale::id* + find_ldbl_sync_facet(locale::id* __idp) + { +# define _GLIBCXX_SYNC_ID(facet, mangled) \ + if (__idp == &::mangled) \ + return &facet::id + + _GLIBCXX_SYNC_ID (num_get, _ZNSt7num_getIcSt19istreambuf_iteratorIcSt11char_traitsIcEEE2idE); + _GLIBCXX_SYNC_ID (num_put, _ZNSt7num_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE2idE); + _GLIBCXX_SYNC_ID (money_get, _ZNSt9money_getIcSt19istreambuf_iteratorIcSt11char_traitsIcEEE2idE); + _GLIBCXX_SYNC_ID (money_put, _ZNSt9money_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE2idE); +# ifdef _GLIBCXX_USE_WCHAR_T + _GLIBCXX_SYNC_ID (num_get, _ZNSt7num_getIwSt19istreambuf_iteratorIwSt11char_traitsIwEEE2idE); + _GLIBCXX_SYNC_ID (num_put, _ZNSt7num_putIwSt19ostreambuf_iteratorIwSt11char_traitsIwEEE2idE); + _GLIBCXX_SYNC_ID (money_get, _ZNSt9money_getIwSt19istreambuf_iteratorIwSt11char_traitsIwEEE2idE); + _GLIBCXX_SYNC_ID (money_put, _ZNSt9money_putIwSt19ostreambuf_iteratorIwSt11char_traitsIwEEE2idE); +# endif + } +} // namespace +#endif + size_t locale::id::_M_id() const throw() { @@ -481,26 +505,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { // XXX GLIBCXX_ABI Deprecated #ifdef _GLIBCXX_LONG_DOUBLE_COMPAT - locale::id *f = 0; -# define _GLIBCXX_SYNC_ID(facet, mangled) \ - if (this == &::mangled) \ - f = &facet::id - _GLIBCXX_SYNC_ID (num_get, _ZNSt7num_getIcSt19istreambuf_iteratorIcSt11char_traitsIcEEE2idE); - _GLIBCXX_SYNC_ID (num_put, _ZNSt7num_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE2idE); - _GLIBCXX_SYNC_ID (money_get, _ZNSt9money_getIcSt19istreambuf_iteratorIcSt11char_traitsIcEEE2idE); - _GLIBCXX_SYNC_ID (money_put, _ZNSt9money_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE2idE); -# ifdef _GLIBCXX_USE_WCHAR_T - _GLIBCXX_SYNC_ID (num_get, _ZNSt7num_getIwSt19istreambuf_iteratorIwSt11char_traitsIwEEE2idE); - _GLIBCXX_SYNC_ID (num_put, _ZNSt7num_putIwSt19ostreambuf_iteratorIwSt11char_traitsIwEEE2idE); - _GLIBCXX_SYNC_ID (money_get, _ZNSt9money_getIwSt19istreambuf_iteratorIwSt11char_traitsIwEEE2idE); - _GLIBCXX_SYNC_ID (money_put, _ZNSt9money_putIwSt19ostreambuf_iteratorIwSt11char_traitsIwEEE2idE); -# endif - if (f) - _M_index = 1 + f->_M_id(); + if (locale::id* f = find_ldbl_sync_facet(this)) + { + const size_t sync_id = f->_M_id(); + _M_index = 1 + sync_id; + return sync_id; + } +#endif + +#ifdef __GTHREADS + if (__gthread_active_p()) + { + if (__atomic_always_lock_free(sizeof(_M_index), &_M_index)) + { + const _Atomic_word next + = 1 + __gnu_cxx::__exchange_and_add(&_S_refcount, 1); + size_t expected = 0; + __atomic_compare_exchange_n(&_M_index, &expected, next, + /* weak = */ false, + /* success = */ __ATOMIC_ACQ_REL, + /* failure = */ __ATOMIC_ACQUIRE); + } + else + { + static __gnu_cxx::__mutex m; + __gnu_cxx::__scoped_lock l(m); + if (!_M_index) + _M_index = ++_S_refcount; + } + } else #endif - _M_index = 1 + __gnu_cxx::__exchange_and_add_dispatch(&_S_refcount, - 1); + _M_index = ++_S_refcount; // single-threaded case } return _M_index - 1; } -- 2.30.2