PR libstdc++/91057 set locale::id::_M_index atomically
authorJonathan Wakely <jwakely@redhat.com>
Wed, 9 Oct 2019 15:59:56 +0000 (16:59 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Wed, 9 Oct 2019 15:59:56 +0000 (16:59 +0100)
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
libstdc++-v3/src/c++98/locale.cc

index 47f0ce86d968784b52e6f6780bae8ff4cad719a7..045fbe3e9184cb48149daf6da8a1bffdb5cfca5a 100644 (file)
@@ -1,5 +1,13 @@
 2019-10-09  Jonathan Wakely  <jwakely@redhat.com>
 
+       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.
index 8652f8559c2af0efccc28a0ecc52aeaf71e8d71b..1d00edc6f5143d1bacc3654251c3f89c20ba99b9 100644 (file)
@@ -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<char>, _ZNSt7num_getIcSt19istreambuf_iteratorIcSt11char_traitsIcEEE2idE);
+    _GLIBCXX_SYNC_ID (num_put<char>, _ZNSt7num_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE2idE);
+    _GLIBCXX_SYNC_ID (money_get<char>, _ZNSt9money_getIcSt19istreambuf_iteratorIcSt11char_traitsIcEEE2idE);
+    _GLIBCXX_SYNC_ID (money_put<char>, _ZNSt9money_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE2idE);
+# ifdef _GLIBCXX_USE_WCHAR_T
+    _GLIBCXX_SYNC_ID (num_get<wchar_t>, _ZNSt7num_getIwSt19istreambuf_iteratorIwSt11char_traitsIwEEE2idE);
+    _GLIBCXX_SYNC_ID (num_put<wchar_t>, _ZNSt7num_putIwSt19ostreambuf_iteratorIwSt11char_traitsIwEEE2idE);
+    _GLIBCXX_SYNC_ID (money_get<wchar_t>, _ZNSt9money_getIwSt19istreambuf_iteratorIwSt11char_traitsIwEEE2idE);
+    _GLIBCXX_SYNC_ID (money_put<wchar_t>, _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<char>, _ZNSt7num_getIcSt19istreambuf_iteratorIcSt11char_traitsIcEEE2idE);
-       _GLIBCXX_SYNC_ID (num_put<char>, _ZNSt7num_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE2idE);
-       _GLIBCXX_SYNC_ID (money_get<char>, _ZNSt9money_getIcSt19istreambuf_iteratorIcSt11char_traitsIcEEE2idE);
-       _GLIBCXX_SYNC_ID (money_put<char>, _ZNSt9money_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE2idE);
-# ifdef _GLIBCXX_USE_WCHAR_T
-       _GLIBCXX_SYNC_ID (num_get<wchar_t>, _ZNSt7num_getIwSt19istreambuf_iteratorIwSt11char_traitsIwEEE2idE);
-       _GLIBCXX_SYNC_ID (num_put<wchar_t>, _ZNSt7num_putIwSt19ostreambuf_iteratorIwSt11char_traitsIwEEE2idE);
-       _GLIBCXX_SYNC_ID (money_get<wchar_t>, _ZNSt9money_getIwSt19istreambuf_iteratorIwSt11char_traitsIwEEE2idE);
-       _GLIBCXX_SYNC_ID (money_put<wchar_t>, _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;
   }