From patchwork Thu Oct 23 16:00:37 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 402574 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 50B7B140095 for ; Fri, 24 Oct 2014 03:00:54 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:subject:message-id:mime-version:content-type; q=dns; s= default; b=Bd9iDF0r7TyO+DrsccIFApEKu5Iq9nZFfnOiPKhnjj3aeIQdBxbsY xbwV3rgaMsGijhNrSYJ63wyHlBtJy7ov2rU0Xy9GQPx4nWCDJATEoHD9wtiDgFkH kuU/DRbC3p3zAQotgvFrvatyTU2BqWxOwUC2zpJLWnG80teUr8T+KE= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:subject:message-id:mime-version:content-type; s= default; bh=wyX1RJW+qQ6XrkYcMn0U7ACsiPg=; b=v66K1hPBWwRI59qsSD53 59O9nBSFhp+gFL3x/vm7XTJFYPycJNcFHtQOZUSfeGNGgUzz12qWn7RXNK2ZU4rO cY7vffbQD+Dfe7FGToqTDSnMefh/nK47H0wkD7JpYwBrmFbjJgKoexQzwQJIZocW NOfPN0R+XFY8x5M7BQDzP8Q= Received: (qmail 23417 invoked by alias); 23 Oct 2014 16:00:45 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 23397 invoked by uid 89); 23 Oct 2014 16:00:44 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.8 required=5.0 tests=AWL, BAYES_00, RP_MATCHES_RCVD, SPF_HELO_PASS, SPF_PASS autolearn=ham version=3.3.2 X-Spam-User: qpsmtpd, 2 recipients X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Thu, 23 Oct 2014 16:00:41 +0000 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s9NG0dee021368 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Thu, 23 Oct 2014 12:00:39 -0400 Received: from localhost (ovpn-116-123.ams2.redhat.com [10.36.116.123]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s9NG0cdD003098; Thu, 23 Oct 2014 12:00:38 -0400 Date: Thu, 23 Oct 2014 17:00:37 +0100 From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [patch] Define std::promise::set_value_at_thread_exit() etc. Message-ID: <20141023160037.GZ3033@redhat.com> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.23 (2014-03-12) This adds: std::notify_all_at_thread_exit() std::promise<>::set_value_at_thread_exit() std::promise<>::set_exception_at_thread_exit() std::packaged_task<>::make_ready_at_thread_exit() There's a linked list of callbacks that run after TLS destructors (called by a pthread_key_create destructor) to make shared states ready and notify condition variables. The core of the change to futures is that the shared state is considered ready when _M_ready == true, instead of when _M_result != nullptr, so that we can store a result in _M_result without making it ready. The callback that would make it ready at thread exit stores a weak_ptr so it can safely check whether the shared state has already been destroyed before thread exit (see thread c++std-parallel-1162 on the SG1 list for related discussion). Tested x86_64-linux, I'd like to commit this next week some time. commit 9f0d052017db0e0484cbfaf79677c62e3532904f Author: Jonathan Wakely Date: Mon Oct 20 12:23:24 2014 +0100 Define *_at_thread_exit() functions. * 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 (__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_result_aside): Define. (__future_base::_State_baseV2::_M_break_promise): Set _M_ready. (__future_base::_State_baseV2::_Make_ready): New helper class. (__future_base::_Task_state_base::__run_not_ready): Declare new pure virtual function. (__future_base::_Task_state::__run_not_ready): 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. diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver index 4c6d994..5404094 100644 --- a/libstdc++-v3/config/abi/pre/gnu.ver +++ b/libstdc++-v3/config/abi/pre/gnu.ver @@ -124,7 +124,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*; @@ -1476,6 +1477,11 @@ GLIBCXX_3.4.21 { # std::ctype_base::blank _ZNSt10ctype_base5blankE; + # 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; diff --git a/libstdc++-v3/include/std/condition_variable b/libstdc++-v3/include/std/condition_variable index 921cb83..a3682c0 100644 --- a/libstdc++-v3/include/std/condition_variable +++ b/libstdc++-v3/include/std/condition_variable @@ -170,6 +170,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } }; + void + notify_all_at_thread_exit(condition_variable&, unique_lock); + + struct __at_thread_exit_elt + { + __at_thread_exit_elt* _M_next; + void (*_M_cb)(void*); + }; + inline namespace _V2 { /// condition_variable_any diff --git a/libstdc++-v3/include/std/future b/libstdc++-v3/include/std/future index 8989474..9008b78 100644 --- a/libstdc++-v3/include/std/future +++ b/libstdc++-v3/include/std/future @@ -294,12 +294,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; @@ -309,7 +309,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { _M_complete_async(); unique_lock __lock(_M_mutex); - _M_cond.wait(__lock, [&] { return _M_ready(); }); + _M_cond.wait(__lock, [&] { return _M_ready; }); return *_M_result; } @@ -318,11 +318,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION wait_for(const chrono::duration<_Rep, _Period>& __rel) { unique_lock __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 @@ -337,11 +337,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION wait_until(const chrono::time_point<_Clock, _Duration>& __abs) { unique_lock __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 @@ -360,12 +360,32 @@ _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)); } void + _M_set_result_aside(function<_Ptr_type()> __res, + weak_ptr<_State_baseV2> __self) + { + unique_ptr<_Make_ready> __mr{new _Make_ready}; + unique_lock __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(); + } + + void _M_break_promise(_Ptr_type __res) { if (static_cast(__res)) @@ -375,6 +395,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { lock_guard __lock(_M_mutex); _M_result.swap(__res); + _M_ready = true; } _M_cond.notify_all(); } @@ -473,14 +494,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_result.swap(__res); } - bool _M_ready() const noexcept { return static_cast(_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. 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 @@ -1012,6 +1038,25 @@ _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_result_aside(_State::__setter(this, __r), _M_future); + } + + void + set_value_at_thread_exit(_Res&& __r) + { + _M_future->_M_set_result_aside(_State::__setter(this, std::move(__r)), + _M_future); + } + + void + set_exception_at_thread_exit(exception_ptr __p) + { + _M_future->_M_set_result_aside(_State::__setter(__p, this), _M_future); + } }; template @@ -1097,6 +1142,18 @@ _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_result_aside(_State::__setter(this, __r), _M_future); + } + + void + set_exception_at_thread_exit(exception_ptr __p) + { + _M_future->_M_set_result_aside(_State::__setter(__p, this), _M_future); + } }; /// Explicit specialization for promise @@ -1172,6 +1229,15 @@ _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_result_aside(_State::__setter(__p, this), _M_future); + } }; // set void @@ -1191,6 +1257,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION promise::set_value() { _M_future->_M_set_result(_State::_Setter{ this }); } + inline void + promise::set_value_at_thread_exit() + { + _M_future->_M_set_result_aside(_State::_Setter{this}, + _M_future); + } + template struct __future_base::_Task_setter { @@ -1251,6 +1324,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION virtual void _M_run(_Args... __args) = 0; + virtual void + _M_run_not_ready(_Args... __args, weak_ptr<_State_base>) = 0; + virtual shared_ptr<_Task_state_base> _M_reset() = 0; @@ -1278,6 +1354,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION this->_M_set_result(_S_task_setter(this->_M_result, __boundfn)); } + virtual void + _M_run_not_ready(_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_result_aside(_S_task_setter(this->_M_result, __boundfn), + std::move(__self)); + } + virtual shared_ptr<_Task_state_base<_Res(_Args...)>> _M_reset(); @@ -1413,6 +1499,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } void + make_ready_at_thread_exit(_ArgTypes... __args) + { + __future_base::_State_base::_S_check(_M_state); + _M_state->_M_run_not_ready(std::forward<_ArgTypes>(__args)..., + _M_state); + } + + void reset() { __future_base::_State_base::_S_check(_M_state); diff --git a/libstdc++-v3/src/c++11/condition_variable.cc b/libstdc++-v3/src/c++11/condition_variable.cc index 7f78c39..c2768eb 100644 --- a/libstdc++-v3/src/c++11/condition_variable.cc +++ b/libstdc++-v3/src/c++11/condition_variable.cc @@ -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& l) + : cv(&cv), mx(l.release()) + { + _M_cb = ¬ifier::run; + __at_thread_exit(this); + } + + ~notifier() + { + mx->unlock(); + cv->notify_all(); + } + + condition_variable* cv; + mutex* mx; + + static void run(void* p) { delete static_cast(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 l) + { + (void) new notifier{cv, l}; + } + _GLIBCXX_END_NAMESPACE_VERSION } // namespace diff --git a/libstdc++-v3/src/c++11/future.cc b/libstdc++-v3/src/c++11/future.cc index 6ffab18..ca42dc19 100644 --- a/libstdc++-v3/src/c++11/future.cc +++ b/libstdc++-v3/src/c++11/future.cc @@ -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 __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 index 0000000..0da545d --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/condition_variable/members/3.cc @@ -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 +// . + +#include +#include +#include + +std::mutex mx; +std::condition_variable cv; +int counter = 0; + +struct Inc +{ + Inc() { ++counter; } + ~Inc() { ++counter; } +}; + + +void func() +{ + std::unique_lock 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 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 index 0000000..5bbdd3d --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/packaged_task/members/at_thread_exit.cc @@ -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 +// . + + +#include +#include + +bool executed = false; + +int execute(int i) { executed = true; return i + 1; } + +std::future f1; + +bool ready(std::future& f) +{ + return f.wait_for(std::chrono::milliseconds(1)) == std::future_status::ready; +} + +void test01() +{ + bool test __attribute__((unused)) = true; + + std::packaged_task 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 index 0000000..3842a13 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/promise/members/at_thread_exit.cc @@ -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 +// . + + +#include +#include + +int copies; +int copies_cmp; + +struct Obj +{ + Obj() = default; + Obj(const Obj&) { ++copies; } +}; + +std::future f1; + +bool ready(std::future& f) +{ + return f.wait_for(std::chrono::milliseconds(1)) == std::future_status::ready; +} + +void test01() +{ + bool test __attribute__((unused)) = true; + + std::promise 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 ); +}