diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver index 4dddfd3d2631..707539a17c3a 100644 --- a/libstdc++-v3/config/abi/pre/gnu.ver +++ b/libstdc++-v3/config/abi/pre/gnu.ver @@ -2371,6 +2371,11 @@ GLIBCXX_3.4.29 { # basic_stringstream::view() _ZNKSt7__cxx1118basic_stringstreamI[cw]St11char_traitsI[cw]ESaI[cw]EE4viewEv; + # std::once_flag::_M_activate() + _ZNSt9once_flag11_M_activateEv; + # std::once_flag::_M_finish(bool) + _ZNSt9once_flag9_M_finishEb; + } GLIBCXX_3.4.28; # Symbols in the support library (libsupc++) have their own tag. diff --git a/libstdc++-v3/include/std/mutex b/libstdc++-v3/include/std/mutex index 12b7e548d179..92ad7b7b6622 100644 --- a/libstdc++-v3/include/std/mutex +++ b/libstdc++-v3/include/std/mutex @@ -46,8 +46,10 @@ # include <condition_variable> # include <thread> #endif -#ifndef _GLIBCXX_HAVE_TLS -# include <bits/std_function.h> +#include <ext/atomicity.h> // __gnu_cxx::__is_single_threaded + +#if defined _GLIBCXX_HAS_GTHREADS && ! defined _GLIBCXX_HAVE_TLS +# include <bits/std_function.h> // std::function #endif namespace std _GLIBCXX_VISIBILITY(default) @@ -667,16 +669,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION }; #endif // C++17 -#ifdef _GLIBCXX_HAS_GTHREADS /// Flag type used by std::call_once struct once_flag { - private: - typedef __gthread_once_t __native_type; - __native_type _M_once = __GTHREAD_ONCE_INIT; - - public: - /// Constructor constexpr once_flag() noexcept = default; /// Deleted copy constructor @@ -684,16 +679,105 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// Deleted assignment operator once_flag& operator=(const once_flag&) = delete; + private: + // There are two different std::once_flag interfaces, abstracting four + // different implementations. + // The preferred interface uses the _M_activate() and _M_finish(bool) + // member functions (introduced in GCC 11), which start and finish an + // active execution respectively. See [thread.once.callonce] in C++11 + // for the definition of active/passive/returning/exceptional executions. + // This interface is supported for Linux (using atomics and futexes) and + // for single-threaded targets with no gthreads support. + // For other targets a pthread_once_t is used with pthread_once, but that + // doesn't work correctly for exceptional executions. That interface + // uses an object of type _Prepare_execution and a lambda expression. +#if defined _GLIBCXX_HAVE_LINUX_FUTEX || ! defined _GLIBCXX_HAS_GTHREADS + enum _Bits : int { _Init = 0, _Active = 1, _Done = 2 }; + + int _M_once = _Bits::_Init; + + // Non-blocking check to see if all executions will be passive now. + bool + _M_passive() const noexcept; + + // Attempts to begin an active execution. Blocks until it either: + // - returns true if an active execution has started on this thread, or + // - returns false if a returning execution happens on another thread. + bool _M_activate(); + + // Must be called to complete an active execution. + void _M_finish(bool __returning) noexcept; + + // RAII helper to call _M_finish. + struct _Active_execution + { + explicit _Active_execution(once_flag& __flag) : _M_flag(__flag) { } + + ~_Active_execution() { _M_flag._M_finish(_M_returning); } + + _Active_execution(const _Active_execution&) = delete; + _Active_execution& operator=(const _Active_execution&) = delete; + + once_flag& _M_flag; + bool _M_returning = false; + }; +#else + __gthread_once_t _M_once = __GTHREAD_ONCE_INIT; + + struct _Prepare_execution; +#endif // ! GTHREADS + template<typename _Callable, typename... _Args> friend void call_once(once_flag& __once, _Callable&& __f, _Args&&... __args); }; +#if ! defined _GLIBCXX_HAS_GTHREADS + // Inline definitions of std::once_flag members for single-threaded targets. + + inline bool + once_flag::_M_passive() const noexcept + { return _M_once == _Bits::_Done; } + + inline bool + once_flag::_M_activate() + { + if (_M_once == _Bits::_Init) + { + _M_once = _Bits::_Active; + return true; + } + else if (!_M_passive()) + __throw_system_error(EDEADLK); + } + + inline void + once_flag::_M_finish(bool returning) noexcept + { _M_once = returning ? _Bits::_Done : _Bits::_Init; } + +#elif defined _GLIBCXX_HAVE_LINUX_FUTEX + + // Define this inline to make passive executions fast. + inline bool + once_flag::_M_passive() const noexcept + { + if (__gnu_cxx::__is_single_threaded()) + return _M_once == _Bits::_Done; + else + return __atomic_load_n(&_M_once, __ATOMIC_ACQUIRE) == _Bits::_Done; + } + +#else // GTHREADS && ! FUTEX + /// @cond undocumented -#ifdef _GLIBCXX_HAVE_TLS +# ifdef _GLIBCXX_HAVE_TLS + // If TLS is available use thread-local state for the type-erased callable + // that is being run by std::call_once in the current thread. extern __thread void* __once_callable; extern __thread void (*__once_call)(); -#else +# else + // Without TLS use a global std::mutex and store the callable in a + // global std::function. extern function<void()> __once_functor; extern void @@ -701,48 +785,92 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION extern mutex& __get_once_mutex(); -#endif +# endif + // This function is passed to pthread_once by std::call_once. + // It runs __once_call() or __once_functor(). extern "C" void __once_proxy(void); + + // RAII type to set up state for pthread_once call. + struct once_flag::_Prepare_execution + { +#ifdef _GLIBCXX_HAVE_TLS + template<typename _Callable> + explicit + _Prepare_execution(_Callable& __c) + { + // Store address in thread-local pointer: + __once_callable = std::__addressof(__c); + // Trampoline function to invoke the closure via thread-local pointer: + __once_call = [] { (*static_cast<_Callable*>(__once_callable))(); }; + } + + ~_Prepare_execution() + { + // PR libstdc++/82481 + __once_callable = nullptr; + __once_call = nullptr; + } +#else // ! TLS + template<typename _Callable> + explicit + _Prepare_execution(_Callable& __c) + { + // Store the callable in the global std::function + __once_functor = __c; + __set_once_functor_lock_ptr(&_M_functor_lock); + } + + ~_Prepare_execution() + { + if (_M_functor_lock) + __set_once_functor_lock_ptr(nullptr); + } + + private: + unique_lock<mutex> _M_functor_lock{__get_once_mutex()}; +#endif // ! TLS + + _Prepare_execution(const _Prepare_execution&) = delete; + _Prepare_execution& operator=(const _Prepare_execution&) = delete; + }; /// @endcond +#endif /// Invoke a callable and synchronize with other calls using the same flag template<typename _Callable, typename... _Args> void call_once(once_flag& __once, _Callable&& __f, _Args&&... __args) { - // _GLIBCXX_RESOLVE_LIB_DEFECTS - // 2442. call_once() shouldn't DECAY_COPY() +#if defined _GLIBCXX_HAVE_LINUX_FUTEX || ! defined _GLIBCXX_HAS_GTHREADS + if (__once._M_passive()) + return; + else if (__once._M_activate()) + { + once_flag::_Active_execution __exec(__once); + + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 2442. call_once() shouldn't DECAY_COPY() + std::__invoke(std::forward<_Callable>(__f), + std::forward<_Args>(__args)...); + + // __f(__args...) did not throw + __exec._M_returning = true; + } +#else + // Closure type that runs the function auto __callable = [&] { std::__invoke(std::forward<_Callable>(__f), std::forward<_Args>(__args)...); }; -#ifdef _GLIBCXX_HAVE_TLS - __once_callable = std::__addressof(__callable); - __once_call = []{ (*(decltype(__callable)*)__once_callable)(); }; -#else - unique_lock<mutex> __functor_lock(__get_once_mutex()); - __once_functor = __callable; - __set_once_functor_lock_ptr(&__functor_lock); -#endif - int __e = __gthread_once(&__once._M_once, &__once_proxy); + once_flag::_Prepare_execution __exec(__callable); -#ifndef _GLIBCXX_HAVE_TLS - if (__functor_lock) - __set_once_functor_lock_ptr(0); -#endif - -#ifdef __clang_analyzer__ - // PR libstdc++/82481 - __once_callable = nullptr; - __once_call = nullptr; -#endif - - if (__e) + // XXX pthread_once does not reset the flag if an exception is thrown. + if (int __e = __gthread_once(&__once._M_once, &__once_proxy)) __throw_system_error(__e); +#endif } -#endif // _GLIBCXX_HAS_GTHREADS // @} group mutexes _GLIBCXX_END_NAMESPACE_VERSION diff --git a/libstdc++-v3/src/c++11/mutex.cc b/libstdc++-v3/src/c++11/mutex.cc index e24502832d42..286f77f9a454 100644 --- a/libstdc++-v3/src/c++11/mutex.cc +++ b/libstdc++-v3/src/c++11/mutex.cc @@ -25,6 +25,65 @@ #include <mutex> #ifdef _GLIBCXX_HAS_GTHREADS + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <syscall.h> +#include <unistd.h> +#include <limits.h> + +bool +std::once_flag::_M_activate() +{ + if (__gnu_cxx::__is_single_threaded()) + { + if (_M_once == _Bits::_Done) + return false; + _M_once = _Bits::_Active; + return true; + } + + while (true) + { + int expected = _Bits::_Init; + constexpr int active = _Bits::_Active; + if (__atomic_compare_exchange_n(&_M_once, &expected, active, false, + __ATOMIC_ACQ_REL, + __ATOMIC_ACQUIRE)) + { + // This thread is now doing an active execution. + return true; + } + + if (expected == _Bits::_Done) + return false; // A returning execution happened, this is passive. + + // Otherwise, an active execution is happening. Wait for it to finish. + constexpr int futex_wait = 128; // FUTEX_WAIT_PRIVATE + syscall (SYS_futex, &_M_once, futex_wait, expected, 0); + } +} + +void +std::once_flag::_M_finish(bool returning) noexcept +{ + const int newval = returning ? _Bits::_Done : _Bits::_Init; + if (__gnu_cxx::__is_single_threaded()) + { + __glibcxx_assert(_M_once == _Bits::_Active); + _M_once = newval; + } + else + { + int prev = __atomic_exchange_n(&_M_once, newval, __ATOMIC_RELEASE); + __glibcxx_assert(prev & _Bits::_Active); + // Wake any other threads waiting for this execution to finish. + constexpr int futex_wake = 129; // FUTEX_WAKE_PRIVATE + syscall (SYS_futex, &_M_once, futex_wake, INT_MAX); + } +} + +#endif // ! FUTEX + #ifndef _GLIBCXX_HAVE_TLS namespace { diff --git a/libstdc++-v3/testsuite/30_threads/call_once/39909.cc b/libstdc++-v3/testsuite/30_threads/call_once/39909.cc index 1f35d7f8b9a1..56568ebd154a 100644 --- a/libstdc++-v3/testsuite/30_threads/call_once/39909.cc +++ b/libstdc++-v3/testsuite/30_threads/call_once/39909.cc @@ -1,6 +1,5 @@ -// { dg-do run } +// { dg-do run { target c++11 } } // { dg-additional-options "-pthread" { target pthread } } -// { dg-require-effective-target c++11 } // { dg-require-gthreads "" } // Copyright (C) 2009-2020 Free Software Foundation, Inc. diff --git a/libstdc++-v3/testsuite/30_threads/call_once/49668.cc b/libstdc++-v3/testsuite/30_threads/call_once/49668.cc index 7eb5426a822a..8fab7c225861 100644 --- a/libstdc++-v3/testsuite/30_threads/call_once/49668.cc +++ b/libstdc++-v3/testsuite/30_threads/call_once/49668.cc @@ -1,7 +1,5 @@ -// { dg-do run } +// { dg-do run { target c++11 } } // { dg-additional-options "-pthread" { target pthread } } -// { dg-require-effective-target c++11 } -// { dg-require-gthreads "" } // Copyright (C) 2011-2020 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/30_threads/call_once/60497.cc b/libstdc++-v3/testsuite/30_threads/call_once/60497.cc index 9955a9eebed3..a2c7567b5059 100644 --- a/libstdc++-v3/testsuite/30_threads/call_once/60497.cc +++ b/libstdc++-v3/testsuite/30_threads/call_once/60497.cc @@ -1,7 +1,5 @@ -// { dg-do compile } +// { dg-do compile { target c++11 } } // { dg-additional-options "-pthread" { target pthread } } -// { dg-require-effective-target c++11 } -// { dg-require-gthreads "" } // Copyright (C) 2014-2020 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/30_threads/call_once/constexpr.cc b/libstdc++-v3/testsuite/30_threads/call_once/66146.cc similarity index 54% rename from libstdc++-v3/testsuite/30_threads/call_once/constexpr.cc rename to libstdc++-v3/testsuite/30_threads/call_once/66146.cc index 2643f6c97424..b1ca0eb6fe8f 100644 --- a/libstdc++-v3/testsuite/30_threads/call_once/constexpr.cc +++ b/libstdc++-v3/testsuite/30_threads/call_once/66146.cc @@ -1,7 +1,4 @@ -// { dg-do compile { target c++11 } } -// { dg-require-gthreads "" } - -// Copyright (C) 2010-2020 Free Software Foundation, Inc. +// Copyright (C) 2020 Free Software Foundation, Inc. // // This file is part of the GNU ISO C++ Library. This library is free // software; you can redistribute it and/or modify it under the @@ -18,12 +15,37 @@ // with this library; see the file COPYING3. If not see // <http://www.gnu.org/licenses/>. -#include <mutex> -#include <testsuite_common_types.h> +// { dg-do run { target c++11 } } +// { dg-skip-if "" { pthread && { ! *-*-*linux* } } } +// { dg-additional-options "-pthread" { target pthread } } -int main() +#include <mutex> +#include <cstdlib> +#include <testsuite_hooks.h> + +void +test01() { - __gnu_test::constexpr_default_constructible test; - test.operator()<std::mutex>(); - return 0; + std::once_flag once; + int counter = 0; + for (int i = 0; i < 10; ++i) + { + try + { + std::call_once(once, [&]{ if (i < 3) throw i; ++counter; }); + VERIFY(i >= 3); + } + catch (int ex) + { + VERIFY(i < 3); + } + } + VERIFY(counter == 1); + std::call_once(once, []{ std::abort(); }); +} + +int +main() +{ + test01(); } diff --git a/libstdc++-v3/testsuite/30_threads/call_once/call_once1.cc b/libstdc++-v3/testsuite/30_threads/call_once/call_once1.cc index 26cfa576e811..d9b6729f6b6b 100644 --- a/libstdc++-v3/testsuite/30_threads/call_once/call_once1.cc +++ b/libstdc++-v3/testsuite/30_threads/call_once/call_once1.cc @@ -1,7 +1,5 @@ -// { dg-do run } +// { dg-do run { target c++11 } } // { dg-additional-options "-pthread" { target pthread } } -// { dg-require-effective-target c++11 } -// { dg-require-gthreads "" } // Copyright (C) 2008-2020 Free Software Foundation, Inc. // @@ -35,7 +33,7 @@ void add_to_value(int i) int main() { - try + try { std::call_once(value_flag, add_to_value, 2); std::call_once(value_flag, add_to_value, 2); diff --git a/libstdc++-v3/testsuite/30_threads/call_once/dr2442.cc b/libstdc++-v3/testsuite/30_threads/call_once/dr2442.cc index 289471015199..61f5cc09ba84 100644 --- a/libstdc++-v3/testsuite/30_threads/call_once/dr2442.cc +++ b/libstdc++-v3/testsuite/30_threads/call_once/dr2442.cc @@ -1,7 +1,5 @@ -// { dg-do run } +// { dg-do run { target c++11 } } // { dg-additional-options "-pthread" { target pthread } } -// { dg-require-effective-target c++11 } -// { dg-require-gthreads "" } // Copyright (C) 2016-2020 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/30_threads/call_once/once_flag.cc b/libstdc++-v3/testsuite/30_threads/call_once/once_flag.cc index 5be04349c03d..f6e1607276f4 100644 --- a/libstdc++-v3/testsuite/30_threads/call_once/once_flag.cc +++ b/libstdc++-v3/testsuite/30_threads/call_once/once_flag.cc @@ -1,5 +1,4 @@ // { dg-do compile { target c++11 } } -// { dg-require-gthreads "" } // Copyright (C) 2008-2020 Free Software Foundation, Inc. // @@ -20,8 +19,17 @@ #include <mutex> +#include <testsuite_common_types.h> void test01() { + static_assert( std::is_default_constructible<std::once_flag>::value, ""); + std::once_flag once_flag; } + +void test02() +{ + __gnu_test::constexpr_default_constructible test; + test.operator()<std::once_flag>(); +} diff --git a/libstdc++-v3/testsuite/30_threads/once_flag/cons/constexpr.cc b/libstdc++-v3/testsuite/30_threads/once_flag/cons/constexpr.cc deleted file mode 100644 index a356e8c58bc2..000000000000 --- a/libstdc++-v3/testsuite/30_threads/once_flag/cons/constexpr.cc +++ /dev/null @@ -1,29 +0,0 @@ -// { dg-do compile { target c++11 } } -// { dg-require-gthreads "" } - -// Copyright (C) 2010-2020 Free Software Foundation, Inc. -// -// This file is part of the GNU ISO C++ Library. This library is free -// software; you can redistribute it and/or modify it under the -// terms of the GNU General Public License as published by the -// Free Software Foundation; either version 3, or (at your option) -// any later version. - -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License along -// with this library; see the file COPYING3. If not see -// <http://www.gnu.org/licenses/>. - -#include <mutex> -#include <testsuite_common_types.h> - -int main() -{ - __gnu_test::constexpr_default_constructible test; - test.operator()<std::once_flag>(); - return 0; -}