From f2f08be7ea13b75632f1ecdddbefb928b8fc0fe4 Mon Sep 17 00:00:00 2001
From: Jonathan Wakely <jwakely.gcc@gmail.com>
Date: Wed, 20 Nov 2013 20:59:19 +0000
Subject: [PATCH] re PR libstdc++/49204 ([C++0x] remaining issues in <future>)

	PR libstdc++/49204
	* include/std/future (__future_base::_State_base): Rename to
	__future_base::_State_baseV2.
	(__future_base::_State_baseV2::~_State_baseV2): Define as defaulted.
	(__future_base::_State_baseV2::_M_run_deferred): Rename to
	_M_complete_async.
	(__future_base::_State_baseV2::_M_has_deferred): Add new virtual.
	(__future_base::_State_baseV2::wait_for): Call _M_has_deferred() to
	test for a deferred function, or call _M_complete_async() to join an
	async thread that has made the shared state ready.
	(__future_base::_State_baseV2::wait_until): Likewise.
	(__future_base::_Async_state_common): Rename to _Async_state_commonV2.
	(__future_base::_Async_state_commonV2::_M_run_deferred): Rename to
	_M_complete_async.
	* src/c++11/compatibility-thread-c++0x.cc (__future_base::_State_base):
	Export old definition.
	(__future_base::_Async_state_common): Likewise.
	* src/c++11/future.cc (__future_base::_State_base::~_State_base):
	Remove.
	* doc/xml/manual/status_cxx2011.xml: Update status.
	* testsuite/30_threads/async/async.cc: Test future_status::timeout
	and future_status::ready.
	* testsuite/30_threads/async/sync.cc: Test future_status::deferred.

From-SVN: r205144
---
 libstdc++-v3/ChangeLog                        | 24 ++++++
 .../doc/xml/manual/status_cxx2011.xml         | 10 +--
 libstdc++-v3/include/std/future               | 84 +++++++++++++------
 .../src/c++11/compatibility-thread-c++0x.cc   | 37 +++++++-
 libstdc++-v3/src/c++11/future.cc              |  2 -
 .../testsuite/30_threads/async/async.cc       | 21 ++++-
 .../testsuite/30_threads/async/sync.cc        | 24 +++++-
 7 files changed, 160 insertions(+), 42 deletions(-)

diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog
index e989a0d1471d..c1e20c2c8e46 100644
--- a/libstdc++-v3/ChangeLog
+++ b/libstdc++-v3/ChangeLog
@@ -4,6 +4,30 @@
 	* include/ext/pointer.h (pointer_traits<>::rebind<>): Add template
 	keyword in nested name.
 
+	PR libstdc++/49204
+	* include/std/future (__future_base::_State_base): Rename to
+	__future_base::_State_baseV2.
+	(__future_base::_State_baseV2::~_State_baseV2): Define as defaulted.
+	(__future_base::_State_baseV2::_M_run_deferred): Rename to
+	_M_complete_async.
+	(__future_base::_State_baseV2::_M_has_deferred): Add new virtual.
+	(__future_base::_State_baseV2::wait_for): Call _M_has_deferred() to
+	test for a deferred function, or call _M_complete_async() to join an
+	async thread that has made the shared state ready.
+	(__future_base::_State_baseV2::wait_until): Likewise.
+	(__future_base::_Async_state_common): Rename to _Async_state_commonV2.
+	(__future_base::_Async_state_commonV2::_M_run_deferred): Rename to
+	_M_complete_async.
+	* src/c++11/compatibility-thread-c++0x.cc (__future_base::_State_base):
+	Export old definition.
+	(__future_base::_Async_state_common): Likewise.
+	* src/c++11/future.cc (__future_base::_State_base::~_State_base):
+	Remove.
+	* doc/xml/manual/status_cxx2011.xml: Update status.
+	* testsuite/30_threads/async/async.cc: Test future_status::timeout
+	and future_status::ready.
+	* testsuite/30_threads/async/sync.cc: Test future_status::deferred.
+
 2013-11-20  David Edelsohn  <dje.gcc@gmail.com>
 
 	* testsuite/17_intro/static.cc: Ignore AIX TOC reload warnings.
diff --git a/libstdc++-v3/doc/xml/manual/status_cxx2011.xml b/libstdc++-v3/doc/xml/manual/status_cxx2011.xml
index 3c4ec69dcad7..bda8a79a30df 100644
--- a/libstdc++-v3/doc/xml/manual/status_cxx2011.xml
+++ b/libstdc++-v3/doc/xml/manual/status_cxx2011.xml
@@ -2503,18 +2503,16 @@ particular release.
       <entry>Missing set_*_at_thread_exit</entry>
     </row>
     <row>
-      <?dbhtml bgcolor="#B0B0B0" ?>
       <entry>30.6.6</entry>
       <entry>Class template <code>future</code></entry>
-      <entry>Partial</entry>
-      <entry>Timed waiting functions do not return future_status::deferred</entry>
+      <entry>Y</entry>
+      <entry/>
     </row>
     <row>
-      <?dbhtml bgcolor="#B0B0B0" ?>
       <entry>30.6.7</entry>
       <entry>Class template <code>shared_future</code></entry>
-      <entry>Partial</entry>
-      <entry>Timed waiting functions do not return future_status::deferred</entry>
+      <entry>Y</entry>
+      <entry/>
     </row>
     <row>
       <entry>30.6.8</entry>
diff --git a/libstdc++-v3/include/std/future b/libstdc++-v3/include/std/future
index 6d6b32b1f4db..b37578600d90 100644
--- a/libstdc++-v3/include/std/future
+++ b/libstdc++-v3/include/std/future
@@ -298,7 +298,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
     /// Base class for state between a promise and one or more
     /// associated futures.
-    class _State_base
+    class _State_baseV2
     {
       typedef _Ptr<_Result_base> _Ptr_type;
 
@@ -309,15 +309,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       once_flag			_M_once;
 
     public:
-      _State_base() noexcept : _M_result(), _M_retrieved(ATOMIC_FLAG_INIT) { }
-      _State_base(const _State_base&) = delete;
-      _State_base& operator=(const _State_base&) = delete;
-      virtual ~_State_base();
+      _State_baseV2() noexcept : _M_result(), _M_retrieved(ATOMIC_FLAG_INIT)
+	{ }
+      _State_baseV2(const _State_baseV2&) = delete;
+      _State_baseV2& operator=(const _State_baseV2&) = delete;
+      virtual ~_State_baseV2() = default;
 
       _Result_base&
       wait()
       {
-	_M_run_deferred();
+	_M_complete_async();
 	unique_lock<mutex> __lock(_M_mutex);
 	_M_cond.wait(__lock, [&] { return _M_ready(); });
 	return *_M_result;
@@ -328,8 +329,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
         wait_for(const chrono::duration<_Rep, _Period>& __rel)
         {
 	  unique_lock<mutex> __lock(_M_mutex);
-	  if (_M_cond.wait_for(__lock, __rel, [&] { return _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(); }))
+	    {
+	      // _GLIBCXX_RESOLVE_LIB_DEFECTS
+	      // 2100.  timed waiting functions must also join
+	      _M_complete_async();
+	      return future_status::ready;
+	    }
 	  return future_status::timeout;
 	}
 
@@ -338,8 +348,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
         wait_until(const chrono::time_point<_Clock, _Duration>& __abs)
         {
 	  unique_lock<mutex> __lock(_M_mutex);
-	  if (_M_cond.wait_until(__lock, __abs, [&] { return _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(); }))
+	    {
+	      // _GLIBCXX_RESOLVE_LIB_DEFECTS
+	      // 2100.  timed waiting functions must also join
+	      _M_complete_async();
+	      return future_status::ready;
+	    }
 	  return future_status::timeout;
 	}
 
@@ -349,7 +368,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
         bool __set = __ignore_failure;
         // all calls to this function are serialized,
         // side-effects of invoking __res only happen once
-        call_once(_M_once, &_State_base::_M_do_set, this, ref(__res),
+        call_once(_M_once, &_State_baseV2::_M_do_set, this, ref(__res),
             ref(__set));
         if (!__set)
           __throw_future_error(int(future_errc::promise_already_satisfied));
@@ -393,7 +412,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
           typename promise<_Res>::_Ptr_type operator()()
           {
-            _State_base::_S_check(_M_promise->_M_future);
+            _State_baseV2::_S_check(_M_promise->_M_future);
             _M_promise->_M_storage->_M_set(_M_arg);
             return std::move(_M_promise->_M_storage);
           }
@@ -407,7 +426,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
         {
           typename promise<_Res>::_Ptr_type operator()()
           {
-            _State_base::_S_check(_M_promise->_M_future);
+            _State_baseV2::_S_check(_M_promise->_M_future);
             _M_promise->_M_storage->_M_set(std::move(_M_arg));
             return std::move(_M_promise->_M_storage);
           }
@@ -423,7 +442,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
         {
           typename promise<_Res>::_Ptr_type operator()()
           {
-            _State_base::_S_check(_M_promise->_M_future);
+            _State_baseV2::_S_check(_M_promise->_M_future);
             _M_promise->_M_storage->_M_error = _M_ex;
             return std::move(_M_promise->_M_storage);
           }
@@ -472,15 +491,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       bool _M_ready() const noexcept { return static_cast<bool>(_M_result); }
 
-      // Misnamed: waits for completion of async function.
-      virtual void _M_run_deferred() { }
+      // Wait for completion of async function.
+      virtual void _M_complete_async() { }
+
+      // Return true if state contains a deferred function.
+      virtual bool _M_has_deferred() const { return false; }
     };
 
+#ifdef _GLIBCXX_ASYNC_ABI_COMPAT
+    class _State_base;
+    class _Async_state_common;
+#else
+    using _State_base = _State_baseV2;
+    class _Async_state_commonV2;
+#endif
+
     template<typename _BoundFn, typename = typename _BoundFn::result_type>
       class _Deferred_state;
 
-    class _Async_state_common;
-
     template<typename _BoundFn, typename = typename _BoundFn::result_type>
       class _Async_state_impl;
 
@@ -538,6 +566,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       void _M_destroy() { delete this; }
     };
 
+#ifndef _GLIBCXX_ASYNC_ABI_COMPAT
 
   /// Common implementation for future and shared_future.
   template<typename _Res>
@@ -1439,26 +1468,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Ptr_type _M_result;
       _BoundFn _M_fn;
 
+      // Run the deferred function.
       virtual void
-      _M_run_deferred()
+      _M_complete_async()
       {
         // safe to call multiple times so ignore failure
         _M_set_result(_S_task_setter(_M_result, _M_fn), true);
       }
+
+      virtual bool
+      _M_has_deferred() const { return static_cast<bool>(_M_result); }
     };
 
-  class __future_base::_Async_state_common : public __future_base::_State_base
+  class __future_base::_Async_state_commonV2
+    : public __future_base::_State_base
   {
   protected:
-#ifdef _GLIBCXX_ASYNC_ABI_COMPAT
-    ~_Async_state_common();
-#else
-    ~_Async_state_common() = default;
-#endif
+    ~_Async_state_commonV2() = default;
 
-    // Allow non-timed waiting functions to block until the thread completes,
-    // as if joined.
-    virtual void _M_run_deferred() { _M_join(); }
+    // Make waiting functions block until the thread completes, as if joined.
+    virtual void _M_complete_async() { _M_join(); }
 
     void _M_join() { std::call_once(_M_once, &thread::join, ref(_M_thread)); }
 
@@ -1468,7 +1497,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _BoundFn, typename _Res>
     class __future_base::_Async_state_impl final
-    : public __future_base::_Async_state_common
+    : public __future_base::_Async_state_commonV2
     {
     public:
       explicit
@@ -1536,6 +1565,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		   std::forward<_Args>(__args)...);
     }
 
+#endif // _GLIBCXX_ASYNC_ABI_COMPAT
 #endif // _GLIBCXX_HAS_GTHREADS && _GLIBCXX_USE_C99_STDINT_TR1
        // && ATOMIC_INT_LOCK_FREE
 
diff --git a/libstdc++-v3/src/c++11/compatibility-thread-c++0x.cc b/libstdc++-v3/src/c++11/compatibility-thread-c++0x.cc
index ecc4ca4b5587..bec7a2b6a649 100644
--- a/libstdc++-v3/src/c++11/compatibility-thread-c++0x.cc
+++ b/libstdc++-v3/src/c++11/compatibility-thread-c++0x.cc
@@ -23,7 +23,7 @@
 // <http://www.gnu.org/licenses/>.
 
 #include <bits/c++config.h>
-#if defined(_GLIBCXX_HAVE_TLS) && defined(_GLIBCXX_SHARED)
+#if defined(_GLIBCXX_SHARED)
 #define _GLIBCXX_ASYNC_ABI_COMPAT
 #endif
 
@@ -78,20 +78,49 @@ _GLIBCXX_ASM_SYMVER(_ZN9__gnu_cxx11try_to_lockE, _ZSt11try_to_lock, GLIBCXX_3.4.
 
 
 // XXX GLIBCXX_ABI Deprecated
-// gcc-4.7.0
+// gcc-4.7.0, gcc-4.9.0
 // <future> export changes
 #if defined(_GLIBCXX_HAS_GTHREADS) && defined(_GLIBCXX_USE_C99_STDINT_TR1) \
   && (ATOMIC_INT_LOCK_FREE > 1)
-#if defined(_GLIBCXX_HAVE_TLS) && defined(_GLIBCXX_SHARED)
+#if defined(_GLIBCXX_SHARED)
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
+  // Replaced by _State_baseV2 in gcc-4.9.0
+  class __future_base::_State_base
+  {
+    typedef _Ptr<_Result_base> _Ptr_type;
+
+    _Ptr_type			_M_result;
+    mutex               	_M_mutex;
+    condition_variable  	_M_cond;
+    atomic_flag         	_M_retrieved;
+    once_flag			_M_once;
+  public:
+    virtual ~_State_base();
+    virtual void _M_run_deferred() { }
+  };
+  __future_base::_State_base::~_State_base() { }
+
+  // Replaced by _Async_state_commonV2 in gcc-4.9.0
+  class __future_base::_Async_state_common : public __future_base::_State_base
+  {
+  protected:
+    ~_Async_state_common();
+    virtual void _M_run_deferred() { _M_join(); }
+    void _M_join() { std::call_once(_M_once, &thread::join, ref(_M_thread)); }
+    thread _M_thread;
+    once_flag _M_once;
+  };
+#if defined(_GLIBCXX_HAVE_TLS)
+  // Replaced with inline definition in gcc-4.8.0
   __future_base::_Async_state_common::~_Async_state_common() { _M_join(); }
 
   // Explicit instantiation due to -fno-implicit-instantiation.
   template void call_once(once_flag&, void (thread::*&&)(), reference_wrapper<thread>&&);
   template _Bind_simple_helper<void (thread::*)(), reference_wrapper<thread>>::__type __bind_simple(void (thread::*&&)(), reference_wrapper<thread>&&);
+#endif // _GLIBCXX_HAVE_TLS
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
-#endif
+#endif // _GLIBCXX_SHARED
 #endif // _GLIBCXX_HAS_GTHREADS && _GLIBCXX_USE_C99_STDINT_TR1
diff --git a/libstdc++-v3/src/c++11/future.cc b/libstdc++-v3/src/c++11/future.cc
index 906ded5476f9..e253ac3cee9b 100644
--- a/libstdc++-v3/src/c++11/future.cc
+++ b/libstdc++-v3/src/c++11/future.cc
@@ -82,8 +82,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   __future_base::_Result_base::_Result_base() = default;
 
   __future_base::_Result_base::~_Result_base() = default;
-
-  __future_base::_State_base::~_State_base() = default;
 #endif
 
 _GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/testsuite/30_threads/async/async.cc b/libstdc++-v3/testsuite/30_threads/async/async.cc
index f2ce205b20df..1f94494d0a33 100644
--- a/libstdc++-v3/testsuite/30_threads/async/async.cc
+++ b/libstdc++-v3/testsuite/30_threads/async/async.cc
@@ -39,6 +39,16 @@ struct work {
 };
 
 void test01()
+{
+  mutex m;
+  condition_variable cv;
+  unique_lock<mutex> l(m);
+  future<void> f1 = async(launch::async, work(), ref(m), ref(cv));
+  cv.wait(l);
+  f1.get();
+}
+
+void test02()
 {
   bool test __attribute__((unused)) = true;
 
@@ -46,12 +56,21 @@ void test01()
   condition_variable cv;
   unique_lock<mutex> l(m);
   future<void> f1 = async(launch::async, work(), ref(m), ref(cv));
+  std::future_status status;
+  status = f1.wait_for(std::chrono::milliseconds(1));
+  VERIFY( status == std::future_status::timeout );
+  status = f1.wait_until(std::chrono::system_clock::now());
+  VERIFY( status == std::future_status::timeout );
   cv.wait(l);
-  f1.get();
+  status = f1.wait_for(std::chrono::milliseconds(0));
+  VERIFY( status == std::future_status::ready );
+  status = f1.wait_until(std::chrono::system_clock::now());
+  VERIFY( status == std::future_status::ready );
 }
 
 int main()
 {
   test01();
+  test02();
   return 0;
 }
diff --git a/libstdc++-v3/testsuite/30_threads/async/sync.cc b/libstdc++-v3/testsuite/30_threads/async/sync.cc
index e9b112ddf574..3e9cd34cd55a 100644
--- a/libstdc++-v3/testsuite/30_threads/async/sync.cc
+++ b/libstdc++-v3/testsuite/30_threads/async/sync.cc
@@ -39,12 +39,32 @@ void test01()
   using namespace std;
 
   int a = 1;
-  int b = 10;
-  int c = 100;
+  int b = 1;
+  int c = 1;
   future<int> f1 = async(launch::deferred, sum(), a, ref(b), cref(c));
+  a = 0;
+  b = 10;
+  c = 100;
+
+  const std::chrono::seconds delay(10);
+  const auto then = std::chrono::system_clock::now() + delay;
 
   VERIFY( f1.valid() );
+  // timed waiting functions should return 'deferred' immediately
+  VERIFY( f1.wait_until(then) == std::future_status::deferred );
+  VERIFY( f1.wait_for(delay) == std::future_status::deferred );
+  VERIFY( std::chrono::system_clock::now() < then );
+
+  f1.wait();
+
+  VERIFY( f1.valid() );
+  // timed waiting functions should return 'ready' immediately
+  VERIFY( f1.wait_until(then) == std::future_status::ready );
+  VERIFY( f1.wait_for(delay) == std::future_status::ready );
+  VERIFY( std::chrono::system_clock::now() < then );
+
   VERIFY( f1.get() == 111 );
+  VERIFY( !f1.valid() );
 }
 
 int main()
-- 
GitLab