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 @@
#include <folly/CPortability.h>
#include <folly/Memory.h>
#include <folly/MoveWrapper.h>
#include <folly/Optional.h>
#include <folly/Portability.h>
#include <folly/ScopeGuard.h>
......
This diff is collapsed.
......@@ -119,8 +119,26 @@ struct Extract<R(Class::*)(Args...)> {
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;
......
......@@ -25,7 +25,6 @@
#include <folly/Optional.h>
#include <folly/Portability.h>
#include <folly/MoveWrapper.h>
#include <folly/futures/DrivableExecutor.h>
#include <folly/futures/Promise.h>
#include <folly/futures/Try.h>
......@@ -177,10 +176,19 @@ class Future {
value(), which may rethrow if this has captured an exception. If func
throws, the exception will be captured in the Future that is returned.
*/
template <typename F, typename R = detail::callableResult<T, F>>
typename R::Return then(F func) {
// gcc 4.8 requires that we cast function reference types to function pointer
// 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;
return thenImplementation<F, R>(std::move(func), Arguments());
return thenImplementation<FF, R>(std::forward<FF>(func), Arguments());
}
/// Variant where func is an member function
......@@ -268,7 +276,7 @@ class Future {
/// func shouldn't throw, but if it does it will be captured and propagated,
/// and discard any value/exception that this Future has obtained.
template <class F>
Future<T> ensure(F func);
Future<T> ensure(F&& func);
/// Like onError, but for timeouts. example:
///
......@@ -386,7 +394,7 @@ class Future {
/// If the predicate does not obtain with the value, the result
/// is a folly::PredicateDoesNotObtain exception
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
/// the result of collect or collectAll
......@@ -458,14 +466,14 @@ class Future {
///
/// thunk behaves like std::function<Future<T2>(void)>
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
/// the predicate holds.
///
/// thunk behaves like std::function<Future<T2>(void)>
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
/// while the predicate continues to hold.
......@@ -474,19 +482,19 @@ class Future {
///
/// predicate behaves like std::function<bool(void)>
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
// e.g. f.then([](Try<T> t){ return t.value(); });
template <typename F, typename R, bool isTry, typename... Args>
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
// e.g. f.then([](Try<T> t){ return makeFuture<T>(t); });
template <typename F, typename R, bool isTry, typename... Args>
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(); }
void setExecutor(Executor* x, int8_t priority = Executor::MID_PRI) {
......
......@@ -149,11 +149,11 @@ class Core {
/// Call only from Future thread.
template <typename F>
void setCallback(F func) {
void setCallback(F&& func) {
bool transitionToArmed = false;
auto setCallback_ = [&]{
context_ = RequestContext::saveContext();
callback_ = std::move(func);
callback_ = std::forward<F>(func);
};
FSM_START(fsm_)
......
......@@ -367,7 +367,7 @@ retryingPolicyCappedJitteredExponentialBackoff(
Duration backoff_min,
Duration backoff_max,
double jitter_param,
URNG rng,
URNG&& rng,
Policy&& p);
inline
......
......@@ -19,7 +19,6 @@
#include <gtest/gtest.h>
#include <folly/MoveWrapper.h>
#include <folly/futures/Future.h>
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