From 18095be17013444d9e91aa8c73ebe5cf58ccb3f1 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Tue, 11 Aug 2020 16:16:22 +0100 Subject: [PATCH] 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. --- 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 __lock(__ctx._M_mutex); + lock_guard __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 __lock(__ctx._M_mutex); + lock_guard __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 __lock(__ctx._M_mutex); + lock_guard __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 __lock(_M_mtx); + lock_guard __lock(_M_mtx); _M_stopped = true; _M_cv.notify_all(); } bool stopped() const noexcept { - lock_guard __lock(_M_mtx); + lock_guard __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> _M_tasks; bool _M_stopped = false; +#ifdef _GLIBCXX_HAS_GTHREADS void _M_run() { @@ -900,7 +923,7 @@ inline namespace v1 { function __f; { - unique_lock __lock(_M_mtx); + unique_lock __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 __f) + _M_post(std::function __f __attribute__((__unused__))) { - lock_guard __lock(_M_mtx); + lock_guard __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 @@ -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 #include #include +#include #include #include #if _GLIBCXX_HAVE_UNISTD_H @@ -90,10 +91,14 @@ inline namespace v1 bool running_in_this_thread() const noexcept { - lock_guard __lock(_M_ctx->_M_mtx); +#ifdef _GLIBCXX_HAS_GTHREADS + lock_guard __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 __lock(_M_ctx->_M_mtx); + lock_guard __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 __lock(_M_mtx); + lock_guard __lock(_M_mtx); _M_stopped = true; _M_reactor._M_notify(); } bool stopped() const noexcept { - lock_guard __lock(_M_mtx); + lock_guard __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(__ctx); - lock_guard __lock(__ioc._M_mtx); + lock_guard __lock(__ioc._M_mtx); __ioc._M_timers.push_back(this); } - mutable mutex _M_qmtx; + mutable execution_context::mutex_type _M_qmtx; }; template @@ -296,7 +301,7 @@ inline namespace v1 push(const _Timer& __t, function __h) { context().get_executor().on_work_started(); - lock_guard __lock(_M_qmtx); + lock_guard __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 __lock(_M_qmtx); + lock_guard __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 __lock(_M_qmtx); + lock_guard __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 __lock(_M_qmtx); + lock_guard __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 __h; error_code __ec; { - lock_guard __lock(_M_qmtx); + lock_guard __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 __lock(_M_mtx); + lock_guard __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 __lock(_M_mtx); + lock_guard __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 _M_work_count; - mutable mutex _M_mtx; + mutable execution_context::mutex_type _M_mtx; queue> _M_op; bool _M_stopped = false; @@ -561,14 +566,22 @@ inline namespace v1 { __monitor(io_context& __c) : _M_ctx(__c) { - lock_guard __lock(_M_ctx._M_mtx); +#ifdef _GLIBCXX_HAS_GTHREADS + lock_guard __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 __lock(_M_ctx._M_mtx); +#ifdef _GLIBCXX_HAS_GTHREADS + lock_guard __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 __lock(_M_mtx); + lock_guard __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 __lock(_M_mtx); + lock_guard __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> _M_ops; +#ifdef _GLIBCXX_HAS_GTHREADS vector _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 // . -// { 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 #include -- 2.30.2