/*
- * Copyright 2017 Facebook, Inc.
+ * Copyright 2017-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#pragma once
#include <algorithm>
#include <chrono>
#include <thread>
-#include <folly/Baton.h>
#include <folly/Optional.h>
#include <folly/executors/InlineExecutor.h>
#include <folly/futures/Timekeeper.h>
#include <folly/futures/detail/Core.h>
+#include <folly/synchronization/Baton.h>
#ifndef FOLLY_FUTURE_USING_FIBER
#if FOLLY_MOBILE || defined(__APPLE__)
template <class T>
T& FutureBase<T>::value() & {
+ return result().value();
+}
+
+template <class T>
+T const& FutureBase<T>::value() const& {
+ return result().value();
+}
+
+template <class T>
+T&& FutureBase<T>::value() && {
+ return std::move(result().value());
+}
+
+template <class T>
+T const&& FutureBase<T>::value() const&& {
+ return std::move(result().value());
+}
+
+template <class T>
+Try<T>& FutureBase<T>::result() & {
throwIfInvalid();
- return core_->getTry().value();
+ return core_->getTry();
}
template <class T>
-T const& FutureBase<T>::value() const& {
+Try<T> const& FutureBase<T>::result() const& {
throwIfInvalid();
- return core_->getTry().value();
+ return core_->getTry();
}
template <class T>
-T&& FutureBase<T>::value() && {
+Try<T>&& FutureBase<T>::result() && {
throwIfInvalid();
- return std::move(core_->getTry().value());
+ return std::move(core_->getTry());
}
template <class T>
-T const&& FutureBase<T>::value() const&& {
+Try<T> const&& FutureBase<T>::result() const&& {
throwIfInvalid();
- return std::move(core_->getTry().value());
+ return std::move(core_->getTry());
}
template <class T>
template <class T>
bool FutureBase<T>::hasValue() {
- return getTry().hasValue();
+ return core_->getTry().hasValue();
}
template <class T>
bool FutureBase<T>::hasException() {
- return getTry().hasException();
+ return core_->getTry().hasException();
}
template <class T>
}
}
-template <class T>
-Try<T>& FutureBase<T>::getTry() {
- throwIfInvalid();
-
- return core_->getTry();
-}
-
template <class T>
void FutureBase<T>::throwIfInvalid() const {
if (!core_) {
if (auto e = this->getExecutor()) {
// We know in a SemiFuture that if we have an executor it should be
// DeferredExecutor. Verify this in debug mode.
- DCHECK(dynamic_cast<DeferredExecutor*>(e));
+ DCHECK(nullptr != dynamic_cast<DeferredExecutor*>(e));
auto ka = static_cast<DeferredExecutor*>(e)->getKeepAliveToken();
static_cast<DeferredExecutor*>(e)->boost();
template <class T>
inline Future<T> SemiFuture<T>::via(Executor* executor, int8_t priority) && {
throwIfInvalid();
+ if (!executor) {
+ throwNoExecutor();
+ }
// If current executor is deferred, boost block to ensure that work
// progresses and is run on the new executor.
if (oldExecutor && executor && (executor != oldExecutor)) {
// We know in a SemiFuture that if we have an executor it should be
// DeferredExecutor. Verify this in debug mode.
- DCHECK(dynamic_cast<DeferredExecutor*>(this->getExecutor()));
+ DCHECK(nullptr != dynamic_cast<DeferredExecutor*>(this->getExecutor()));
if (static_cast<DeferredExecutor*>(oldExecutor)) {
executor->add([oldExecutorKA = oldExecutor->getKeepAliveToken()]() {
static_cast<DeferredExecutor*>(oldExecutorKA.get())->boost();
// We know in a SemiFuture that if we have an executor it should be
// DeferredExecutor (either it was that way before, or we just created it).
// Verify this in debug mode.
- DCHECK(dynamic_cast<DeferredExecutor*>(e));
+ DCHECK(nullptr != dynamic_cast<DeferredExecutor*>(e));
// Convert to a folly::future with a deferred executor
// Will be low-cost if this is not a new executor as via optimises for that
// case
auto sf =
std::move(*this)
- .via(defKeepAlive.get())
+ .via(e)
// Then add the work, with a wrapper function that captures the
// keepAlive so the executor is destroyed at the right time.
.then(
return f;
}
-template <class T>
-Try<T>& Future<T>::getTryVia(DrivableExecutor* e) {
- return waitVia(e).getTry();
-}
-
template <class Func>
auto via(Executor* x, Func&& func)
-> Future<typename isFuture<decltype(std::declval<Func>()())>::Inner> {
});
doBoost(f);
f = std::move(ret);
- if (baton->timed_wait(dur)) {
+ if (baton->try_wait_for(dur)) {
assert(f.isReady());
}
}
assert(f.isReady());
}
+template <class T>
+void waitViaImpl(SemiFuture<T>& f, DrivableExecutor* e) {
+ // Set callback so to ensure that the via executor has something on it
+ // so that once the preceding future triggers this callback, drive will
+ // always have a callback to satisfy it
+ if (f.isReady()) {
+ return;
+ }
+ f = std::move(f).via(e).then([](T&& t) { return std::move(t); });
+ while (!f.isReady()) {
+ e->drive();
+ }
+ assert(f.isReady());
+}
+
} // namespace detail
} // namespace futures
return std::move(*this);
}
+template <class T>
+SemiFuture<T>& SemiFuture<T>::waitVia(DrivableExecutor* e) & {
+ futures::detail::waitViaImpl(*this, e);
+ return *this;
+}
+
+template <class T>
+SemiFuture<T>&& SemiFuture<T>::waitVia(DrivableExecutor* e) && {
+ futures::detail::waitViaImpl(*this, e);
+ return std::move(*this);
+}
+
template <class T>
T SemiFuture<T>::get() && {
- return std::move(wait().value());
+ return std::move(wait()).value();
}
template <class T>
}
}
+template <class T>
+Try<T> SemiFuture<T>::getTry() && {
+ return std::move(wait()).result();
+}
+
+template <class T>
+Try<T> SemiFuture<T>::getTry(Duration dur) && {
+ wait(dur);
+ if (this->isReady()) {
+ return std::move(this->result());
+ } else {
+ throwTimedOut();
+ }
+}
+
+template <class T>
+T SemiFuture<T>::getVia(DrivableExecutor* e) && {
+ return std::move(waitVia(e)).value();
+}
+
+template <class T>
+Try<T> SemiFuture<T>::getTryVia(DrivableExecutor* e) && {
+ return std::move(waitVia(e)).result();
+}
+
template <class T>
Future<T>& Future<T>::wait() & {
futures::detail::waitImpl(*this);
}
}
+template <class T>
+Try<T>& Future<T>::getTry() {
+ return result();
+}
+
template <class T>
T Future<T>::getVia(DrivableExecutor* e) {
return std::move(waitVia(e).value());
}
+template <class T>
+Try<T>& Future<T>::getTryVia(DrivableExecutor* e) {
+ return waitVia(e).getTry();
+}
+
namespace futures {
namespace detail {
template <class T>