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 {
executor_->add(std::exchange(func_, nullptr));
}
Executor* getExecutor() const {
assert(executor_.get());
return executor_.get();
}
void setExecutor(folly::Executor::KeepAlive<> executor) {
if (nestedExecutors_) {
auto nestedExecutors = std::exchange(nestedExecutors_, nullptr);
......@@ -895,6 +900,23 @@ SemiFuture<T>::defer(F&& func) && {
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 <typename F>
SemiFuture<typename futures::detail::valueCallableResult<T, F>::value_type>
......
......@@ -112,7 +112,10 @@ struct callableResult {
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 {
typedef detail::argResult<true, F, Try<T>&&> Arg;
typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture;
......@@ -120,6 +123,17 @@ struct tryCallableResult {
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>
struct valueCallableResult {
typedef detail::argResult<false, F, T&&> Arg;
......
......@@ -697,6 +697,11 @@ class SemiFuture : private futures::detail::FutureBase<T> {
SemiFuture<typename futures::detail::tryCallableResult<T, F>::value_type>
defer(F&& func) &&;
template <typename F>
SemiFuture<
typename futures::detail::tryExecutorCallableResult<T, F>::value_type>
defer(F&& func) &&;
template <typename R, typename... Args>
auto defer(R (&func)(Args...)) && {
return std::move(*this).defer(&func);
......
......@@ -1063,3 +1063,10 @@ TEST(SemiFuture, DeferWithNestedSemiFuture) {
EXPECT_GE(
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