template <typename T>
struct Lift : std::conditional<std::is_same<T, void>::value, Unit, T> {};
template <typename T>
+ using LiftT = typename Lift<T>::type;
+ template <typename T>
struct Drop : std::conditional<std::is_same<T, Unit>::value, void, T> {};
+ template <typename T>
+ using DropT = typename Drop<T>::type;
constexpr bool operator==(const Unit& /*other*/) const {
return true;
} // namespace futures
template <class T>
-Future<T> Future<T>::makeEmpty() {
- return Future<T>(futures::detail::EmptyConstruct{});
+SemiFuture<typename std::decay<T>::type> makeSemiFuture(T&& t) {
+ return makeSemiFuture(Try<typename std::decay<T>::type>(std::forward<T>(t)));
+}
+
+inline SemiFuture<Unit> makeSemiFuture() {
+ return makeSemiFuture(Unit{});
+}
+
+// makeSemiFutureWith(SemiFuture<T>()) -> SemiFuture<T>
+template <class F>
+typename std::enable_if<
+ isSemiFuture<typename std::result_of<F()>::type>::value,
+ typename std::result_of<F()>::type>::type
+makeSemiFutureWith(F&& func) {
+ using InnerType =
+ typename isSemiFuture<typename std::result_of<F()>::type>::Inner;
+ try {
+ return std::forward<F>(func)();
+ } catch (std::exception& e) {
+ return makeSemiFuture<InnerType>(
+ exception_wrapper(std::current_exception(), e));
+ } catch (...) {
+ return makeSemiFuture<InnerType>(
+ exception_wrapper(std::current_exception()));
+ }
+}
+
+// makeSemiFutureWith(T()) -> SemiFuture<T>
+// makeSemiFutureWith(void()) -> SemiFuture<Unit>
+template <class F>
+typename std::enable_if<
+ !(isSemiFuture<typename std::result_of<F()>::type>::value),
+ SemiFuture<Unit::LiftT<typename std::result_of<F()>::type>>>::type
+makeSemiFutureWith(F&& func) {
+ using LiftedResult = Unit::LiftT<typename std::result_of<F()>::type>;
+ return makeSemiFuture<LiftedResult>(
+ makeTryWith([&func]() mutable { return std::forward<F>(func)(); }));
+}
+
+template <class T>
+SemiFuture<T> makeSemiFuture(std::exception_ptr const& e) {
+ return makeSemiFuture(Try<T>(e));
+}
+
+template <class T>
+SemiFuture<T> makeSemiFuture(exception_wrapper ew) {
+ return makeSemiFuture(Try<T>(std::move(ew)));
+}
+
+template <class T, class E>
+typename std::
+ enable_if<std::is_base_of<std::exception, E>::value, SemiFuture<T>>::type
+ makeSemiFuture(E const& e) {
+ return makeSemiFuture(Try<T>(make_exception_wrapper<E>(e)));
}
template <class T>
-Future<T>::Future(Future<T>&& other) noexcept : core_(other.core_) {
+SemiFuture<T> makeSemiFuture(Try<T>&& t) {
+ return SemiFuture<T>(new futures::detail::Core<T>(std::move(t)));
+}
+
+template <class T>
+SemiFuture<T> SemiFuture<T>::makeEmpty() {
+ return SemiFuture<T>(futures::detail::EmptyConstruct{});
+}
+
+template <class T>
+SemiFuture<T>::SemiFuture(SemiFuture<T>&& other) noexcept : core_(other.core_) {
other.core_ = nullptr;
}
template <class T>
-Future<T>& Future<T>::operator=(Future<T>&& other) noexcept {
+SemiFuture<T>& SemiFuture<T>::operator=(SemiFuture<T>&& other) noexcept {
std::swap(core_, other.core_);
return *this;
}
+template <class T>
+SemiFuture<T>::SemiFuture(Future<T>&& other) noexcept : core_(other.core_) {
+ other.core_ = nullptr;
+}
+
+template <class T>
+SemiFuture<T>& SemiFuture<T>::operator=(Future<T>&& other) noexcept {
+ std::swap(core_, other.core_);
+ return *this;
+}
+
+template <class T>
+template <class T2, typename>
+SemiFuture<T>::SemiFuture(T2&& val)
+ : core_(new futures::detail::Core<T>(Try<T>(std::forward<T2>(val)))) {}
+
+template <class T>
+template <typename T2>
+SemiFuture<T>::SemiFuture(
+ typename std::enable_if<std::is_same<Unit, T2>::value>::type*)
+ : core_(new futures::detail::Core<T>(Try<T>(T()))) {}
+
+template <class T>
+template <
+ class... Args,
+ typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>::
+ type>
+SemiFuture<T>::SemiFuture(in_place_t, Args&&... args)
+ : core_(
+ new futures::detail::Core<T>(in_place, std::forward<Args>(args)...)) {
+}
+
+template <class T>
+SemiFuture<T>::~SemiFuture() {
+ detach();
+}
+
+template <class T>
+typename std::add_lvalue_reference<T>::type SemiFuture<T>::value() {
+ throwIfInvalid();
+
+ return core_->getTry().value();
+}
+
+template <class T>
+typename std::add_lvalue_reference<const T>::type SemiFuture<T>::value() const {
+ throwIfInvalid();
+
+ return core_->getTry().value();
+}
+
+template <class T>
+inline Future<T> SemiFuture<T>::via(Executor* executor, int8_t priority) && {
+ throwIfInvalid();
+
+ setExecutor(executor, priority);
+
+ auto newFuture = Future<T>(core_);
+ core_ = nullptr;
+ return newFuture;
+}
+
+template <class T>
+inline Future<T> SemiFuture<T>::via(Executor* executor, int8_t priority) & {
+ throwIfInvalid();
+ Promise<T> p;
+ auto f = p.getFuture();
+ auto func = [p = std::move(p)](Try<T>&& t) mutable {
+ p.setTry(std::move(t));
+ };
+ using R = futures::detail::callableResult<T, decltype(func)>;
+ thenImplementation<decltype(func), R>(std::move(func), typename R::Arg());
+ return std::move(f).via(executor, priority);
+}
+
+template <class T>
+bool SemiFuture<T>::isReady() const {
+ throwIfInvalid();
+ return core_->ready();
+}
+
+template <class T>
+bool SemiFuture<T>::hasValue() {
+ return getTry().hasValue();
+}
+
+template <class T>
+bool SemiFuture<T>::hasException() {
+ return getTry().hasException();
+}
+
+template <class T>
+void SemiFuture<T>::detach() {
+ if (core_) {
+ core_->detachFuture();
+ core_ = nullptr;
+ }
+}
+
+template <class T>
+Try<T>& SemiFuture<T>::getTry() {
+ throwIfInvalid();
+
+ return core_->getTry();
+}
+
+template <class T>
+void SemiFuture<T>::throwIfInvalid() const {
+ if (!core_)
+ throwNoState();
+}
+
+template <class T>
+Optional<Try<T>> SemiFuture<T>::poll() {
+ Optional<Try<T>> o;
+ if (core_->ready()) {
+ o = std::move(core_->getTry());
+ }
+ return o;
+}
+
+template <class T>
+void SemiFuture<T>::raise(exception_wrapper exception) {
+ core_->raise(std::move(exception));
+}
+
+template <class T>
+template <class F>
+void SemiFuture<T>::setCallback_(F&& func) {
+ throwIfInvalid();
+ core_->setCallback(std::forward<F>(func));
+}
+
+template <class T>
+SemiFuture<T>::SemiFuture(futures::detail::EmptyConstruct) noexcept
+ : core_(nullptr) {}
+
+template <class T>
+Future<T> Future<T>::makeEmpty() {
+ return Future<T>(futures::detail::EmptyConstruct{});
+}
+
+template <class T>
+Future<T>::Future(Future<T>&& other) noexcept
+ : SemiFuture<T>(std::move(other)) {}
+
+template <class T>
+Future<T>& Future<T>::operator=(Future<T>&& other) noexcept {
+ SemiFuture<T>::operator=(SemiFuture<T>{std::move(other)});
+ return *this;
+}
+
template <class T>
template <
class T2,
std::move(other).then([](T2&& v) { return T(std::move(v)); }));
}
+// TODO: isSemiFuture
template <class T>
template <class T2, typename>
-Future<T>::Future(T2&& val)
- : core_(new futures::detail::Core<T>(Try<T>(std::forward<T2>(val)))) {}
+Future<T>::Future(T2&& val) : SemiFuture<T>(std::forward<T2>(val)) {}
template <class T>
template <typename T2>
Future<T>::Future(typename std::enable_if<std::is_same<Unit, T2>::value>::type*)
- : core_(new futures::detail::Core<T>(Try<T>(T()))) {}
+ : SemiFuture<T>() {}
template <class T>
template <
typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>::
type>
Future<T>::Future(in_place_t, Args&&... args)
- : core_(
- new futures::detail::Core<T>(in_place, std::forward<Args>(args)...)) {
-}
+ : SemiFuture<T>(in_place, std::forward<Args>(args)...) {}
template <class T>
Future<T>::~Future() {
- detach();
-}
-
-template <class T>
-void Future<T>::detach() {
- if (core_) {
- core_->detachFuture();
- core_ = nullptr;
- }
-}
-
-template <class T>
-void Future<T>::throwIfInvalid() const {
- if (!core_)
- throwNoState();
-}
-
-template <class T>
-template <class F>
-void Future<T>::setCallback_(F&& func) {
- throwIfInvalid();
- core_->setCallback(std::forward<F>(func));
}
// unwrap
template <class T>
template <typename F, typename R, bool isTry, typename... Args>
typename std::enable_if<!R::ReturnsFuture::value, typename R::Return>::type
-Future<T>::thenImplementation(
+SemiFuture<T>::thenImplementation(
F&& func,
futures::detail::argResult<isTry, F, Args...>) {
static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument");
typedef typename R::ReturnsFuture::Inner B;
- throwIfInvalid();
+ this->throwIfInvalid();
Promise<B> p;
- p.core_->setInterruptHandlerNoLock(core_->getInterruptHandler());
+ p.core_->setInterruptHandlerNoLock(this->core_->getInterruptHandler());
// grab the Future now before we lose our handle on the Promise
auto f = p.getFuture();
- f.core_->setExecutorNoLock(getExecutor());
+ f.core_->setExecutorNoLock(this->getExecutor());
/* This is a bit tricky.
in some circumstances, but I think it should be explicit not implicit
in the destruction of the Future used to create it.
*/
- setCallback_(
+ this->setCallback_(
[state = futures::detail::makeCoreCallbackState(
- std::move(p), std::forward<F>(func))](Try<T> && t) mutable {
+ std::move(p), std::forward<F>(func))](Try<T>&& t) mutable {
+
if (!isTry && t.hasException()) {
state.setException(std::move(t.exception()));
} else {
[&] { return state.invoke(t.template get<isTry, Args>()...); }));
}
});
-
return f;
}
template <class T>
template <typename F, typename R, bool isTry, typename... Args>
typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type
-Future<T>::thenImplementation(
+SemiFuture<T>::thenImplementation(
F&& func,
futures::detail::argResult<isTry, F, Args...>) {
static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument");
typedef typename R::ReturnsFuture::Inner B;
-
- throwIfInvalid();
+ this->throwIfInvalid();
Promise<B> p;
- p.core_->setInterruptHandlerNoLock(core_->getInterruptHandler());
+ p.core_->setInterruptHandlerNoLock(this->core_->getInterruptHandler());
// grab the Future now before we lose our handle on the Promise
auto f = p.getFuture();
- f.core_->setExecutorNoLock(getExecutor());
+ f.core_->setExecutorNoLock(this->getExecutor());
- setCallback_(
+ this->setCallback_(
[state = futures::detail::makeCoreCallbackState(
- std::move(p), std::forward<F>(func))](Try<T> && t) mutable {
+ std::move(p), std::forward<F>(func))](Try<T>&& t) mutable {
if (!isTry && t.hasException()) {
state.setException(std::move(t.exception()));
} else {
typedef typename std::remove_cv<typename std::remove_reference<
typename futures::detail::ArgType<Args...>::FirstArg>::type>::type
FirstArg;
+
return then([instance, func](Try<T>&& t){
return (instance->*func)(t.template get<isTry<FirstArg>::value, Args>()...);
});
"Return type of onError callback must be T or Future<T>");
Promise<T> p;
- p.core_->setInterruptHandlerNoLock(core_->getInterruptHandler());
+ p.core_->setInterruptHandlerNoLock(this->core_->getInterruptHandler());
auto f = p.getFuture();
- setCallback_(
+ this->setCallback_(
[state = futures::detail::makeCoreCallbackState(
- std::move(p), std::forward<F>(func))](Try<T> && t) mutable {
+ std::move(p), std::forward<F>(func))](Try<T>&& t) mutable {
if (auto e = t.template tryGetExceptionObject<Exn>()) {
state.setTry(makeTryWith([&] { return state.invoke(*e); }));
} else {
Promise<T> p;
auto f = p.getFuture();
- setCallback_(
+ this->setCallback_(
[state = futures::detail::makeCoreCallbackState(
- std::move(p), std::forward<F>(func))](Try<T> && t) mutable {
+ std::move(p), std::forward<F>(func))](Try<T>&& t) mutable {
if (auto e = t.template tryGetExceptionObject<Exn>()) {
auto tf2 = state.tryInvoke(*e);
if (tf2.hasException()) {
Promise<T> p;
auto f = p.getFuture();
- setCallback_(
+ this->setCallback_(
[state = futures::detail::makeCoreCallbackState(
std::move(p), std::forward<F>(func))](Try<T> t) mutable {
if (t.hasException()) {
Promise<T> p;
auto f = p.getFuture();
- setCallback_(
+ this->setCallback_(
[state = futures::detail::makeCoreCallbackState(
- std::move(p), std::forward<F>(func))](Try<T> && t) mutable {
+ std::move(p), std::forward<F>(func))](Try<T>&& t) mutable {
if (t.hasException()) {
state.setTry(makeTryWith(
[&] { return state.invoke(std::move(t.exception())); }));
return f;
}
-template <class T>
-typename std::add_lvalue_reference<T>::type Future<T>::value() {
- throwIfInvalid();
-
- return core_->getTry().value();
-}
-
-template <class T>
-typename std::add_lvalue_reference<const T>::type Future<T>::value() const {
- throwIfInvalid();
-
- return core_->getTry().value();
-}
-
-template <class T>
-Try<T>& Future<T>::getTry() {
- throwIfInvalid();
-
- return core_->getTry();
-}
-
template <class T>
Try<T>& Future<T>::getTryVia(DrivableExecutor* e) {
return waitVia(e).getTry();
}
-template <class T>
-Optional<Try<T>> Future<T>::poll() {
- Optional<Try<T>> o;
- if (core_->ready()) {
- o = std::move(core_->getTry());
- }
- return o;
-}
-
-template <class T>
-inline Future<T> Future<T>::via(Executor* executor, int8_t priority) && {
- throwIfInvalid();
-
- setExecutor(executor, priority);
-
- return std::move(*this);
-}
-
-template <class T>
-inline Future<T> Future<T>::via(Executor* executor, int8_t priority) & {
- throwIfInvalid();
-
- Promise<T> p;
- auto f = p.getFuture();
- then([p = std::move(p)](Try<T> && t) mutable { p.setTry(std::move(t)); });
- return std::move(f).via(executor, priority);
-}
-
template <class Func>
auto via(Executor* x, Func&& func)
-> Future<typename isFuture<decltype(std::declval<Func>()())>::Inner> {
}
template <class T>
-bool Future<T>::isReady() const {
- throwIfInvalid();
- return core_->ready();
-}
-
-template <class T>
-bool Future<T>::hasValue() {
- return getTry().hasValue();
-}
-
-template <class T>
-bool Future<T>::hasException() {
- return getTry().hasException();
-}
-
-template <class T>
-void Future<T>::raise(exception_wrapper exception) {
- core_->raise(std::move(exception));
-}
-
-template <class T>
-Future<T>::Future(futures::detail::EmptyConstruct) noexcept : core_(nullptr) {}
+Future<T>::Future(futures::detail::EmptyConstruct) noexcept
+ : SemiFuture<T>(futures::detail::EmptyConstruct{}) {}
// makeFuture
return makeFuture(Try<typename std::decay<T>::type>(std::forward<T>(t)));
}
-inline // for multiple translation units
-Future<Unit> makeFuture() {
+inline Future<Unit> makeFuture() {
return makeFuture(Unit{});
}
template <class F>
typename std::enable_if<
!(isFuture<typename std::result_of<F()>::type>::value),
- Future<typename Unit::Lift<typename std::result_of<F()>::type>::type>>::type
+ Future<Unit::LiftT<typename std::result_of<F()>::type>>>::type
makeFutureWith(F&& func) {
- using LiftedResult =
- typename Unit::Lift<typename std::result_of<F()>::type>::type;
+ using LiftedResult = Unit::LiftT<typename std::result_of<F()>::type>;
return makeFuture<LiftedResult>(
makeTryWith([&func]() mutable { return std::forward<F>(func)(); }));
}
}
});
- return ctx->promise.getFuture().via(getExecutor());
+ return ctx->promise.getFuture().via(this->getExecutor());
}
// delayed
namespace futures {
namespace detail {
-template <class T>
-void waitImpl(Future<T>& f) {
+template <class FutureType, typename T = typename FutureType::value_type>
+void waitImpl(FutureType& f) {
// short-circuit if there's nothing to do
if (f.isReady()) return;
assert(f.isReady());
}
-template <class T>
-void waitImpl(Future<T>& f, Duration dur) {
+template <class FutureType, typename T = typename FutureType::value_type>
+void waitImpl(FutureType& f, Duration dur) {
// short-circuit if there's nothing to do
if (f.isReady()) {
return;
} // namespace detail
} // namespace futures
+template <class T>
+SemiFuture<T>& SemiFuture<T>::wait() & {
+ futures::detail::waitImpl(*this);
+ return *this;
+}
+
+template <class T>
+SemiFuture<T>&& SemiFuture<T>::wait() && {
+ futures::detail::waitImpl(*this);
+ return std::move(*this);
+}
+
+template <class T>
+SemiFuture<T>& SemiFuture<T>::wait(Duration dur) & {
+ futures::detail::waitImpl(*this, dur);
+ return *this;
+}
+
+template <class T>
+SemiFuture<T>&& SemiFuture<T>::wait(Duration dur) && {
+ futures::detail::waitImpl(*this, dur);
+ return std::move(*this);
+}
+
+template <class T>
+T SemiFuture<T>::get() {
+ return std::move(wait().value());
+}
+
+template <class T>
+T SemiFuture<T>::get(Duration dur) {
+ wait(dur);
+ if (this->isReady()) {
+ return std::move(this->value());
+ } else {
+ throwTimedOut();
+ }
+}
+
template <class T>
Future<T>& Future<T>::wait() & {
futures::detail::waitImpl(*this);
return std::move(*this);
}
-template <class T>
-T Future<T>::get() {
- return std::move(wait().value());
-}
-
-template <class T>
-T Future<T>::get(Duration dur) {
- wait(dur);
- if (isReady()) {
- return std::move(value());
- } else {
- throwTimedOut();
- }
-}
-
template <class T>
T Future<T>::getVia(DrivableExecutor* e) {
return std::move(waitVia(e).value());
extern template class Future<int64_t>;
extern template class Future<std::string>;
extern template class Future<double>;
-
} // namespace folly
template <class> class Promise;
+template <class T>
+class SemiFuture;
+
+template <typename T>
+struct isSemiFuture : std::false_type {
+ using Inner = typename Unit::Lift<T>::type;
+};
+
+template <typename T>
+struct isSemiFuture<SemiFuture<T>> : std::true_type {
+ typedef T Inner;
+};
+
+template <typename T>
+struct isSemiFuture<Future<T>> : std::true_type {
+ typedef T Inner;
+};
+
template <typename T>
struct isFuture : std::false_type {
using Inner = typename Unit::Lift<T>::type;
namespace folly {
// Instantiate the most common Future types to save compile time
+template class SemiFuture<Unit>;
+template class SemiFuture<bool>;
+template class SemiFuture<int>;
+template class SemiFuture<int64_t>;
+template class SemiFuture<std::string>;
+template class SemiFuture<double>;
template class Future<Unit>;
template class Future<bool>;
template class Future<int>;
template class Future<int64_t>;
template class Future<std::string>;
template class Future<double>;
-
}
namespace folly { namespace futures {
namespace folly {
template <class T>
-class Future {
+class Future;
+
+template <class T>
+class SemiFuture {
public:
typedef T value_type;
- static Future<T> makeEmpty(); // equivalent to moved-from
+ static SemiFuture<T> makeEmpty(); // equivalent to moved-from
// not copyable
- Future(Future const&) = delete;
- Future& operator=(Future const&) = delete;
+ SemiFuture(SemiFuture const&) = delete;
+ SemiFuture& operator=(SemiFuture const&) = delete;
// movable
- Future(Future&&) noexcept;
- Future& operator=(Future&&) noexcept;
+ SemiFuture(SemiFuture&&) noexcept;
+ SemiFuture& operator=(SemiFuture&&) noexcept;
- // converting move
- template <
- class T2,
- typename std::enable_if<
- !std::is_same<T, typename std::decay<T2>::type>::value &&
- std::is_constructible<T, T2&&>::value &&
- std::is_convertible<T2&&, T>::value,
- int>::type = 0>
- /* implicit */ Future(Future<T2>&&);
- template <
- class T2,
- typename std::enable_if<
- !std::is_same<T, typename std::decay<T2>::type>::value &&
- std::is_constructible<T, T2&&>::value &&
- !std::is_convertible<T2&&, T>::value,
- int>::type = 0>
- explicit Future(Future<T2>&&);
- template <
- class T2,
- typename std::enable_if<
- !std::is_same<T, typename std::decay<T2>::type>::value &&
- std::is_constructible<T, T2&&>::value,
- int>::type = 0>
- Future& operator=(Future<T2>&&);
+ // safe move-constructabilty from Future
+ /* implicit */ SemiFuture(Future<T>&&) noexcept;
+ SemiFuture& operator=(Future<T>&&) noexcept;
/// Construct a Future from a value (perfect forwarding)
- template <class T2 = T, typename =
- typename std::enable_if<
- !isFuture<typename std::decay<T2>::type>::value>::type>
- /* implicit */ Future(T2&& val);
+ template <
+ class T2 = T,
+ typename = typename std::enable_if<
+ !isFuture<typename std::decay<T2>::type>::value>::type>
+ /* implicit */ SemiFuture(T2&& val);
template <class T2 = T>
- /* implicit */ Future(
+ /* implicit */ SemiFuture(
typename std::enable_if<std::is_same<Unit, T2>::value>::type* = nullptr);
template <
class... Args,
typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>::
type = 0>
- explicit Future(in_place_t, Args&&... args);
+ explicit SemiFuture(in_place_t, Args&&... args);
- ~Future();
+ ~SemiFuture();
/** Return the reference to result. Should not be called if !isReady().
Will rethrow the exception if an exception has been
/** A reference to the Try of the value */
Try<T>& getTry();
- /// Call e->drive() repeatedly until the future is fulfilled. Examples
- /// of DrivableExecutor include EventBase and ManualExecutor. Returns a
- /// reference to the Try of the value.
- Try<T>& getTryVia(DrivableExecutor* e);
-
/// If the promise has been fulfilled, return an Optional with the Try<T>.
/// Otherwise return an empty Optional.
/// Note that this moves the Try<T> out.
/// exception).
T get(Duration dur);
+ /// Block until this Future is complete. Returns a reference to this Future.
+ SemiFuture<T>& wait() &;
+
+ /// Overload of wait() for rvalue Futures
+ SemiFuture<T>&& wait() &&;
+
+ /// Block until this Future is complete or until the given Duration passes.
+ /// Returns a reference to this Future
+ SemiFuture<T>& wait(Duration) &;
+
+ /// Overload of wait(Duration) for rvalue Futures
+ SemiFuture<T>&& wait(Duration) &&;
+
+ /// This is not the method you're looking for.
+ ///
+ /// This needs to be public because it's used by make* and when*, and it's
+ /// not worth listing all those and their fancy template signatures as
+ /// friends. But it's not for public consumption.
+ template <class F>
+ void setCallback_(F&& func);
+
+ bool isActive() {
+ return core_->isActive();
+ }
+
+ template <class E>
+ void raise(E&& exception) {
+ raise(make_exception_wrapper<typename std::remove_reference<E>::type>(
+ std::forward<E>(exception)));
+ }
+
+ /// Raise an interrupt. If the promise holder has an interrupt
+ /// handler it will be called and potentially stop asynchronous work from
+ /// being done. This is advisory only - a promise holder may not set an
+ /// interrupt handler, or may do anything including ignore. But, if you know
+ /// your future supports this the most likely result is stopping or
+ /// preventing the asynchronous operation (if in time), and the promise
+ /// holder setting an exception on the future. (That may happen
+ /// asynchronously, of course.)
+ void raise(exception_wrapper interrupt);
+
+ void cancel() {
+ raise(FutureCancellation());
+ }
+
+ protected:
+ typedef futures::detail::Core<T>* corePtr;
+
+ // shared core state object
+ corePtr core_;
+
+ explicit SemiFuture(corePtr obj) : core_(obj) {}
+
+ explicit SemiFuture(futures::detail::EmptyConstruct) noexcept;
+
+ void detach();
+
+ void throwIfInvalid() const;
+
+ friend class Promise<T>;
+ template <class>
+ friend class SemiFuture;
+
+ template <class T2>
+ friend SemiFuture<T2> makeSemiFuture(Try<T2>&&);
+
+ Executor* getExecutor() {
+ return core_->getExecutor();
+ }
+
+ void setExecutor(Executor* x, int8_t priority = Executor::MID_PRI) {
+ core_->setExecutor(x, priority);
+ }
+
+ // 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, futures::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, futures::detail::argResult<isTry, F, Args...>);
+};
+
+template <class T>
+class Future : public SemiFuture<T> {
+ public:
+ typedef T value_type;
+
+ static Future<T> makeEmpty(); // equivalent to moved-from
+
+ // not copyable
+ Future(Future const&) = delete;
+ Future& operator=(Future const&) = delete;
+
+ // movable
+ Future(Future&&) noexcept;
+ Future& operator=(Future&&) noexcept;
+
+ // converting move
+ template <
+ class T2,
+ typename std::enable_if<
+ !std::is_same<T, typename std::decay<T2>::type>::value &&
+ std::is_constructible<T, T2&&>::value &&
+ std::is_convertible<T2&&, T>::value,
+ int>::type = 0>
+ /* implicit */ Future(Future<T2>&&);
+ template <
+ class T2,
+ typename std::enable_if<
+ !std::is_same<T, typename std::decay<T2>::type>::value &&
+ std::is_constructible<T, T2&&>::value &&
+ !std::is_convertible<T2&&, T>::value,
+ int>::type = 0>
+ explicit Future(Future<T2>&&);
+ template <
+ class T2,
+ typename std::enable_if<
+ !std::is_same<T, typename std::decay<T2>::type>::value &&
+ std::is_constructible<T, T2&&>::value,
+ int>::type = 0>
+ Future& operator=(Future<T2>&&);
+
+ /// Construct a Future from a value (perfect forwarding)
+ template <
+ class T2 = T,
+ typename = typename std::enable_if<
+ !isFuture<typename std::decay<T2>::type>::value &&
+ !isSemiFuture<typename std::decay<T2>::type>::value>::type>
+ /* implicit */ Future(T2&& val);
+
+ template <class T2 = T>
+ /* implicit */ Future(
+ typename std::enable_if<std::is_same<Unit, T2>::value>::type* = nullptr);
+
+ template <
+ class... Args,
+ typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>::
+ type = 0>
+ explicit Future(in_place_t, Args&&... args);
+
+ ~Future();
+
+ /// Call e->drive() repeatedly until the future is fulfilled. Examples
+ /// of DrivableExecutor include EventBase and ManualExecutor. Returns a
+ /// reference to the Try of the value.
+ Try<T>& getTryVia(DrivableExecutor* e);
+
/// Call e->drive() repeatedly until the future is fulfilled. Examples
/// of DrivableExecutor include EventBase and ManualExecutor. Returns the
/// value (moved out), or throws the exception.
*/
template <typename F, typename R = futures::detail::callableResult<T, F>>
typename R::Return then(F&& func) {
- return thenImplementation<F, R>(std::forward<F>(func), typename R::Arg());
+ return this->template thenImplementation<F, R>(
+ std::forward<F>(func), typename R::Arg());
}
/// Variant where func is an member function
/// via x, and c executes via the same executor (if any) that f had.
template <class Executor, class Arg, class... Args>
auto then(Executor* x, Arg&& arg, Args&&... args) {
- auto oldX = getExecutor();
- setExecutor(x);
+ auto oldX = this->getExecutor();
+ this->setExecutor(x);
return this->then(std::forward<Arg>(arg), std::forward<Args>(args)...)
.via(oldX);
}
template <class F>
Future<T> onTimeout(Duration, F&& func, Timekeeper* = nullptr);
- /// This is not the method you're looking for.
- ///
- /// This needs to be public because it's used by make* and when*, and it's
- /// not worth listing all those and their fancy template signatures as
- /// friends. But it's not for public consumption.
- template <class F>
- void setCallback_(F&& func);
-
/// A Future's callback is executed when all three of these conditions have
/// become true: it has a value (set by the Promise), it has a callback (set
/// by then), and it is active (active by default).
///
/// Inactive Futures will activate upon destruction.
FOLLY_DEPRECATED("do not use") Future<T>& activate() & {
- core_->activate();
+ this->core_->activate();
return *this;
}
FOLLY_DEPRECATED("do not use") Future<T>& deactivate() & {
- core_->deactivate();
+ this->core_->deactivate();
return *this;
}
FOLLY_DEPRECATED("do not use") Future<T> activate() && {
- core_->activate();
+ this->core_->activate();
return std::move(*this);
}
FOLLY_DEPRECATED("do not use") Future<T> deactivate() && {
- core_->deactivate();
+ this->core_->deactivate();
return std::move(*this);
}
- bool isActive() {
- return core_->isActive();
- }
-
- template <class E>
- void raise(E&& exception) {
- raise(make_exception_wrapper<typename std::remove_reference<E>::type>(
- std::forward<E>(exception)));
- }
-
- /// Raise an interrupt. If the promise holder has an interrupt
- /// handler it will be called and potentially stop asynchronous work from
- /// being done. This is advisory only - a promise holder may not set an
- /// interrupt handler, or may do anything including ignore. But, if you know
- /// your future supports this the most likely result is stopping or
- /// preventing the asynchronous operation (if in time), and the promise
- /// holder setting an exception on the future. (That may happen
- /// asynchronously, of course.)
- void raise(exception_wrapper interrupt);
-
- void cancel() {
- raise(FutureCancellation());
- }
-
/// Throw TimedOut if this Future does not complete within the given
/// duration from now. The optional Timeekeeper is as with futures::sleep().
Future<T> within(Duration, Timekeeper* = nullptr);
auto thenMultiWithExecutor(Executor* x, Callback&& fn, Callbacks&&... fns) {
// thenMultiExecutor with two callbacks is
// via(x).then(a).thenMulti(b, ...).via(oldX)
- auto oldX = getExecutor();
- setExecutor(x);
+ auto oldX = this->getExecutor();
+ this->setExecutor(x);
return then(std::forward<Callback>(fn))
.thenMulti(std::forward<Callbacks>(fns)...)
.via(oldX);
return then([]{ return Unit{}; });
}
+ // Convert this Future to a SemiFuture to safely export from a library
+ // without exposing a continuation interface
+ SemiFuture<T> semi() {
+ return SemiFuture<T>{std::move(*this)};
+ }
+
protected:
typedef futures::detail::Core<T>* corePtr;
- // shared core state object
- corePtr core_;
-
- explicit
- Future(corePtr obj) : core_(obj) {}
+ explicit Future(corePtr obj) : SemiFuture<T>(obj) {}
explicit Future(futures::detail::EmptyConstruct) noexcept;
- void detach();
-
- void throwIfInvalid() const;
-
friend class Promise<T>;
template <class> friend class Future;
+ friend class SemiFuture<T>;
template <class T2>
friend Future<T2> makeFuture(Try<T2>&&);
/// predicate behaves like std::function<bool(void)>
template <class P, class F>
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, futures::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, futures::detail::argResult<isTry, F, Args...>);
-
- Executor* getExecutor() { return core_->getExecutor(); }
- void setExecutor(Executor* x, int8_t priority = Executor::MID_PRI) {
- core_->setExecutor(x, priority);
- }
};
} // namespace folly
namespace folly {
// forward declaration
+template <class T>
+class SemiFuture;
template <class T> class Future;
namespace futures {
private:
typedef typename Future<T>::corePtr corePtr;
+ template <class>
+ friend class SemiFuture;
template <class> friend class Future;
template <class, class>
friend class futures::detail::CoreCallbackState;
TEST(Executor, ThrowableThen) {
InlineExecutor x;
+ auto f = Future<Unit>().then([]() { throw std::runtime_error("Faildog"); });
+
+ /*
auto f = Future<Unit>().via(&x).then([](){
throw std::runtime_error("Faildog");
- });
+ });*/
EXPECT_THROW(f.value(), std::exception);
}
--- /dev/null
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/Baton.h>
+#include <folly/Executor.h>
+#include <folly/Memory.h>
+#include <folly/Unit.h>
+#include <folly/dynamic.h>
+#include <folly/futures/Future.h>
+#include <folly/io/async/EventBase.h>
+#include <folly/portability/GTest.h>
+
+#include <algorithm>
+#include <atomic>
+#include <memory>
+#include <numeric>
+#include <string>
+#include <thread>
+#include <type_traits>
+
+using namespace folly;
+
+#define EXPECT_TYPE(x, T) EXPECT_TRUE((std::is_same<decltype(x), T>::value))
+
+typedef FutureException eggs_t;
+static eggs_t eggs("eggs");
+
+// Future
+
+TEST(SemiFuture, makeEmpty) {
+ auto f = SemiFuture<int>::makeEmpty();
+ EXPECT_THROW(f.isReady(), NoState);
+}
+
+TEST(SemiFuture, futureDefaultCtor) {
+ SemiFuture<Unit>();
+}
+
+TEST(SemiFuture, makeSemiFutureWithUnit) {
+ int count = 0;
+ SemiFuture<Unit> fu = makeSemiFutureWith([&] { count++; });
+ EXPECT_EQ(1, count);
+}
+
+namespace {
+SemiFuture<int> onErrorHelperEggs(const eggs_t&) {
+ return makeSemiFuture(10);
+}
+SemiFuture<int> onErrorHelperGeneric(const std::exception&) {
+ return makeSemiFuture(20);
+}
+} // namespace
+
+TEST(SemiFuture, special) {
+ EXPECT_FALSE(std::is_copy_constructible<SemiFuture<int>>::value);
+ EXPECT_FALSE(std::is_copy_assignable<SemiFuture<int>>::value);
+ EXPECT_TRUE(std::is_move_constructible<SemiFuture<int>>::value);
+ EXPECT_TRUE(std::is_move_assignable<SemiFuture<int>>::value);
+}
+
+TEST(SemiFuture, value) {
+ auto f = makeSemiFuture(std::unique_ptr<int>(new int(42)));
+ auto up = std::move(f.value());
+ EXPECT_EQ(42, *up);
+
+ EXPECT_THROW(makeSemiFuture<int>(eggs).value(), eggs_t);
+}
+
+TEST(SemiFuture, hasException) {
+ EXPECT_TRUE(makeSemiFuture<int>(eggs).getTry().hasException());
+ EXPECT_FALSE(makeSemiFuture(42).getTry().hasException());
+}
+
+TEST(SemiFuture, hasValue) {
+ EXPECT_TRUE(makeSemiFuture(42).getTry().hasValue());
+ EXPECT_FALSE(makeSemiFuture<int>(eggs).getTry().hasValue());
+}
+
+TEST(SemiFuture, makeSemiFuture) {
+ EXPECT_TYPE(makeSemiFuture(42), SemiFuture<int>);
+ EXPECT_EQ(42, makeSemiFuture(42).value());
+
+ EXPECT_TYPE(makeSemiFuture<float>(42), SemiFuture<float>);
+ EXPECT_EQ(42, makeSemiFuture<float>(42).value());
+
+ auto fun = [] { return 42; };
+ EXPECT_TYPE(makeSemiFutureWith(fun), SemiFuture<int>);
+ EXPECT_EQ(42, makeSemiFutureWith(fun).value());
+
+ auto funf = [] { return makeSemiFuture<int>(43); };
+ EXPECT_TYPE(makeSemiFutureWith(funf), SemiFuture<int>);
+ EXPECT_EQ(43, makeSemiFutureWith(funf).value());
+
+ auto failfun = []() -> int { throw eggs; };
+ EXPECT_TYPE(makeSemiFutureWith(failfun), SemiFuture<int>);
+ EXPECT_NO_THROW(makeSemiFutureWith(failfun));
+ EXPECT_THROW(makeSemiFutureWith(failfun).value(), eggs_t);
+
+ auto failfunf = []() -> SemiFuture<int> { throw eggs; };
+ EXPECT_TYPE(makeSemiFutureWith(failfunf), SemiFuture<int>);
+ EXPECT_NO_THROW(makeSemiFutureWith(failfunf));
+ EXPECT_THROW(makeSemiFutureWith(failfunf).value(), eggs_t);
+
+ EXPECT_TYPE(makeSemiFuture(), SemiFuture<Unit>);
+}
+
+TEST(SemiFuture, Constructor) {
+ auto f1 = []() -> SemiFuture<int> { return SemiFuture<int>(3); }();
+ EXPECT_EQ(f1.value(), 3);
+ auto f2 = []() -> SemiFuture<Unit> { return SemiFuture<Unit>(); }();
+ EXPECT_NO_THROW(f2.value());
+}
+
+TEST(SemiFuture, ImplicitConstructor) {
+ auto f1 = []() -> SemiFuture<int> { return 3; }();
+ EXPECT_EQ(f1.value(), 3);
+}
+
+TEST(SemiFuture, InPlaceConstructor) {
+ auto f = SemiFuture<std::pair<int, double>>(in_place, 5, 3.2);
+ EXPECT_EQ(5, f.value().first);
+}
+
+TEST(SemiFuture, makeSemiFutureNoThrow) {
+ makeSemiFuture().value();
+}
+
+TEST(SemiFuture, ConstructSemiFutureFromEmptyFuture) {
+ auto f = SemiFuture<int>{Future<int>::makeEmpty()};
+ EXPECT_THROW(f.isReady(), NoState);
+}
+
+TEST(SemiFuture, ConstructSemiFutureFromFutureDefaultCtor) {
+ SemiFuture<Unit>(Future<Unit>{});
+}
+
+TEST(SemiFuture, MakeSemiFutureFromFutureWithUnit) {
+ int count = 0;
+ SemiFuture<Unit> fu = SemiFuture<Unit>{makeFutureWith([&] { count++; })};
+ EXPECT_EQ(1, count);
+}
+
+TEST(SemiFuture, MakeSemiFutureFromFutureWithValue) {
+ auto f = SemiFuture<std::unique_ptr<int>>{
+ makeFuture(std::unique_ptr<int>(new int(42)))};
+ auto up = std::move(f.value());
+ EXPECT_EQ(42, *up);
+}
+
+TEST(SemiFuture, MakeSemiFutureFromReadyFuture) {
+ Promise<int> p;
+ auto f = SemiFuture<int>{p.getFuture()};
+ EXPECT_FALSE(f.isReady());
+ p.setValue(42);
+ EXPECT_TRUE(f.isReady());
+}
+
+TEST(SemiFuture, MakeSemiFutureFromNotReadyFuture) {
+ Promise<int> p;
+ auto f = SemiFuture<int>{p.getFuture()};
+ EXPECT_THROW(f.value(), eggs_t);
+}
+
+TEST(SemiFuture, MakeFutureFromSemiFuture) {
+ folly::EventBase e;
+ Promise<int> p;
+ std::atomic<int> result{0};
+ auto f = SemiFuture<int>{p.getFuture()};
+ auto future = std::move(f).via(&e).then([&](int value) {
+ result = value;
+ return value;
+ });
+ e.loop();
+ EXPECT_EQ(result, 0);
+ EXPECT_FALSE(future.isReady());
+ p.setValue(42);
+ e.loop();
+ EXPECT_TRUE(future.isReady());
+ ASSERT_EQ(future.value(), 42);
+ ASSERT_EQ(result, 42);
+}
+
+TEST(SemiFuture, MakeFutureFromSemiFutureLValue) {
+ folly::EventBase e;
+ Promise<int> p;
+ std::atomic<int> result{0};
+ auto f = SemiFuture<int>{p.getFuture()};
+ auto future = f.via(&e).then([&](int value) {
+ result = value;
+ return value;
+ });
+ e.loop();
+ EXPECT_EQ(result, 0);
+ EXPECT_FALSE(future.isReady());
+ p.setValue(42);
+ e.loop();
+ EXPECT_TRUE(future.isReady());
+ ASSERT_EQ(future.value(), 42);
+ ASSERT_EQ(result, 42);
+}