Define *_at_thread_exit() functions.
authorredi <redi@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 2 Dec 2014 01:51:25 +0000 (01:51 +0000)
committerredi <redi@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 2 Dec 2014 01:51:25 +0000 (01:51 +0000)
* 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.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@218255 138bc75d-0d04-0410-961f-82ee72b054a4

libstdc++-v3/ChangeLog
libstdc++-v3/config/abi/pre/gnu.ver
libstdc++-v3/include/std/condition_variable
libstdc++-v3/include/std/future
libstdc++-v3/src/c++11/condition_variable.cc
libstdc++-v3/src/c++11/future.cc
libstdc++-v3/testsuite/30_threads/condition_variable/members/3.cc [new file with mode: 0644]
libstdc++-v3/testsuite/30_threads/packaged_task/members/at_thread_exit.cc [new file with mode: 0644]
libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit.cc [new file with mode: 0644]

index 12dd1e3..19499dc 100644 (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
index 5176b29..c73ebe7 100644 (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;
 
 
index 921cb83..a3682c0 100644 (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
index 8989474..60c2e4e 100644 (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();
 
@@ -1413,6 +1546,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
       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()
       {
        __future_base::_State_base::_S_check(_M_state);
@@ -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;
index 7f78c39..c2768eb 100644 (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
 
index 6ffab18..ca42dc1 100644 (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
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 (file)
index 0000000..0da545d
--- /dev/null
@@ -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();
+}
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 (file)
index 0000000..5bbdd3d
--- /dev/null
@@ -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 );
+}
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 (file)
index 0000000..3842a13
--- /dev/null
@@ -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 );
+}