Commit 76663af2 authored by Sven Over's avatar Sven Over Committed by Facebook Github Bot 9

folly/futures: replace MoveWrappers with generalised lambda capture

Summary:Now that folly::Future uses folly::Function, we can use non-copyable
callbacks. That allows us to get rid of folly::MoveWrapper in the
implementaion.

This diff also enforces perfect forwarding in the implementation of
folly::Future, thereby reducing the number of times that a callable
that is passed to Future::then et al. gets passed by value.

Before folly::Function, Future::then(callback) has invoked the move
constructor of the callback type 5 times for small callback objects
(fitting into the in-place storage inside folly::detail::Core) and
6 times for large callback objects. This has been reduced to 5 times
in all cases with the switch to UniqueFunction. This diff reduces it
to 2 times.

Reviewed By: yfeldblum

Differential Revision: D2976647

fb-gh-sync-id: 9da470d7e9130bd7ad8af762fd238ef9a3ac5892
fbshipit-source-id: 9da470d7e9130bd7ad8af762fd238ef9a3ac5892
parent a92b878a
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <folly/CPortability.h> #include <folly/CPortability.h>
#include <folly/Memory.h> #include <folly/Memory.h>
#include <folly/MoveWrapper.h>
#include <folly/Optional.h> #include <folly/Optional.h>
#include <folly/Portability.h> #include <folly/Portability.h>
#include <folly/ScopeGuard.h> #include <folly/ScopeGuard.h>
......
This diff is collapsed.
...@@ -119,8 +119,26 @@ struct Extract<R(Class::*)(Args...)> { ...@@ -119,8 +119,26 @@ struct Extract<R(Class::*)(Args...)> {
typedef typename ArgType<Args...>::FirstArg FirstArg; typedef typename ArgType<Args...>::FirstArg FirstArg;
}; };
} // detail // gcc-4.8 refuses to capture a function reference in a lambda. This can be
// mitigated by casting them to function pointer types first. The following
// helper is used in Future.h to achieve that where necessary.
// When compiling with gcc versions 4.9 and up, as well as clang, we do not
// need to apply FunctionReferenceToPointer (i.e. T can be used instead of
// FunctionReferenceToPointer<T>).
// Applying FunctionReferenceToPointer first, the code works on all tested
// compiler versions: gcc 4.8 and above, cland 3.5 and above.
template <typename T>
struct FunctionReferenceToPointer {
using type = T;
};
template <typename R, typename... Args>
struct FunctionReferenceToPointer<R (&)(Args...)> {
using type = R (*)(Args...);
};
} // detail
class Timekeeper; class Timekeeper;
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
#include <folly/Optional.h> #include <folly/Optional.h>
#include <folly/Portability.h> #include <folly/Portability.h>
#include <folly/MoveWrapper.h>
#include <folly/futures/DrivableExecutor.h> #include <folly/futures/DrivableExecutor.h>
#include <folly/futures/Promise.h> #include <folly/futures/Promise.h>
#include <folly/futures/Try.h> #include <folly/futures/Try.h>
...@@ -177,10 +176,19 @@ class Future { ...@@ -177,10 +176,19 @@ class Future {
value(), which may rethrow if this has captured an exception. If func value(), which may rethrow if this has captured an exception. If func
throws, the exception will be captured in the Future that is returned. throws, the exception will be captured in the Future that is returned.
*/ */
template <typename F, typename R = detail::callableResult<T, F>> // gcc 4.8 requires that we cast function reference types to function pointer
typename R::Return then(F func) { // types. Fore more details see the comment on FunctionReferenceToPointer
// in Future-pre.h.
// gcc versions 4.9 and above (as well as clang) do not require this hack.
// For those, the FF tenplate parameter can be removed and occurences of FF
// replaced with F.
template <
typename F,
typename FF = typename detail::FunctionReferenceToPointer<F>::type,
typename R = detail::callableResult<T, FF>>
typename R::Return then(F&& func) {
typedef typename R::Arg Arguments; typedef typename R::Arg Arguments;
return thenImplementation<F, R>(std::move(func), Arguments()); return thenImplementation<FF, R>(std::forward<FF>(func), Arguments());
} }
/// Variant where func is an member function /// Variant where func is an member function
...@@ -268,7 +276,7 @@ class Future { ...@@ -268,7 +276,7 @@ class Future {
/// func shouldn't throw, but if it does it will be captured and propagated, /// func shouldn't throw, but if it does it will be captured and propagated,
/// and discard any value/exception that this Future has obtained. /// and discard any value/exception that this Future has obtained.
template <class F> template <class F>
Future<T> ensure(F func); Future<T> ensure(F&& func);
/// Like onError, but for timeouts. example: /// Like onError, but for timeouts. example:
/// ///
...@@ -386,7 +394,7 @@ class Future { ...@@ -386,7 +394,7 @@ class Future {
/// If the predicate does not obtain with the value, the result /// If the predicate does not obtain with the value, the result
/// is a folly::PredicateDoesNotObtain exception /// is a folly::PredicateDoesNotObtain exception
template <class F> template <class F>
Future<T> filter(F predicate); Future<T> filter(F&& predicate);
/// Like reduce, but works on a Future<std::vector<T / Try<T>>>, for example /// Like reduce, but works on a Future<std::vector<T / Try<T>>>, for example
/// the result of collect or collectAll /// the result of collect or collectAll
...@@ -458,14 +466,14 @@ class Future { ...@@ -458,14 +466,14 @@ class Future {
/// ///
/// thunk behaves like std::function<Future<T2>(void)> /// thunk behaves like std::function<Future<T2>(void)>
template <class F> template <class F>
friend Future<Unit> times(const int n, F thunk); friend Future<Unit> times(int n, F&& thunk);
/// Carry out the computation contained in the given future if /// Carry out the computation contained in the given future if
/// the predicate holds. /// the predicate holds.
/// ///
/// thunk behaves like std::function<Future<T2>(void)> /// thunk behaves like std::function<Future<T2>(void)>
template <class F> template <class F>
friend Future<Unit> when(bool p, F thunk); friend Future<Unit> when(bool p, F&& thunk);
/// Carry out the computation contained in the given future if /// Carry out the computation contained in the given future if
/// while the predicate continues to hold. /// while the predicate continues to hold.
...@@ -474,19 +482,19 @@ class Future { ...@@ -474,19 +482,19 @@ class Future {
/// ///
/// predicate behaves like std::function<bool(void)> /// predicate behaves like std::function<bool(void)>
template <class P, class F> template <class P, class F>
friend Future<Unit> whileDo(P predicate, F thunk); friend Future<Unit> whileDo(P&& predicate, F&& thunk);
// Variant: returns a value // Variant: returns a value
// e.g. f.then([](Try<T> t){ return t.value(); }); // e.g. f.then([](Try<T> t){ return t.value(); });
template <typename F, typename R, bool isTry, typename... Args> template <typename F, typename R, bool isTry, typename... Args>
typename std::enable_if<!R::ReturnsFuture::value, typename R::Return>::type typename std::enable_if<!R::ReturnsFuture::value, typename R::Return>::type
thenImplementation(F func, detail::argResult<isTry, F, Args...>); thenImplementation(F&& func, detail::argResult<isTry, F, Args...>);
// Variant: returns a Future // Variant: returns a Future
// e.g. f.then([](Try<T> t){ return makeFuture<T>(t); }); // e.g. f.then([](Try<T> t){ return makeFuture<T>(t); });
template <typename F, typename R, bool isTry, typename... Args> template <typename F, typename R, bool isTry, typename... Args>
typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type
thenImplementation(F func, detail::argResult<isTry, F, Args...>); thenImplementation(F&& func, detail::argResult<isTry, F, Args...>);
Executor* getExecutor() { return core_->getExecutor(); } Executor* getExecutor() { return core_->getExecutor(); }
void setExecutor(Executor* x, int8_t priority = Executor::MID_PRI) { void setExecutor(Executor* x, int8_t priority = Executor::MID_PRI) {
......
...@@ -149,11 +149,11 @@ class Core { ...@@ -149,11 +149,11 @@ class Core {
/// Call only from Future thread. /// Call only from Future thread.
template <typename F> template <typename F>
void setCallback(F func) { void setCallback(F&& func) {
bool transitionToArmed = false; bool transitionToArmed = false;
auto setCallback_ = [&]{ auto setCallback_ = [&]{
context_ = RequestContext::saveContext(); context_ = RequestContext::saveContext();
callback_ = std::move(func); callback_ = std::forward<F>(func);
}; };
FSM_START(fsm_) FSM_START(fsm_)
......
...@@ -367,7 +367,7 @@ retryingPolicyCappedJitteredExponentialBackoff( ...@@ -367,7 +367,7 @@ retryingPolicyCappedJitteredExponentialBackoff(
Duration backoff_min, Duration backoff_min,
Duration backoff_max, Duration backoff_max,
double jitter_param, double jitter_param,
URNG rng, URNG&& rng,
Policy&& p); Policy&& p);
inline inline
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <folly/MoveWrapper.h>
#include <folly/futures/Future.h> #include <folly/futures/Future.h>
using namespace folly; using namespace folly;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment