Commit 78c1e242 authored by Andrii Grynenko's avatar Andrii Grynenko Committed by Facebook Github Bot

Add a defer variant that passes Executor* to the callback

Summary: This can allow converting asynchronous APIs that require an Executor to lazy SemiFuture.

Reviewed By: yfeldblum

Differential Revision: D13502409

fbshipit-source-id: 8c824f314d83f3ab208e7b384c9e535cf40210f1
parent 11ce85ed
...@@ -522,6 +522,11 @@ class DeferredExecutor final : public Executor { ...@@ -522,6 +522,11 @@ class DeferredExecutor final : public Executor {
executor_->add(std::exchange(func_, nullptr)); executor_->add(std::exchange(func_, nullptr));
} }
Executor* getExecutor() const {
assert(executor_.get());
return executor_.get();
}
void setExecutor(folly::Executor::KeepAlive<> executor) { void setExecutor(folly::Executor::KeepAlive<> executor) {
if (nestedExecutors_) { if (nestedExecutors_) {
auto nestedExecutors = std::exchange(nestedExecutors_, nullptr); auto nestedExecutors = std::exchange(nestedExecutors_, nullptr);
...@@ -895,6 +900,23 @@ SemiFuture<T>::defer(F&& func) && { ...@@ -895,6 +900,23 @@ SemiFuture<T>::defer(F&& func) && {
return sf; return sf;
} }
template <class T>
template <typename F>
SemiFuture<
typename futures::detail::tryExecutorCallableResult<T, F>::value_type>
SemiFuture<T>::defer(F&& func) && {
DeferredExecutor* deferredExecutor = getDeferredExecutor();
if (!deferredExecutor) {
auto newDeferredExecutor = DeferredExecutor::create();
deferredExecutor = newDeferredExecutor.get();
this->setExecutor(std::move(newDeferredExecutor));
}
return std::move(*this).defer(
[deferredExecutor, f = std::forward<F>(func)](Try<T>&& t) mutable {
return f(deferredExecutor->getExecutor(), std::move(t));
});
}
template <class T> template <class T>
template <typename F> template <typename F>
SemiFuture<typename futures::detail::valueCallableResult<T, F>::value_type> SemiFuture<typename futures::detail::valueCallableResult<T, F>::value_type>
......
...@@ -112,7 +112,10 @@ struct callableResult { ...@@ -112,7 +112,10 @@ struct callableResult {
typedef Future<typename ReturnsFuture::Inner> Return; typedef Future<typename ReturnsFuture::Inner> Return;
}; };
template <typename T, typename F> template <
typename T,
typename F,
typename = std::enable_if_t<is_invocable<F, Try<T>&&>::value>>
struct tryCallableResult { struct tryCallableResult {
typedef detail::argResult<true, F, Try<T>&&> Arg; typedef detail::argResult<true, F, Try<T>&&> Arg;
typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture; typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture;
...@@ -120,6 +123,17 @@ struct tryCallableResult { ...@@ -120,6 +123,17 @@ struct tryCallableResult {
typedef Future<value_type> Return; typedef Future<value_type> Return;
}; };
template <
typename T,
typename F,
typename = std::enable_if_t<is_invocable<F, Executor*, Try<T>&&>::value>>
struct tryExecutorCallableResult {
typedef detail::argResult<true, F, Executor*, Try<T>&&> Arg;
typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture;
typedef typename ReturnsFuture::Inner value_type;
typedef Future<value_type> Return;
};
template <typename T, typename F> template <typename T, typename F>
struct valueCallableResult { struct valueCallableResult {
typedef detail::argResult<false, F, T&&> Arg; typedef detail::argResult<false, F, T&&> Arg;
......
...@@ -697,6 +697,11 @@ class SemiFuture : private futures::detail::FutureBase<T> { ...@@ -697,6 +697,11 @@ class SemiFuture : private futures::detail::FutureBase<T> {
SemiFuture<typename futures::detail::tryCallableResult<T, F>::value_type> SemiFuture<typename futures::detail::tryCallableResult<T, F>::value_type>
defer(F&& func) &&; defer(F&& func) &&;
template <typename F>
SemiFuture<
typename futures::detail::tryExecutorCallableResult<T, F>::value_type>
defer(F&& func) &&;
template <typename R, typename... Args> template <typename R, typename... Args>
auto defer(R (&func)(Args...)) && { auto defer(R (&func)(Args...)) && {
return std::move(*this).defer(&func); return std::move(*this).defer(&func);
......
...@@ -1063,3 +1063,10 @@ TEST(SemiFuture, DeferWithNestedSemiFuture) { ...@@ -1063,3 +1063,10 @@ TEST(SemiFuture, DeferWithNestedSemiFuture) {
EXPECT_GE( EXPECT_GE(
std::chrono::steady_clock::now() - start, std::chrono::milliseconds{300}); std::chrono::steady_clock::now() - start, std::chrono::milliseconds{300});
} }
TEST(SemiFuture, DeferWithExecutor) {
ManualExecutor executor;
auto sf = makeSemiFuture().defer(
[&](Executor* e, Try<Unit>) { EXPECT_EQ(&executor, e); });
std::move(sf).via(&executor).getVia(&executor);
}
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