diff mbox

PR libstdc++/80316 make promise::set_value throw no_state error

Message ID 20170404194421.GA29097@redhat.com
State New
Headers show

Commit Message

Jonathan Wakely April 4, 2017, 7:44 p.m. UTC
We got a bug report from a customer pointing out that calling
promise::set_value on a moved-from promise crashes instead of throwing
an exception with error code future_errc::no_state.

This fixes it, by moving the _S_check calls to *before* we deference
the pointer that the calls check!

This passes all tests, including the more comprehensive ones I've
added as part of this commit, but I think it can wait for stage 1
anyway. We've been shipping this bug for a couple of releases already.

	PR libstdc++/80316
	* include/std/future (_State_baseV2::_Setter::operator()): Remove
	_S_check calls that are done after the pointer to the shared state is
	already dereferenced.
	(_State_baseV2::_Setter<_Res, void>): Define specialization for void
	as partial specialization so it can be defined within the definition
	of _State_baseV2.
	(_State_baseV2::__setter): Call _S_check.
	(_State_baseV2::__setter(promise<void>*)): Add overload for use by
	promise<void>::set_value and promise<void>::set_value_at_thread_exit.
	(promise<T>, promise<T&>, promise<void>): Make _State a friend.
	(_State_baseV2::_Setter<void, void>): Remove explicit specialization.
	(promise<void>::set_value, promise<void>::set_value_at_thread_exit):
	Use new __setter overload.
	* testsuite/30_threads/promise/members/at_thread_exit2.cc: New test.
	* testsuite/30_threads/promise/members/set_exception.cc: Test
	promise<T&> and promise<void> specializations.
	* testsuite/30_threads/promise/members/set_exception2.cc: Likewise.
	Test for no_state error condition.
	* testsuite/30_threads/promise/members/set_value2.cc: Likewise.
commit 83c1e45d0759dd095c7b35d67fb5ba98dc7bf909
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Apr 4 19:48:33 2017 +0100

    PR libstdc++/80316 make promise::set_value throw no_state error
    
    	PR libstdc++/80316
    	* include/std/future (_State_baseV2::_Setter::operator()): Remove
    	_S_check calls that are done after the pointer to the shared state is
    	already dereferenced.
    	(_State_baseV2::_Setter<_Res, void>): Define specialization for void
    	as partial specialization so it can be defined within the definition
    	of _State_baseV2.
    	(_State_baseV2::__setter): Call _S_check.
    	(_State_baseV2::__setter(promise<void>*)): Add overload for use by
    	promise<void>::set_value and promise<void>::set_value_at_thread_exit.
    	(promise<T>, promise<T&>, promise<void>): Make _State a friend.
    	(_State_baseV2::_Setter<void, void>): Remove explicit specialization.
    	(promise<void>::set_value, promise<void>::set_value_at_thread_exit):
    	Use new __setter overload.
    	* testsuite/30_threads/promise/members/at_thread_exit2.cc: New test.
    	* testsuite/30_threads/promise/members/set_exception.cc: Test
    	promise<T&> and promise<void> specializations.
    	* testsuite/30_threads/promise/members/set_exception2.cc: Likewise.
    	Test for no_state error condition.
    	* testsuite/30_threads/promise/members/set_value2.cc: Likewise.

Comments

Jonathan Wakely April 21, 2017, 2:54 p.m. UTC | #1
On 4 April 2017 at 20:44, Jonathan Wakely wrote:
> We got a bug report from a customer pointing out that calling
> promise::set_value on a moved-from promise crashes instead of throwing
> an exception with error code future_errc::no_state.
>
> This fixes it, by moving the _S_check calls to *before* we deference
> the pointer that the calls check!
>
> This passes all tests, including the more comprehensive ones I've
> added as part of this commit, but I think it can wait for stage 1
> anyway. We've been shipping this bug for a couple of releases already.
>
>         PR libstdc++/80316
>         * include/std/future (_State_baseV2::_Setter::operator()): Remove
>         _S_check calls that are done after the pointer to the shared state
> is
>         already dereferenced.
>         (_State_baseV2::_Setter<_Res, void>): Define specialization for void
>         as partial specialization so it can be defined within the definition
>         of _State_baseV2.
>         (_State_baseV2::__setter): Call _S_check.
>         (_State_baseV2::__setter(promise<void>*)): Add overload for use by
>         promise<void>::set_value and
> promise<void>::set_value_at_thread_exit.
>         (promise<T>, promise<T&>, promise<void>): Make _State a friend.
>         (_State_baseV2::_Setter<void, void>): Remove explicit
> specialization.
>         (promise<void>::set_value, promise<void>::set_value_at_thread_exit):
>         Use new __setter overload.
>         * testsuite/30_threads/promise/members/at_thread_exit2.cc: New test.
>         * testsuite/30_threads/promise/members/set_exception.cc: Test
>         promise<T&> and promise<void> specializations.
>         * testsuite/30_threads/promise/members/set_exception2.cc: Likewise.
>         Test for no_state error condition.
>         * testsuite/30_threads/promise/members/set_value2.cc: Likewise.


This is now committed to trunk.
Jonathan Wakely July 11, 2017, 11:53 a.m. UTC | #2
On 21/04/17 15:54 +0100, Jonathan Wakely wrote:
>On 4 April 2017 at 20:44, Jonathan Wakely wrote:
>> We got a bug report from a customer pointing out that calling
>> promise::set_value on a moved-from promise crashes instead of throwing
>> an exception with error code future_errc::no_state.
>>
>> This fixes it, by moving the _S_check calls to *before* we deference
>> the pointer that the calls check!
>>
>> This passes all tests, including the more comprehensive ones I've
>> added as part of this commit, but I think it can wait for stage 1
>> anyway. We've been shipping this bug for a couple of releases already.
>>
>>         PR libstdc++/80316
>>         * include/std/future (_State_baseV2::_Setter::operator()): Remove
>>         _S_check calls that are done after the pointer to the shared state
>> is
>>         already dereferenced.
>>         (_State_baseV2::_Setter<_Res, void>): Define specialization for void
>>         as partial specialization so it can be defined within the definition
>>         of _State_baseV2.
>>         (_State_baseV2::__setter): Call _S_check.
>>         (_State_baseV2::__setter(promise<void>*)): Add overload for use by
>>         promise<void>::set_value and
>> promise<void>::set_value_at_thread_exit.
>>         (promise<T>, promise<T&>, promise<void>): Make _State a friend.
>>         (_State_baseV2::_Setter<void, void>): Remove explicit
>> specialization.
>>         (promise<void>::set_value, promise<void>::set_value_at_thread_exit):
>>         Use new __setter overload.
>>         * testsuite/30_threads/promise/members/at_thread_exit2.cc: New test.
>>         * testsuite/30_threads/promise/members/set_exception.cc: Test
>>         promise<T&> and promise<void> specializations.
>>         * testsuite/30_threads/promise/members/set_exception2.cc: Likewise.
>>         Test for no_state error condition.
>>         * testsuite/30_threads/promise/members/set_value2.cc: Likewise.
>
>
>This is now committed to trunk.

And now also to gcc-7-branch.
Jonathan Wakely July 11, 2017, 12:39 p.m. UTC | #3
On 11/07/17 12:53 +0100, Jonathan Wakely wrote:
>On 21/04/17 15:54 +0100, Jonathan Wakely wrote:
>>On 4 April 2017 at 20:44, Jonathan Wakely wrote:
>>>We got a bug report from a customer pointing out that calling
>>>promise::set_value on a moved-from promise crashes instead of throwing
>>>an exception with error code future_errc::no_state.
>>>
>>>This fixes it, by moving the _S_check calls to *before* we deference
>>>the pointer that the calls check!
>>>
>>>This passes all tests, including the more comprehensive ones I've
>>>added as part of this commit, but I think it can wait for stage 1
>>>anyway. We've been shipping this bug for a couple of releases already.
>>>
>>>        PR libstdc++/80316
>>>        * include/std/future (_State_baseV2::_Setter::operator()): Remove
>>>        _S_check calls that are done after the pointer to the shared state
>>>is
>>>        already dereferenced.
>>>        (_State_baseV2::_Setter<_Res, void>): Define specialization for void
>>>        as partial specialization so it can be defined within the definition
>>>        of _State_baseV2.
>>>        (_State_baseV2::__setter): Call _S_check.
>>>        (_State_baseV2::__setter(promise<void>*)): Add overload for use by
>>>        promise<void>::set_value and
>>>promise<void>::set_value_at_thread_exit.
>>>        (promise<T>, promise<T&>, promise<void>): Make _State a friend.
>>>        (_State_baseV2::_Setter<void, void>): Remove explicit
>>>specialization.
>>>        (promise<void>::set_value, promise<void>::set_value_at_thread_exit):
>>>        Use new __setter overload.
>>>        * testsuite/30_threads/promise/members/at_thread_exit2.cc: New test.
>>>        * testsuite/30_threads/promise/members/set_exception.cc: Test
>>>        promise<T&> and promise<void> specializations.
>>>        * testsuite/30_threads/promise/members/set_exception2.cc: Likewise.
>>>        Test for no_state error condition.
>>>        * testsuite/30_threads/promise/members/set_value2.cc: Likewise.
>>
>>
>>This is now committed to trunk.
>
>And now also to gcc-7-branch.

And gcc-6-branch.
Christophe Lyon July 12, 2017, 7:46 a.m. UTC | #4
On 11 July 2017 at 14:39, Jonathan Wakely <jwakely@redhat.com> wrote:
> On 11/07/17 12:53 +0100, Jonathan Wakely wrote:
>>
>> On 21/04/17 15:54 +0100, Jonathan Wakely wrote:
>>>
>>> On 4 April 2017 at 20:44, Jonathan Wakely wrote:
>>>>
>>>> We got a bug report from a customer pointing out that calling
>>>> promise::set_value on a moved-from promise crashes instead of throwing
>>>> an exception with error code future_errc::no_state.
>>>>
>>>> This fixes it, by moving the _S_check calls to *before* we deference
>>>> the pointer that the calls check!
>>>>
>>>> This passes all tests, including the more comprehensive ones I've
>>>> added as part of this commit, but I think it can wait for stage 1
>>>> anyway. We've been shipping this bug for a couple of releases already.
>>>>
>>>>        PR libstdc++/80316
>>>>        * include/std/future (_State_baseV2::_Setter::operator()): Remove
>>>>        _S_check calls that are done after the pointer to the shared
>>>> state
>>>> is
>>>>        already dereferenced.
>>>>        (_State_baseV2::_Setter<_Res, void>): Define specialization for
>>>> void
>>>>        as partial specialization so it can be defined within the
>>>> definition
>>>>        of _State_baseV2.
>>>>        (_State_baseV2::__setter): Call _S_check.
>>>>        (_State_baseV2::__setter(promise<void>*)): Add overload for use
>>>> by
>>>>        promise<void>::set_value and
>>>> promise<void>::set_value_at_thread_exit.
>>>>        (promise<T>, promise<T&>, promise<void>): Make _State a friend.
>>>>        (_State_baseV2::_Setter<void, void>): Remove explicit
>>>> specialization.
>>>>        (promise<void>::set_value,
>>>> promise<void>::set_value_at_thread_exit):
>>>>        Use new __setter overload.
>>>>        * testsuite/30_threads/promise/members/at_thread_exit2.cc: New
>>>> test.
>>>>        * testsuite/30_threads/promise/members/set_exception.cc: Test
>>>>        promise<T&> and promise<void> specializations.
>>>>        * testsuite/30_threads/promise/members/set_exception2.cc:
>>>> Likewise.
>>>>        Test for no_state error condition.
>>>>        * testsuite/30_threads/promise/members/set_value2.cc: Likewise.
>>>
>>>
>>>
>>> This is now committed to trunk.
>>
>>
>> And now also to gcc-7-branch.
>
>
> And gcc-6-branch.
>

Hi Jonathan,

The new test fails to compile on arm when forcing -march=armv5t:
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit2.cc:
In function 'void test01()':^M
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit2.cc:31:
error: aggregate 'std::promise<int> p1' has incomplete type and cannot
be defined^M
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit2.cc:44:
error: 'make_exception_ptr' is not a member of 'std'^M
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit2.cc:52:
error: variable 'std::promise<int> p2' has initializer but incomplete
type^M
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit2.cc:64:
error: 'make_exception_ptr' is not a member of 'std'^M
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit2.cc:
In function 'void test02()':^M
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit2.cc:75:
error: aggregate 'std::promise<int&> p1' has incomplete type and
cannot be defined^M
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit2.cc:89:
error: 'make_exception_ptr' is not a member of 'std'^M
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit2.cc:97:
error: variable 'std::promise<int&> p2' has initializer but incomplete
type^M
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit2.cc:110:
error: 'make_exception_ptr' is not a member of 'std'^M
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit2.cc:
In function 'void test03()':^M
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit2.cc:121:
error: aggregate 'std::promise<void> p1' has incomplete type and
cannot be defined^M
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit2.cc:134:
error: 'make_exception_ptr' is not a member of 'std'^M
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit2.cc:142:
error: variable 'std::promise<void> p2' has initializer but incomplete
type^M
/aci-gcc-fsf/sources/gcc-fsf/gccsrc/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit2.cc:153:
error: 'make_exception_ptr' is not a member of 'std'^M
compiler exited with status 1

I can see this on target arm-none-linux-gnueabihf --with-mode arm
--with-cpu cortex-a9 --with-fpu vfp and setting -march=armv5t in
runtestflags.

It also looks like you forgot to add a ChangeLog entry for the
testsuite changes.

There is no such error on the gcc-7 branch.

Thanks,

Christophe
diff mbox

Patch

diff --git a/libstdc++-v3/include/std/future b/libstdc++-v3/include/std/future
index cb53561..73d5a60 100644
--- a/libstdc++-v3/include/std/future
+++ b/libstdc++-v3/include/std/future
@@ -471,7 +471,6 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  // Used by std::promise to copy construct the result.
           typename promise<_Res>::_Ptr_type operator()() const
           {
-            _State_baseV2::_S_check(_M_promise->_M_future);
             _M_promise->_M_storage->_M_set(*_M_arg);
             return std::move(_M_promise->_M_storage);
           }
@@ -486,7 +485,6 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  // Used by std::promise to move construct the result.
           typename promise<_Res>::_Ptr_type operator()() const
           {
-            _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);
           }
@@ -494,6 +492,18 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
           _Res*             _M_arg;
         };
 
+      // set void
+      template<typename _Res>
+	struct _Setter<_Res, void>
+	{
+	  static_assert(is_void<_Res>::value, "Only used for promise<void>");
+
+	  typename promise<_Res>::_Ptr_type operator()() const
+	  { return std::move(_M_promise->_M_storage); }
+
+	  promise<_Res>*    _M_promise;
+	};
+
       struct __exception_ptr_tag { };
 
       // set exceptions
@@ -503,7 +513,6 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  // Used by std::promise to store an exception as the result.
           typename promise<_Res>::_Ptr_type operator()() const
           {
-            _State_baseV2::_S_check(_M_promise->_M_future);
             _M_promise->_M_storage->_M_error = *_M_ex;
             return std::move(_M_promise->_M_storage);
           }
@@ -516,6 +525,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
         static _Setter<_Res, _Arg&&>
         __setter(promise<_Res>* __prom, _Arg&& __arg)
         {
+	  _S_check(__prom->_M_future);
           return _Setter<_Res, _Arg&&>{ __prom, std::__addressof(__arg) };
         }
 
@@ -523,9 +533,18 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
         static _Setter<_Res, __exception_ptr_tag>
         __setter(exception_ptr& __ex, promise<_Res>* __prom)
         {
+	  _S_check(__prom->_M_future);
           return _Setter<_Res, __exception_ptr_tag>{ __prom, &__ex };
         }
 
+      template<typename _Res>
+	static _Setter<_Res, void>
+	__setter(promise<_Res>* __prom)
+	{
+	  _S_check(__prom->_M_future);
+	  return _Setter<_Res, void>{ __prom };
+	}
+
       template<typename _Tp>
         static void
         _S_check(const shared_ptr<_Tp>& __p)
@@ -1027,6 +1046,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef __future_base::_Result<_Res>	_Res_type;
       typedef __future_base::_Ptr<_Res_type>	_Ptr_type;
       template<typename, typename> friend class _State::_Setter;
+      friend _State;
 
       shared_ptr<_State>                        _M_future;
       _Ptr_type                                 _M_storage;
@@ -1137,6 +1157,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef __future_base::_Result<_Res&>	_Res_type;
       typedef __future_base::_Ptr<_Res_type> 	_Ptr_type;
       template<typename, typename> friend class _State::_Setter;
+      friend _State;
 
       shared_ptr<_State>                        _M_future;
       _Ptr_type                                 _M_storage;
@@ -1226,6 +1247,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef __future_base::_Result<void>	_Res_type;
       typedef __future_base::_Ptr<_Res_type> 	_Ptr_type;
       template<typename, typename> friend class _State::_Setter;
+      friend _State;
 
       shared_ptr<_State>                        _M_future;
       _Ptr_type                                 _M_storage;
@@ -1286,14 +1308,17 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return future<void>(_M_future); }
 
       // Setting the result
-      void set_value();
+      void
+      set_value()
+      { _M_future->_M_set_result(_State::__setter(this)); }
 
       void
       set_exception(exception_ptr __p)
       { _M_future->_M_set_result(_State::__setter(__p, this)); }
 
       void
-      set_value_at_thread_exit();
+      set_value_at_thread_exit()
+      { _M_future->_M_set_delayed_result(_State::__setter(this), _M_future); }
 
       void
       set_exception_at_thread_exit(exception_ptr __p)
@@ -1303,30 +1328,6 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
     };
 
-  // set void
-  template<>
-    struct __future_base::_State_base::_Setter<void, void>
-    {
-      promise<void>::_Ptr_type operator()() const
-      {
-        _State_base::_S_check(_M_promise->_M_future);
-        return std::move(_M_promise->_M_storage);
-      }
-
-      promise<void>*    _M_promise;
-    };
-
-  inline void
-  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
     {
diff --git a/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit2.cc b/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit2.cc
new file mode 100644
index 0000000..9429a99
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit2.cc
@@ -0,0 +1,167 @@ 
+// { dg-do run { target *-*-freebsd* *-*-dragonfly* *-*-netbsd* *-*-linux* *-*-gnu* *-*-solaris* *-*-cygwin *-*-rtems* *-*-darwin* powerpc-ibm-aix* } }
+// { dg-options "-pthread" { target *-*-freebsd* *-*-dragonfly* *-*-netbsd* *-*-linux* *-*-gnu* *-*-solaris* powerpc-ibm-aix* } }
+// { dg-require-effective-target c++11 }
+// { dg-require-cstdint "" }
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2014-2017 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/>.
+
+// Test set_value_at_thread_exit error conditions
+
+#include <future>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+  std::promise<int> p1;
+  p1.set_value(1);
+  try
+  {
+    p1.set_value_at_thread_exit(2);
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY( e.code() == std::future_errc::promise_already_satisfied );
+  }
+  try
+  {
+    p1.set_exception_at_thread_exit(std::make_exception_ptr(3));
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY( e.code() == std::future_errc::promise_already_satisfied );
+  }
+
+  std::promise<int> p2(std::move(p1));
+  try
+  {
+    p1.set_value_at_thread_exit(2);
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY( e.code() == std::future_errc::no_state );
+  }
+  try
+  {
+    p1.set_exception_at_thread_exit(std::make_exception_ptr(3));
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY( e.code() == std::future_errc::no_state );
+  }
+}
+
+void test02()
+{
+  std::promise<int&> p1;
+  int i = 1;
+  p1.set_value(i);
+  try
+  {
+    p1.set_value_at_thread_exit(i);
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY( e.code() == std::future_errc::promise_already_satisfied );
+  }
+  try
+  {
+    p1.set_exception_at_thread_exit(std::make_exception_ptr(3));
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY( e.code() == std::future_errc::promise_already_satisfied );
+  }
+
+  std::promise<int&> p2(std::move(p1));
+  try
+  {
+    int i = 0;
+    p1.set_value_at_thread_exit(i);
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY( e.code() == std::future_errc::no_state );
+  }
+  try
+  {
+    p1.set_exception_at_thread_exit(std::make_exception_ptr(3));
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY( e.code() == std::future_errc::no_state );
+  }
+}
+
+void test03()
+{
+  std::promise<void> p1;
+  int i = 0;
+  p1.set_value();
+  try {
+    p1.set_value_at_thread_exit();
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY( e.code() == std::future_errc::promise_already_satisfied );
+  }
+  try
+  {
+    p1.set_exception_at_thread_exit(std::make_exception_ptr(3));
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY( e.code() == std::future_errc::promise_already_satisfied );
+  }
+
+  std::promise<void> p2(std::move(p1));
+  try {
+    p1.set_value_at_thread_exit();
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY( e.code() == std::future_errc::no_state );
+  }
+  try
+  {
+    p1.set_exception_at_thread_exit(std::make_exception_ptr(3));
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY( e.code() == std::future_errc::no_state );
+  }
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/promise/members/set_exception.cc b/libstdc++-v3/testsuite/30_threads/promise/members/set_exception.cc
index 6ff4a34..4646e38 100644
--- a/libstdc++-v3/testsuite/30_threads/promise/members/set_exception.cc
+++ b/libstdc++-v3/testsuite/30_threads/promise/members/set_exception.cc
@@ -21,6 +21,7 @@ 
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
+// Test that promise::set_exception stores an exception.
 
 #include <future>
 #include <testsuite_hooks.h>
@@ -48,8 +49,56 @@  void test01()
   VERIFY( !f1.valid() );
 }
 
+void test02()
+{
+  bool test = false;
+
+  std::promise<int&> p1;
+  std::future<int&> f1 = p1.get_future();
+
+  VERIFY( f1.valid() );
+
+  p1.set_exception(std::make_exception_ptr(0));
+
+  try
+  {
+    f1.get();
+  }
+  catch (int)
+  {
+    test = true;
+  }
+  VERIFY( test );
+  VERIFY( !f1.valid() );
+}
+
+void test03()
+{
+  bool test = false;
+
+  std::promise<void> p1;
+  std::future<void> f1 = p1.get_future();
+
+  VERIFY( f1.valid() );
+
+  p1.set_exception(std::make_exception_ptr(0));
+
+  try
+  {
+    f1.get();
+  }
+  catch (int)
+  {
+    test = true;
+  }
+  VERIFY( test );
+  VERIFY( !f1.valid() );
+}
+
 int main()
 {
   test01();
+  test02();
+  test03();
   return 0;
 }
diff --git a/libstdc++-v3/testsuite/30_threads/promise/members/set_exception2.cc b/libstdc++-v3/testsuite/30_threads/promise/members/set_exception2.cc
index 1b1a066..dc2c4a8 100644
--- a/libstdc++-v3/testsuite/30_threads/promise/members/set_exception2.cc
+++ b/libstdc++-v3/testsuite/30_threads/promise/members/set_exception2.cc
@@ -21,10 +21,13 @@ 
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
+// Test that promise::set_exception throws when required.
 
 #include <future>
 #include <testsuite_hooks.h>
 
+// Check for promise_already_satisfied error conditions.
+
 void test01()
 {
   bool test = false;
@@ -83,9 +86,187 @@  void test02()
   VERIFY( test );
 }
 
+void test03()
+{
+  bool test = false;
+
+  std::promise<int&> p1;
+  std::future<int&> f1 = p1.get_future();
+
+  p1.set_exception(std::make_exception_ptr(0));
+
+  try
+  {
+    p1.set_exception(std::make_exception_ptr(1));
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY(e.code() ==
+        std::make_error_code(std::future_errc::promise_already_satisfied));
+    test = true;
+  }
+
+  try
+  {
+    f1.get();
+    test = false;
+  }
+  catch(int i)
+  {
+    VERIFY( i == 0 );
+  }
+
+  VERIFY( test );
+}
+
+void test04()
+{
+  bool test = false;
+
+  std::promise<int&> p1;
+  std::future<int&> f1 = p1.get_future();
+
+  int i = 2;
+  p1.set_value(i);
+
+  try
+  {
+    p1.set_exception(std::make_exception_ptr(0));
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY(e.code() ==
+        std::make_error_code(std::future_errc::promise_already_satisfied));
+    test = true;
+  }
+
+  VERIFY( test );
+}
+
+void test05()
+{
+  bool test = false;
+
+  std::promise<void> p1;
+  std::future<void> f1 = p1.get_future();
+
+  p1.set_exception(std::make_exception_ptr(0));
+
+  try
+  {
+    p1.set_exception(std::make_exception_ptr(1));
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY(e.code() ==
+        std::make_error_code(std::future_errc::promise_already_satisfied));
+    test = true;
+  }
+
+  try
+  {
+    f1.get();
+    test = false;
+  }
+  catch(int i)
+  {
+    VERIFY( i == 0 );
+  }
+
+  VERIFY( test );
+}
+
+void test06()
+{
+  bool test = false;
+
+  std::promise<void> p1;
+  std::future<void> f1 = p1.get_future();
+
+  p1.set_value();
+
+  try
+  {
+    p1.set_exception(std::make_exception_ptr(0));
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY(e.code() ==
+        std::make_error_code(std::future_errc::promise_already_satisfied));
+    test = true;
+  }
+
+  VERIFY( test );
+}
+
+// Check for no_state error condition (PR libstdc++/80316)
+
+void test07()
+{
+  using namespace std;
+
+  promise<int> p1;
+  promise<int> p2(std::move(p1));
+  try
+  {
+    p1.set_exception(std::make_exception_ptr(1));
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY(e.code() == make_error_code(future_errc::no_state));
+  }
+}
+
+void test08()
+{
+  using namespace std;
+
+  promise<int&> p1;
+  promise<int&> p2(std::move(p1));
+  try
+  {
+    int i = 0;
+    p1.set_exception(std::make_exception_ptr(1));
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY(e.code() == make_error_code(future_errc::no_state));
+  }
+}
+
+void test09()
+{
+  using namespace std;
+
+  promise<void> p1;
+  promise<void> p2(std::move(p1));
+  try
+  {
+    p1.set_exception(std::make_exception_ptr(1));
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY(e.code() == make_error_code(future_errc::no_state));
+  }
+}
+
 int main()
 {
   test01();
   test02();
+  test03();
+  test04();
+  test05();
+  test06();
+  test07();
+  test08();
+  test09();
   return 0;
 }
diff --git a/libstdc++-v3/testsuite/30_threads/promise/members/set_value2.cc b/libstdc++-v3/testsuite/30_threads/promise/members/set_value2.cc
index 0c4e87a..33c8ed9 100644
--- a/libstdc++-v3/testsuite/30_threads/promise/members/set_value2.cc
+++ b/libstdc++-v3/testsuite/30_threads/promise/members/set_value2.cc
@@ -21,10 +21,13 @@ 
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
+// Test that promise::set_value throws when required.
 
 #include <future>
 #include <testsuite_hooks.h>
 
+// Check for promise_already_satisfied error conditions.
+
 void test01()
 {
   bool test = false;
@@ -79,9 +82,298 @@  void test02()
   VERIFY( test );
 }
 
+void test03()
+{
+  bool test = false;
+
+  std::promise<int> p1;
+  std::future<int> f1 = p1.get_future();
+
+  p1.set_exception(std::make_exception_ptr(4));
+
+  try
+  {
+    p1.set_value(3);
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY(e.code() ==
+        std::make_error_code(std::future_errc::promise_already_satisfied));
+    test = true;
+  }
+
+  std::chrono::milliseconds delay(1);
+  VERIFY( f1.wait_for(delay) == std::future_status::ready );
+  test = false;
+  try
+  {
+    f1.get();
+    VERIFY( false );
+  }
+  catch (int e)
+  {
+    VERIFY(e == 4 );
+    test = true;
+  }
+
+  VERIFY( test );
+}
+
+void test04()
+{
+  bool test = false;
+
+  std::promise<int&> p1;
+  std::future<int&> f1 = p1.get_future();
+
+  int i = 1;
+  p1.set_value(i);
+
+  try
+  {
+    p1.set_value(i);
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY(e.code() ==
+        std::make_error_code(std::future_errc::promise_already_satisfied));
+    test = true;
+  }
+
+  std::chrono::milliseconds delay(1);
+  VERIFY( f1.wait_for(delay) == std::future_status::ready );
+  VERIFY( f1.get() == 1 );
+  VERIFY( test );
+}
+
+void test05()
+{
+  bool test = false;
+
+  std::promise<int&> p1;
+  std::future<int&> f1 = p1.get_future();
+
+  int i = 3;
+  p1.set_value(i);
+
+  try
+  {
+    p1.set_exception(std::make_exception_ptr(4));
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY(e.code() ==
+        std::make_error_code(std::future_errc::promise_already_satisfied));
+    test = true;
+  }
+
+  std::chrono::milliseconds delay(1);
+  VERIFY( f1.wait_for(delay) == std::future_status::ready );
+  VERIFY( f1.get() == 3 );
+  VERIFY( test );
+}
+
+void test06()
+{
+  bool test = false;
+
+  std::promise<int&> p1;
+  std::future<int&> f1 = p1.get_future();
+
+  p1.set_exception(std::make_exception_ptr(4));
+
+  try
+  {
+    int i = 3;
+    p1.set_value(i);
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY(e.code() ==
+        std::make_error_code(std::future_errc::promise_already_satisfied));
+    test = true;
+  }
+
+  std::chrono::milliseconds delay(1);
+  VERIFY( f1.wait_for(delay) == std::future_status::ready );
+  test = false;
+  try
+  {
+    f1.get();
+    VERIFY( false );
+  }
+  catch (int e)
+  {
+    VERIFY(e == 4 );
+    test = true;
+  }
+
+  VERIFY( test );
+}
+
+void test07()
+{
+  bool test = false;
+
+  std::promise<void> p1;
+  std::future<void> f1 = p1.get_future();
+
+  p1.set_value();
+
+  try
+  {
+    p1.set_value();
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY(e.code() ==
+        std::make_error_code(std::future_errc::promise_already_satisfied));
+    test = true;
+  }
+
+  std::chrono::milliseconds delay(1);
+  VERIFY( f1.wait_for(delay) == std::future_status::ready );
+  f1.get();
+  VERIFY( test );
+}
+
+void test08()
+{
+  bool test = false;
+
+  std::promise<void> p1;
+  std::future<void> f1 = p1.get_future();
+
+  p1.set_value();
+
+  try
+  {
+    p1.set_exception(std::make_exception_ptr(4));
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY(e.code() ==
+        std::make_error_code(std::future_errc::promise_already_satisfied));
+    test = true;
+  }
+
+  std::chrono::milliseconds delay(1);
+  VERIFY( f1.wait_for(delay) == std::future_status::ready );
+  f1.get();
+  VERIFY( test );
+}
+
+void test09()
+{
+  bool test = false;
+
+  std::promise<void> p1;
+  std::future<void> f1 = p1.get_future();
+
+  p1.set_exception(std::make_exception_ptr(4));
+
+  try
+  {
+    p1.set_value();
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY(e.code() ==
+        std::make_error_code(std::future_errc::promise_already_satisfied));
+    test = true;
+  }
+
+  std::chrono::milliseconds delay(1);
+  VERIFY( f1.wait_for(delay) == std::future_status::ready );
+  test = false;
+  try
+  {
+    f1.get();
+    VERIFY( false );
+  }
+  catch (int e)
+  {
+    VERIFY(e == 4 );
+    test = true;
+  }
+
+  VERIFY( test );
+}
+
+// Check for no_state error condition (PR libstdc++/80316)
+
+void test10()
+{
+  using namespace std;
+
+  promise<int> p1;
+  promise<int> p2(std::move(p1));
+  try
+  {
+    p1.set_value(1);
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY(e.code() == make_error_code(future_errc::no_state));
+  }
+}
+
+void test11()
+{
+  using namespace std;
+
+  promise<int&> p1;
+  promise<int&> p2(std::move(p1));
+  try
+  {
+    int i = 0;
+    p1.set_value(i);
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY(e.code() == make_error_code(future_errc::no_state));
+  }
+}
+
+void test12()
+{
+  using namespace std;
+
+  promise<void> p1;
+  promise<void> p2(std::move(p1));
+  try
+  {
+    p1.set_value();
+    VERIFY( false );
+  }
+  catch (std::future_error& e)
+  {
+    VERIFY(e.code() == make_error_code(future_errc::no_state));
+  }
+}
+
 int main()
 {
   test01();
   test02();
+  test03();
+  test04();
+  test05();
+  test06();
+  test07();
+  test08();
+  test09();
+  test10();
+  test11();
+  test12();
   return 0;
 }