From 4880236e3fdcec453e6f5f3ac04ea7dae3d31a8c Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Sat, 9 Jul 2011 10:13:01 +0000 Subject: [PATCH] re PR libstdc++/49668 ([C++0x] std::thread does not forward its args as rvalues) 2011-07-09 Jonathan Wakely PR libstdc++/49668 * include/std/functional (__bind_simple): Define. * include/std/future (_Task_setter): Parameterize by type of result pointer instead of state object. (_S_task_setter): Type deduction helper. (_Task_state): Use _S_task_setter and __bind_simple. (_Deferred_state, _Async_state): Store call wrapper directly not as std::function. Use _S_task_setter and __bind_simple. (_S_make_deferred_state, _S_make_async_state): Type deduction helpers. (async): Use new functions and __bind_simple. * include/std/mutex (call_once): Use __bind_simple. * include/std/thread (thread): Likewise. Remove unused headers. * src/thread.cc: Add header. * testsuite/30_threads/async/49668.cc: New. * testsuite/30_threads/call_once/49668.cc: New. * testsuite/30_threads/thread/cons/49668.cc: New. * testsuite/30_threads/thread/cons/moveable.cc: Remove unused bool. From-SVN: r176073 --- libstdc++-v3/ChangeLog | 20 ++++ libstdc++-v3/include/std/functional | 71 +++++++++++ libstdc++-v3/include/std/future | 111 ++++++++++++------ libstdc++-v3/include/std/mutex | 4 +- libstdc++-v3/include/std/thread | 4 +- libstdc++-v3/src/thread.cc | 1 + .../testsuite/30_threads/async/49668.cc | 61 ++++++++++ .../testsuite/30_threads/call_once/49668.cc | 51 ++++++++ .../30_threads/packaged_task/49668.cc | 50 ++++++++ .../testsuite/30_threads/thread/cons/49668.cc | 51 ++++++++ .../30_threads/thread/cons/moveable.cc | 4 +- 11 files changed, 381 insertions(+), 47 deletions(-) create mode 100644 libstdc++-v3/testsuite/30_threads/async/49668.cc create mode 100644 libstdc++-v3/testsuite/30_threads/call_once/49668.cc create mode 100644 libstdc++-v3/testsuite/30_threads/packaged_task/49668.cc create mode 100644 libstdc++-v3/testsuite/30_threads/thread/cons/49668.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index c6af2be9903..663683f4a6b 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,23 @@ +2011-07-09 Jonathan Wakely + + PR libstdc++/49668 + * include/std/functional (__bind_simple): Define. + * include/std/future (_Task_setter): Parameterize by type of result + pointer instead of state object. + (_S_task_setter): Type deduction helper. + (_Task_state): Use _S_task_setter and __bind_simple. + (_Deferred_state, _Async_state): Store call wrapper directly not as + std::function. Use _S_task_setter and __bind_simple. + (_S_make_deferred_state, _S_make_async_state): Type deduction helpers. + (async): Use new functions and __bind_simple. + * include/std/mutex (call_once): Use __bind_simple. + * include/std/thread (thread): Likewise. Remove unused headers. + * src/thread.cc: Add header. + * testsuite/30_threads/async/49668.cc: New. + * testsuite/30_threads/call_once/49668.cc: New. + * testsuite/30_threads/thread/cons/49668.cc: New. + * testsuite/30_threads/thread/cons/moveable.cc: Remove unused bool. + 2011-07-08 Rainer Orth * configure.host (abi_baseline_subdir_switch): Describe. diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional index 85df22017f6..df3f9ceb7b4 100644 --- a/libstdc++-v3/include/std/functional +++ b/libstdc++-v3/include/std/functional @@ -1499,6 +1499,77 @@ _GLIBCXX_HAS_NESTED_TYPE(result_type) std::forward<_BoundArgs>(__args)...); } + template + struct _Bind_simple; + + template + struct _Bind_simple<_Callable(_Args...)> + { + typedef typename result_of<_Callable(_Args...)>::type result_type; + + template::type> + explicit + _Bind_simple(const _Callable& __callable, _Args2&&... __args) + : _M_bound(__callable, std::forward<_Args2>(__args)...) + { } + + template::type> + explicit + _Bind_simple(_Callable&& __callable, _Args2&&... __args) + : _M_bound(std::move(__callable), std::forward<_Args2>(__args)...) + { } + + _Bind_simple(const _Bind_simple&) = default; + _Bind_simple(_Bind_simple&&) = default; + + result_type + operator()() + { + typedef typename _Build_index_tuple::__type _Indices; + return _M_invoke(_Indices()); + } + + private: + + template + typename result_of<_Callable(_Args...)>::type + _M_invoke(_Index_tuple<_Indices...>) + { + // std::bind always forwards bound arguments as lvalues, + // but this type can call functions which only accept rvalues. + return std::forward<_Callable>(std::get<0>(_M_bound))( + std::forward<_Args>(std::get<_Indices+1>(_M_bound))...); + } + + std::tuple<_Callable, _Args...> _M_bound; + }; + + template + struct _Bind_simple_helper + { + typedef _Maybe_wrap_member_pointer::type> + __maybe_type; + typedef typename __maybe_type::type __func_type; + typedef _Bind_simple<__func_type(typename decay<_BoundArgs>::type...)> + __type; + }; + + // Simplified version of std::bind for internal use, without support for + // unbound arguments, placeholders or nested bind expressions. + template + typename _Bind_simple_helper<_Callable, _Args...>::__type + __bind_simple(_Callable&& __callable, _Args&&... __args) + { + typedef _Bind_simple_helper<_Callable, _Args...> __helper_type; + typedef typename __helper_type::__maybe_type __maybe_type; + typedef typename __helper_type::__type __result_type; + return __result_type( + __maybe_type::__do_wrap( std::forward<_Callable>(__callable)), + std::forward<_Args>(__args)...); + } + /** * @brief Exception class thrown when class template function's * operator() is called with an empty target. diff --git a/libstdc++-v3/include/std/future b/libstdc++-v3/include/std/future index 2b3e982200d..fc2d3248b01 100644 --- a/libstdc++-v3/include/std/future +++ b/libstdc++-v3/include/std/future @@ -488,17 +488,42 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION virtual void _M_run_deferred() { } }; - template + template class _Deferred_state; - template + template class _Async_state; template class _Task_state; - template + template + static std::shared_ptr<_State_base> + _S_make_deferred_state(_BoundFn&& __fn); + + template + static std::shared_ptr<_State_base> + _S_make_async_state(_BoundFn&& __fn); + + template struct _Task_setter; + + template + class _Task_setter_helper + { + typedef typename remove_reference<_BoundFn>::type::result_type __res; + public: + typedef _Task_setter<_Res_ptr, __res> __type; + }; + + template + static typename _Task_setter_helper<_Res_ptr, _BoundFn>::__type + _S_task_setter(_Res_ptr& __ptr, _BoundFn&& __call) + { + typedef _Task_setter_helper<_Res_ptr, _BoundFn> __helper_type; + typedef typename __helper_type::__type _Setter; + return _Setter{ __ptr, std::ref(__call) }; + } }; /// Partial specialization for reference types. @@ -1165,29 +1190,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } - template + template struct __future_base::_Task_setter { - typename _StateT::_Ptr_type operator()() + _Ptr_type operator()() { __try { - _M_state->_M_result->_M_set(_M_fn()); + _M_result->_M_set(_M_fn()); } __catch(...) { - _M_state->_M_result->_M_error = current_exception(); + _M_result->_M_error = current_exception(); } - return std::move(_M_state->_M_result); + return std::move(_M_result); } - _StateT* _M_state; + _Ptr_type& _M_result; std::function<_Res()> _M_fn; }; - template - struct __future_base::_Task_setter<_StateT, void> + template + struct __future_base::_Task_setter<_Ptr_type, void> { - typename _StateT::_Ptr_type operator()() + _Ptr_type operator()() { __try { @@ -1195,11 +1220,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } __catch(...) { - _M_state->_M_result->_M_error = current_exception(); + _M_result->_M_error = current_exception(); } - return std::move(_M_state->_M_result); + return std::move(_M_result); } - _StateT* _M_state; + _Ptr_type& _M_result; std::function _M_fn; }; @@ -1223,13 +1248,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_run(_Args... __args) { // bound arguments decay so wrap lvalue references - auto __bound = std::bind<_Res>(std::ref(_M_task), - _S_maybe_wrap_ref(std::forward<_Args>(__args))...); - _Task_setter<_Task_state> __setter{ this, std::move(__bound) }; + auto __boundfn = std::__bind_simple(std::ref(_M_task), + _S_maybe_wrap_ref(std::forward<_Args>(__args))...); + auto __setter = _S_task_setter(_M_result, std::move(__boundfn)); _M_set_result(std::move(__setter)); } - template friend class _Task_setter; typedef typename __future_base::_Ptr<_Result<_Res>>::type _Ptr_type; _Ptr_type _M_result; std::function<_Res(_Args...)> _M_task; @@ -1331,40 +1355,34 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : public true_type { }; - template + template class __future_base::_Deferred_state : public __future_base::_State_base { public: - typedef _Res _Res_type; - explicit - _Deferred_state(std::function<_Res()>&& __fn) + _Deferred_state(_BoundFn&& __fn) : _M_result(new _Result<_Res>()), _M_fn(std::move(__fn)) { } private: - template friend class _Task_setter; typedef typename __future_base::_Ptr<_Result<_Res>>::type _Ptr_type; _Ptr_type _M_result; - std::function<_Res()> _M_fn; + _BoundFn _M_fn; virtual void _M_run_deferred() { - _Task_setter<_Deferred_state> __setter{ this, _M_fn }; // safe to call multiple times so ignore failure - _M_set_result(std::move(__setter), true); + _M_set_result(_S_task_setter(_M_result, _M_fn), true); } }; - template + template class __future_base::_Async_state : public __future_base::_State_base { public: - typedef _Res _Res_type; - explicit - _Async_state(std::function<_Res()>&& __fn) + _Async_state(_BoundFn&& __fn) : _M_result(new _Result<_Res>()), _M_fn(std::move(__fn)), _M_thread(mem_fn(&_Async_state::_M_do_run), this) { } @@ -1374,17 +1392,34 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION private: void _M_do_run() { - _Task_setter<_Async_state> __setter{ this, std::move(_M_fn) }; - _M_set_result(std::move(__setter)); + _M_set_result(_S_task_setter(_M_result, _M_fn)); } - template friend class _Task_setter; typedef typename __future_base::_Ptr<_Result<_Res>>::type _Ptr_type; _Ptr_type _M_result; - std::function<_Res()> _M_fn; + _BoundFn _M_fn; thread _M_thread; }; + template + inline std::shared_ptr<__future_base::_State_base> + __future_base::_S_make_deferred_state(_BoundFn&& __fn) + { + typedef typename remove_reference<_BoundFn>::type __fn_type; + typedef _Deferred_state<__fn_type> __state_type; + return std::make_shared<__state_type>(std::move(__fn)); + } + + template + inline std::shared_ptr<__future_base::_State_base> + __future_base::_S_make_async_state(_BoundFn&& __fn) + { + typedef typename remove_reference<_BoundFn>::type __fn_type; + typedef _Async_state<__fn_type> __state_type; + return std::make_shared<__state_type>(std::move(__fn)); + } + + /// async template future::type> @@ -1394,14 +1429,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION std::shared_ptr<__future_base::_State_base> __state; if ((__policy & (launch::async|launch::deferred)) == launch::async) { - typedef typename __future_base::_Async_state _State; - __state = std::make_shared<_State>(std::bind( + __state = __future_base::_S_make_async_state(std::__bind_simple( std::forward<_Fn>(__fn), std::forward<_Args>(__args)...)); } else { - typedef typename __future_base::_Deferred_state _State; - __state = std::make_shared<_State>(std::bind( + __state = __future_base::_S_make_deferred_state(std::__bind_simple( std::forward<_Fn>(__fn), std::forward<_Args>(__args)...)); } return future(__state); diff --git a/libstdc++-v3/include/std/mutex b/libstdc++-v3/include/std/mutex index 2ba95769504..1c66afc9e2c 100644 --- a/libstdc++-v3/include/std/mutex +++ b/libstdc++-v3/include/std/mutex @@ -801,13 +801,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION call_once(once_flag& __once, _Callable&& __f, _Args&&... __args) { #ifdef _GLIBCXX_HAVE_TLS - auto __bound_functor = std::bind(std::forward<_Callable>(__f), + auto __bound_functor = std::__bind_simple(std::forward<_Callable>(__f), std::forward<_Args>(__args)...); __once_callable = &__bound_functor; __once_call = &__once_call_impl; #else unique_lock __functor_lock(__get_once_mutex()); - __once_functor = std::bind(std::forward<_Callable>(__f), + __once_functor = std::__bind_simple(std::forward<_Callable>(__f), std::forward<_Args>(__args)...); __set_once_functor_lock_ptr(&__functor_lock); #endif diff --git a/libstdc++-v3/include/std/thread b/libstdc++-v3/include/std/thread index ab85735154e..8cc06903ebf 100644 --- a/libstdc++-v3/include/std/thread +++ b/libstdc++-v3/include/std/thread @@ -38,8 +38,6 @@ #include #include #include -#include -#include #include #include #include @@ -132,7 +130,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION explicit thread(_Callable&& __f, _Args&&... __args) { - _M_start_thread(_M_make_routine(std::bind( + _M_start_thread(_M_make_routine(std::__bind_simple( std::forward<_Callable>(__f), std::forward<_Args>(__args)...))); } diff --git a/libstdc++-v3/src/thread.cc b/libstdc++-v3/src/thread.cc index cc23c19768f..09e7fc5909d 100644 --- a/libstdc++-v3/src/thread.cc +++ b/libstdc++-v3/src/thread.cc @@ -24,6 +24,7 @@ #include +#include #include #if defined(_GLIBCXX_USE_GET_NPROCS) diff --git a/libstdc++-v3/testsuite/30_threads/async/49668.cc b/libstdc++-v3/testsuite/30_threads/async/49668.cc new file mode 100644 index 00000000000..4d5bd0515ea --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/async/49668.cc @@ -0,0 +1,61 @@ +// { dg-do run { target *-*-freebsd* *-*-netbsd* *-*-linux* *-*-solaris* *-*-cygwin *-*-darwin* alpha*-*-osf* mips-sgi-irix6* } } +// { dg-options " -std=gnu++0x -pthread" { target *-*-freebsd* *-*-netbsd* *-*-linux* alpha*-*-osf* mips-sgi-irix6* } } +// { dg-options " -std=gnu++0x -pthreads" { target *-*-solaris* } } +// { dg-options " -std=gnu++0x " { target *-*-cygwin *-*-darwin* } } +// { dg-require-cstdint "" } +// { dg-require-gthreads "" } + +// Copyright (C) 2011 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 + +struct moveable +{ + moveable() = default; + moveable(moveable&&) = default; + moveable(const moveable&) = delete; +}; + +using std::launch; +namespace ph = std::placeholders; + +typedef decltype(ph::_1) placeholder_type; + +bool f(moveable, placeholder_type) { return true; } + +void test01() +{ + auto fut = std::async(launch::async, f, moveable(), ph::_1); + bool test = fut.get(); + VERIFY( test ); +} + +void test02() +{ + auto fut = std::async(launch::deferred, f, moveable(), ph::_1); + bool test = fut.get(); + VERIFY( test ); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/call_once/49668.cc b/libstdc++-v3/testsuite/30_threads/call_once/49668.cc new file mode 100644 index 00000000000..3215182f01d --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/call_once/49668.cc @@ -0,0 +1,51 @@ +// { dg-do run { target *-*-freebsd* *-*-netbsd* *-*-linux* *-*-solaris* *-*-cygwin *-*-darwin* alpha*-*-osf* mips-sgi-irix6* } } +// { dg-options " -std=gnu++0x -pthread" { target *-*-freebsd* *-*-netbsd* *-*-linux* alpha*-*-osf* mips-sgi-irix6* } } +// { dg-options " -std=gnu++0x -pthreads" { target *-*-solaris* } } +// { dg-options " -std=gnu++0x " { target *-*-cygwin *-*-darwin* } } +// { dg-require-cstdint "" } +// { dg-require-gthreads "" } + +// Copyright (C) 2011 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 + +struct moveable +{ + moveable() = default; + moveable(moveable&&) = default; + moveable(const moveable&) = delete; +}; + +typedef decltype(std::placeholders::_1) placeholder_type; + +void f(moveable, placeholder_type, bool& b) { b = true; } + +void test01() +{ + bool test = false; + std::once_flag once; + std::call_once(once, f, moveable(), std::placeholders::_1, std::ref(test)); + VERIFY( test ); +} + +int main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/30_threads/packaged_task/49668.cc b/libstdc++-v3/testsuite/30_threads/packaged_task/49668.cc new file mode 100644 index 00000000000..55a2f07c1e8 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/packaged_task/49668.cc @@ -0,0 +1,50 @@ +// { dg-do run { target *-*-freebsd* *-*-netbsd* *-*-linux* *-*-solaris* *-*-cygwin *-*-darwin* alpha*-*-osf* mips-sgi-irix6* } } +// { dg-options " -std=gnu++0x -pthread" { target *-*-freebsd* *-*-netbsd* *-*-linux* alpha*-*-osf* mips-sgi-irix6* } } +// { dg-options " -std=gnu++0x -pthreads" { target *-*-solaris* } } +// { dg-options " -std=gnu++0x " { target *-*-cygwin *-*-darwin* } } +// { dg-require-cstdint "" } +// { dg-require-gthreads "" } + +// Copyright (C) 2011 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 + +struct moveable +{ + moveable() = default; + moveable(moveable&&) = default; +}; + +typedef decltype(std::placeholders::_1) placeholder_type; + +bool f(moveable, placeholder_type) { return true; } + +void test01() +{ + std::packaged_task p(f); + p(moveable(), std::placeholders::_1); + bool test = p.get_future().get(); + VERIFY( test ); +} + +int main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/30_threads/thread/cons/49668.cc b/libstdc++-v3/testsuite/30_threads/thread/cons/49668.cc new file mode 100644 index 00000000000..f6faa8c182e --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/thread/cons/49668.cc @@ -0,0 +1,51 @@ +// { dg-do run { target *-*-freebsd* *-*-netbsd* *-*-linux* *-*-solaris* *-*-cygwin *-*-darwin* alpha*-*-osf* mips-sgi-irix6* } } +// { dg-options " -std=gnu++0x -pthread" { target *-*-freebsd* *-*-netbsd* *-*-linux* alpha*-*-osf* mips-sgi-irix6* } } +// { dg-options " -std=gnu++0x -pthreads" { target *-*-solaris* } } +// { dg-options " -std=gnu++0x " { target *-*-cygwin *-*-darwin* } } +// { dg-require-cstdint "" } +// { dg-require-gthreads "" } + +// Copyright (C) 2011 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 + +struct moveable +{ + moveable() = default; + moveable(moveable&&) = default; + moveable(const moveable&) = delete; +}; + +typedef decltype(std::placeholders::_1) placeholder_type; + +void f(moveable, placeholder_type, bool& b) { b = true; } + +void test01() +{ + bool test = false; + std::thread t(f, moveable(), std::placeholders::_1, std::ref(test)); + t.join(); + VERIFY( test ); +} + +int main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/30_threads/thread/cons/moveable.cc b/libstdc++-v3/testsuite/30_threads/thread/cons/moveable.cc index f3f5c7baf36..961ae0259bf 100644 --- a/libstdc++-v3/testsuite/30_threads/thread/cons/moveable.cc +++ b/libstdc++-v3/testsuite/30_threads/thread/cons/moveable.cc @@ -5,7 +5,7 @@ // { dg-require-cstdint "" } // { dg-require-gthreads "" } -// Copyright (C) 2009 Free Software Foundation, Inc. +// Copyright (C) 2009, 2010, 2011 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 @@ -27,8 +27,6 @@ #include #include -bool functor_was_called = false; - struct moveable { moveable() = default; -- 2.30.2