This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[patch] Avoid memory allocations when making futures ready


This patch replaces some reference members with pointers, so that the
types containing them become trivial and are more efficient to pass as
function arguments and can use the small object optimization when
stored in a std::function, so that promise::set_value() doesn't need
to allocate any memory.

_Task_setter is also changed to store a pointer to the task, instead
of using a std::function to hold it.

Tested x86_64-linux, committed to trunk.
commit 09c2fd279f76b886efa8bdb0b9b2dcb0956ab7f5
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Sat May 17 14:09:40 2014 +0100

    	* include/std/future (_State_baseV2::_M_set_result): Pass pointers to
    	_M_do_set.
    	(_State_baseV2::_M_do_set): Change parameters to pointers.
    	(_State_baseV2::_Setter): Change _M_arg from reference to pointer.
    	(_State_baseV2::__setter): Initialize _Setter with pointers.
    	(_State_baseV2::__setter(promise<void>*)): Remove overload.
    	(promise::set_value, promise::set_exception): Pass setter directly
    	to _M_set_result.
    	(_State_baseV2::_Task_setter): Add template parameter for callable
    	type and replace std::function member with pointer to that type.
    	Change _M_result member from reference to pointer.
    	(_State_baseV2::_S_task_setter): Change parameter to lvalue reference
    	and initialize _Task_setter with pointers.
    	(__location_invariant): Specialize for _Setter and _Task_setter.

diff --git a/libstdc++-v3/include/std/future b/libstdc++-v3/include/std/future
index be2ed96..c3aaaec 100644
--- a/libstdc++-v3/include/std/future
+++ b/libstdc++-v3/include/std/future
@@ -358,7 +358,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
         // 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,
-		  ref(__res), ref(__lock));
+		  std::__addressof(__res), std::__addressof(__lock));
 	if (__lock.owns_lock())
 	  _M_cond.notify_all();
 	else if (!__ignore_failure)
@@ -396,19 +396,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
         struct _Setter<_Res, _Arg&>
         {
           // check this is only used by promise<R>::set_value(const R&)
-          // or promise<R>::set_value(R&)
+          // or promise<R&>::set_value(R&)
           static_assert(is_same<_Res, _Arg&>::value  // promise<R&>
-              || is_same<const _Res, _Arg>::value,  // promise<R>
+              || is_same<const _Res, _Arg>::value,   // promise<R>
               "Invalid specialisation");
 
           typename promise<_Res>::_Ptr_type operator()()
           {
             _State_baseV2::_S_check(_M_promise->_M_future);
-            _M_promise->_M_storage->_M_set(_M_arg);
+            _M_promise->_M_storage->_M_set(*_M_arg);
             return std::move(_M_promise->_M_storage);
           }
           promise<_Res>*    _M_promise;
-          _Arg&             _M_arg;
+          _Arg*             _M_arg;
         };
 
       // set rvalues
@@ -418,11 +418,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
           typename promise<_Res>::_Ptr_type operator()()
           {
             _State_baseV2::_S_check(_M_promise->_M_future);
-            _M_promise->_M_storage->_M_set(std::move(_M_arg));
+            _M_promise->_M_storage->_M_set(std::move(*_M_arg));
             return std::move(_M_promise->_M_storage);
           }
           promise<_Res>*    _M_promise;
-          _Res&             _M_arg;
+          _Res*             _M_arg;
         };
 
       struct __exception_ptr_tag { };
@@ -434,31 +434,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
           typename promise<_Res>::_Ptr_type operator()()
           {
             _State_baseV2::_S_check(_M_promise->_M_future);
-            _M_promise->_M_storage->_M_error = _M_ex;
+            _M_promise->_M_storage->_M_error = *_M_ex;
             return std::move(_M_promise->_M_storage);
           }
 
           promise<_Res>*   _M_promise;
-          exception_ptr&    _M_ex;
+          exception_ptr*    _M_ex;
         };
 
       template<typename _Res, typename _Arg>
         static _Setter<_Res, _Arg&&>
         __setter(promise<_Res>* __prom, _Arg&& __arg)
         {
-          return _Setter<_Res, _Arg&&>{ __prom, __arg };
+          return _Setter<_Res, _Arg&&>{ __prom, &__arg };
         }
 
       template<typename _Res>
         static _Setter<_Res, __exception_ptr_tag>
         __setter(exception_ptr& __ex, promise<_Res>* __prom)
         {
-          return _Setter<_Res, __exception_ptr_tag>{ __prom, __ex };
+          return _Setter<_Res, __exception_ptr_tag>{ __prom, &__ex };
         }
 
-      static _Setter<void, void>
-      __setter(promise<void>* __prom);
-
       template<typename _Tp>
         static void
         _S_check(const shared_ptr<_Tp>& __p)
@@ -469,10 +466,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
     private:
       void
-      _M_do_set(function<_Ptr_type()>& __f, unique_lock<mutex>& __lock)
+      _M_do_set(function<_Ptr_type()>* __f, unique_lock<mutex>* __lock)
       {
-        _Ptr_type __res = __f(); // do not hold lock while running setter
-	__lock.lock();
+        _Ptr_type __res = (*__f)(); // do not hold lock while running setter
+	__lock->lock();
         _M_result.swap(__res);
       }
 
@@ -514,15 +511,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       static std::shared_ptr<_State_base>
       _S_make_async_state(_BoundFn&& __fn);
 
-    template<typename _Res_ptr,
+    template<typename _Res_ptr, typename _Fn,
 	     typename _Res = typename _Res_ptr::element_type::result_type>
       struct _Task_setter;
 
     template<typename _Res_ptr, typename _BoundFn>
-      static _Task_setter<_Res_ptr>
-      _S_task_setter(_Res_ptr& __ptr, _BoundFn&& __call)
+      static _Task_setter<_Res_ptr, _BoundFn>
+      _S_task_setter(_Res_ptr& __ptr, _BoundFn& __call)
       {
-	return _Task_setter<_Res_ptr>{ __ptr, std::ref(__call) };
+	return { std::__addressof(__ptr), std::__addressof(__call) };
       }
   };
 
@@ -554,6 +551,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       void _M_destroy() { delete this; }
     };
 
+  // Allow _Setter objects to be stored locally in std::function
+  template<typename _Res, typename _Arg>
+    struct __is_location_invariant
+    <__future_base::_State_base::_Setter<_Res, _Arg>>
+    : true_type { };
+
+  // Allow _Task_setter objects to be stored locally in std::function
+  template<typename _Res_ptr, typename _Fn, typename _Res>
+    struct __is_location_invariant
+    <__future_base::_Task_setter<_Res_ptr, _Fn, _Res>>
+    : true_type { };
+
 #ifndef _GLIBCXX_ASYNC_ABI_COMPAT
 
   /// Common implementation for future and shared_future.
@@ -994,24 +1003,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // Setting the result
       void
       set_value(const _Res& __r)
-      {
-        auto __setter = _State::__setter(this, __r);
-        _M_future->_M_set_result(std::move(__setter));
-      }
+      { _M_future->_M_set_result(_State::__setter(this, __r)); }
 
       void
       set_value(_Res&& __r)
-      {
-        auto __setter = _State::__setter(this, std::move(__r));
-        _M_future->_M_set_result(std::move(__setter));
-      }
+      { _M_future->_M_set_result(_State::__setter(this, std::move(__r))); }
 
       void
       set_exception(exception_ptr __p)
-      {
-        auto __setter = _State::__setter(__p, this);
-        _M_future->_M_set_result(std::move(__setter));
-      }
+      { _M_future->_M_set_result(_State::__setter(__p, this)); }
     };
 
   template<typename _Res>
@@ -1092,17 +1092,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // Setting the result
       void
       set_value(_Res& __r)
-      {
-        auto __setter = _State::__setter(this, __r);
-        _M_future->_M_set_result(std::move(__setter));
-      }
+      { _M_future->_M_set_result(_State::__setter(this, __r)); }
 
       void
       set_exception(exception_ptr __p)
-      {
-        auto __setter = _State::__setter(__p, this);
-        _M_future->_M_set_result(std::move(__setter));
-      }
+      { _M_future->_M_set_result(_State::__setter(__p, this)); }
     };
 
   /// Explicit specialization for promise<void>
@@ -1177,10 +1171,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       void
       set_exception(exception_ptr __p)
-      {
-        auto __setter = _State::__setter(__p, this);
-        _M_future->_M_set_result(std::move(__setter));
-      }
+      { _M_future->_M_set_result(_State::__setter(__p, this)); }
     };
 
   // set void
@@ -1196,28 +1187,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       promise<void>*    _M_promise;
     };
 
-  inline __future_base::_State_base::_Setter<void, void>
-  __future_base::_State_base::__setter(promise<void>* __prom)
-  {
-    return _Setter<void, void>{ __prom };
-  }
-
   inline void
   promise<void>::set_value()
-  {
-    auto __setter = _State::__setter(this);
-    _M_future->_M_set_result(std::move(__setter));
-  }
-
+  { _M_future->_M_set_result(_State::_Setter<void, void>{ this }); }
 
-  template<typename _Ptr_type, typename _Res>
+  template<typename _Ptr_type, typename _Fn, typename _Res>
     struct __future_base::_Task_setter
     {
       _Ptr_type operator()()
       {
 	__try
 	  {
-	    _M_result->_M_set(_M_fn());
+	    (*_M_result)->_M_set((*_M_fn)());
 	  }
 	__catch(const __cxxabiv1::__forced_unwind&)
 	  {
@@ -1225,22 +1206,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  }
 	__catch(...)
 	  {
-	    _M_result->_M_error = current_exception();
+	    (*_M_result)->_M_error = current_exception();
 	  }
-	return std::move(_M_result);
+	return std::move(*_M_result);
       }
-      _Ptr_type&                _M_result;
-      std::function<_Res()>     _M_fn;
+      _Ptr_type*	_M_result;
+      _Fn*		_M_fn;
     };
 
-  template<typename _Ptr_type>
-    struct __future_base::_Task_setter<_Ptr_type, void>
+  template<typename _Ptr_type, typename _Fn>
+    struct __future_base::_Task_setter<_Ptr_type, _Fn, void>
     {
       _Ptr_type operator()()
       {
 	__try
 	  {
-	    _M_fn();
+	    (*_M_fn)();
 	  }
 	__catch(const __cxxabiv1::__forced_unwind&)
 	  {
@@ -1248,12 +1229,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  }
 	__catch(...)
 	  {
-	    _M_result->_M_error = current_exception();
+	    (*_M_result)->_M_error = current_exception();
 	  }
-	return std::move(_M_result);
+	return std::move(*_M_result);
       }
-      _Ptr_type&                _M_result;
-      std::function<void()>     _M_fn;
+      _Ptr_type*	_M_result;
+      _Fn*		_M_fn;
     };
 
   template<typename _Res, typename... _Args>
@@ -1294,8 +1275,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	// 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))...);
-	auto __setter = _S_task_setter(this->_M_result, std::move(__boundfn));
-	this->_M_set_result(std::move(__setter));
+	this->_M_set_result(_S_task_setter(this->_M_result, __boundfn));
       }
 
       virtual shared_ptr<_Task_state_base<_Res(_Args...)>>

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]