From: Jonathan Wakely Date: Tue, 2 Dec 2014 01:51:25 +0000 (+0000) Subject: Define *_at_thread_exit() functions. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=9db7c9316e484499286d39d4c15e77310bedd3e9;p=gcc.git 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 --- diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 12dd1e3c4cb..19499dc390f 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,36 @@ +2014-12-02 Jonathan Wakely + + * 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 PR libstdc++/63840 diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver index 5176b2927bb..c73ebe7a655 100644 --- a/libstdc++-v3/config/abi/pre/gnu.ver +++ b/libstdc++-v3/config/abi/pre/gnu.ver @@ -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; diff --git a/libstdc++-v3/include/std/condition_variable b/libstdc++-v3/include/std/condition_variable index 921cb837de9..a3682c0bc79 100644 --- a/libstdc++-v3/include/std/condition_variable +++ b/libstdc++-v3/include/std/condition_variable @@ -170,6 +170,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } }; + void + notify_all_at_thread_exit(condition_variable&, unique_lock); + + struct __at_thread_exit_elt + { + __at_thread_exit_elt* _M_next; + void (*_M_cb)(void*); + }; + inline namespace _V2 { /// condition_variable_any diff --git a/libstdc++-v3/include/std/future b/libstdc++-v3/include/std/future index 8989474c74c..60c2e4eb6d9 100644 --- a/libstdc++-v3/include/std/future +++ b/libstdc++-v3/include/std/future @@ -202,7 +202,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION virtual ~_Result_base(); }; - /// Result. + /// A unique_ptr for result objects. + template + using _Ptr = unique_ptr<_Res, _Result_base::_Deleter>; + + /// A result object that has storage for an object of type _Res. template 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 - using _Ptr = unique_ptr<_Res, _Result_base::_Deleter>; - - /// Result_alloc. + /// A result object that uses an allocator. template struct _Result_alloc final : _Result<_Res>, _Alloc { @@ -266,6 +266,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } }; + // Create a result object that uses an allocator. template 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 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 __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 __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 __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 __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 __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::value, // promise "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 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 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* __lock) { @@ -473,14 +530,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_result.swap(__res); } - bool _M_ready() const noexcept { return static_cast(_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 @@ -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 @@ -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::set_value() { _M_future->_M_set_result(_State::_Setter{ this }); } + inline void + promise::set_value_at_thread_exit() + { + _M_future->_M_set_delayed_result(_State::_Setter{this}, + _M_future); + } + template 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 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 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 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(_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 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; diff --git a/libstdc++-v3/src/c++11/condition_variable.cc b/libstdc++-v3/src/c++11/condition_variable.cc index 7f78c39b03d..c2768eb86e4 100644 --- a/libstdc++-v3/src/c++11/condition_variable.cc +++ b/libstdc++-v3/src/c++11/condition_variable.cc @@ -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& 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(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 l) + { + (void) new notifier{cv, l}; + } + _GLIBCXX_END_NAMESPACE_VERSION } // namespace diff --git a/libstdc++-v3/src/c++11/future.cc b/libstdc++-v3/src/c++11/future.cc index 6ffab18d4d4..ca42dc19b8b 100644 --- a/libstdc++-v3/src/c++11/future.cc +++ b/libstdc++-v3/src/c++11/future.cc @@ -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 __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 diff --git a/libstdc++-v3/testsuite/30_threads/condition_variable/members/3.cc b/libstdc++-v3/testsuite/30_threads/condition_variable/members/3.cc new file mode 100644 index 00000000000..0da545d1439 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/condition_variable/members/3.cc @@ -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 +// . + +#include +#include +#include + +std::mutex mx; +std::condition_variable cv; +int counter = 0; + +struct Inc +{ + Inc() { ++counter; } + ~Inc() { ++counter; } +}; + + +void func() +{ + std::unique_lock 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 lock{mx}; + std::thread t{func}; + cv.wait(lock, [&]{ return counter == 2; }); + t.join(); +} diff --git a/libstdc++-v3/testsuite/30_threads/packaged_task/members/at_thread_exit.cc b/libstdc++-v3/testsuite/30_threads/packaged_task/members/at_thread_exit.cc new file mode 100644 index 00000000000..5bbdd3da670 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/packaged_task/members/at_thread_exit.cc @@ -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 +// . + + +#include +#include + +bool executed = false; + +int execute(int i) { executed = true; return i + 1; } + +std::future f1; + +bool ready(std::future& f) +{ + return f.wait_for(std::chrono::milliseconds(1)) == std::future_status::ready; +} + +void test01() +{ + bool test __attribute__((unused)) = true; + + std::packaged_task 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 ); +} diff --git a/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit.cc b/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit.cc new file mode 100644 index 00000000000..3842a131688 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit.cc @@ -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 +// . + + +#include +#include + +int copies; +int copies_cmp; + +struct Obj +{ + Obj() = default; + Obj(const Obj&) { ++copies; } +}; + +std::future f1; + +bool ready(std::future& f) +{ + return f.wait_for(std::chrono::milliseconds(1)) == std::future_status::ready; +} + +void test01() +{ + bool test __attribute__((unused)) = true; + + std::promise 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 ); +}