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 <future> do not need to include the whole of <thread> just to be able to create a std::thread. That avoids including <stop_token> and std::jthread where not required. This also means we can use std::thread::id in <stop_token> instead of using the gthread wrappers directly. libstdc++-v3/ChangeLog: * include/Makefile.am: Add new <bits/std_thread.h> header. * include/Makefile.in: Regenerate. * include/std/future: Include new header instead of <thread>. * include/std/stop_token: Include new header instead of <bits/gthr.h>. (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<thread::id>, 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. I'm sending this for consideration, I haven't pushed it. This removes a number of ugly preprocessor checks for _GLIBCXX_HAS_GTHREADS because things like std::this_thread::get_id() and std::this_thread::yield() are always available. The patch is missing changes to the testsuite to remove some (but certainly not all) of the { dg-require-gthreads "" } directives. That hasn't been done yet because it isn't sufficient. The testsuite also filters out any test with the string "thread" in its path (see testsuite/libstdc++-dg/conformance.exp) so that needs to be changed if we want any tests under 30_threads to run for non-gthreads targets. A follow-up patch could make futures and promises available on targets with no gthreads support. For example, to use with coroutines. That needs a little more work, as we'd need a non-gthreads version of __atomic_futex_unsigned. Thoughts?
commit 8bd28626e5f9b28192d0e868e922f26d4f40187e Author: Jonathan Wakely <jwak...@redhat.com> Date: Wed Nov 11 15:43:03 2020 libstdc++: Enable <thread> 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 <future> do not need to include the whole of <thread> just to be able to create a std::thread. That avoids including <stop_token> and std::jthread where not required. This also means we can use std::thread::id in <stop_token> instead of using the gthread wrappers directly. libstdc++-v3/ChangeLog: * include/Makefile.am: Add new <bits/std_thread.h> header. * include/Makefile.in: Regenerate. * include/std/future: Include new header instead of <thread>. * include/std/stop_token: Include new header instead of <bits/gthr.h>. (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<thread::id>, 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 +// <http://www.gnu.org/licenses/>. + +/** @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 <bits/c++config.h> + +#include <tuple> // std::tuple +#include <bits/invoke.h> // std::__invoke +#include <bits/unique_ptr.h> // std::unique_ptr + +#if _GLIBCXX_THREAD_ABI_COMPAT +#include <bits/shared_ptr.h> +#endif + +#ifdef _GLIBCXX_HAS_GTHREADS +# include <bits/gthr.h> +#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<id>; + + 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<class _CharT, class _Traits> + 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<typename _Tp> + using __not_same = __not_<is_same<__remove_cvref_t<_Tp>, thread>>; + + public: + thread() noexcept = default; + + template<typename _Callable, typename... _Args, + typename = _Require<__not_same<_Callable>>> + explicit + thread(_Callable&& __f, _Args&&... __args) + { + static_assert( __is_invocable<typename decay<_Callable>::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<void(*)()>(&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<typename _Callable> + struct _State_impl : public _State + { + _Callable _M_func; + + template<typename... _Args> + _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<typename _Tuple> + struct _Invoker + { + _Tuple _M_t; + + template<typename> + struct __result; + template<typename _Fn, typename... _Args> + struct __result<tuple<_Fn, _Args...>> + : __invoke_result<_Fn, _Args...> + { }; + + template<size_t... _Ind> + 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<tuple_size<_Tuple>::value>::__type; + return _M_invoke(_Indices()); + } + }; + + public: + template<typename... _Tp> + using _Call_wrapper = _Invoker<tuple<typename decay<_Tp>::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 <thread> + + 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 <mutex> // call_once -#include <thread> #include <condition_variable> // __at_thread_exit_elt #include <system_error> #include <atomic> @@ -46,6 +45,7 @@ #include <bits/unique_ptr.h> #include <bits/shared_ptr.h> #include <bits/std_function.h> +#include <bits/std_thread.h> #include <bits/uses_allocator.h> #include <ext/aligned_buffer.h> 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 <atomic> +#include <bits/std_thread.h> -#ifdef _GLIBCXX_HAS_GTHREADS -# define __cpp_lib_jthread 201911L -# include <bits/gthr.h> -# if __has_include(<semaphore>) -# include <semaphore> -# endif +#if __has_include(<semaphore>) +# include <semaphore> #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<value_type> _M_owners{1}; std::atomic<value_type> _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 <chrono> // std::chrono::* -#ifdef _GLIBCXX_USE_NANOSLEEP -# include <cerrno> // errno, EINTR -# include <time.h> // nanosleep -#endif - -#if defined(_GLIBCXX_HAS_GTHREADS) -#include <bits/gthr.h> - -#include <memory> // std::unique_ptr -#include <tuple> // std::tuple - #if __cplusplus > 201703L # include <compare> // std::strong_ordering # include <stop_token> // std::stop_source, std::stop_token, std::nostopstate #endif +#include <bits/std_thread.h> // std::thread, get_id, yield #include <bits/functional_hash.h> // std::hash -#include <bits/invoke.h> // std::__invoke -#endif // _GLIBCXX_HAS_GTHREADS +#ifdef _GLIBCXX_USE_NANOSLEEP +# include <cerrno> // errno, EINTR +# include <time.h> // 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<id>; - - 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<class _CharT, class _Traits> - 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<typename _Tp> - using __not_same = __not_<is_same<__remove_cvref_t<_Tp>, thread>>; - - public: - thread() noexcept = default; - - template<typename _Callable, typename... _Args, - typename = _Require<__not_same<_Callable>>> - explicit - thread(_Callable&& __f, _Args&&... __args) - { - static_assert( __is_invocable<typename decay<_Callable>::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<void(*)()>(&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<typename _Callable> - struct _State_impl : public _State - { - _Callable _M_func; - - template<typename... _Args> - _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<typename _Tuple> - struct _Invoker - { - _Tuple _M_t; - - template<typename> - struct __result; - template<typename _Fn, typename... _Args> - struct __result<tuple<_Fn, _Args...>> - : __invoke_result<_Fn, _Args...> - { }; - - template<size_t... _Ind> - 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<tuple_size<_Tuple>::value>::__type; - return _M_invoke(_Indices()); - } - }; - - public: - template<typename... _Tp> - using _Call_wrapper = _Invoker<tuple<typename decay<_Tp>::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 <bits/std_thread.h> #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<typename _Rep, typename _Period> 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<typename _Clock, typename _Duration> 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 <sys/sysinfo.h> # define _GLIBCXX_NPROCS get_nprocs() @@ -65,12 +64,16 @@ static inline int get_nprocs() #elif defined(_GLIBCXX_USE_SC_NPROC_ONLN) # include <unistd.h> # 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