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:
Jonathan Wakely 2014-12-02 01:51:25 +00:00 committed by Jonathan Wakely
parent 8581fd6499
commit 9db7c9316e
9 changed files with 527 additions and 30 deletions

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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_cond.notify_all();
{
_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;

View File

@ -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 = &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
} // namespace

View File

@ -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

View File

@ -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();
}

View File

@ -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 );
}

View File

@ -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 );
}