X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Ffutures%2FFuture-inl.h;h=5818244cdc3443818ec8d887815eb1c8aef9c2dd;hb=3066914f085865bc104192f3431315be6ea9012a;hp=c7ddeb0a80e609559cd202575c878ecbbd218f4a;hpb=084ff8d9934675912ee1af8687611eb34a630579;p=folly.git diff --git a/folly/futures/Future-inl.h b/folly/futures/Future-inl.h index c7ddeb0a..5818244c 100644 --- a/folly/futures/Future-inl.h +++ b/folly/futures/Future-inl.h @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include @@ -129,102 +129,25 @@ inline auto makeCoreCallbackState(Promise&& p, F&& f) noexcept( return CoreCallbackState>>( std::move(p), std::forward(f)); } -} // namespace detail -} // namespace futures - -template -SemiFuture::type> makeSemiFuture(T&& t) { - return makeSemiFuture(Try::type>(std::forward(t))); -} - -// makeSemiFutureWith(SemiFuture()) -> SemiFuture -template -typename std::enable_if< - isSemiFuture::type>::value, - typename std::result_of::type>::type -makeSemiFutureWith(F&& func) { - using InnerType = - typename isSemiFuture::type>::Inner; - try { - return std::forward(func)(); - } catch (std::exception& e) { - return makeSemiFuture( - exception_wrapper(std::current_exception(), e)); - } catch (...) { - return makeSemiFuture( - exception_wrapper(std::current_exception())); - } -} - -// makeSemiFutureWith(T()) -> SemiFuture -// makeSemiFutureWith(void()) -> SemiFuture -template -typename std::enable_if< - !(isSemiFuture::type>::value), - SemiFuture::type>>>::type -makeSemiFutureWith(F&& func) { - using LiftedResult = Unit::LiftT::type>; - return makeSemiFuture( - makeTryWith([&func]() mutable { return std::forward(func)(); })); -} - -template -SemiFuture makeSemiFuture(std::exception_ptr const& e) { - return makeSemiFuture(Try(e)); -} - -template -SemiFuture makeSemiFuture(exception_wrapper ew) { - return makeSemiFuture(Try(std::move(ew))); -} - -template -typename std:: - enable_if::value, SemiFuture>::type - makeSemiFuture(E const& e) { - return makeSemiFuture(Try(make_exception_wrapper(e))); -} - -template -SemiFuture makeSemiFuture(Try&& t) { - return SemiFuture(new futures::detail::Core(std::move(t))); -} template -SemiFuture SemiFuture::makeEmpty() { - return SemiFuture(futures::detail::EmptyConstruct{}); -} - -template -SemiFuture::SemiFuture(SemiFuture&& other) noexcept : core_(other.core_) { +FutureBase::FutureBase(SemiFuture&& other) noexcept : core_(other.core_) { other.core_ = nullptr; } template -SemiFuture& SemiFuture::operator=(SemiFuture&& other) noexcept { - std::swap(core_, other.core_); - return *this; -} - -template -SemiFuture::SemiFuture(Future&& other) noexcept : core_(other.core_) { +FutureBase::FutureBase(Future&& other) noexcept : core_(other.core_) { other.core_ = nullptr; } -template -SemiFuture& SemiFuture::operator=(Future&& other) noexcept { - std::swap(core_, other.core_); - return *this; -} - template template -SemiFuture::SemiFuture(T2&& val) +FutureBase::FutureBase(T2&& val) : core_(new futures::detail::Core(Try(std::forward(val)))) {} template template -SemiFuture::SemiFuture( +FutureBase::FutureBase( typename std::enable_if::value>::type*) : core_(new futures::detail::Core(Try(T()))) {} @@ -233,92 +156,68 @@ template < class... Args, typename std::enable_if::value, int>:: type> -SemiFuture::SemiFuture(in_place_t, Args&&... args) +FutureBase::FutureBase(in_place_t, Args&&... args) : core_( new futures::detail::Core(in_place, std::forward(args)...)) { } template -SemiFuture::~SemiFuture() { - detach(); +template +void FutureBase::assign(FutureType& other) noexcept { + std::swap(core_, other.core_); } -// This must be defined after the constructors to avoid a bug in MSVC -// https://connect.microsoft.com/VisualStudio/feedback/details/3142777/out-of-line-constructor-definition-after-implicit-reference-causes-incorrect-c2244 -inline SemiFuture makeSemiFuture() { - return makeSemiFuture(Unit{}); +template +FutureBase::~FutureBase() { + detach(); } template -T& SemiFuture::value() & { +T& FutureBase::value() & { throwIfInvalid(); return core_->getTry().value(); } template -T const& SemiFuture::value() const& { +T const& FutureBase::value() const& { throwIfInvalid(); return core_->getTry().value(); } template -T&& SemiFuture::value() && { +T&& FutureBase::value() && { throwIfInvalid(); return std::move(core_->getTry().value()); } template -T const&& SemiFuture::value() const&& { +T const&& FutureBase::value() const&& { throwIfInvalid(); return std::move(core_->getTry().value()); } template -inline Future SemiFuture::via(Executor* executor, int8_t priority) && { - throwIfInvalid(); - - setExecutor(executor, priority); - - auto newFuture = Future(core_); - core_ = nullptr; - return newFuture; -} - -template -inline Future SemiFuture::via(Executor* executor, int8_t priority) & { - throwIfInvalid(); - Promise p; - auto f = p.getFuture(); - auto func = [p = std::move(p)](Try&& t) mutable { - p.setTry(std::move(t)); - }; - using R = futures::detail::callableResult; - thenImplementation(std::move(func), typename R::Arg()); - return std::move(f).via(executor, priority); -} - -template -bool SemiFuture::isReady() const { +bool FutureBase::isReady() const { throwIfInvalid(); return core_->ready(); } template -bool SemiFuture::hasValue() { +bool FutureBase::hasValue() { return getTry().hasValue(); } template -bool SemiFuture::hasException() { +bool FutureBase::hasException() { return getTry().hasException(); } template -void SemiFuture::detach() { +void FutureBase::detach() { if (core_) { core_->detachFuture(); core_ = nullptr; @@ -326,21 +225,21 @@ void SemiFuture::detach() { } template -Try& SemiFuture::getTry() { +Try& FutureBase::getTry() { throwIfInvalid(); return core_->getTry(); } template -void SemiFuture::throwIfInvalid() const { +void FutureBase::throwIfInvalid() const { if (!core_) { throwNoState(); -} + } } template -Optional> SemiFuture::poll() { +Optional> FutureBase::poll() { Optional> o; if (core_->ready()) { o = std::move(core_->getTry()); @@ -349,104 +248,21 @@ Optional> SemiFuture::poll() { } template -void SemiFuture::raise(exception_wrapper exception) { +void FutureBase::raise(exception_wrapper exception) { core_->raise(std::move(exception)); } template template -void SemiFuture::setCallback_(F&& func) { +void FutureBase::setCallback_(F&& func) { throwIfInvalid(); core_->setCallback(std::forward(func)); } template -SemiFuture::SemiFuture(futures::detail::EmptyConstruct) noexcept +FutureBase::FutureBase(futures::detail::EmptyConstruct) noexcept : core_(nullptr) {} -template -Future Future::makeEmpty() { - return Future(futures::detail::EmptyConstruct{}); -} - -template -Future::Future(Future&& other) noexcept - : SemiFuture(std::move(other)) {} - -template -Future& Future::operator=(Future&& other) noexcept { - SemiFuture::operator=(SemiFuture{std::move(other)}); - return *this; -} - -template -template < - class T2, - typename std::enable_if< - !std::is_same::type>::value && - std::is_constructible::value && - std::is_convertible::value, - int>::type> -Future::Future(Future&& other) - : Future(std::move(other).then([](T2&& v) { return T(std::move(v)); })) {} - -template -template < - class T2, - typename std::enable_if< - !std::is_same::type>::value && - std::is_constructible::value && - !std::is_convertible::value, - int>::type> -Future::Future(Future&& other) - : Future(std::move(other).then([](T2&& v) { return T(std::move(v)); })) {} - -template -template < - class T2, - typename std::enable_if< - !std::is_same::type>::value && - std::is_constructible::value, - int>::type> -Future& Future::operator=(Future&& other) { - return operator=( - std::move(other).then([](T2&& v) { return T(std::move(v)); })); -} - -// TODO: isSemiFuture -template -template -Future::Future(T2&& val) : SemiFuture(std::forward(val)) {} - -template -template -Future::Future(typename std::enable_if::value>::type*) - : SemiFuture() {} - -template -template < - class... Args, - typename std::enable_if::value, int>:: - type> -Future::Future(in_place_t, Args&&... args) - : SemiFuture(in_place, std::forward(args)...) {} - -template -Future::~Future() { -} - -// unwrap - -template -template -typename std::enable_if::value, - Future::Inner>>::type -Future::unwrap() { - return then([](Future::Inner> internal_future) { - return internal_future; - }); -} - // then // Variant: returns a value @@ -454,7 +270,7 @@ Future::unwrap() { template template typename std::enable_if::type -SemiFuture::thenImplementation( +FutureBase::thenImplementation( F&& func, futures::detail::argResult) { static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument"); @@ -517,7 +333,7 @@ SemiFuture::thenImplementation( template template typename std::enable_if::type -SemiFuture::thenImplementation( +FutureBase::thenImplementation( F&& func, futures::detail::argResult) { static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument"); @@ -550,6 +366,264 @@ SemiFuture::thenImplementation( return f; } +} // namespace detail +} // namespace futures + +template +SemiFuture::type> makeSemiFuture(T&& t) { + return makeSemiFuture(Try::type>(std::forward(t))); +} + +// makeSemiFutureWith(SemiFuture()) -> SemiFuture +template +typename std::enable_if< + isSemiFuture::type>::value, + typename std::result_of::type>::type +makeSemiFutureWith(F&& func) { + using InnerType = + typename isSemiFuture::type>::Inner; + try { + return std::forward(func)(); + } catch (std::exception& e) { + return makeSemiFuture( + exception_wrapper(std::current_exception(), e)); + } catch (...) { + return makeSemiFuture( + exception_wrapper(std::current_exception())); + } +} + +// makeSemiFutureWith(T()) -> SemiFuture +// makeSemiFutureWith(void()) -> SemiFuture +template +typename std::enable_if< + !(isSemiFuture::type>::value), + SemiFuture::type>>>::type +makeSemiFutureWith(F&& func) { + using LiftedResult = Unit::LiftT::type>; + return makeSemiFuture( + makeTryWith([&func]() mutable { return std::forward(func)(); })); +} + +template +SemiFuture makeSemiFuture(std::exception_ptr const& e) { + return makeSemiFuture(Try(e)); +} + +template +SemiFuture makeSemiFuture(exception_wrapper ew) { + return makeSemiFuture(Try(std::move(ew))); +} + +template +typename std:: + enable_if::value, SemiFuture>::type + makeSemiFuture(E const& e) { + return makeSemiFuture(Try(make_exception_wrapper(e))); +} + +template +SemiFuture makeSemiFuture(Try&& t) { + return SemiFuture(new futures::detail::Core(std::move(t))); +} + +// This must be defined after the constructors to avoid a bug in MSVC +// https://connect.microsoft.com/VisualStudio/feedback/details/3142777/out-of-line-constructor-definition-after-implicit-reference-causes-incorrect-c2244 +inline SemiFuture makeSemiFuture() { + return makeSemiFuture(Unit{}); +} + +template +SemiFuture SemiFuture::makeEmpty() { + return SemiFuture(futures::detail::EmptyConstruct{}); +} + +template +SemiFuture::SemiFuture(SemiFuture&& other) noexcept + : futures::detail::FutureBase(std::move(other)) {} + +template +SemiFuture::SemiFuture(Future&& other) noexcept + : futures::detail::FutureBase(std::move(other)) { + // SemiFuture should not have an executor on construction + if (this->core_) { + this->setExecutor(nullptr); + } +} + +template +SemiFuture& SemiFuture::operator=(SemiFuture&& other) noexcept { + this->assign(other); + return *this; +} + +template +SemiFuture& SemiFuture::operator=(Future&& other) noexcept { + this->assign(other); + // SemiFuture should not have an executor on construction + if (this->core_) { + this->setExecutor(nullptr); + } + return *this; +} + +template +void SemiFuture::boost_() { + // If a SemiFuture has an executor it should be deferred, so boost it + 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(nullptr != dynamic_cast(e)); + + auto ka = static_cast(e)->getKeepAliveToken(); + static_cast(e)->boost(); + } +} + +template +inline Future SemiFuture::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. + auto oldExecutor = this->getExecutor(); + 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(nullptr != dynamic_cast(this->getExecutor())); + if (static_cast(oldExecutor)) { + executor->add([oldExecutorKA = oldExecutor->getKeepAliveToken()]() { + static_cast(oldExecutorKA.get())->boost(); + }); + } + } + + this->setExecutor(executor, priority); + + auto newFuture = Future(this->core_); + this->core_ = nullptr; + return newFuture; +} + +template +template +SemiFuture::Return::value_type> +SemiFuture::defer(F&& func) && { + // If we already have a deferred executor, use it, otherwise create one + auto defKeepAlive = this->getExecutor() + ? this->getExecutor()->getKeepAliveToken() + : DeferredExecutor::create(); + auto e = defKeepAlive.get(); + // 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(nullptr != dynamic_cast(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(e) + // Then add the work, with a wrapper function that captures the + // keepAlive so the executor is destroyed at the right time. + .then( + DeferredExecutor::wrap(std::move(defKeepAlive), std::move(func))) + // Finally, convert back o a folly::SemiFuture to hide the executor + .semi(); + // Carry deferred executor through chain as constructor from Future will + // nullify it + sf.setExecutor(e); + return sf; +} + +template +Future Future::makeEmpty() { + return Future(futures::detail::EmptyConstruct{}); +} + +template +Future::Future(Future&& other) noexcept + : futures::detail::FutureBase(std::move(other)) {} + +template +Future& Future::operator=(Future&& other) noexcept { + this->assign(other); + return *this; +} + +template +template < + class T2, + typename std::enable_if< + !std::is_same::type>::value && + std::is_constructible::value && + std::is_convertible::value, + int>::type> +Future::Future(Future&& other) + : Future(std::move(other).then([](T2&& v) { return T(std::move(v)); })) {} + +template +template < + class T2, + typename std::enable_if< + !std::is_same::type>::value && + std::is_constructible::value && + !std::is_convertible::value, + int>::type> +Future::Future(Future&& other) + : Future(std::move(other).then([](T2&& v) { return T(std::move(v)); })) {} + +template +template < + class T2, + typename std::enable_if< + !std::is_same::type>::value && + std::is_constructible::value, + int>::type> +Future& Future::operator=(Future&& other) { + return operator=( + std::move(other).then([](T2&& v) { return T(std::move(v)); })); +} + +// unwrap + +template +template +typename std:: + enable_if::value, Future::Inner>>::type + Future::unwrap() { + return then([](Future::Inner> internal_future) { + return internal_future; + }); +} + +template +inline Future Future::via(Executor* executor, int8_t priority) && { + this->throwIfInvalid(); + + this->setExecutor(executor, priority); + + auto newFuture = Future(this->core_); + this->core_ = nullptr; + return newFuture; +} + +template +inline Future Future::via(Executor* executor, int8_t priority) & { + this->throwIfInvalid(); + Promise p; + auto f = p.getFuture(); + auto func = [p = std::move(p)](Try&& t) mutable { + p.setTry(std::move(t)); + }; + using R = futures::detail::callableResult; + this->template thenImplementation( + std::move(func), typename R::Arg()); + return std::move(f).via(executor, priority); +} template template @@ -733,10 +807,6 @@ auto via(Executor* x, Func&& func) return via(x).then(std::forward(func)); } -template -Future::Future(futures::detail::EmptyConstruct) noexcept - : SemiFuture(futures::detail::EmptyConstruct{}) {} - // makeFuture template @@ -1262,15 +1332,23 @@ Future Future::within(Duration dur, E e, Timekeeper* tk) { template Future Future::delayed(Duration dur, Timekeeper* tk) { return collectAll(*this, futures::sleep(dur, tk)) - .then([](std::tuple, Try> tup) { - Try& t = std::get<0>(tup); - return makeFuture(std::move(t)); - }); + .then([](std::tuple, Try> tup) { + Try& t = std::get<0>(tup); + return makeFuture(std::move(t)); + }); } namespace futures { namespace detail { +template +void doBoost(folly::Future& /* usused */) {} + +template +void doBoost(folly::SemiFuture& f) { + f.boost_(); +} + template void waitImpl(FutureType& f) { // short-circuit if there's nothing to do @@ -1280,6 +1358,7 @@ void waitImpl(FutureType& f) { FutureBatonType baton; f.setCallback_([&](const Try& /* t */) { baton.post(); }); + doBoost(f); baton.wait(); assert(f.isReady()); } @@ -1294,10 +1373,11 @@ void waitImpl(FutureType& f, Duration dur) { Promise promise; auto ret = promise.getFuture(); auto baton = std::make_shared(); - f.setCallback_([ baton, promise = std::move(promise) ](Try && t) mutable { + f.setCallback_([baton, promise = std::move(promise)](Try&& t) mutable { promise.setTry(std::move(t)); baton->post(); }); + doBoost(f); f = std::move(ret); if (baton->timed_wait(dur)) { assert(f.isReady()); @@ -1347,12 +1427,12 @@ SemiFuture&& SemiFuture::wait(Duration dur) && { } template -T SemiFuture::get() { +T SemiFuture::get() && { return std::move(wait().value()); } template -T SemiFuture::get(Duration dur) { +T SemiFuture::get(Duration dur) && { wait(dur); if (this->isReady()) { return std::move(this->value()); @@ -1397,6 +1477,21 @@ Future&& Future::waitVia(DrivableExecutor* e) && { return std::move(*this); } +template +T Future::get() { + return std::move(wait().value()); +} + +template +T Future::get(Duration dur) { + wait(dur); + if (this->isReady()) { + return std::move(this->value()); + } else { + throwTimedOut(); + } +} + template T Future::getVia(DrivableExecutor* e) { return std::move(waitVia(e).value()); @@ -1466,15 +1561,15 @@ Future times(const int n, F&& thunk) { } namespace futures { - template - std::vector> map(It first, It last, F func) { - std::vector> results; - for (auto it = first; it != last; it++) { - results.push_back(it->then(func)); - } - return results; +template +std::vector> map(It first, It last, F func) { + std::vector> results; + for (auto it = first; it != last; it++) { + results.push_back(it->then(func)); } + return results; } +} // namespace futures // Instantiate the most common Future types to save compile time extern template class Future;