From: Jonathan Wakely Date: Fri, 21 Apr 2017 14:49:19 +0000 (+0100) Subject: PR libstdc++/80316 make promise::set_value throw no_state error X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=946ecd6ab2e0303070800f527f629f1e148a1bb3;p=gcc.git 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*)): Add overload for use by promise::set_value and promise::set_value_at_thread_exit. (promise, promise, promise): Make _State a friend. (_State_baseV2::_Setter): Remove explicit specialization. (promise::set_value, promise::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 and promise 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. From-SVN: r247064 --- diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 2e587737e8f..bd43faa6c6d 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,5 +1,26 @@ 2017-04-21 Jonathan Wakely + 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*)): Add overload for use by + promise::set_value and promise::set_value_at_thread_exit. + (promise, promise, promise): Make _State a friend. + (_State_baseV2::_Setter): Remove explicit specialization. + (promise::set_value, promise::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 and promise 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. + * include/backward/auto_ptr.h: Ignore deprecated warnings from use of auto_ptr. * include/bits/shared_ptr.h: Likewise. diff --git a/libstdc++-v3/include/std/future b/libstdc++-v3/include/std/future index cb53561472d..73d5a60a918 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 + struct _Setter<_Res, void> + { + static_assert(is_void<_Res>::value, "Only used for promise"); + + 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 + static _Setter<_Res, void> + __setter(promise<_Res>* __prom) + { + _S_check(__prom->_M_future); + return _Setter<_Res, void>{ __prom }; + } + template 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 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 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 _Res_type; typedef __future_base::_Ptr<_Res_type> _Ptr_type; template 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(_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 - { - promise::_Ptr_type operator()() const - { - _State_base::_S_check(_M_promise->_M_future); - return std::move(_M_promise->_M_storage); - } - - promise* _M_promise; - }; - - inline void - promise::set_value() - { _M_future->_M_set_result(_State::_Setter{ this }); } - - inline void - promise::set_value_at_thread_exit() - { - _M_future->_M_set_delayed_result(_State::_Setter{this}, - _M_future); - } - template 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 00000000000..9429a9958d2 --- /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 +// . + +// Test set_value_at_thread_exit error conditions + +#include +#include + +void test01() +{ + std::promise 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 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 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 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 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 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 6ff4a348f2a..4646e381a2f 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 // . +// Test that promise::set_exception stores an exception. #include #include @@ -48,8 +49,56 @@ void test01() VERIFY( !f1.valid() ); } +void test02() +{ + bool test = false; + + std::promise p1; + std::future 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 p1; + std::future 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 1b1a0667860..dc2c4a88a9c 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 // . +// Test that promise::set_exception throws when required. #include #include +// 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 p1; + std::future 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 p1; + std::future 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 p1; + std::future 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 p1; + std::future 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 p1; + promise 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 p1; + promise 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 p1; + promise 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 0c4e87a253d..33c8ed9ede4 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 // . +// Test that promise::set_value throws when required. #include #include +// 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 p1; + std::future 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 p1; + std::future 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 p1; + std::future 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 p1; + std::future 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 p1; + std::future 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 p1; + std::future 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 p1; + std::future 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 p1; + promise 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 p1; + promise 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 p1; + promise 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; }