commit 8bd28626e5f9b28192d0e868e922f26d4f40187e Author: Jonathan Wakely Date: Wed Nov 11 15:43:03 2020 libstdc++: Enable without gthreads This makes it possible to use std::thread in single-threaded builds. All member functions are available, but attempting to create a new thread will throw an exception. The main benefit for most targets is that other headers such as do not need to include the whole of just to be able to create a std::thread. That avoids including and std::jthread where not required. This also means we can use std::thread::id in instead of using the gthread wrappers directly. libstdc++-v3/ChangeLog: * include/Makefile.am: Add new header. * include/Makefile.in: Regenerate. * include/std/future: Include new header instead of . * include/std/stop_token: Include new header instead of . (stop_token::_S_yield()): Use this_thread::yield(). (_Stop_state_t::_M_requester): Change type to std::thread::id. (_Stop_state_t::_M_request_stop()): Use this_thread::get_id(). (_Stop_state_t::_M_remove_callback(_Stop_cb*)): Likewise. Use __is_single_threaded() to decide whether to synchronize. * include/std/thread (thread, operator==, this_thread::get_id) (this_thread::yield): Move to new header. (operator<=>, operator!=, operator<, operator<=, operator>) (operator>=, hash, operator<<): Define even when gthreads not available. * src/c++11/thread.cc (_GLIBCXX_NPROCS): Define to 0 when gthreads not available. (thread::_State::~_State(), thread::join(), thread::detach()) (thread::_M_start_thread(_State_ptr, void(*)())) (thread::hardware_concurrency()): Define even when gthreads not available. * include/bits/std_thread.h: New file. (thread, operator==, this_thread::get_id, this_thread::yield): Define even when gthreads not available. diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 292d89da8ba7..7979f1c589d6 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -187,6 +187,7 @@ bits_headers = \ ${bits_srcdir}/std_abs.h \ ${bits_srcdir}/std_function.h \ ${bits_srcdir}/std_mutex.h \ + ${bits_srcdir}/std_thread.h \ ${bits_srcdir}/stl_algo.h \ ${bits_srcdir}/stl_algobase.h \ ${bits_srcdir}/stl_bvector.h \ diff --git a/libstdc++-v3/include/bits/std_thread.h b/libstdc++-v3/include/bits/std_thread.h new file mode 100644 index 000000000000..41cc942788a1 --- /dev/null +++ b/libstdc++-v3/include/bits/std_thread.h @@ -0,0 +1,326 @@ +// std::thread declarations -*- C++ -*- + +// Copyright (C) 2008-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. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +/** @file bits/std_thread.h + * This is an internal header file, included by other library headers. + * Do not attempt to use it directly. @headername{thread} + */ + +#ifndef _GLIBCXX_THREAD_H +#define _GLIBCXX_THREAD_H 1 + +#pragma GCC system_header + +#if __cplusplus >= 201103L +#include + +#include // std::tuple +#include // std::__invoke +#include // std::unique_ptr + +#if _GLIBCXX_THREAD_ABI_COMPAT +#include +#endif + +#ifdef _GLIBCXX_HAS_GTHREADS +# include +#endif + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + /** @addtogroup threads + * @{ + */ + + /// thread + class thread + { + public: + // Abstract base class for types that wrap arbitrary functors to be + // invoked in the new thread of execution. + struct _State + { + virtual ~_State(); + virtual void _M_run() = 0; + }; + using _State_ptr = unique_ptr<_State>; + +#ifdef _GLIBCXX_HAS_GTHREADS + using native_handle_type = __gthread_t; +#else + using native_handle_type = int; +#endif + + /// thread::id + class id + { + native_handle_type _M_thread; + + public: + id() noexcept : _M_thread() { } + + explicit + id(native_handle_type __id) : _M_thread(__id) { } + + private: + friend class thread; + friend struct hash; + + friend bool + operator==(id __x, id __y) noexcept; + +#if __cpp_lib_three_way_comparison + friend strong_ordering + operator<=>(id __x, id __y) noexcept; +#else + friend bool + operator<(id __x, id __y) noexcept; +#endif + + template + friend basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __out, id __id); + }; + + private: + id _M_id; + + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 2097. packaged_task constructors should be constrained + // 3039. Unnecessary decay in thread and packaged_task + template + using __not_same = __not_, thread>>; + + public: + thread() noexcept = default; + + template>> + explicit + thread(_Callable&& __f, _Args&&... __args) + { + static_assert( __is_invocable::type, + typename decay<_Args>::type...>::value, + "std::thread arguments must be invocable after conversion to rvalues" + ); + +#if defined _GLIBCXX_HAS_GTHREADS && defined GTHR_ACTIVE_PROXY + // Create a reference to pthread_create, not just the gthr weak symbol. + auto __depend = reinterpret_cast(&pthread_create); +#else + auto __depend = nullptr; +#endif + using _Wrapper = _Call_wrapper<_Callable, _Args...>; + // Create a call wrapper with DECAY_COPY(__f) as its target object + // and DECAY_COPY(__args)... as its bound argument entities. + _M_start_thread(_State_ptr(new _State_impl<_Wrapper>( + std::forward<_Callable>(__f), std::forward<_Args>(__args)...)), + __depend); + } + + ~thread() + { + if (joinable()) + std::terminate(); + } + + thread(const thread&) = delete; + + thread(thread&& __t) noexcept + { swap(__t); } + + thread& operator=(const thread&) = delete; + + thread& operator=(thread&& __t) noexcept + { + if (joinable()) + std::terminate(); + swap(__t); + return *this; + } + + void + swap(thread& __t) noexcept + { std::swap(_M_id, __t._M_id); } + + bool + joinable() const noexcept + { return !(_M_id == id()); } + + void + join(); + + void + detach(); + + id + get_id() const noexcept + { return _M_id; } + + /** @pre thread is joinable + */ + native_handle_type + native_handle() + { return _M_id._M_thread; } + + // Returns a value that hints at the number of hardware thread contexts. + static unsigned int + hardware_concurrency() noexcept; + + private: + template + struct _State_impl : public _State + { + _Callable _M_func; + + template + _State_impl(_Args&&... __args) + : _M_func{{std::forward<_Args>(__args)...}} + { } + + void + _M_run() { _M_func(); } + }; + + void + _M_start_thread(_State_ptr, void (*)()); + +#if _GLIBCXX_THREAD_ABI_COMPAT + public: + struct _Impl_base; + typedef shared_ptr<_Impl_base> __shared_base_type; + struct _Impl_base + { + __shared_base_type _M_this_ptr; + virtual ~_Impl_base() = default; + virtual void _M_run() = 0; + }; + + private: + void + _M_start_thread(__shared_base_type, void (*)()); + + void + _M_start_thread(__shared_base_type); +#endif + + private: + // A call wrapper that does INVOKE(forwarded tuple elements...) + template + struct _Invoker + { + _Tuple _M_t; + + template + struct __result; + template + struct __result> + : __invoke_result<_Fn, _Args...> + { }; + + template + typename __result<_Tuple>::type + _M_invoke(_Index_tuple<_Ind...>) + { return std::__invoke(std::get<_Ind>(std::move(_M_t))...); } + + typename __result<_Tuple>::type + operator()() + { + using _Indices + = typename _Build_index_tuple::value>::__type; + return _M_invoke(_Indices()); + } + }; + + public: + template + using _Call_wrapper = _Invoker::type...>>; + }; + + + inline void + swap(thread& __x, thread& __y) noexcept + { __x.swap(__y); } + + inline bool + operator==(thread::id __x, thread::id __y) noexcept + { + // pthread_equal is undefined if either thread ID is not valid, so we + // can't safely use __gthread_equal on default-constructed values (nor + // the non-zero value returned by this_thread::get_id() for + // single-threaded programs using GNU libc). Assume EqualityComparable. + return __x._M_thread == __y._M_thread; + } + + // N.B. other comparison operators are defined in + + namespace this_thread + { + /// this_thread::get_id + inline thread::id + get_id() noexcept + { +#ifdef _GLIBCXX_HAS_GTHREADS + +#ifdef __GLIBC__ + // For the GNU C library pthread_self() is usable without linking to + // libpthread, but prior to version 2.27 the version in libc returns 0, + // which breaks the invariant this_thread::get_id() != thread::id{}. + // + // We know that pthread_t is a scalar type in the GNU C library, + // so just use (__gthread_t)1 as the ID of the main (and only) thread. + // + // This uses __gthread_active_p not __gnu_cxx::__is_single_threaded + // because we don't want the thread::id of the main thread to change + // if additional threads are created later. + if (!__gthread_active_p()) + return thread::id((__gthread_t)1); +#endif + + return thread::id(__gthread_self()); +#else + return thread::id(1); +#endif + } + + /// this_thread::yield + inline void + yield() noexcept + { +#if defined _GLIBCXX_HAS_GTHREADS && defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + } // namespace this_thread + + /// @} + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // C++11 + +#endif // _GLIBCXX_THREAD_H diff --git a/libstdc++-v3/include/std/future b/libstdc++-v3/include/std/future index 5d948018c75c..a3e1ea2c5e40 100644 --- a/libstdc++-v3/include/std/future +++ b/libstdc++-v3/include/std/future @@ -36,7 +36,6 @@ #else #include // call_once -#include #include // __at_thread_exit_elt #include #include @@ -46,6 +45,7 @@ #include #include #include +#include #include #include diff --git a/libstdc++-v3/include/std/stop_token b/libstdc++-v3/include/std/stop_token index 7cd01c9713ee..5b2d5a0427c7 100644 --- a/libstdc++-v3/include/std/stop_token +++ b/libstdc++-v3/include/std/stop_token @@ -32,15 +32,14 @@ #if __cplusplus > 201703L #include +#include -#ifdef _GLIBCXX_HAS_GTHREADS -# define __cpp_lib_jthread 201911L -# include -# if __has_include() -# include -# endif +#if __has_include() +# include #endif +#define __cpp_lib_jthread 201911L + namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION @@ -105,9 +104,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { #if defined __i386__ || defined __x86_64__ __builtin_ia32_pause(); -#elif defined _GLIBCXX_HAS_GTHREADS && defined _GLIBCXX_USE_SCHED_YIELD - __gthread_yield(); #endif + this_thread::yield(); } #ifndef __cpp_lib_semaphore @@ -162,18 +160,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION std::atomic _M_owners{1}; std::atomic _M_value{_S_ssrc_counter_inc}; _Stop_cb* _M_head = nullptr; - struct - { -#ifdef _GLIBCXX_HAS_GTHREADS - __gthread_t _M_id; - void _M_set() { _M_id = __gthread_self(); } - bool _M_is_current_thread() const - { return __gthread_equal(_M_id, __gthread_self()); } -#else - void _M_set() { } - constexpr bool _M_is_current_thread() const { return true; } -#endif - } _M_requester; + std::thread::id _M_requester; _Stop_state_t() = default; @@ -246,7 +233,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } while (!_M_try_lock_and_stop(__old)); - _M_requester._M_set(); + _M_requester = this_thread::get_id(); while (_M_head) { @@ -273,10 +260,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (!__destroyed) { __cb->_M_destroyed = nullptr; -#ifdef _GLIBCXX_HAS_GTHREADS + // synchronize with destructor of stop_callback that owns *__cb - __cb->_M_done.release(); -#endif + if (!__gnu_cxx::__is_single_threaded()) + __cb->_M_done.release(); } // Avoid relocking if we already know there are no more callbacks. @@ -353,7 +340,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Despite appearances there is no data race on _M_requester. The only // write to it happens before the callback is removed from the list, // and removing it from the list happens before this read. - if (!_M_requester._M_is_current_thread()) + if (!(_M_requester == this_thread::get_id())) { // Synchronize with completion of callback. __cb->_M_done.acquire(); diff --git a/libstdc++-v3/include/std/thread b/libstdc++-v3/include/std/thread index 080036e26097..6ea8a51c0cf8 100644 --- a/libstdc++-v3/include/std/thread +++ b/libstdc++-v3/include/std/thread @@ -37,26 +37,18 @@ #include // std::chrono::* -#ifdef _GLIBCXX_USE_NANOSLEEP -# include // errno, EINTR -# include // nanosleep -#endif - -#if defined(_GLIBCXX_HAS_GTHREADS) -#include - -#include // std::unique_ptr -#include // std::tuple - #if __cplusplus > 201703L # include // std::strong_ordering # include // std::stop_source, std::stop_token, std::nostopstate #endif +#include // std::thread, get_id, yield #include // std::hash -#include // std::__invoke -#endif // _GLIBCXX_HAS_GTHREADS +#ifdef _GLIBCXX_USE_NANOSLEEP +# include // errno, EINTR +# include // nanosleep +#endif namespace std _GLIBCXX_VISIBILITY(default) { @@ -70,221 +62,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @{ */ -#if defined(_GLIBCXX_HAS_GTHREADS) - /// thread - class thread - { - public: - // Abstract base class for types that wrap arbitrary functors to be - // invoked in the new thread of execution. - struct _State - { - virtual ~_State(); - virtual void _M_run() = 0; - }; - using _State_ptr = unique_ptr<_State>; - - typedef __gthread_t native_handle_type; - - /// thread::id - class id - { - native_handle_type _M_thread; - - public: - id() noexcept : _M_thread() { } - - explicit - id(native_handle_type __id) : _M_thread(__id) { } - - private: - friend class thread; - friend struct hash; - - friend bool - operator==(id __x, id __y) noexcept; - -#if __cpp_lib_three_way_comparison - friend strong_ordering - operator<=>(id __x, id __y) noexcept; -#else - friend bool - operator<(id __x, id __y) noexcept; -#endif - - template - friend basic_ostream<_CharT, _Traits>& - operator<<(basic_ostream<_CharT, _Traits>& __out, id __id); - }; - - private: - id _M_id; - - // _GLIBCXX_RESOLVE_LIB_DEFECTS - // 2097. packaged_task constructors should be constrained - // 3039. Unnecessary decay in thread and packaged_task - template - using __not_same = __not_, thread>>; - - public: - thread() noexcept = default; - - template>> - explicit - thread(_Callable&& __f, _Args&&... __args) - { - static_assert( __is_invocable::type, - typename decay<_Args>::type...>::value, - "std::thread arguments must be invocable after conversion to rvalues" - ); - -#ifdef GTHR_ACTIVE_PROXY - // Create a reference to pthread_create, not just the gthr weak symbol. - auto __depend = reinterpret_cast(&pthread_create); -#else - auto __depend = nullptr; -#endif - using _Wrapper = _Call_wrapper<_Callable, _Args...>; - // Create a call wrapper with DECAY_COPY(__f) as its target object - // and DECAY_COPY(__args)... as its bound argument entities. - _M_start_thread(_State_ptr(new _State_impl<_Wrapper>( - std::forward<_Callable>(__f), std::forward<_Args>(__args)...)), - __depend); - } - - ~thread() - { - if (joinable()) - std::terminate(); - } - - thread(const thread&) = delete; - - thread(thread&& __t) noexcept - { swap(__t); } - - thread& operator=(const thread&) = delete; - - thread& operator=(thread&& __t) noexcept - { - if (joinable()) - std::terminate(); - swap(__t); - return *this; - } - - void - swap(thread& __t) noexcept - { std::swap(_M_id, __t._M_id); } - - bool - joinable() const noexcept - { return !(_M_id == id()); } - - void - join(); - - void - detach(); - - id - get_id() const noexcept - { return _M_id; } - - /** @pre thread is joinable - */ - native_handle_type - native_handle() - { return _M_id._M_thread; } - - // Returns a value that hints at the number of hardware thread contexts. - static unsigned int - hardware_concurrency() noexcept; - - private: - template - struct _State_impl : public _State - { - _Callable _M_func; - - template - _State_impl(_Args&&... __args) - : _M_func{{std::forward<_Args>(__args)...}} - { } - - void - _M_run() { _M_func(); } - }; - - void - _M_start_thread(_State_ptr, void (*)()); - -#if _GLIBCXX_THREAD_ABI_COMPAT - public: - struct _Impl_base; - typedef shared_ptr<_Impl_base> __shared_base_type; - struct _Impl_base - { - __shared_base_type _M_this_ptr; - virtual ~_Impl_base() = default; - virtual void _M_run() = 0; - }; - - private: - void - _M_start_thread(__shared_base_type, void (*)()); - - void - _M_start_thread(__shared_base_type); -#endif - - private: - // A call wrapper that does INVOKE(forwarded tuple elements...) - template - struct _Invoker - { - _Tuple _M_t; - - template - struct __result; - template - struct __result> - : __invoke_result<_Fn, _Args...> - { }; - - template - typename __result<_Tuple>::type - _M_invoke(_Index_tuple<_Ind...>) - { return std::__invoke(std::get<_Ind>(std::move(_M_t))...); } - - typename __result<_Tuple>::type - operator()() - { - using _Indices - = typename _Build_index_tuple::value>::__type; - return _M_invoke(_Indices()); - } - }; - - public: - template - using _Call_wrapper = _Invoker::type...>>; - }; - - inline void - swap(thread& __x, thread& __y) noexcept - { __x.swap(__y); } - - inline bool - operator==(thread::id __x, thread::id __y) noexcept - { - // pthread_equal is undefined if either thread ID is not valid, so we - // can't safely use __gthread_equal on default-constructed values (nor - // the non-zero value returned by this_thread::get_id() for - // single-threaded programs using GNU libc). Assume EqualityComparable. - return __x._M_thread == __y._M_thread; - } + // std::thread is defined in #if __cpp_lib_three_way_comparison inline strong_ordering @@ -336,7 +114,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION else return __out << __id._M_thread; } -#endif // _GLIBCXX_HAS_GTHREADS /** @namespace std::this_thread * @brief ISO C++ 2011 namespace for interacting with the current thread @@ -345,36 +122,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION */ namespace this_thread { -#if defined _GLIBCXX_HAS_GTHREADS - /// get_id - inline thread::id - get_id() noexcept - { -#ifdef __GLIBC__ - // For the GNU C library pthread_self() is usable without linking to - // libpthread.so but returns 0, so we cannot use it in single-threaded - // programs, because this_thread::get_id() != thread::id{} must be true. - // We know that pthread_t is an integral type in the GNU C library. - if (!__gthread_active_p()) - return thread::id(1); -#endif - return thread::id(__gthread_self()); - } -#endif // _GLIBCXX_HAS_GTHREADS - - /// yield - inline void - yield() noexcept - { -#if defined _GLIBCXX_HAS_GTHREADS && defined _GLIBCXX_USE_SCHED_YIELD - __gthread_yield(); -#endif - } - void __sleep_for(chrono::seconds, chrono::nanoseconds); - /// sleep_for + /// this_thread::sleep_for template inline void sleep_for(const chrono::duration<_Rep, _Period>& __rtime) @@ -396,7 +147,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif } - /// sleep_until + /// this_thread::sleep_until template inline void sleep_until(const chrono::time_point<_Clock, _Duration>& __atime) @@ -421,6 +172,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #ifdef __cpp_lib_jthread + /// A thread that can be requested to stop and automatically joined. class jthread { public: diff --git a/libstdc++-v3/src/c++11/thread.cc b/libstdc++-v3/src/c++11/thread.cc index a4c87d816a58..43c85305c6e6 100644 --- a/libstdc++-v3/src/c++11/thread.cc +++ b/libstdc++-v3/src/c++11/thread.cc @@ -40,7 +40,6 @@ #endif #ifdef _GLIBCXX_HAS_GTHREADS - #if defined(_GLIBCXX_USE_GET_NPROCS) # include # define _GLIBCXX_NPROCS get_nprocs() @@ -65,12 +64,16 @@ static inline int get_nprocs() #elif defined(_GLIBCXX_USE_SC_NPROC_ONLN) # include # define _GLIBCXX_NPROCS sysconf(_SC_NPROC_ONLN) -#else +#endif +#endif // _GLIBCXX_HAS_GTHREADS + +#ifndef _GLIBCXX_NPROCS # define _GLIBCXX_NPROCS 0 #endif namespace std _GLIBCXX_VISIBILITY(default) { +#ifdef _GLIBCXX_HAS_GTHREADS extern "C" { static void* @@ -96,6 +99,7 @@ namespace std _GLIBCXX_VISIBILITY(default) } #endif } // extern "C" +#endif // _GLIBCXX_HAS_GTHREADS _GLIBCXX_BEGIN_NAMESPACE_VERSION @@ -106,8 +110,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { int __e = EINVAL; +#ifdef _GLIBCXX_HAS_GTHREADS if (_M_id != id()) __e = __gthread_join(_M_id._M_thread, 0); +#endif if (__e) __throw_system_error(__e); @@ -120,8 +126,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { int __e = EINVAL; +#ifdef _GLIBCXX_HAS_GTHREADS if (_M_id != id()) __e = __gthread_detach(_M_id._M_thread); +#endif if (__e) __throw_system_error(__e); @@ -132,15 +140,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void thread::_M_start_thread(_State_ptr state, void (*)()) { +#ifdef _GLIBCXX_HAS_GTHREADS const int err = __gthread_create(&_M_id._M_thread, &execute_native_thread_routine, state.get()); if (err) __throw_system_error(err); state.release(); +#else + __throw_system_error(EINVAL); +#endif } -#if _GLIBCXX_THREAD_ABI_COMPAT +#if defined _GLIBCXX_HAS_GTHREADS && _GLIBCXX_THREAD_ABI_COMPAT void thread::_M_start_thread(__shared_base_type __b) { @@ -169,7 +181,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __throw_system_error(__e); } } -#endif +#endif // defined _GLIBCXX_HAS_GTHREADS && _GLIBCXX_THREAD_ABI_COMPAT unsigned int thread::hardware_concurrency() noexcept @@ -180,14 +192,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __n; } -_GLIBCXX_END_NAMESPACE_VERSION -} // namespace std - -#endif // _GLIBCXX_HAS_GTHREADS - -namespace std _GLIBCXX_VISIBILITY(default) -{ -_GLIBCXX_BEGIN_NAMESPACE_VERSION namespace this_thread { void