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.
This commit is contained in:
parent
2a6918e4fa
commit
18095be170
3 changed files with 87 additions and 38 deletions
|
@ -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
|
||||
|
||||
/// @}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Reference in a new issue