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 | |
23 | namespace folly { |
24 | |
25 | class 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 | */ |
39 | template <class T> |
40 | class 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 | */ |
98 | template <class T> |
99 | FutureSplitter<T> splitFuture(Future<T>&& future) { |
100 | return FutureSplitter<T>{std::move(future)}; |
101 | } |
102 | } // namespace folly |
103 | |