[gcc(refs/users/wschmidt/heads/builtins3)] libstdc++: Make Networking TS work without gthreads [PR 89760]

William Schmidt wschmidt@gcc.gnu.org
Tue Aug 18 18:35:54 GMT 2020


https://gcc.gnu.org/g:18095be17013444d9e91aa8c73ebe5cf58ccb3f1

commit 18095be17013444d9e91aa8c73ebe5cf58ccb3f1
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Aug 11 16:16:22 2020 +0100

    libstdc++: Make Networking TS work without gthreads [PR 89760]
    
    Make the experimental Networking TS code work without std::mutex and
    std::condition_variable.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/89760
            * include/experimental/executor [!_GLIBCXX_HAS_GTHREADS]:
            (execution_context::mutex_type): Define dummy mutex type.
            (system_context): Use execution_context::mutex_type.
            (system_context) [!_GLIBCXX_HAS_GTHREADS]: Define dummy
            thread and condition variable types.
            [!_GLIBCXX_HAS_GTHREADS] (system_context::_M_run()): Do not
            define.
            (system_context::_M_post) [!_GLIBCXX_HAS_GTHREADS]: Throw
            an exception when threads aren't available.
            (strand::running_in_this_thread()): Defer to _M_state.
            (strand::_State::running_in_this_thread()): New function.
            (use_future_t): Do not depend on _GLIBCXX_USE_C99_STDINT_TR1.
            * include/experimental/io_context (io_context): Use the
            execution_context::mutex_type alias. Replace stack of thread
            IDs with counter.
            * testsuite/experimental/net/execution_context/use_service.cc:
            Enable test for non-pthread targets.

Diff:
---
 libstdc++-v3/include/experimental/executor         | 65 +++++++++++++++++-----
 libstdc++-v3/include/experimental/io_context       | 53 ++++++++++++------
 .../net/execution_context/use_service.cc           |  7 +--
 3 files changed, 87 insertions(+), 38 deletions(-)

diff --git a/libstdc++-v3/include/experimental/executor b/libstdc++-v3/include/experimental/executor
index 1561050ae23..45e813f6747 100644
--- a/libstdc++-v3/include/experimental/executor
+++ b/libstdc++-v3/include/experimental/executor
@@ -506,7 +506,16 @@ inline namespace v1
       bool _M_active;
     };
 
-    mutable std::mutex _M_mutex;
+#if defined(_GLIBCXX_HAS_GTHREADS)
+    using mutex_type = std::mutex;
+#else
+    struct mutex_type
+    {
+      void lock() const { }
+      void unlock() const { }
+    };
+#endif
+    mutable mutex_type _M_mutex;
 
     // Sorted in order of beginning of service object lifetime.
     std::list<_ServicePtr> _M_services;
@@ -553,7 +562,7 @@ inline namespace v1
       static_assert(is_base_of<_Key, _Service>::value,
 	  "a service type must match or derive from its key_type");
       auto __key = execution_context::_S_key<_Key>();
-      std::lock_guard<std::mutex> __lock(__ctx._M_mutex);
+      lock_guard<execution_context::mutex_type> __lock(__ctx._M_mutex);
       auto& __svc = __ctx._M_keys[__key];
       if (__svc == nullptr)
 	{
@@ -577,7 +586,7 @@ inline namespace v1
       static_assert(is_base_of<_Key, _Service>::value,
 	  "a service type must match or derive from its key_type");
       auto __key = execution_context::_S_key<_Key>();
-      std::lock_guard<std::mutex> __lock(__ctx._M_mutex);
+      lock_guard<execution_context::mutex_type> __lock(__ctx._M_mutex);
       auto& __svc = __ctx._M_keys[__key];
       if (__svc != nullptr)
 	throw service_already_exists();
@@ -599,7 +608,7 @@ inline namespace v1
 	  "a service type must derive from execution_context::service");
       static_assert(is_base_of<_Key, _Service>::value,
 	  "a service type must match or derive from its key_type");
-      std::lock_guard<std::mutex> __lock(__ctx._M_mutex);
+      lock_guard<execution_context::mutex_type> __lock(__ctx._M_mutex);
       return __ctx._M_keys.count(execution_context::_S_key<_Key>());
     }
 
@@ -865,20 +874,21 @@ inline namespace v1
 
     void stop()
     {
-      lock_guard<mutex> __lock(_M_mtx);
+      lock_guard<mutex_type> __lock(_M_mtx);
       _M_stopped = true;
       _M_cv.notify_all();
     }
 
     bool stopped() const noexcept
     {
-      lock_guard<mutex> __lock(_M_mtx);
+      lock_guard<mutex_type> __lock(_M_mtx);
       return _M_stopped;
     }
 
     void join()
     {
-      _M_thread.join();
+      if (_M_thread.joinable())
+	_M_thread.join();
     }
 
   private:
@@ -887,12 +897,25 @@ inline namespace v1
     struct __tag { explicit __tag() = default; };
     system_context(__tag) { }
 
+#ifndef _GLIBCXX_HAS_GTHREADS
+    struct thread
+    {
+      bool joinable() const { return false; }
+      void join() { }
+    };
+    struct condition_variable
+    {
+      void notify_all() { }
+    };
+#endif
+
     thread			_M_thread;
-    mutable mutex		_M_mtx;
+    mutable mutex_type		_M_mtx; // XXX can we reuse base's _M_mutex?
     condition_variable		_M_cv;
     queue<function<void()>>	_M_tasks;
     bool			_M_stopped = false;
 
+#ifdef _GLIBCXX_HAS_GTHREADS
     void
     _M_run()
     {
@@ -900,7 +923,7 @@ inline namespace v1
 	{
 	  function<void()> __f;
 	  {
-	    unique_lock<mutex> __lock(_M_mtx);
+	    unique_lock<mutex_type> __lock(_M_mtx);
 	    _M_cv.wait(__lock,
 		       [this]{ return _M_stopped || !_M_tasks.empty(); });
 	    if (_M_stopped)
@@ -911,17 +934,22 @@ inline namespace v1
 	  __f();
 	}
     }
+#endif
 
     void
-    _M_post(std::function<void()> __f)
+    _M_post(std::function<void()> __f __attribute__((__unused__)))
     {
-      lock_guard<mutex> __lock(_M_mtx);
+      lock_guard<mutex_type> __lock(_M_mtx);
       if (_M_stopped)
 	return;
+#ifdef _GLIBCXX_HAS_GTHREADS
       if (!_M_thread.joinable())
 	_M_thread = std::thread(&system_context::_M_run, this);
       _M_tasks.push(std::move(__f)); // XXX allocator not used
       _M_cv.notify_one();
+#else
+      __throw_system_error(EOPNOTSUPP);
+#endif
     }
 
     static system_context&
@@ -1536,7 +1564,7 @@ inline namespace v1
 
       bool
       running_in_this_thread() const noexcept
-      { return std::this_thread::get_id() == _M_state->_M_running_on; }
+      { return _M_state->running_in_this_thread(); }
 
       execution_context&
       context() const noexcept
@@ -1572,13 +1600,21 @@ inline namespace v1
       // TODO add synchronised queue
       struct _State
       {
+#if defined(_GLIBCXX_HAS_GTHREADS)
+	bool
+	running_in_this_thread() const
+	{ return std::this_thread::get_id() == _M_state->_M_running_on; }
+
 	std::thread::id _M_running_on;
+#else
+	bool running_in_this_thread() const { return true; }
+#endif
       };
       shared_ptr<_State> _M_state;
       _Executor _M_inner_ex;
     };
 
-#if defined(_GLIBCXX_HAS_GTHREADS) && defined(_GLIBCXX_USE_C99_STDINT_TR1)
+#if defined(_GLIBCXX_HAS_GTHREADS)
 
   // Completion token for asynchronous operations initiated with use_future.
   template<typename _Func, typename _Alloc>
@@ -1970,7 +2006,6 @@ inline namespace v1
   // (probably need to move _Type outside of handler_type so we don't have
   // a non-deduced context)
 
-
 #endif
 
   // [async.packaged.task.specializations]
@@ -1994,7 +2029,7 @@ inline namespace v1
       return_type _M_future;
     };
 
-#endif
+#endif // _GLIBCXX_HAS_GTHREADS
 
   /// @}
 
diff --git a/libstdc++-v3/include/experimental/io_context b/libstdc++-v3/include/experimental/io_context
index 5fb6544e821..561bd37acbd 100644
--- a/libstdc++-v3/include/experimental/io_context
+++ b/libstdc++-v3/include/experimental/io_context
@@ -40,6 +40,7 @@
 #include <functional>
 #include <system_error>
 #include <thread>
+#include <vector>
 #include <experimental/netfwd>
 #include <experimental/executor>
 #if _GLIBCXX_HAVE_UNISTD_H
@@ -90,10 +91,14 @@ inline namespace v1
 
       bool running_in_this_thread() const noexcept
       {
-	lock_guard<mutex> __lock(_M_ctx->_M_mtx);
+#ifdef _GLIBCXX_HAS_GTHREADS
+	lock_guard<execution_context::mutex_type> __lock(_M_ctx->_M_mtx);
 	auto __end = _M_ctx->_M_call_stack.end();
 	return std::find(_M_ctx->_M_call_stack.begin(), __end,
 			 this_thread::get_id()) != __end;
+#else
+	return _M_ctx->_M_run_count != 0;
+#endif
       }
 
       io_context& context() const noexcept { return *_M_ctx; }
@@ -115,7 +120,7 @@ inline namespace v1
 	void
 	post(_Func&& __f, const _ProtoAllocator& __a) const
 	{
-	  lock_guard<mutex> __lock(_M_ctx->_M_mtx);
+	  lock_guard<execution_context::mutex_type> __lock(_M_ctx->_M_mtx);
 	  // TODO (re-use functionality in system_context)
 	  _M_ctx->_M_reactor._M_notify();
 	}
@@ -217,14 +222,14 @@ inline namespace v1
 
     void stop()
     {
-      lock_guard<mutex> __lock(_M_mtx);
+      lock_guard<execution_context::mutex_type> __lock(_M_mtx);
       _M_stopped = true;
       _M_reactor._M_notify();
     }
 
     bool stopped() const noexcept
     {
-      lock_guard<mutex> __lock(_M_mtx);
+      lock_guard<execution_context::mutex_type> __lock(_M_mtx);
       return _M_stopped;
     }
 
@@ -270,11 +275,11 @@ inline namespace v1
       __timer_queue_base(execution_context& __ctx) : service(__ctx)
       {
 	auto& __ioc = static_cast<io_context&>(__ctx);
-	lock_guard<mutex> __lock(__ioc._M_mtx);
+	lock_guard<execution_context::mutex_type> __lock(__ioc._M_mtx);
 	__ioc._M_timers.push_back(this);
       }
 
-      mutable mutex	_M_qmtx;
+      mutable execution_context::mutex_type _M_qmtx;
     };
 
     template<typename _Timer, typename _Key = typename _Timer::_Key>
@@ -296,7 +301,7 @@ inline namespace v1
 	push(const _Timer& __t, function<void(error_code)> __h)
 	{
 	  context().get_executor().on_work_started();
-	  lock_guard<mutex> __lock(_M_qmtx);
+	  lock_guard<execution_context::mutex_type> __lock(_M_qmtx);
 	  _M_queue.emplace(__t, _M_next_id++, std::move(__h));
 	  // no need to notify reactor unless this timer went to the front?
 	}
@@ -305,7 +310,7 @@ inline namespace v1
 	size_t
 	cancel(const _Timer& __t)
 	{
-	  lock_guard<mutex> __lock(_M_qmtx);
+	  lock_guard<execution_context::mutex_type> __lock(_M_qmtx);
 	  size_t __count = 0;
 	  auto __last = _M_queue.end();
 	  for (auto __it = _M_queue.begin(), __end = __last; __it != __end;
@@ -327,7 +332,7 @@ inline namespace v1
 	bool
 	cancel_one(const _Timer& __t)
 	{
-	  lock_guard<mutex> __lock(_M_qmtx);
+	  lock_guard<execution_context::mutex_type> __lock(_M_qmtx);
 	  const auto __end = _M_queue.end();
 	  auto __oldest = __end;
 	  for (auto __it = _M_queue.begin(); __it != __end; ++__it)
@@ -346,7 +351,7 @@ inline namespace v1
 	{
 	  typename _Timer::time_point __exp;
 	  {
-	    lock_guard<mutex> __lock(_M_qmtx);
+	    lock_guard<execution_context::mutex_type> __lock(_M_qmtx);
 	    if (_M_queue.empty())
 	      return chrono::milliseconds::max();  // no pending timers
 	    if (_M_queue.top()._M_key == nullptr)
@@ -367,7 +372,7 @@ inline namespace v1
 	  function<void(error_code)> __h;
 	  error_code __ec;
 	  {
-	    lock_guard<mutex> __lock(_M_qmtx);
+	    lock_guard<execution_context::mutex_type> __lock(_M_qmtx);
 
 	    if (_M_queue.top()._M_key == nullptr) // cancelled
 	      {
@@ -474,7 +479,7 @@ inline namespace v1
       void
       async_wait(int __fd, int __w, _Op&& __op)
       {
-	lock_guard<mutex> __lock(_M_mtx);
+	lock_guard<execution_context::mutex_type> __lock(_M_mtx);
 	// TODO need push_back, use std::list not std::forward_list
 	auto __tail = _M_ops.before_begin(), __it = _M_ops.begin();
 	while (__it != _M_ops.end())
@@ -493,7 +498,7 @@ inline namespace v1
 
     void cancel(int __fd, error_code&)
     {
-      lock_guard<mutex> __lock(_M_mtx);
+      lock_guard<execution_context::mutex_type> __lock(_M_mtx);
       const auto __end = _M_ops.end();
       auto __it = _M_ops.begin();
       auto __prev = _M_ops.before_begin();
@@ -553,7 +558,7 @@ inline namespace v1
       };
 
     atomic<count_type>		_M_work_count;
-    mutable mutex		_M_mtx;
+    mutable execution_context::mutex_type		_M_mtx;
     queue<function<void()>>	_M_op;
     bool			_M_stopped = false;
 
@@ -561,14 +566,22 @@ inline namespace v1
     {
       __monitor(io_context& __c) : _M_ctx(__c)
       {
-	lock_guard<mutex> __lock(_M_ctx._M_mtx);
+#ifdef _GLIBCXX_HAS_GTHREADS
+	lock_guard<execution_context::mutex_type> __lock(_M_ctx._M_mtx);
 	_M_ctx._M_call_stack.push_back(this_thread::get_id());
+#else
+	_M_ctx._M_run_count++;
+#endif
       }
 
       ~__monitor()
       {
-	lock_guard<mutex> __lock(_M_ctx._M_mtx);
+#ifdef _GLIBCXX_HAS_GTHREADS
+	lock_guard<execution_context::mutex_type> __lock(_M_ctx._M_mtx);
 	_M_ctx._M_call_stack.pop_back();
+#else
+	_M_ctx._M_run_count--;
+#endif
 	if (_M_ctx._M_outstanding_work() == 0)
 	  {
 	    _M_ctx._M_stopped = true;
@@ -613,7 +626,7 @@ inline namespace v1
 	  chrono::milliseconds __ms{0};
 
 	  {
-	    lock_guard<mutex> __lock(_M_mtx);
+	    lock_guard<execution_context::mutex_type> __lock(_M_mtx);
 
 	    if (_M_stopped)
 	      return false;
@@ -677,7 +690,7 @@ inline namespace v1
 	  if (__fds.empty()) // nothing to do
 	    return false;
 
-	  lock_guard<mutex> __lock(_M_mtx);
+	  lock_guard<execution_context::mutex_type> __lock(_M_mtx);
 	  for (auto __it = _M_ops.begin(), __end = _M_ops.end(),
 	      __prev = _M_ops.before_begin(); __it != __end; ++__it, ++__prev)
 	    {
@@ -839,7 +852,11 @@ inline namespace v1
     vector<__timer_queue_base*>			_M_timers;
     forward_list<unique_ptr<__async_operation>>	_M_ops;
 
+#ifdef _GLIBCXX_HAS_GTHREADS
     vector<thread::id>	_M_call_stack;
+#else
+    int _M_run_count = 0;
+#endif
   };
 
   inline bool
diff --git a/libstdc++-v3/testsuite/experimental/net/execution_context/use_service.cc b/libstdc++-v3/testsuite/experimental/net/execution_context/use_service.cc
index d242d63cb94..2a3af881db5 100644
--- a/libstdc++-v3/testsuite/experimental/net/execution_context/use_service.cc
+++ b/libstdc++-v3/testsuite/experimental/net/execution_context/use_service.cc
@@ -15,11 +15,8 @@
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
-// { dg-do run }
-// { dg-options "-pthread"  }
-// { dg-require-effective-target c++14 }
-// { dg-require-effective-target pthread }
-// { dg-require-gthreads "" }
+// { dg-do run { target c++14 } }
+// { dg-additional-options "-pthread" { target pthread } }
 
 #include <experimental/executor>
 #include <testsuite_hooks.h>


More information about the Libstdc++-cvs mailing list