[PATCH] libstdc++: Enable <thread> without gthreads

Jonathan Wakely jwakely@redhat.com
Wed Nov 11 16:13:40 GMT 2020


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?

-------------- next part --------------
commit 8bd28626e5f9b28192d0e868e922f26d4f40187e
Author: Jonathan Wakely <jwakely@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


More information about the Gcc-patches mailing list