Commit bf814b82 authored by Lee Howes's avatar Lee Howes Committed by Facebook Github Bot

Separate Try-taking defer and value-taking deferValue in SemiFuture.

Summary: This avoids ambiguity with an auto-parameterised lambda and leads on to a consistent use of deferError in a followup diff.

Reviewed By: yfeldblum

Differential Revision: D7224490

fbshipit-source-id: df410cd97ca0991db8fcb46267dd6a4236f3f6a3
parent 387fc2ca
...@@ -718,7 +718,8 @@ inline Future<T> SemiFuture<T>::toUnsafeFuture() && { ...@@ -718,7 +718,8 @@ inline Future<T> SemiFuture<T>::toUnsafeFuture() && {
template <class T> template <class T>
template <typename F> template <typename F>
SemiFuture<typename futures::detail::callableResult<T, F>::Return::value_type> SemiFuture<
typename futures::detail::deferCallableResult<T, F>::Return::value_type>
SemiFuture<T>::defer(F&& func) && { SemiFuture<T>::defer(F&& func) && {
DeferredExecutor* deferredExecutor = getDeferredExecutor(); DeferredExecutor* deferredExecutor = getDeferredExecutor();
if (!deferredExecutor) { if (!deferredExecutor) {
...@@ -734,6 +735,19 @@ SemiFuture<T>::defer(F&& func) && { ...@@ -734,6 +735,19 @@ SemiFuture<T>::defer(F&& func) && {
return sf; return sf;
} }
template <class T>
template <typename F>
SemiFuture<typename futures::detail::deferValueCallableResult<T, F>::Return::
value_type>
SemiFuture<T>::deferValue(F&& func) && {
return std::move(*this).defer(
[f = std::forward<F>(func)](folly::Try<T>&& t) mutable {
return f(t.template get<
false,
typename futures::detail::Extract<F>::FirstArg>());
});
}
template <class T> template <class T>
Future<T> Future<T>::makeEmpty() { Future<T> Future<T>::makeEmpty() {
return Future<T>(futures::detail::EmptyConstruct{}); return Future<T>(futures::detail::EmptyConstruct{});
......
...@@ -130,6 +130,32 @@ struct callableResult { ...@@ -130,6 +130,32 @@ struct callableResult {
typedef Future<typename ReturnsFuture::Inner> Return; typedef Future<typename ReturnsFuture::Inner> Return;
}; };
template <typename T, typename F>
struct deferCallableResult {
typedef typename std::conditional<
callableWith<F>::value,
detail::argResult<false, F>,
typename std::conditional<
callableWith<F, Try<T>&&>::value,
detail::argResult<true, F, Try<T>&&>,
detail::argResult<true, F, Try<T>&>>::type>::type Arg;
typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture;
typedef Future<typename ReturnsFuture::Inner> Return;
};
template <typename T, typename F>
struct deferValueCallableResult {
typedef typename std::conditional<
callableWith<F>::value,
detail::argResult<false, F>,
typename std::conditional<
callableWith<F, T&&>::value,
detail::argResult<false, F, T&&>,
detail::argResult<false, F, T&>>::type>::type Arg;
typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture;
typedef Future<typename ReturnsFuture::Inner> Return;
};
template <typename L> template <typename L>
struct Extract : Extract<decltype(&L::operator())> { }; struct Extract : Extract<decltype(&L::operator())> { };
......
...@@ -315,6 +315,7 @@ class SemiFuture : private futures::detail::FutureBase<T> { ...@@ -315,6 +315,7 @@ class SemiFuture : private futures::detail::FutureBase<T> {
/** /**
* Defer work to run on the consumer of the future. * Defer work to run on the consumer of the future.
* Function must take a Try as a parameter.
* This work will be run eithe ron an executor that the caller sets on the * This work will be run eithe ron an executor that the caller sets on the
* SemiFuture, or inline with the call to .get(). * SemiFuture, or inline with the call to .get().
* NB: This is a custom method because boost-blocking executors is a * NB: This is a custom method because boost-blocking executors is a
...@@ -323,9 +324,20 @@ class SemiFuture : private futures::detail::FutureBase<T> { ...@@ -323,9 +324,20 @@ class SemiFuture : private futures::detail::FutureBase<T> {
* of driveable executor here. * of driveable executor here.
*/ */
template <typename F> template <typename F>
SemiFuture<typename futures::detail::callableResult<T, F>::Return::value_type> SemiFuture<
typename futures::detail::deferCallableResult<T, F>::Return::value_type>
defer(F&& func) &&; defer(F&& func) &&;
/**
* Defer for functions taking a T rather than a Try<T>.
*/
template <typename F>
SemiFuture<typename futures::detail::deferValueCallableResult<T, F>::Return::
value_type>
deferValue(F&& func) &&;
// TODO: OnError
/// Return a future that completes inline, as if the future had no executor. /// Return a future that completes inline, as if the future had no executor.
/// Intended for porting legacy code without behavioural change, and for rare /// Intended for porting legacy code without behavioural change, and for rare
/// cases where this is really the intended behaviour. /// cases where this is really the intended behaviour.
......
...@@ -478,7 +478,7 @@ TEST(SemiFuture, SimpleDeferWithValue) { ...@@ -478,7 +478,7 @@ TEST(SemiFuture, SimpleDeferWithValue) {
std::atomic<int> innerResult{0}; std::atomic<int> innerResult{0};
Promise<int> p; Promise<int> p;
auto f = p.getSemiFuture().toUnsafeFuture(); auto f = p.getSemiFuture().toUnsafeFuture();
auto sf = std::move(f).semi().defer([&](int a) { innerResult = a; }); auto sf = std::move(f).semi().deferValue([&](int a) { innerResult = a; });
p.setValue(7); p.setValue(7);
// Run "F" here inline in the calling thread // Run "F" here inline in the calling thread
std::move(sf).get(); std::move(sf).get();
...@@ -491,7 +491,7 @@ TEST(SemiFuture, ChainingDefertoThenWithValue) { ...@@ -491,7 +491,7 @@ TEST(SemiFuture, ChainingDefertoThenWithValue) {
EventBase e2; EventBase e2;
Promise<int> p; Promise<int> p;
auto f = p.getSemiFuture().toUnsafeFuture(); auto f = p.getSemiFuture().toUnsafeFuture();
auto sf = std::move(f).semi().defer([&](int a) { auto sf = std::move(f).semi().deferValue([&](int a) {
innerResult = a; innerResult = a;
return a; return a;
}); });
...@@ -528,7 +528,7 @@ TEST(SemiFuture, DeferWithinContinuation) { ...@@ -528,7 +528,7 @@ TEST(SemiFuture, DeferWithinContinuation) {
auto resultF = std::move(f).then([&, p3 = std::move(p2)](int outer) mutable { auto resultF = std::move(f).then([&, p3 = std::move(p2)](int outer) mutable {
result = outer; result = outer;
return makeSemiFuture<int>(std::move(outer)) return makeSemiFuture<int>(std::move(outer))
.defer([&, p4 = std::move(p3)](int inner) mutable { .deferValue([&, p4 = std::move(p3)](int inner) mutable {
innerResult = inner; innerResult = inner;
p4.setValue(inner); p4.setValue(inner);
return inner; return inner;
......
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