Commit 9db7c931 by Jonathan Wakely Committed by Jonathan Wakely

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
parent 8581fd64
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> 2014-12-01 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/63840 PR libstdc++/63840
......
...@@ -128,7 +128,8 @@ GLIBCXX_3.4 { ...@@ -128,7 +128,8 @@ GLIBCXX_3.4 {
std::messages*; std::messages*;
std::money*; std::money*;
# std::n[^u]*; # std::n[^u]*;
std::n[^aue]*; std::n[^aueo]*;
std::nothrow;
std::nu[^m]*; std::nu[^m]*;
std::num[^e]*; std::num[^e]*;
std::ostrstream*; std::ostrstream*;
...@@ -1500,6 +1501,11 @@ GLIBCXX_3.4.21 { ...@@ -1500,6 +1501,11 @@ GLIBCXX_3.4.21 {
# std::_Sp_locker::* # std::_Sp_locker::*
_ZNSt10_Sp_locker[CD]*; _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; } GLIBCXX_3.4.20;
......
...@@ -170,6 +170,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -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 { inline namespace _V2 {
/// condition_variable_any /// condition_variable_any
......
...@@ -202,7 +202,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -202,7 +202,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
virtual ~_Result_base(); 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> template<typename _Res>
struct _Result : _Result_base struct _Result : _Result_base
{ {
...@@ -243,11 +247,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -243,11 +247,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
void _M_destroy() { delete this; } void _M_destroy() { delete this; }
}; };
/// A unique_ptr based on the instantiating type. /// A result object that uses an allocator.
template<typename _Res>
using _Ptr = unique_ptr<_Res, _Result_base::_Deleter>;
/// Result_alloc.
template<typename _Res, typename _Alloc> template<typename _Res, typename _Alloc>
struct _Result_alloc final : _Result<_Res>, _Alloc struct _Result_alloc final : _Result<_Res>, _Alloc
{ {
...@@ -266,6 +266,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -266,6 +266,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
} }
}; };
// Create a result object that uses an allocator.
template<typename _Res, typename _Allocator> template<typename _Res, typename _Allocator>
static _Ptr<_Result_alloc<_Res, _Allocator>> static _Ptr<_Result_alloc<_Res, _Allocator>>
_S_allocate_result(const _Allocator& __a) _S_allocate_result(const _Allocator& __a)
...@@ -278,6 +279,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -278,6 +279,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return _Ptr<__result_type>(__p); return _Ptr<__result_type>(__p);
} }
// Keep it simple for std::allocator.
template<typename _Res, typename _Tp> template<typename _Res, typename _Tp>
static _Ptr<_Result<_Res>> static _Ptr<_Result<_Res>>
_S_allocate_result(const std::allocator<_Tp>& __a) _S_allocate_result(const std::allocator<_Tp>& __a)
...@@ -285,8 +287,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -285,8 +287,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return _Ptr<_Result<_Res>>(new _Result<_Res>); return _Ptr<_Result<_Res>>(new _Result<_Res>);
} }
/// Base class for state between a promise and one or more // Base class for various types of shared state created by an
/// associated futures. // asynchronous provider (such as a std::promise) and shared with one
// or more associated futures.
class _State_baseV2 class _State_baseV2
{ {
typedef _Ptr<_Result_base> _Ptr_type; typedef _Ptr<_Result_base> _Ptr_type;
...@@ -294,12 +297,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -294,12 +297,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_Ptr_type _M_result; _Ptr_type _M_result;
mutex _M_mutex; mutex _M_mutex;
condition_variable _M_cond; condition_variable _M_cond;
atomic_flag _M_retrieved; atomic_flag _M_retrieved = ATOMIC_FLAG_INIT;
bool _M_ready = false;
once_flag _M_once; once_flag _M_once;
public: public:
_State_baseV2() noexcept : _M_result(), _M_retrieved(ATOMIC_FLAG_INIT) _State_baseV2() noexcept = default;
{ }
_State_baseV2(const _State_baseV2&) = delete; _State_baseV2(const _State_baseV2&) = delete;
_State_baseV2& operator=(const _State_baseV2&) = delete; _State_baseV2& operator=(const _State_baseV2&) = delete;
virtual ~_State_baseV2() = default; virtual ~_State_baseV2() = default;
...@@ -307,9 +310,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -307,9 +310,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_Result_base& _Result_base&
wait() wait()
{ {
// Run any deferred function or join any asynchronous thread:
_M_complete_async(); _M_complete_async();
unique_lock<mutex> __lock(_M_mutex); unique_lock<mutex> __lock(_M_mutex);
_M_cond.wait(__lock, [&] { return _M_ready(); }); _M_cond.wait(__lock, [&] { return _M_ready; });
return *_M_result; return *_M_result;
} }
...@@ -318,15 +323,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -318,15 +323,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
wait_for(const chrono::duration<_Rep, _Period>& __rel) wait_for(const chrono::duration<_Rep, _Period>& __rel)
{ {
unique_lock<mutex> __lock(_M_mutex); unique_lock<mutex> __lock(_M_mutex);
if (_M_ready()) if (_M_ready)
return future_status::ready; return future_status::ready;
if (_M_has_deferred()) if (_M_has_deferred())
return future_status::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 // _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2100. timed waiting functions must also join // 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(); _M_complete_async();
return future_status::ready; return future_status::ready;
} }
return future_status::timeout; return future_status::timeout;
...@@ -337,20 +350,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -337,20 +350,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
wait_until(const chrono::time_point<_Clock, _Duration>& __abs) wait_until(const chrono::time_point<_Clock, _Duration>& __abs)
{ {
unique_lock<mutex> __lock(_M_mutex); unique_lock<mutex> __lock(_M_mutex);
if (_M_ready()) if (_M_ready)
return future_status::ready; return future_status::ready;
if (_M_has_deferred()) if (_M_has_deferred())
return future_status::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 // _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2100. timed waiting functions must also join // 2100. timed waiting functions must also join
// See wait_for(...) above.
_M_complete_async(); _M_complete_async();
return future_status::ready; return future_status::ready;
} }
return future_status::timeout; 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 void
_M_set_result(function<_Ptr_type()> __res, bool __ignore_failure = false) _M_set_result(function<_Ptr_type()> __res, bool __ignore_failure = false)
{ {
...@@ -360,11 +381,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -360,11 +381,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
call_once(_M_once, &_State_baseV2::_M_do_set, this, call_once(_M_once, &_State_baseV2::_M_do_set, this,
std::__addressof(__res), std::__addressof(__lock)); std::__addressof(__res), std::__addressof(__lock));
if (__lock.owns_lock()) if (__lock.owns_lock())
{
_M_ready = true;
_M_cond.notify_all(); _M_cond.notify_all();
}
else if (!__ignore_failure) else if (!__ignore_failure)
__throw_future_error(int(future_errc::promise_already_satisfied)); __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 void
_M_break_promise(_Ptr_type __res) _M_break_promise(_Ptr_type __res)
{ {
...@@ -372,15 +420,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -372,15 +420,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ {
error_code __ec(make_error_code(future_errc::broken_promise)); error_code __ec(make_error_code(future_errc::broken_promise));
__res->_M_error = make_exception_ptr(future_error(__ec)); __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); lock_guard<mutex> __lock(_M_mutex);
_M_result.swap(__res); _M_result.swap(__res);
_M_ready = true;
} }
_M_cond.notify_all(); _M_cond.notify_all();
} }
} }
// Called when this object is passed to a future. // Called when this object is first passed to a future.
void void
_M_set_retrieved_flag() _M_set_retrieved_flag()
{ {
...@@ -401,6 +454,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -401,6 +454,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|| is_same<const _Res, _Arg>::value, // promise<R> || is_same<const _Res, _Arg>::value, // promise<R>
"Invalid specialisation"); "Invalid specialisation");
// Used by std::promise to copy construct the result.
typename promise<_Res>::_Ptr_type operator()() typename promise<_Res>::_Ptr_type operator()()
{ {
_State_baseV2::_S_check(_M_promise->_M_future); _State_baseV2::_S_check(_M_promise->_M_future);
...@@ -415,6 +469,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -415,6 +469,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Res> template<typename _Res>
struct _Setter<_Res, _Res&&> struct _Setter<_Res, _Res&&>
{ {
// Used by std::promise to move construct the result.
typename promise<_Res>::_Ptr_type operator()() typename promise<_Res>::_Ptr_type operator()()
{ {
_State_baseV2::_S_check(_M_promise->_M_future); _State_baseV2::_S_check(_M_promise->_M_future);
...@@ -431,6 +486,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -431,6 +486,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Res> template<typename _Res>
struct _Setter<_Res, __exception_ptr_tag> struct _Setter<_Res, __exception_ptr_tag>
{ {
// Used by std::promise to store an exception as the result.
typename promise<_Res>::_Ptr_type operator()() typename promise<_Res>::_Ptr_type operator()()
{ {
_State_baseV2::_S_check(_M_promise->_M_future); _State_baseV2::_S_check(_M_promise->_M_future);
...@@ -465,6 +521,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -465,6 +521,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
} }
private: private:
// The function invoked with std::call_once(_M_once, ...).
void void
_M_do_set(function<_Ptr_type()>* __f, unique_lock<mutex>* __lock) _M_do_set(function<_Ptr_type()>* __f, unique_lock<mutex>* __lock)
{ {
...@@ -473,14 +530,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -473,14 +530,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_M_result.swap(__res); _M_result.swap(__res);
} }
bool _M_ready() const noexcept { return static_cast<bool>(_M_result); }
// Wait for completion of async function. // Wait for completion of async function.
virtual void _M_complete_async() { } virtual void _M_complete_async() { }
// Return true if state contains a deferred function. // Return true if state corresponds to a deferred function.
// Caller must own _M_mutex.
virtual bool _M_has_deferred() const { return false; } 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 #ifdef _GLIBCXX_ASYNC_ABI_COMPAT
...@@ -531,7 +592,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -531,7 +592,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_Result() noexcept : _M_value_ptr() { } _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; } _Res& _M_get() noexcept { return *_M_value_ptr; }
...@@ -1012,6 +1075,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1012,6 +1075,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
void void
set_exception(exception_ptr __p) set_exception(exception_ptr __p)
{ _M_future->_M_set_result(_State::__setter(__p, this)); } { _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> template<typename _Res>
...@@ -1097,6 +1181,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1097,6 +1181,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
void void
set_exception(exception_ptr __p) set_exception(exception_ptr __p)
{ _M_future->_M_set_result(_State::__setter(__p, this)); } { _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> /// Explicit specialization for promise<void>
...@@ -1172,6 +1270,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1172,6 +1270,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
void void
set_exception(exception_ptr __p) set_exception(exception_ptr __p)
{ _M_future->_M_set_result(_State::__setter(__p, this)); } { _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 // set void
...@@ -1191,9 +1299,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1191,9 +1299,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
promise<void>::set_value() promise<void>::set_value()
{ _M_future->_M_set_result(_State::_Setter<void, void>{ this }); } { _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> template<typename _Ptr_type, typename _Fn, typename _Res>
struct __future_base::_Task_setter struct __future_base::_Task_setter
{ {
// Invoke the function and provide the result to the caller.
_Ptr_type operator()() _Ptr_type operator()()
{ {
__try __try
...@@ -1237,6 +1353,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1237,6 +1353,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_Fn* _M_fn; _Fn* _M_fn;
}; };
// Holds storage for a packaged_task's result.
template<typename _Res, typename... _Args> template<typename _Res, typename... _Args>
struct __future_base::_Task_state_base<_Res(_Args...)> struct __future_base::_Task_state_base<_Res(_Args...)>
: __future_base::_State_base : __future_base::_State_base
...@@ -1248,8 +1365,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1248,8 +1365,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: _M_result(_S_allocate_result<_Res>(__a)) : _M_result(_S_allocate_result<_Res>(__a))
{ } { }
// Invoke the stored task and make the state ready.
virtual void
_M_run(_Args&&... __args) = 0;
// Invoke the stored task and make the state ready at thread exit.
virtual void virtual void
_M_run(_Args... __args) = 0; _M_run_delayed(_Args&&... __args, weak_ptr<_State_base>) = 0;
virtual shared_ptr<_Task_state_base> virtual shared_ptr<_Task_state_base>
_M_reset() = 0; _M_reset() = 0;
...@@ -1258,6 +1380,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1258,6 +1380,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_Ptr_type _M_result; _Ptr_type _M_result;
}; };
// Holds a packaged_task's stored task.
template<typename _Fn, typename _Alloc, typename _Res, typename... _Args> template<typename _Fn, typename _Alloc, typename _Res, typename... _Args>
struct __future_base::_Task_state<_Fn, _Alloc, _Res(_Args...)> final struct __future_base::_Task_state<_Fn, _Alloc, _Res(_Args...)> final
: __future_base::_Task_state_base<_Res(_Args...)> : __future_base::_Task_state_base<_Res(_Args...)>
...@@ -1270,7 +1393,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1270,7 +1393,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
private: private:
virtual void virtual void
_M_run(_Args... __args) _M_run(_Args&&... __args)
{ {
// bound arguments decay so wrap lvalue references // bound arguments decay so wrap lvalue references
auto __boundfn = std::__bind_simple(std::ref(_M_impl._M_fn), auto __boundfn = std::__bind_simple(std::ref(_M_impl._M_fn),
...@@ -1278,6 +1401,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1278,6 +1401,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
this->_M_set_result(_S_task_setter(this->_M_result, __boundfn)); 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...)>> virtual shared_ptr<_Task_state_base<_Res(_Args...)>>
_M_reset(); _M_reset();
...@@ -1413,6 +1546,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1413,6 +1546,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
} }
void 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() reset()
{ {
__future_base::_State_base::_S_check(_M_state); __future_base::_State_base::_S_check(_M_state);
...@@ -1434,6 +1574,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1434,6 +1574,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: public true_type { }; : public true_type { };
// Shared state created by std::async().
// Holds a deferred function and storage for its result.
template<typename _BoundFn, typename _Res> template<typename _BoundFn, typename _Res>
class __future_base::_Deferred_state final class __future_base::_Deferred_state final
: public __future_base::_State_base : public __future_base::_State_base
...@@ -1453,14 +1595,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1453,14 +1595,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
virtual void virtual void
_M_complete_async() _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); _M_set_result(_S_task_setter(_M_result, _M_fn), true);
} }
virtual bool // Caller should check whether the state is ready first, because this
_M_has_deferred() const { return static_cast<bool>(_M_result); } // 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 class __future_base::_Async_state_commonV2
: public __future_base::_State_base : public __future_base::_State_base
{ {
...@@ -1468,6 +1617,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1468,6 +1617,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
~_Async_state_commonV2() = default; ~_Async_state_commonV2() = default;
// Make waiting functions block until the thread completes, as if joined. // 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(); } virtual void _M_complete_async() { _M_join(); }
void _M_join() { std::call_once(_M_once, &thread::join, ref(_M_thread)); } void _M_join() { std::call_once(_M_once, &thread::join, ref(_M_thread)); }
...@@ -1476,6 +1639,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1476,6 +1639,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
once_flag _M_once; 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> template<typename _BoundFn, typename _Res>
class __future_base::_Async_state_impl final class __future_base::_Async_state_impl final
: public __future_base::_Async_state_commonV2 : public __future_base::_Async_state_commonV2
...@@ -1500,7 +1665,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -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: private:
typedef __future_base::_Ptr<_Result<_Res>> _Ptr_type; typedef __future_base::_Ptr<_Result<_Res>> _Ptr_type;
......
...@@ -77,6 +77,80 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -77,6 +77,80 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__throw_system_error(__e); __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 = &notifier::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 _GLIBCXX_END_NAMESPACE_VERSION
} // namespace } // namespace
......
...@@ -82,6 +82,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -82,6 +82,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__future_base::_Result_base::_Result_base() = default; __future_base::_Result_base::_Result_base() = default;
__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 #endif
_GLIBCXX_END_NAMESPACE_VERSION _GLIBCXX_END_NAMESPACE_VERSION
......
// { 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();
}
// { 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 );
}
// { 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 );
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment