public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] libstdc++: Enable <thread> without gthreads
@ 2020-11-11 16:13 Jonathan Wakely
  2020-11-11 17:31 ` Jonathan Wakely
  0 siblings, 1 reply; 8+ messages in thread
From: Jonathan Wakely @ 2020-11-11 16:13 UTC (permalink / raw)
  To: libstdc++, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 2627 bytes --]

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?


[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 25559 bytes --]

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

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] libstdc++: Enable <thread> without gthreads
  2020-11-11 16:13 [PATCH] libstdc++: Enable <thread> without gthreads Jonathan Wakely
@ 2020-11-11 17:31 ` Jonathan Wakely
  2020-11-12 17:07   ` Jonathan Wakely
  0 siblings, 1 reply; 8+ messages in thread
From: Jonathan Wakely @ 2020-11-11 17:31 UTC (permalink / raw)
  To: libstdc++, gcc-patches

On 11/11/20 16:13 +0000, Jonathan Wakely wrote:
>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.

I forgot to mention that this patch also reduces the size of the
<thread> header, by only including <bits/unique_ptr.h> instead of the
whole of <memory>. That could be done separately from the rest of the
changes here.

It would be possible to split std::thread and this_thread::get_id()
into a new header without also making them work without gthreads.

That would still reduce the size of the <future> header, because it
wouldn't need the whole of <thread>. But it wouldn't get rid of
preprocessor checks for _GLIBCXX_HAS_GTHREADS in <stop_token>.

Allowing std::this_thread::get_id() and std::this_thread::yield() to
work without threads seems worth doing (we already make
std::this_thread::sleep_until and std::this_thread::sleep_for work
without threads).


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] libstdc++: Enable <thread> without gthreads
  2020-11-11 17:31 ` Jonathan Wakely
@ 2020-11-12 17:07   ` Jonathan Wakely
  2020-11-16 22:43     ` Thomas Rodgers
  2020-11-19 18:25     ` Tom Tromey
  0 siblings, 2 replies; 8+ messages in thread
From: Jonathan Wakely @ 2020-11-12 17:07 UTC (permalink / raw)
  To: libstdc++, gcc-patches; +Cc: tom

[-- Attachment #1: Type: text/plain, Size: 2812 bytes --]

On 11/11/20 17:31 +0000, Jonathan Wakely wrote:
>On 11/11/20 16:13 +0000, Jonathan Wakely wrote:
>>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.
>
>I forgot to mention that this patch also reduces the size of the
><thread> header, by only including <bits/unique_ptr.h> instead of the
>whole of <memory>. That could be done separately from the rest of the
>changes here.
>
>It would be possible to split std::thread and this_thread::get_id()
>into a new header without also making them work without gthreads.
>
>That would still reduce the size of the <future> header, because it
>wouldn't need the whole of <thread>. But it wouldn't get rid of
>preprocessor checks for _GLIBCXX_HAS_GTHREADS in <stop_token>.
>
>Allowing std::this_thread::get_id() and std::this_thread::yield() to
>work without threads seems worth doing (we already make
>std::this_thread::sleep_until and std::this_thread::sleep_for work
>without threads).

Here's a slightly more conservative version of the patch. This moves
std::thread and this_thread::get_id() and this_thread::yield() to a
new header, and makes *most* of std::thread defined without gthreads
(because we need the nested thread::id type to be returned from
this_thread::get_id()). But it doesn't declare the std::thread
constructor that creates new threads.

That means std::thread is present, but you can't even try to create
new threads. This means we don't need to export the std::thread
symbols from libstdc++.so for a target where they are unusable and
just throw an exception.

This still has the main benefits of making <future> include a lot less
code, and removing some #if conditions in <stop_token>.

One other change from the previous patch worth mentioning is that I've
made <bits/std_thread.h> include <bits/refwrap.h> so that
std::reference_wrapper (and std::ref and std::cref) are defined by
<thread>. That isn't required, but it is a tiny header and being able
to use std::ref to pass lvalues to new threads without including
all of <functional> seems like a kindness to users.

Both this and the previous patch require some GDB changes, because GDB
currently assumes that if std::thread is declared in <thread> that it
is usable and multiple threads are supported. That's no longer true,
because we would declare a useless std::thread after this patch. Tom
Tromey has patches to make GDB handle this though.

Tested powerpc64le-linux, --enable-threads and --disable-threads.

Thoughts?



[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 24667 bytes --]

commit 68a99d44890957d6c5b128116a6af6bb4bcfaad3
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Nov 12 15:26:02 2020

    libstdc++: Move std::thread to a new header
    
    This makes it possible to use std::thread without including the whole of
    <thread>. It also makes this_thread::get_id() and this_thread::yield()
    available even when there is no gthreads support (e.g. when GCC is built
    with --disable-threads or --enable-threads=single).
    
    In order for the std::thread::id return type of this_thread::get_id() to
    be defined, std:thread itself is defined unconditionally. However the
    constructor that creates new threads is not defined for single-threaded
    builds. The thread::join() and thread::detach() member functions are
    defined inline for single-threaded builds and just throw an exception
    (because we know the thread cannot be joinable if the constructor that
    creates joinable threads doesn't exit).
    
    The thread::hardware_concurrency() member function is also defined
    inline and returns 0 (as suggested by the standard when the value "is
    not computable or well-defined").
    
    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 this_thread::get_id() and this_thread::yield()
    in <stop_token> instead of using the gthread functions directly. This
    removes some preprocessor conditionals, simplifying the code.
    
    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: Include <memory>.
            * include/bits/std_thread.h: New file.
            (thread, operator==, this_thread::get_id, this_thread::yield):
            Define even when gthreads not available.
            [!_GLIBCXX_HAS_GTHREADS] (thread::join, thread::detach)
            (thread::hardware_concurrency): Define inline.

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..96c8d1bb19ff
--- /dev/null
+++ b/libstdc++-v3/include/bits/std_thread.h
@@ -0,0 +1,337 @@
+// 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 <exception>		// std::terminate
+#include <iosfwd>		// std::basic_ostream
+#include <tuple>		// std::tuple
+#include <bits/invoke.h>	// std::__invoke
+#include <bits/refwrap.h>       // not required, but helpful to users
+#include <bits/unique_ptr.h>	// std::unique_ptr
+
+#ifdef _GLIBCXX_HAS_GTHREADS
+# include <bits/gthr.h>
+#else
+# include <errno.h>
+# include <bits/functexcept.h>
+#endif
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  /** @addtogroup threads
+   *  @{
+   */
+
+  /// thread
+  class thread
+  {
+  public:
+#ifdef _GLIBCXX_HAS_GTHREADS
+    // 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>;
+
+    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;
+
+#ifdef _GLIBCXX_HAS_GTHREADS
+    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);
+      }
+#endif // _GLIBCXX_HAS_GTHREADS
+
+    ~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;
+
+#ifdef _GLIBCXX_HAS_GTHREADS
+  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...>>;
+#endif // _GLIBCXX_HAS_GTHREADS
+  };
+
+#ifndef _GLIBCXX_HAS_GTHREADS
+  inline void thread::join() { std::__throw_system_error(EINVAL); }
+  inline void thread::detach() { std::__throw_system_error(EINVAL); }
+  inline unsigned int thread::hardware_concurrency() { return 0; }
+#endif
+
+  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..e4dd1687a4b2 100644
--- a/libstdc++-v3/src/c++11/thread.cc
+++ b/libstdc++-v3/src/c++11/thread.cc
@@ -24,6 +24,7 @@
 
 
 #define _GLIBCXX_THREAD_ABI_COMPAT 1
+#include <memory> // include this first so <thread> can use shared_ptr
 #include <thread>
 #include <system_error>
 #include <cerrno>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] libstdc++: Enable <thread> without gthreads
  2020-11-12 17:07   ` Jonathan Wakely
@ 2020-11-16 22:43     ` Thomas Rodgers
  2020-11-19 13:36       ` Jonathan Wakely
  2020-11-19 18:25     ` Tom Tromey
  1 sibling, 1 reply; 8+ messages in thread
From: Thomas Rodgers @ 2020-11-16 22:43 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: libstdc++, gcc-patches List, tom

This patch looks good to me.

It would be great to find a way to do a similar refactoring of condition_variable.

> On Nov 12, 2020, at 9:07 AM, Jonathan Wakely via Libstdc++ <libstdc++@gcc.gnu.org> wrote:
> 
> On 11/11/20 17:31 +0000, Jonathan Wakely wrote:
>> On 11/11/20 16:13 +0000, Jonathan Wakely wrote:
>>> 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.
>> 
>> I forgot to mention that this patch also reduces the size of the
>> <thread> header, by only including <bits/unique_ptr.h> instead of the
>> whole of <memory>. That could be done separately from the rest of the
>> changes here.
>> 
>> It would be possible to split std::thread and this_thread::get_id()
>> into a new header without also making them work without gthreads.
>> 
>> That would still reduce the size of the <future> header, because it
>> wouldn't need the whole of <thread>. But it wouldn't get rid of
>> preprocessor checks for _GLIBCXX_HAS_GTHREADS in <stop_token>.
>> 
>> Allowing std::this_thread::get_id() and std::this_thread::yield() to
>> work without threads seems worth doing (we already make
>> std::this_thread::sleep_until and std::this_thread::sleep_for work
>> without threads).
> 
> Here's a slightly more conservative version of the patch. This moves
> std::thread and this_thread::get_id() and this_thread::yield() to a
> new header, and makes *most* of std::thread defined without gthreads
> (because we need the nested thread::id type to be returned from
> this_thread::get_id()). But it doesn't declare the std::thread
> constructor that creates new threads.
> 
> That means std::thread is present, but you can't even try to create
> new threads. This means we don't need to export the std::thread
> symbols from libstdc++.so for a target where they are unusable and
> just throw an exception.
> 
> This still has the main benefits of making <future> include a lot less
> code, and removing some #if conditions in <stop_token>.
> 
> One other change from the previous patch worth mentioning is that I've
> made <bits/std_thread.h> include <bits/refwrap.h> so that
> std::reference_wrapper (and std::ref and std::cref) are defined by
> <thread>. That isn't required, but it is a tiny header and being able
> to use std::ref to pass lvalues to new threads without including
> all of <functional> seems like a kindness to users.
> 
> Both this and the previous patch require some GDB changes, because GDB
> currently assumes that if std::thread is declared in <thread> that it
> is usable and multiple threads are supported. That's no longer true,
> because we would declare a useless std::thread after this patch. Tom
> Tromey has patches to make GDB handle this though.
> 
> Tested powerpc64le-linux, --enable-threads and --disable-threads.
> 
> Thoughts?
> 
> 
> <patch.txt>


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] libstdc++: Enable <thread> without gthreads
  2020-11-16 22:43     ` Thomas Rodgers
@ 2020-11-19 13:36       ` Jonathan Wakely
  2020-11-19 19:08         ` Jonathan Wakely
  0 siblings, 1 reply; 8+ messages in thread
From: Jonathan Wakely @ 2020-11-19 13:36 UTC (permalink / raw)
  To: Thomas Rodgers; +Cc: libstdc++, gcc-patches List, tom

On 16/11/20 14:43 -0800, Thomas Rodgers wrote:
>This patch looks good to me.

Committed now.

>It would be great to find a way to do a similar refactoring of condition_variable.

Yes, probably once stage 1 opens for GCC 12.


>> On Nov 12, 2020, at 9:07 AM, Jonathan Wakely via Libstdc++ <libstdc++@gcc.gnu.org> wrote:
>>
>> On 11/11/20 17:31 +0000, Jonathan Wakely wrote:
>>> On 11/11/20 16:13 +0000, Jonathan Wakely wrote:
>>>> 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.
>>>
>>> I forgot to mention that this patch also reduces the size of the
>>> <thread> header, by only including <bits/unique_ptr.h> instead of the
>>> whole of <memory>. That could be done separately from the rest of the
>>> changes here.
>>>
>>> It would be possible to split std::thread and this_thread::get_id()
>>> into a new header without also making them work without gthreads.
>>>
>>> That would still reduce the size of the <future> header, because it
>>> wouldn't need the whole of <thread>. But it wouldn't get rid of
>>> preprocessor checks for _GLIBCXX_HAS_GTHREADS in <stop_token>.
>>>
>>> Allowing std::this_thread::get_id() and std::this_thread::yield() to
>>> work without threads seems worth doing (we already make
>>> std::this_thread::sleep_until and std::this_thread::sleep_for work
>>> without threads).
>>
>> Here's a slightly more conservative version of the patch. This moves
>> std::thread and this_thread::get_id() and this_thread::yield() to a
>> new header, and makes *most* of std::thread defined without gthreads
>> (because we need the nested thread::id type to be returned from
>> this_thread::get_id()). But it doesn't declare the std::thread
>> constructor that creates new threads.
>>
>> That means std::thread is present, but you can't even try to create
>> new threads. This means we don't need to export the std::thread
>> symbols from libstdc++.so for a target where they are unusable and
>> just throw an exception.
>>
>> This still has the main benefits of making <future> include a lot less
>> code, and removing some #if conditions in <stop_token>.
>>
>> One other change from the previous patch worth mentioning is that I've
>> made <bits/std_thread.h> include <bits/refwrap.h> so that
>> std::reference_wrapper (and std::ref and std::cref) are defined by
>> <thread>. That isn't required, but it is a tiny header and being able
>> to use std::ref to pass lvalues to new threads without including
>> all of <functional> seems like a kindness to users.
>>
>> Both this and the previous patch require some GDB changes, because GDB
>> currently assumes that if std::thread is declared in <thread> that it
>> is usable and multiple threads are supported. That's no longer true,
>> because we would declare a useless std::thread after this patch. Tom
>> Tromey has patches to make GDB handle this though.
>>
>> Tested powerpc64le-linux, --enable-threads and --disable-threads.
>>
>> Thoughts?
>>
>>
>> <patch.txt>
>


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] libstdc++: Enable <thread> without gthreads
  2020-11-12 17:07   ` Jonathan Wakely
  2020-11-16 22:43     ` Thomas Rodgers
@ 2020-11-19 18:25     ` Tom Tromey
  1 sibling, 0 replies; 8+ messages in thread
From: Tom Tromey @ 2020-11-19 18:25 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: libstdc++, gcc-patches, tom

>>>>> "Jonathan" == Jonathan Wakely <jwakely@redhat.com> writes:

Jonathan> Here's a slightly more conservative version of the patch. This moves
Jonathan> std::thread and this_thread::get_id() and this_thread::yield() to a
Jonathan> new header, and makes *most* of std::thread defined without gthreads
Jonathan> (because we need the nested thread::id type to be returned from
Jonathan> this_thread::get_id()). But it doesn't declare the std::thread
Jonathan> constructor that creates new threads.
...
Jonathan> Both this and the previous patch require some GDB changes, because GDB
Jonathan> currently assumes that if std::thread is declared in <thread> that it
Jonathan> is usable and multiple threads are supported. That's no longer true,
Jonathan> because we would declare a useless std::thread after this patch. Tom
Jonathan> Tromey has patches to make GDB handle this though.

It turns out that with this approach, there's nothing to do in gdb,
because luckily the configure check looks to see if the constructor is
usable:

    AC_CACHE_CHECK([for std::thread],
		   gdb_cv_cxx_std_thread,
		   [AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
    [[#include <thread>
      void callback() { }]],
    [[std::thread t(callback);]])],

I will probably still check in the patch to catch system_error when
starting a thread, though.

thanks,
Tom

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] libstdc++: Enable <thread> without gthreads
  2020-11-19 13:36       ` Jonathan Wakely
@ 2020-11-19 19:08         ` Jonathan Wakely
  2020-11-20 11:52           ` Jonathan Wakely
  0 siblings, 1 reply; 8+ messages in thread
From: Jonathan Wakely @ 2020-11-19 19:08 UTC (permalink / raw)
  To: Thomas Rodgers; +Cc: libstdc++, gcc-patches List, tom

[-- Attachment #1: Type: text/plain, Size: 301 bytes --]

On 19/11/20 13:36 +0000, Jonathan Wakely wrote:
>On 16/11/20 14:43 -0800, Thomas Rodgers wrote:
>>This patch looks good to me.
>
>Committed now.

This patch was also needed, but I don't understand why I didn't see
the FAILs on gcc135 in teh cfarm.

Anyway, tested x86_64-linux, committed to trunk.




[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 1239 bytes --]

commit 5e6a43158d2e5b26616716c50badedd3400c6bea
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Nov 19 16:17:33 2020

    libstdc++: Add missing header to some tests
    
    These tests use std::this_thread::sleep_for without including <thread>.
    
    libstdc++-v3/ChangeLog:
    
            * testsuite/30_threads/async/async.cc: Include <thread>.
            * testsuite/30_threads/future/members/93456.cc: Likewise.

diff --git a/libstdc++-v3/testsuite/30_threads/async/async.cc b/libstdc++-v3/testsuite/30_threads/async/async.cc
index 1c779bfbcad4..b06c2553c952 100644
--- a/libstdc++-v3/testsuite/30_threads/async/async.cc
+++ b/libstdc++-v3/testsuite/30_threads/async/async.cc
@@ -22,6 +22,7 @@
 
 
 #include <future>
+#include <thread>
 #include <testsuite_hooks.h>
 
 using namespace std;
diff --git a/libstdc++-v3/testsuite/30_threads/future/members/93456.cc b/libstdc++-v3/testsuite/30_threads/future/members/93456.cc
index 8d6a5148ce3c..9d1cbcef0013 100644
--- a/libstdc++-v3/testsuite/30_threads/future/members/93456.cc
+++ b/libstdc++-v3/testsuite/30_threads/future/members/93456.cc
@@ -22,6 +22,7 @@
 
 
 #include <future>
+#include <thread>
 #include <chrono>
 #include <climits>
 #include <testsuite_hooks.h>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] libstdc++: Enable <thread> without gthreads
  2020-11-19 19:08         ` Jonathan Wakely
@ 2020-11-20 11:52           ` Jonathan Wakely
  0 siblings, 0 replies; 8+ messages in thread
From: Jonathan Wakely @ 2020-11-20 11:52 UTC (permalink / raw)
  To: Thomas Rodgers; +Cc: libstdc++, gcc-patches List, tom

On 19/11/20 19:08 +0000, Jonathan Wakely wrote:
>On 19/11/20 13:36 +0000, Jonathan Wakely wrote:
>>On 16/11/20 14:43 -0800, Thomas Rodgers wrote:
>>>This patch looks good to me.
>>
>>Committed now.
>
>This patch was also needed, but I don't understand why I didn't see
>the FAILs on gcc135 in teh cfarm.

PCH. My test config on gcc135 uses PCH, and so all headers were
included. The host I tested on later uses --disable-libstdcxx-pch and
so saw the failure.

Mystery solved.


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2020-11-20 11:52 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-11 16:13 [PATCH] libstdc++: Enable <thread> without gthreads Jonathan Wakely
2020-11-11 17:31 ` Jonathan Wakely
2020-11-12 17:07   ` Jonathan Wakely
2020-11-16 22:43     ` Thomas Rodgers
2020-11-19 13:36       ` Jonathan Wakely
2020-11-19 19:08         ` Jonathan Wakely
2020-11-20 11:52           ` Jonathan Wakely
2020-11-19 18:25     ` Tom Tromey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).