re PR libstdc++/49204 ([C++0x] remaining issues in <future>)
authorJonathan Wakely <jwakely.gcc@gmail.com>
Wed, 20 Nov 2013 20:59:19 +0000 (20:59 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Wed, 20 Nov 2013 20:59:19 +0000 (20:59 +0000)
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
libstdc++-v3/doc/xml/manual/status_cxx2011.xml
libstdc++-v3/include/std/future
libstdc++-v3/src/c++11/compatibility-thread-c++0x.cc
libstdc++-v3/src/c++11/future.cc
libstdc++-v3/testsuite/30_threads/async/async.cc
libstdc++-v3/testsuite/30_threads/async/sync.cc

index e989a0d..c1e20c2 100644 (file)
@@ -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.
index 3c4ec69..bda8a79 100644 (file)
@@ -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>
index 6d6b32b..b375786 100644 (file)
@@ -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
 
index ecc4ca4..bec7a2b 100644 (file)
@@ -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
index 906ded5..e253ac3 100644 (file)
@@ -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
index f2ce205..1f94494 100644 (file)
@@ -40,18 +40,37 @@ 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;
 
   mutex m;
   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;
 }
index e9b112d..3e9cd34 100644 (file)
@@ -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()