1/*
2 * Copyright 2017-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
19#include <folly/futures/Future.h>
20#include <folly/futures/SharedPromise.h>
21#include <folly/lang/Exception.h>
22
23namespace folly {
24
25class FOLLY_EXPORT FutureSplitterInvalid : public FutureException {
26 public:
27 FutureSplitterInvalid()
28 : FutureException("No Future in this FutureSplitter") {}
29};
30
31/*
32 * FutureSplitter provides a `getFuture()' method which can be called multiple
33 * times, returning a new Future each time. These futures are completed when the
34 * original Future passed to the FutureSplitter constructor is completed, and
35 * are completed on the same executor (if any) and at the same priority as the
36 * original Future. Calls to `getFuture()' after that time return a completed
37 * Future.
38 */
39template <class T>
40class FutureSplitter {
41 public:
42 /**
43 * Default constructor for convenience only. It is an error to call
44 * `getFuture()` on a default-constructed FutureSplitter which has not had
45 * a correctly-constructed FutureSplitter copy- or move-assigned into it.
46 */
47 FutureSplitter() = default;
48
49 /**
50 * Provide a way to split a Future<T>.
51 */
52 explicit FutureSplitter(Future<T>&& future)
53 : promise_(std::make_shared<SharedPromise<T>>()),
54 e_(getExecutorFrom(future)),
55 priority_(future.getPriority()) {
56 std::move(future).thenTry([promise = promise_](Try<T>&& theTry) {
57 promise->setTry(std::move(theTry));
58 });
59 }
60
61 /**
62 * This can be called an unlimited number of times per FutureSplitter.
63 */
64 Future<T> getFuture() {
65 if (promise_ == nullptr) {
66 throw_exception<FutureSplitterInvalid>();
67 }
68 return promise_->getSemiFuture().via(e_, priority_);
69 }
70
71 /**
72 * This can be called an unlimited number of times per FutureSplitter.
73 */
74 SemiFuture<T> getSemiFuture() {
75 if (promise_ == nullptr) {
76 throw_exception<FutureSplitterInvalid>();
77 }
78 return promise_->getSemiFuture();
79 }
80
81 private:
82 std::shared_ptr<SharedPromise<T>> promise_;
83 Executor* e_ = nullptr;
84 int8_t priority_{-1};
85
86 static Executor* getExecutorFrom(Future<T>& f) {
87 // If the passed future had a null executor, use an inline executor
88 // to ensure that .via is safe
89 auto* e = f.getExecutor();
90 return e ? e : &InlineExecutor::instance();
91 }
92};
93
94/**
95 * Convenience function, allowing us to exploit template argument deduction to
96 * improve readability.
97 */
98template <class T>
99FutureSplitter<T> splitFuture(Future<T>&& future) {
100 return FutureSplitter<T>{std::move(future)};
101}
102} // namespace folly
103