commit 9f0d052017db0e0484cbfaf79677c62e3532904f
Author: Jonathan Wakely <jwakely@redhat.com>
Date: Mon Oct 20 12:23:24 2014 +0100
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 (__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_result_aside): Define.
(__future_base::_State_baseV2::_M_break_promise): Set _M_ready.
(__future_base::_State_baseV2::_Make_ready): New helper class.
(__future_base::_Task_state_base::__run_not_ready): Declare new pure
virtual function.
(__future_base::_Task_state::__run_not_ready): 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.
@@ -124,7 +124,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*;
@@ -1476,6 +1477,11 @@ GLIBCXX_3.4.21 {
# std::ctype_base::blank
_ZNSt10ctype_base5blankE;
+ # 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
@@ -294,12 +294,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;
@@ -309,7 +309,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{
_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,11 +318,11 @@ _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
@@ -337,11 +337,11 @@ _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
@@ -360,12 +360,32 @@ _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));
}
void
+ _M_set_result_aside(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();
+ }
+
+ void
_M_break_promise(_Ptr_type __res)
{
if (static_cast<bool>(__res))
@@ -375,6 +395,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{
lock_guard<mutex> __lock(_M_mutex);
_M_result.swap(__res);
+ _M_ready = true;
}
_M_cond.notify_all();
}
@@ -473,14 +494,19 @@ _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.
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
@@ -1012,6 +1038,25 @@ _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_result_aside(_State::__setter(this, __r), _M_future);
+ }
+
+ void
+ set_value_at_thread_exit(_Res&& __r)
+ {
+ _M_future->_M_set_result_aside(_State::__setter(this, std::move(__r)),
+ _M_future);
+ }
+
+ void
+ set_exception_at_thread_exit(exception_ptr __p)
+ {
+ _M_future->_M_set_result_aside(_State::__setter(__p, this), _M_future);
+ }
};
template<typename _Res>
@@ -1097,6 +1142,18 @@ _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_result_aside(_State::__setter(this, __r), _M_future);
+ }
+
+ void
+ set_exception_at_thread_exit(exception_ptr __p)
+ {
+ _M_future->_M_set_result_aside(_State::__setter(__p, this), _M_future);
+ }
};
/// Explicit specialization for promise<void>
@@ -1172,6 +1229,15 @@ _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_result_aside(_State::__setter(__p, this), _M_future);
+ }
};
// set void
@@ -1191,6 +1257,13 @@ _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_result_aside(_State::_Setter<void, void>{this},
+ _M_future);
+ }
+
template<typename _Ptr_type, typename _Fn, typename _Res>
struct __future_base::_Task_setter
{
@@ -1251,6 +1324,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
virtual void
_M_run(_Args... __args) = 0;
+ virtual void
+ _M_run_not_ready(_Args... __args, weak_ptr<_State_base>) = 0;
+
virtual shared_ptr<_Task_state_base>
_M_reset() = 0;
@@ -1278,6 +1354,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
this->_M_set_result(_S_task_setter(this->_M_result, __boundfn));
}
+ virtual void
+ _M_run_not_ready(_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_result_aside(_S_task_setter(this->_M_result, __boundfn),
+ std::move(__self));
+ }
+
virtual shared_ptr<_Task_state_base<_Res(_Args...)>>
_M_reset();
@@ -1413,6 +1499,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
void
+ make_ready_at_thread_exit(_ArgTypes... __args)
+ {
+ __future_base::_State_base::_S_check(_M_state);
+ _M_state->_M_run_not_ready(std::forward<_ArgTypes>(__args)...,
+ _M_state);
+ }
+
+ void
reset()
{
__future_base::_State_base::_S_check(_M_state);
@@ -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
new file mode 100644
@@ -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();
+}
new file mode 100644
@@ -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 );
+}
new file mode 100644
@@ -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 );
+}