sapling/eden/fs/utils/ImmediateFuture.h
Zhengchao Liu 1f9541251f utils: add ImmediateFuture::ensure
Summary:
We are adding tracing to nfs. To record the elapsed time of a call, we need to invoke a callback when future is ready (has value or exception). For fuse, this is done with `Future::ensure`. Nfs is using `ImmediateFuture`, so I am adding `ImmediateFuture::ensure` accordingly.

https://www.internalfb.com/intern/diffusion/FBS/browsefile/master/fbcode/eden/fs/fuse/FuseChannel.cpp?commit=64091acc4e2905bfb58b4795e705fa6b554c487c&lines=1733-1735

Reviewed By: chadaustin

Differential Revision: D29278838

fbshipit-source-id: 176d6361a0ef1dd4a89db470acbb36b4868e04ef
2021-06-23 14:23:40 -07:00

183 lines
5.8 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#pragma once
#include <folly/Executor.h>
#include <folly/futures/Future.h>
#include <variant>
#include "eden/fs/utils/ImmediateFuture-pre.h"
namespace facebook::eden {
/**
* An ImmediateFuture is a small wrapper around either a folly::SemiFuture<T>,
* or a folly::Try<T>. This allows code to not pay the overhead of
* folly::SemiFuture for when an immediate value is available. In particular, an
* ImmediateFuture will not allocate memory in this case.
*/
template <typename T>
class ImmediateFuture {
public:
/**
* The type of the stored value.
*/
using value_type = T;
/**
* Default construct an ImmediateFuture with T's default constructor.
*/
ImmediateFuture() noexcept(std::is_nothrow_default_constructible_v<T>)
: inner_{folly::Try<T>{T{}}} {}
/**
* Construct an ImmediateFuture with an already constructed value. No
* folly::SemiFuture will be allocated.
*/
/* implicit */ ImmediateFuture(folly::Try<T>&& value) noexcept(
std::is_nothrow_move_constructible_v<folly::Try<T>>)
: inner_{std::move(value)} {}
/**
* Construct an ImmediateFuture with an already constructed value. No
* folly::SemiFuture will be allocated.
*/
/* implicit */ ImmediateFuture(T value) noexcept(
std::is_nothrow_move_constructible_v<folly::Try<T>>)
: ImmediateFuture{folly::Try<T>{std::move(value)}} {}
/**
* Construct an ImmediateFuture with a SemiFuture.
*/
/* implicit */ ImmediateFuture(folly::SemiFuture<T>&& fut) noexcept(
std::is_nothrow_move_constructible_v<folly::SemiFuture<T>>)
: inner_{std::move(fut)} {}
~ImmediateFuture() = default;
ImmediateFuture(const ImmediateFuture<T>&) = delete;
ImmediateFuture& operator=(const ImmediateFuture<T>&) = delete;
ImmediateFuture(ImmediateFuture<T>&&) = default;
ImmediateFuture& operator=(ImmediateFuture<T>&&) = default;
/**
* Queue the func continuation once this future is ready.
*
* When the ImmediateFuture is an immediate value, the passed in function
* will be called without waiting. When a SemiFuture value, the function will
* be called in the same executor as the previous future executor.
*
* Func must be a function taking a T as the only argument, its return value
* must be of a type that an ImmediateFuture can be constructed from
* (folly::Try, folly::SemiFuture, etc).
*
* This method must be called with an rvalue-ref and should no longer be used
* afterwards:
*
* ImmediateFuture<int> fut{42};
* std::move(fut).thenValue([](int value) { return value + 1; });
*/
template <typename Func>
ImmediateFuture<detail::continuation_result_t<Func, T>> thenValue(
Func&& func) &&;
/**
* Queue the func continuation once this future is ready.
*
* When the ImmediateFuture is an immediate value, the passed in function
* will be called without waiting. When a SemiFuture value, the function will
* be called in the same executor as the previous future executor.
*
* Func must be a function taking a Try<T> as the only argument, its return
* value must be of a type that an ImmediateFuture can be constructed from
* (folly::Try, folly::SemiFuture, etc).
*
* This method must be called with an rvalue-ref and should no longer be used
* afterwards:
*
* ImmediateFuture<int> fut{42};
* std::move(fut).thenTry([](Try<int> value) { return *value + 1; });
*/
template <typename Func>
ImmediateFuture<detail::continuation_result_t<Func, folly::Try<T>>> thenTry(
Func&& func) &&;
/**
* Call func unconditionally once this future is ready and
* the value/exception is passed through to the resulting Future.
*
* Func is like std::function<void()>. If func throws, its exception
* will be propagated and the original value/exception discarded.
*
* This method must be called with an rvalue-ref and should no longer be used
* afterwards:
*
* ImmediateFuture<int> fut{42};
* std::move(fut).ensure([&]() { cleanup(); });
*/
template <typename Func>
ImmediateFuture<T> ensure(Func&& func) &&;
/**
* Build a SemiFuture out of this ImmediateFuture and returns it.
*
* The returned semi future can then be executed on an executor with its
* via() method. When this ImmediateFuture stores an immediate value, this
* will allocate a new SemiFuture that is ready.
*
* Can be used as such:
*
* ImmediateFuture<T> immFut = ...;
* folly::Future<T> fut = std::move(fut).semi().via(executor);
*/
folly::SemiFuture<T> semi() &&;
/**
* Wait for the future to complete and return its value or throw its
* exception.
*
* When the future is an immediate value, this returns without waiting.
*
* A folly::FutureTimeout will be thrown if the timeout is reached.
*/
T get(folly::HighResDuration timeout = folly::HighResDuration::max()) &&;
/**
* Wait for the future to complete and return the Try value.
*
* When the future is an immediate value, this returns without waiting.
*
* A folly::FutureTimeout will be thrown if the timeout is reached.
*/
folly::Try<T> getTry(
folly::HighResDuration timeout = folly::HighResDuration::max()) &&;
bool hasImmediate() const {
return std::holds_alternative<folly::Try<T>>(inner_);
}
private:
std::variant<folly::Try<T>, folly::SemiFuture<T>> inner_;
};
/**
* Build an ImmediateFuture from func.
*
* Exceptions thrown by func will be captured in the returned ImmediateFuture.
*
* This is a shorthand for:
*
* ImmediateFuture<folly::Unit>().thenTry([](auto&&) { return func(); });
*/
template <typename Func>
auto makeImmediateFutureWith(Func&& func);
} // namespace facebook::eden
#include "eden/fs/utils/ImmediateFuture-inl.h"