mirror of
https://gcc.gnu.org/git/gcc.git
synced 2025-01-12 22:14:18 +08:00
Define *_at_thread_exit() functions.
* config/abi/pre/gnu.ver: Add new exports. * include/std/condition_variable (notify_all_at_thread_exit): Declare. (__at_thread_exit_elt): New base class. * include/std/future: Add comments documenting the implementation. (__future_base::_State_baseV2::_State_baseV2()): Use brace-or-equal initializers and define constructor as defaulted. (__future_base::_State_baseV2::_M_ready): Replace member function with member variable. (__future_base::_State_baseV2::_M_set_result): Set _M_ready. (__future_base::_State_baseV2::_M_set_delayed_result): Define. (__future_base::_State_baseV2::_M_break_promise): Set _M_ready. (__future_base::_State_baseV2::_Make_ready): New helper class. (__future_base::_Deferred_state::_M_has_deferred): Remove requirement for caller to own mutex. (__future_base::_Async_state_impl::~_Async_state_impl): Call join directly. (__future_base::_Task_state_base::_M_run): Take arguments by reference. (__future_base::_Task_state_base::_M_run_delayed): Declare new pure virtual function. (__future_base::_Task_state::_M_run_delayed): Define override. (promise::set_value_at_thread_exit): Define. (promise::set_exception_at_thread_exit): Define. (packaged_task::make_ready_at_thread_exit): Define. * src/c++11/condition_variable.cc (notify_all_at_thread_exit): Define. * src/c++11/future.cc (__future_base::_State_baseV2::_Make_ready::_M_set): Define. * testsuite/30_threads/condition_variable/members/3.cc: New. * testsuite/30_threads/packaged_task/members/at_thread_exit.cc: New. * testsuite/30_threads/promise/members/at_thread_exit.cc: New. From-SVN: r218255
This commit is contained in:
parent
8581fd6499
commit
9db7c9316e
@ -1,3 +1,36 @@
|
||||
2014-12-02 Jonathan Wakely <jwakely@redhat.com>
|
||||
|
||||
* config/abi/pre/gnu.ver: Add new exports.
|
||||
* include/std/condition_variable (notify_all_at_thread_exit): Declare.
|
||||
(__at_thread_exit_elt): New base class.
|
||||
* include/std/future: Add comments documenting the implementation.
|
||||
(__future_base::_State_baseV2::_State_baseV2()): Use brace-or-equal
|
||||
initializers and define constructor as defaulted.
|
||||
(__future_base::_State_baseV2::_M_ready): Replace member function
|
||||
with member variable.
|
||||
(__future_base::_State_baseV2::_M_set_result): Set _M_ready.
|
||||
(__future_base::_State_baseV2::_M_set_delayed_result): Define.
|
||||
(__future_base::_State_baseV2::_M_break_promise): Set _M_ready.
|
||||
(__future_base::_State_baseV2::_Make_ready): New helper class.
|
||||
(__future_base::_Deferred_state::_M_has_deferred): Remove requirement
|
||||
for caller to own mutex.
|
||||
(__future_base::_Async_state_impl::~_Async_state_impl): Call join
|
||||
directly.
|
||||
(__future_base::_Task_state_base::_M_run): Take arguments by
|
||||
reference.
|
||||
(__future_base::_Task_state_base::_M_run_delayed): Declare new pure
|
||||
virtual function.
|
||||
(__future_base::_Task_state::_M_run_delayed): Define override.
|
||||
(promise::set_value_at_thread_exit): Define.
|
||||
(promise::set_exception_at_thread_exit): Define.
|
||||
(packaged_task::make_ready_at_thread_exit): Define.
|
||||
* src/c++11/condition_variable.cc (notify_all_at_thread_exit): Define.
|
||||
* src/c++11/future.cc
|
||||
(__future_base::_State_baseV2::_Make_ready::_M_set): Define.
|
||||
* testsuite/30_threads/condition_variable/members/3.cc: New.
|
||||
* testsuite/30_threads/packaged_task/members/at_thread_exit.cc: New.
|
||||
* testsuite/30_threads/promise/members/at_thread_exit.cc: New.
|
||||
|
||||
2014-12-01 Jonathan Wakely <jwakely@redhat.com>
|
||||
|
||||
PR libstdc++/63840
|
||||
|
@ -128,7 +128,8 @@ GLIBCXX_3.4 {
|
||||
std::messages*;
|
||||
std::money*;
|
||||
# std::n[^u]*;
|
||||
std::n[^aue]*;
|
||||
std::n[^aueo]*;
|
||||
std::nothrow;
|
||||
std::nu[^m]*;
|
||||
std::num[^e]*;
|
||||
std::ostrstream*;
|
||||
@ -1500,6 +1501,11 @@ GLIBCXX_3.4.21 {
|
||||
# std::_Sp_locker::*
|
||||
_ZNSt10_Sp_locker[CD]*;
|
||||
|
||||
# std::notify_all_at_thread_exit
|
||||
_ZSt25notify_all_at_thread_exitRSt18condition_variableSt11unique_lockISt5mutexE;
|
||||
# std::__future_base::_State_baseV2::_Make_ready::_M_set()
|
||||
_ZNSt13__future_base13_State_baseV211_Make_ready6_M_setEv;
|
||||
|
||||
} GLIBCXX_3.4.20;
|
||||
|
||||
|
||||
|
@ -170,6 +170,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
notify_all_at_thread_exit(condition_variable&, unique_lock<mutex>);
|
||||
|
||||
struct __at_thread_exit_elt
|
||||
{
|
||||
__at_thread_exit_elt* _M_next;
|
||||
void (*_M_cb)(void*);
|
||||
};
|
||||
|
||||
inline namespace _V2 {
|
||||
|
||||
/// condition_variable_any
|
||||
|
@ -202,7 +202,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
virtual ~_Result_base();
|
||||
};
|
||||
|
||||
/// Result.
|
||||
/// A unique_ptr for result objects.
|
||||
template<typename _Res>
|
||||
using _Ptr = unique_ptr<_Res, _Result_base::_Deleter>;
|
||||
|
||||
/// A result object that has storage for an object of type _Res.
|
||||
template<typename _Res>
|
||||
struct _Result : _Result_base
|
||||
{
|
||||
@ -243,11 +247,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
void _M_destroy() { delete this; }
|
||||
};
|
||||
|
||||
/// A unique_ptr based on the instantiating type.
|
||||
template<typename _Res>
|
||||
using _Ptr = unique_ptr<_Res, _Result_base::_Deleter>;
|
||||
|
||||
/// Result_alloc.
|
||||
/// A result object that uses an allocator.
|
||||
template<typename _Res, typename _Alloc>
|
||||
struct _Result_alloc final : _Result<_Res>, _Alloc
|
||||
{
|
||||
@ -266,6 +266,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
}
|
||||
};
|
||||
|
||||
// Create a result object that uses an allocator.
|
||||
template<typename _Res, typename _Allocator>
|
||||
static _Ptr<_Result_alloc<_Res, _Allocator>>
|
||||
_S_allocate_result(const _Allocator& __a)
|
||||
@ -278,6 +279,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
return _Ptr<__result_type>(__p);
|
||||
}
|
||||
|
||||
// Keep it simple for std::allocator.
|
||||
template<typename _Res, typename _Tp>
|
||||
static _Ptr<_Result<_Res>>
|
||||
_S_allocate_result(const std::allocator<_Tp>& __a)
|
||||
@ -285,8 +287,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
return _Ptr<_Result<_Res>>(new _Result<_Res>);
|
||||
}
|
||||
|
||||
/// Base class for state between a promise and one or more
|
||||
/// associated futures.
|
||||
// Base class for various types of shared state created by an
|
||||
// asynchronous provider (such as a std::promise) and shared with one
|
||||
// or more associated futures.
|
||||
class _State_baseV2
|
||||
{
|
||||
typedef _Ptr<_Result_base> _Ptr_type;
|
||||
@ -294,12 +297,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
_Ptr_type _M_result;
|
||||
mutex _M_mutex;
|
||||
condition_variable _M_cond;
|
||||
atomic_flag _M_retrieved;
|
||||
atomic_flag _M_retrieved = ATOMIC_FLAG_INIT;
|
||||
bool _M_ready = false;
|
||||
once_flag _M_once;
|
||||
|
||||
public:
|
||||
_State_baseV2() noexcept : _M_result(), _M_retrieved(ATOMIC_FLAG_INIT)
|
||||
{ }
|
||||
_State_baseV2() noexcept = default;
|
||||
_State_baseV2(const _State_baseV2&) = delete;
|
||||
_State_baseV2& operator=(const _State_baseV2&) = delete;
|
||||
virtual ~_State_baseV2() = default;
|
||||
@ -307,9 +310,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
_Result_base&
|
||||
wait()
|
||||
{
|
||||
// Run any deferred function or join any asynchronous thread:
|
||||
_M_complete_async();
|
||||
|
||||
unique_lock<mutex> __lock(_M_mutex);
|
||||
_M_cond.wait(__lock, [&] { return _M_ready(); });
|
||||
_M_cond.wait(__lock, [&] { return _M_ready; });
|
||||
return *_M_result;
|
||||
}
|
||||
|
||||
@ -318,15 +323,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
wait_for(const chrono::duration<_Rep, _Period>& __rel)
|
||||
{
|
||||
unique_lock<mutex> __lock(_M_mutex);
|
||||
if (_M_ready())
|
||||
if (_M_ready)
|
||||
return future_status::ready;
|
||||
if (_M_has_deferred())
|
||||
return future_status::deferred;
|
||||
if (_M_cond.wait_for(__lock, __rel, [&] { return _M_ready(); }))
|
||||
if (_M_cond.wait_for(__lock, __rel, [&] { return _M_ready; }))
|
||||
{
|
||||
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
||||
// 2100. timed waiting functions must also join
|
||||
// This call is a no-op by default except on an async future,
|
||||
// in which case the async thread is joined. It's also not a
|
||||
// no-op for a deferred future, but such a future will never
|
||||
// reach this point because it returns future_status::deferred
|
||||
// instead of waiting for the future to become ready (see
|
||||
// above). Async futures synchronize in this call, so we need
|
||||
// no further synchronization here.
|
||||
_M_complete_async();
|
||||
|
||||
return future_status::ready;
|
||||
}
|
||||
return future_status::timeout;
|
||||
@ -337,20 +350,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
wait_until(const chrono::time_point<_Clock, _Duration>& __abs)
|
||||
{
|
||||
unique_lock<mutex> __lock(_M_mutex);
|
||||
if (_M_ready())
|
||||
if (_M_ready)
|
||||
return future_status::ready;
|
||||
if (_M_has_deferred())
|
||||
return future_status::deferred;
|
||||
if (_M_cond.wait_until(__lock, __abs, [&] { return _M_ready(); }))
|
||||
if (_M_cond.wait_until(__lock, __abs, [&] { return _M_ready; }))
|
||||
{
|
||||
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
||||
// 2100. timed waiting functions must also join
|
||||
// See wait_for(...) above.
|
||||
_M_complete_async();
|
||||
|
||||
return future_status::ready;
|
||||
}
|
||||
return future_status::timeout;
|
||||
}
|
||||
|
||||
// Provide a result to the shared state and make it ready.
|
||||
// Atomically performs:
|
||||
// if (!_M_ready) {
|
||||
// _M_result = __res();
|
||||
// _M_ready = true;
|
||||
// }
|
||||
void
|
||||
_M_set_result(function<_Ptr_type()> __res, bool __ignore_failure = false)
|
||||
{
|
||||
@ -360,11 +381,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
call_once(_M_once, &_State_baseV2::_M_do_set, this,
|
||||
std::__addressof(__res), std::__addressof(__lock));
|
||||
if (__lock.owns_lock())
|
||||
{
|
||||
_M_ready = true;
|
||||
_M_cond.notify_all();
|
||||
}
|
||||
else if (!__ignore_failure)
|
||||
__throw_future_error(int(future_errc::promise_already_satisfied));
|
||||
}
|
||||
|
||||
// Provide a result to the shared state but delay making it ready
|
||||
// until the calling thread exits.
|
||||
// Atomically performs:
|
||||
// if (!_M_ready) {
|
||||
// _M_result = __res();
|
||||
// }
|
||||
void
|
||||
_M_set_delayed_result(function<_Ptr_type()> __res,
|
||||
weak_ptr<_State_baseV2> __self)
|
||||
{
|
||||
unique_ptr<_Make_ready> __mr{new _Make_ready};
|
||||
unique_lock<mutex> __lock(_M_mutex, defer_lock);
|
||||
// all calls to this function are serialized,
|
||||
// side-effects of invoking __res only happen once
|
||||
call_once(_M_once, &_State_baseV2::_M_do_set, this,
|
||||
std::__addressof(__res), std::__addressof(__lock));
|
||||
if (!__lock.owns_lock())
|
||||
__throw_future_error(int(future_errc::promise_already_satisfied));
|
||||
__mr->_M_shared_state = std::move(__self);
|
||||
__mr->_M_set();
|
||||
__mr.release();
|
||||
}
|
||||
|
||||
// Abandon this shared state.
|
||||
void
|
||||
_M_break_promise(_Ptr_type __res)
|
||||
{
|
||||
@ -372,15 +420,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
{
|
||||
error_code __ec(make_error_code(future_errc::broken_promise));
|
||||
__res->_M_error = make_exception_ptr(future_error(__ec));
|
||||
// This function is only called when the last asynchronous result
|
||||
// provider is abandoning this shared state, so noone can be
|
||||
// trying to make the shared state ready at the same time, and
|
||||
// we can access _M_result directly instead of through call_once.
|
||||
{
|
||||
lock_guard<mutex> __lock(_M_mutex);
|
||||
_M_result.swap(__res);
|
||||
_M_ready = true;
|
||||
}
|
||||
_M_cond.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
// Called when this object is passed to a future.
|
||||
// Called when this object is first passed to a future.
|
||||
void
|
||||
_M_set_retrieved_flag()
|
||||
{
|
||||
@ -401,6 +454,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
|| is_same<const _Res, _Arg>::value, // promise<R>
|
||||
"Invalid specialisation");
|
||||
|
||||
// Used by std::promise to copy construct the result.
|
||||
typename promise<_Res>::_Ptr_type operator()()
|
||||
{
|
||||
_State_baseV2::_S_check(_M_promise->_M_future);
|
||||
@ -415,6 +469,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
template<typename _Res>
|
||||
struct _Setter<_Res, _Res&&>
|
||||
{
|
||||
// Used by std::promise to move construct the result.
|
||||
typename promise<_Res>::_Ptr_type operator()()
|
||||
{
|
||||
_State_baseV2::_S_check(_M_promise->_M_future);
|
||||
@ -431,6 +486,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
template<typename _Res>
|
||||
struct _Setter<_Res, __exception_ptr_tag>
|
||||
{
|
||||
// Used by std::promise to store an exception as the result.
|
||||
typename promise<_Res>::_Ptr_type operator()()
|
||||
{
|
||||
_State_baseV2::_S_check(_M_promise->_M_future);
|
||||
@ -465,6 +521,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
}
|
||||
|
||||
private:
|
||||
// The function invoked with std::call_once(_M_once, ...).
|
||||
void
|
||||
_M_do_set(function<_Ptr_type()>* __f, unique_lock<mutex>* __lock)
|
||||
{
|
||||
@ -473,14 +530,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
_M_result.swap(__res);
|
||||
}
|
||||
|
||||
bool _M_ready() const noexcept { return static_cast<bool>(_M_result); }
|
||||
|
||||
// Wait for completion of async function.
|
||||
virtual void _M_complete_async() { }
|
||||
|
||||
// Return true if state contains a deferred function.
|
||||
// Caller must own _M_mutex.
|
||||
// Return true if state corresponds to a deferred function.
|
||||
virtual bool _M_has_deferred() const { return false; }
|
||||
|
||||
struct _Make_ready final : __at_thread_exit_elt
|
||||
{
|
||||
weak_ptr<_State_baseV2> _M_shared_state;
|
||||
static void _S_run(void*);
|
||||
void _M_set();
|
||||
};
|
||||
};
|
||||
|
||||
#ifdef _GLIBCXX_ASYNC_ABI_COMPAT
|
||||
@ -531,7 +592,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
|
||||
_Result() noexcept : _M_value_ptr() { }
|
||||
|
||||
void _M_set(_Res& __res) noexcept { _M_value_ptr = &__res; }
|
||||
void
|
||||
_M_set(_Res& __res) noexcept
|
||||
{ _M_value_ptr = std::addressof(__res); }
|
||||
|
||||
_Res& _M_get() noexcept { return *_M_value_ptr; }
|
||||
|
||||
@ -1012,6 +1075,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
void
|
||||
set_exception(exception_ptr __p)
|
||||
{ _M_future->_M_set_result(_State::__setter(__p, this)); }
|
||||
|
||||
void
|
||||
set_value_at_thread_exit(const _Res& __r)
|
||||
{
|
||||
_M_future->_M_set_delayed_result(_State::__setter(this, __r),
|
||||
_M_future);
|
||||
}
|
||||
|
||||
void
|
||||
set_value_at_thread_exit(_Res&& __r)
|
||||
{
|
||||
_M_future->_M_set_delayed_result(
|
||||
_State::__setter(this, std::move(__r)), _M_future);
|
||||
}
|
||||
|
||||
void
|
||||
set_exception_at_thread_exit(exception_ptr __p)
|
||||
{
|
||||
_M_future->_M_set_delayed_result(_State::__setter(__p, this),
|
||||
_M_future);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename _Res>
|
||||
@ -1097,6 +1181,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
void
|
||||
set_exception(exception_ptr __p)
|
||||
{ _M_future->_M_set_result(_State::__setter(__p, this)); }
|
||||
|
||||
void
|
||||
set_value_at_thread_exit(_Res& __r)
|
||||
{
|
||||
_M_future->_M_set_delayed_result(_State::__setter(this, __r),
|
||||
_M_future);
|
||||
}
|
||||
|
||||
void
|
||||
set_exception_at_thread_exit(exception_ptr __p)
|
||||
{
|
||||
_M_future->_M_set_delayed_result(_State::__setter(__p, this),
|
||||
_M_future);
|
||||
}
|
||||
};
|
||||
|
||||
/// Explicit specialization for promise<void>
|
||||
@ -1172,6 +1270,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
void
|
||||
set_exception(exception_ptr __p)
|
||||
{ _M_future->_M_set_result(_State::__setter(__p, this)); }
|
||||
|
||||
void
|
||||
set_value_at_thread_exit();
|
||||
|
||||
void
|
||||
set_exception_at_thread_exit(exception_ptr __p)
|
||||
{
|
||||
_M_future->_M_set_delayed_result(_State::__setter(__p, this),
|
||||
_M_future);
|
||||
}
|
||||
};
|
||||
|
||||
// set void
|
||||
@ -1191,9 +1299,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
promise<void>::set_value()
|
||||
{ _M_future->_M_set_result(_State::_Setter<void, void>{ this }); }
|
||||
|
||||
inline void
|
||||
promise<void>::set_value_at_thread_exit()
|
||||
{
|
||||
_M_future->_M_set_delayed_result(_State::_Setter<void, void>{this},
|
||||
_M_future);
|
||||
}
|
||||
|
||||
template<typename _Ptr_type, typename _Fn, typename _Res>
|
||||
struct __future_base::_Task_setter
|
||||
{
|
||||
// Invoke the function and provide the result to the caller.
|
||||
_Ptr_type operator()()
|
||||
{
|
||||
__try
|
||||
@ -1237,6 +1353,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
_Fn* _M_fn;
|
||||
};
|
||||
|
||||
// Holds storage for a packaged_task's result.
|
||||
template<typename _Res, typename... _Args>
|
||||
struct __future_base::_Task_state_base<_Res(_Args...)>
|
||||
: __future_base::_State_base
|
||||
@ -1248,8 +1365,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
: _M_result(_S_allocate_result<_Res>(__a))
|
||||
{ }
|
||||
|
||||
// Invoke the stored task and make the state ready.
|
||||
virtual void
|
||||
_M_run(_Args... __args) = 0;
|
||||
_M_run(_Args&&... __args) = 0;
|
||||
|
||||
// Invoke the stored task and make the state ready at thread exit.
|
||||
virtual void
|
||||
_M_run_delayed(_Args&&... __args, weak_ptr<_State_base>) = 0;
|
||||
|
||||
virtual shared_ptr<_Task_state_base>
|
||||
_M_reset() = 0;
|
||||
@ -1258,6 +1380,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
_Ptr_type _M_result;
|
||||
};
|
||||
|
||||
// Holds a packaged_task's stored task.
|
||||
template<typename _Fn, typename _Alloc, typename _Res, typename... _Args>
|
||||
struct __future_base::_Task_state<_Fn, _Alloc, _Res(_Args...)> final
|
||||
: __future_base::_Task_state_base<_Res(_Args...)>
|
||||
@ -1270,7 +1393,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
|
||||
private:
|
||||
virtual void
|
||||
_M_run(_Args... __args)
|
||||
_M_run(_Args&&... __args)
|
||||
{
|
||||
// bound arguments decay so wrap lvalue references
|
||||
auto __boundfn = std::__bind_simple(std::ref(_M_impl._M_fn),
|
||||
@ -1278,6 +1401,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
this->_M_set_result(_S_task_setter(this->_M_result, __boundfn));
|
||||
}
|
||||
|
||||
virtual void
|
||||
_M_run_delayed(_Args&&... __args, weak_ptr<_State_base> __self)
|
||||
{
|
||||
// bound arguments decay so wrap lvalue references
|
||||
auto __boundfn = std::__bind_simple(std::ref(_M_impl._M_fn),
|
||||
_S_maybe_wrap_ref(std::forward<_Args>(__args))...);
|
||||
this->_M_set_delayed_result(_S_task_setter(this->_M_result, __boundfn),
|
||||
std::move(__self));
|
||||
}
|
||||
|
||||
virtual shared_ptr<_Task_state_base<_Res(_Args...)>>
|
||||
_M_reset();
|
||||
|
||||
@ -1412,6 +1545,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
_M_state->_M_run(std::forward<_ArgTypes>(__args)...);
|
||||
}
|
||||
|
||||
void
|
||||
make_ready_at_thread_exit(_ArgTypes... __args)
|
||||
{
|
||||
__future_base::_State_base::_S_check(_M_state);
|
||||
_M_state->_M_run_delayed(std::forward<_ArgTypes>(__args)..., _M_state);
|
||||
}
|
||||
|
||||
void
|
||||
reset()
|
||||
{
|
||||
@ -1434,6 +1574,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
: public true_type { };
|
||||
|
||||
|
||||
// Shared state created by std::async().
|
||||
// Holds a deferred function and storage for its result.
|
||||
template<typename _BoundFn, typename _Res>
|
||||
class __future_base::_Deferred_state final
|
||||
: public __future_base::_State_base
|
||||
@ -1453,14 +1595,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
virtual void
|
||||
_M_complete_async()
|
||||
{
|
||||
// safe to call multiple times so ignore failure
|
||||
// Multiple threads can call a waiting function on the future and
|
||||
// reach this point at the same time. The call_once in _M_set_result
|
||||
// ensures only the first one run the deferred function, stores the
|
||||
// result in _M_result, swaps that with the base _M_result and makes
|
||||
// the state ready. Tell _M_set_result to ignore failure so all later
|
||||
// calls do nothing.
|
||||
_M_set_result(_S_task_setter(_M_result, _M_fn), true);
|
||||
}
|
||||
|
||||
virtual bool
|
||||
_M_has_deferred() const { return static_cast<bool>(_M_result); }
|
||||
// Caller should check whether the state is ready first, because this
|
||||
// function will return true even after the deferred function has run.
|
||||
virtual bool _M_has_deferred() const { true; }
|
||||
};
|
||||
|
||||
// Common functionality hoisted out of the _Async_state_impl template.
|
||||
class __future_base::_Async_state_commonV2
|
||||
: public __future_base::_State_base
|
||||
{
|
||||
@ -1468,6 +1617,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
~_Async_state_commonV2() = default;
|
||||
|
||||
// Make waiting functions block until the thread completes, as if joined.
|
||||
//
|
||||
// This function is used by wait() to satisfy the first requirement below
|
||||
// and by wait_for() / wait_until() to satisfy the second.
|
||||
//
|
||||
// [futures.async]:
|
||||
//
|
||||
// — a call to a waiting function on an asynchronous return object that
|
||||
// shares the shared state created by this async call shall block until
|
||||
// the associated thread has completed, as if joined, or else time out.
|
||||
//
|
||||
// — the associated thread completion synchronizes with the return from
|
||||
// the first function that successfully detects the ready status of the
|
||||
// shared state or with the return from the last function that releases
|
||||
// the shared state, whichever happens first.
|
||||
virtual void _M_complete_async() { _M_join(); }
|
||||
|
||||
void _M_join() { std::call_once(_M_once, &thread::join, ref(_M_thread)); }
|
||||
@ -1476,6 +1639,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
once_flag _M_once;
|
||||
};
|
||||
|
||||
// Shared state created by std::async().
|
||||
// Starts a new thread that runs a function and makes the shared state ready.
|
||||
template<typename _BoundFn, typename _Res>
|
||||
class __future_base::_Async_state_impl final
|
||||
: public __future_base::_Async_state_commonV2
|
||||
@ -1500,7 +1665,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
} };
|
||||
}
|
||||
|
||||
~_Async_state_impl() { _M_join(); }
|
||||
// Must not destroy _M_result and _M_fn until the thread finishes.
|
||||
// Call join() directly rather than through _M_join() because no other
|
||||
// thread can be referring to this state if it is being destroyed.
|
||||
~_Async_state_impl() { if (_M_thread.joinable()) _M_thread.join(); }
|
||||
|
||||
private:
|
||||
typedef __future_base::_Ptr<_Result<_Res>> _Ptr_type;
|
||||
|
@ -77,6 +77,80 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
__throw_system_error(__e);
|
||||
}
|
||||
|
||||
extern void
|
||||
__at_thread_exit(__at_thread_exit_elt*);
|
||||
|
||||
namespace
|
||||
{
|
||||
__gthread_key_t key;
|
||||
|
||||
void run(void* p)
|
||||
{
|
||||
auto elt = (__at_thread_exit_elt*)p;
|
||||
while (elt)
|
||||
{
|
||||
auto next = elt->_M_next;
|
||||
elt->_M_cb(elt);
|
||||
elt = next;
|
||||
}
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
auto elt = (__at_thread_exit_elt*)__gthread_getspecific(key);
|
||||
__gthread_setspecific(key, nullptr);
|
||||
run(elt);
|
||||
}
|
||||
|
||||
struct notifier final : __at_thread_exit_elt
|
||||
{
|
||||
notifier(condition_variable& cv, unique_lock<mutex>& l)
|
||||
: cv(&cv), mx(l.release())
|
||||
{
|
||||
_M_cb = ¬ifier::run;
|
||||
__at_thread_exit(this);
|
||||
}
|
||||
|
||||
~notifier()
|
||||
{
|
||||
mx->unlock();
|
||||
cv->notify_all();
|
||||
}
|
||||
|
||||
condition_variable* cv;
|
||||
mutex* mx;
|
||||
|
||||
static void run(void* p) { delete static_cast<notifier*>(p); }
|
||||
};
|
||||
|
||||
|
||||
void key_init() {
|
||||
struct key_s {
|
||||
key_s() { __gthread_key_create (&key, run); }
|
||||
~key_s() { __gthread_key_delete (key); }
|
||||
};
|
||||
static key_s ks;
|
||||
// Also make sure the callbacks are run by std::exit.
|
||||
std::atexit (run);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
__at_thread_exit(__at_thread_exit_elt* elt)
|
||||
{
|
||||
static __gthread_once_t once = __GTHREAD_ONCE_INIT;
|
||||
__gthread_once (&once, key_init);
|
||||
|
||||
elt->_M_next = (__at_thread_exit_elt*)__gthread_getspecific(key);
|
||||
__gthread_setspecific(key, elt);
|
||||
}
|
||||
|
||||
void
|
||||
notify_all_at_thread_exit(condition_variable& cv, unique_lock<mutex> l)
|
||||
{
|
||||
(void) new notifier{cv, l};
|
||||
}
|
||||
|
||||
_GLIBCXX_END_NAMESPACE_VERSION
|
||||
} // namespace
|
||||
|
||||
|
@ -82,6 +82,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
__future_base::_Result_base::_Result_base() = default;
|
||||
|
||||
__future_base::_Result_base::~_Result_base() = default;
|
||||
|
||||
void
|
||||
__future_base::_State_baseV2::_Make_ready::_S_run(void* p)
|
||||
{
|
||||
unique_ptr<_Make_ready> mr{static_cast<_Make_ready*>(p)};
|
||||
if (auto state = mr->_M_shared_state.lock())
|
||||
{
|
||||
{
|
||||
lock_guard<mutex> __lock{state->_M_mutex};
|
||||
state->_M_ready = true;
|
||||
}
|
||||
state->_M_cond.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
// defined in src/c++11/condition_variable.cc
|
||||
extern void
|
||||
__at_thread_exit(__at_thread_exit_elt* elt);
|
||||
|
||||
void
|
||||
__future_base::_State_baseV2::_Make_ready::_M_set()
|
||||
{
|
||||
_M_cb = &_Make_ready::_S_run;
|
||||
__at_thread_exit(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
_GLIBCXX_END_NAMESPACE_VERSION
|
||||
|
@ -0,0 +1,55 @@
|
||||
// { dg-do run { target *-*-freebsd* *-*-dragonfly* *-*-netbsd* *-*-linux* *-*-gnu* *-*-solaris* *-*-cygwin *-*-darwin* powerpc-ibm-aix* } }
|
||||
// { dg-options " -std=gnu++11 -pthread" { target *-*-freebsd* *-*-dragonfly* *-*-netbsd* *-*-linux* *-*-gnu* powerpc-ibm-aix* } }
|
||||
// { dg-options " -std=gnu++11 -pthreads" { target *-*-solaris* } }
|
||||
// { dg-options " -std=gnu++11 " { target *-*-cygwin *-*-darwin* } }
|
||||
// { dg-require-cstdint "" }
|
||||
// { dg-require-gthreads "" }
|
||||
|
||||
// Copyright (C) 2014 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.
|
||||
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this library; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
std::mutex mx;
|
||||
std::condition_variable cv;
|
||||
int counter = 0;
|
||||
|
||||
struct Inc
|
||||
{
|
||||
Inc() { ++counter; }
|
||||
~Inc() { ++counter; }
|
||||
};
|
||||
|
||||
|
||||
void func()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock{mx};
|
||||
std::notify_all_at_thread_exit(cv, std::move(lock));
|
||||
static thread_local Inc inc;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
bool test __attribute__((unused)) = true;
|
||||
|
||||
std::unique_lock<std::mutex> lock{mx};
|
||||
std::thread t{func};
|
||||
cv.wait(lock, [&]{ return counter == 2; });
|
||||
t.join();
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
// { dg-do run { target *-*-freebsd* *-*-dragonfly* *-*-netbsd* *-*-linux* *-*-gnu* *-*-solaris* *-*-cygwin *-*-darwin* powerpc-ibm-aix* } }
|
||||
// { dg-options " -std=gnu++11 -pthread" { target *-*-freebsd* *-*-dragonfly* *-*-netbsd* *-*-linux* *-*-gnu* powerpc-ibm-aix* } }
|
||||
// { dg-options " -std=gnu++11 -pthreads" { target *-*-solaris* } }
|
||||
// { dg-options " -std=gnu++11 " { target *-*-cygwin *-*-darwin* } }
|
||||
// { dg-require-cstdint "" }
|
||||
// { dg-require-gthreads "" }
|
||||
// { dg-require-atomic-builtins "" }
|
||||
|
||||
// Copyright (C) 2014 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.
|
||||
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this library; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
#include <future>
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
bool executed = false;
|
||||
|
||||
int execute(int i) { executed = true; return i + 1; }
|
||||
|
||||
std::future<int> f1;
|
||||
|
||||
bool ready(std::future<int>& f)
|
||||
{
|
||||
return f.wait_for(std::chrono::milliseconds(1)) == std::future_status::ready;
|
||||
}
|
||||
|
||||
void test01()
|
||||
{
|
||||
bool test __attribute__((unused)) = true;
|
||||
|
||||
std::packaged_task<int(int)> p1(execute);
|
||||
f1 = p1.get_future();
|
||||
|
||||
p1.make_ready_at_thread_exit(1);
|
||||
|
||||
VERIFY( executed );
|
||||
VERIFY( p1.valid() );
|
||||
VERIFY( !ready(f1) );
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::thread t{test01};
|
||||
t.join();
|
||||
VERIFY( ready(f1) );
|
||||
VERIFY( f1.get() == 2 );
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
// { dg-do run { target *-*-freebsd* *-*-dragonfly* *-*-netbsd* *-*-linux* *-*-gnu* *-*-solaris* *-*-cygwin *-*-darwin* powerpc-ibm-aix* } }
|
||||
// { dg-options " -std=gnu++11 -pthread" { target *-*-freebsd* *-*-dragonfly* *-*-netbsd* *-*-linux* *-*-gnu* powerpc-ibm-aix* } }
|
||||
// { dg-options " -std=gnu++11 -pthreads" { target *-*-solaris* } }
|
||||
// { dg-options " -std=gnu++11 " { target *-*-cygwin *-*-darwin* } }
|
||||
// { dg-require-cstdint "" }
|
||||
// { dg-require-gthreads "" }
|
||||
// { dg-require-atomic-builtins "" }
|
||||
|
||||
// Copyright (C) 2014 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.
|
||||
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this library; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
#include <future>
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
int copies;
|
||||
int copies_cmp;
|
||||
|
||||
struct Obj
|
||||
{
|
||||
Obj() = default;
|
||||
Obj(const Obj&) { ++copies; }
|
||||
};
|
||||
|
||||
std::future<Obj> f1;
|
||||
|
||||
bool ready(std::future<Obj>& f)
|
||||
{
|
||||
return f.wait_for(std::chrono::milliseconds(1)) == std::future_status::ready;
|
||||
}
|
||||
|
||||
void test01()
|
||||
{
|
||||
bool test __attribute__((unused)) = true;
|
||||
|
||||
std::promise<Obj> p1;
|
||||
f1 = p1.get_future();
|
||||
|
||||
p1.set_value_at_thread_exit( {} );
|
||||
|
||||
copies_cmp = copies;
|
||||
|
||||
VERIFY( !ready(f1) );
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::thread t{test01};
|
||||
t.join();
|
||||
VERIFY( ready(f1) );
|
||||
VERIFY( copies == copies_cmp );
|
||||
}
|
Loading…
Reference in New Issue
Block a user