]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/include/seastar/core/shared_future.hh
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / seastar / include / seastar / core / shared_future.hh
1 /*
2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
6 *
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
16 * under the License.
17 */
18
19 /*
20 * Copyright (C) 2015 ScyllaDB
21 */
22
23 #pragma once
24
25 #include <seastar/core/future.hh>
26 #include <seastar/core/expiring_fifo.hh>
27
28 namespace seastar {
29
30 /// \addtogroup future-module
31 /// @{
32
33 /// Changes the clock used by shared_future<> and shared_promise<> when passed as the first template parameter.
34 template<typename Clock>
35 struct with_clock {};
36
37 /// \cond internal
38
39 template <typename... T>
40 struct future_option_traits;
41
42 template <typename Clock, typename... T>
43 struct future_option_traits<with_clock<Clock>, T...> {
44 using clock_type = Clock;
45
46 template<template <typename...> class Class>
47 struct parametrize {
48 using type = Class<T...>;
49 };
50 };
51
52 template <typename... T>
53 struct future_option_traits {
54 using clock_type = lowres_clock;
55
56 template<template <typename...> class Class>
57 struct parametrize {
58 using type = Class<T...>;
59 };
60 };
61
62 /// \endcond
63
64 /// \brief Like \ref future except the result can be waited for by many fibers.
65 ///
66 /// Represents a value which may not yet be ready. A fiber can wait for the value using
67 /// the \ref future obtained by calling \ref get_future() or casting to \ref future type.
68 /// Multiple fibers are allowed to obtain a \ref future for the result using the same
69 /// instance of \ref shared_future.
70 ///
71 /// All futures obtained from shared_future should end up in the same state. However,
72 /// if the value's copy constructor throws, some of the futures may end up in a failed state
73 /// with an exception thrown from the copy constructor and end up with a state
74 /// different than other futures.
75 ///
76 /// The scope of shared_future instance doesn't have to include scopes of the futures
77 /// obtained from that instance. In that sense the returned futures are independent.
78 ///
79 /// shared_future can be copied at any time and all copies will resolve with the same value.
80 ///
81 /// shared_future can be in a disengaged state when it's default-constructed or moved-from.
82 /// When it's in such a state we say it's invalid and obtaining futures must not be attempted.
83 ///
84 /// The types in the parameter pack T must all be copy-constructible.
85 ///
86 /// When the first type in the parameter pack is \ref with_clock then it has the effect
87 /// of changing the clock used for timeouts by this instance. This type is omitted from
88 /// the parameter of the future<> objects.
89 ///
90 /// Example:
91 ///
92 /// future<int> f;
93 /// shared_future<with_clock<manual_clock>, int> sf(std::move(f));
94 /// future<int> f2 = sf;
95 ///
96 template<typename... T>
97 class shared_future {
98 template <typename... U> friend class shared_promise;
99 using options = future_option_traits<T...>;
100 public:
101 using clock = typename options::clock_type;
102 using time_point = typename clock::time_point;
103 using future_type = typename future_option_traits<T...>::template parametrize<future>::type;
104 using promise_type = typename future_option_traits<T...>::template parametrize<promise>::type;
105 using value_tuple_type = typename future_option_traits<T...>::template parametrize<std::tuple>::type;
106 private:
107 using promise_expiry = typename future_option_traits<T...>::template parametrize<promise_expiry>::type;
108
109 /// \cond internal
110 class shared_state : public enable_lw_shared_from_this<shared_state> {
111 future_type _original_future;
112 expiring_fifo<promise_type, promise_expiry, clock> _peers;
113
114 public:
115 ~shared_state() {
116 // Don't warn if the shared future is exceptional. Any
117 // warnings will be reported by the futures returned by
118 // get_future.
119 if (_original_future.failed()) {
120 _original_future.ignore_ready_future();
121 }
122 }
123 explicit shared_state(future_type f) noexcept : _original_future(std::move(f)) { }
124 void resolve(future_type&& f) noexcept {
125 _original_future = std::move(f);
126 auto& state = _original_future._state;
127 if (_original_future.failed()) {
128 while (_peers) {
129 _peers.front().set_exception(state.get_exception());
130 _peers.pop_front();
131 }
132 } else {
133 while (_peers) {
134 auto& p = _peers.front();
135 try {
136 p.set_value(state.get_value());
137 } catch (...) {
138 p.set_exception(std::current_exception());
139 }
140 _peers.pop_front();
141 }
142 }
143 }
144
145 future_type get_future(time_point timeout = time_point::max()) noexcept {
146 // Note that some functions called below may throw,
147 // like pushing to _peers or copying _original_future's ready value.
148 // We'd rather terminate than propagate these errors similar to
149 // .then()'s failure to allocate a continuation as the caller cannot
150 // distinguish between an error returned by the original future to
151 // failing to perform `get_future` itself.
152 memory::scoped_critical_alloc_section _;
153 if (!_original_future.available()) {
154 promise_type p;
155 auto f = p.get_future();
156 if (_original_future._state.valid()) {
157 // _original_future's result is forwarded to each peer.
158 (void)_original_future.then_wrapped([s = this->shared_from_this()] (future_type&& f) mutable {
159 s->resolve(std::move(f));
160 });
161 }
162 _peers.push_back(std::move(p), timeout);
163 return f;
164 } else if (_original_future.failed()) {
165 return future_type(exception_future_marker(), std::exception_ptr(_original_future._state.get_exception()));
166 } else {
167 return future_type(ready_future_marker(), _original_future._state.get_value());
168 }
169 }
170
171 bool available() const noexcept {
172 return _original_future.available();
173 }
174
175 bool failed() const noexcept {
176 return _original_future.failed();
177 }
178 };
179 /// \endcond
180 lw_shared_ptr<shared_state> _state;
181 public:
182 /// \brief Forwards the result of future \c f into this shared_future.
183 shared_future(future_type f)
184 : _state(make_lw_shared<shared_state>(std::move(f))) { }
185
186 shared_future() = default; // noexcept, based on the respective lw_shared_ptr constructor
187 shared_future(const shared_future&) = default; // noexcept, based on the respective lw_shared_ptr constructor
188 shared_future& operator=(const shared_future&) = default; // noexcept, based on respective constructor
189 shared_future(shared_future&&) = default; // noexcept, based on the respective lw_shared_ptr constructor
190 shared_future& operator=(shared_future&&) = default; // noexcept, based on the respective constructor
191
192 /// \brief Creates a new \c future which will resolve with the result of this shared_future
193 ///
194 /// \param timeout When engaged, the returned future will resolve with \ref timed_out_error
195 /// if this shared_future doesn't resolve before timeout is reached.
196 ///
197 /// This object must be in a valid state.
198 future_type get_future(time_point timeout = time_point::max()) const noexcept {
199 return _state->get_future(timeout);
200 }
201
202 /// \brief Returns true if the future is available (ready or failed)
203 ///
204 /// \note This object must be in a valid state.
205 bool available() const noexcept {
206 return _state->available();
207 }
208
209 /// \brief Returns true if the future is failed
210 ///
211 /// \note This object must be in a valid state.
212 bool failed() const noexcept {
213 return _state->failed();
214 }
215
216 /// \brief Equivalent to \ref get_future()
217 operator future_type() const noexcept {
218 return get_future();
219 }
220
221 /// \brief Returns true if the instance is in valid state
222 bool valid() const noexcept {
223 return bool(_state);
224 }
225 };
226
227 /// \brief Like \ref promise except that its counterpart is \ref shared_future instead of \ref future
228 ///
229 /// When the shared_promise is made ready, every waiter is also made ready.
230 ///
231 /// Like the shared_future, the types in the parameter pack T must all be copy-constructible.
232 template <typename... T>
233 class shared_promise {
234 public:
235 using shared_future_type = shared_future<T...>;
236 using future_type = typename shared_future_type::future_type;
237 using promise_type = typename shared_future_type::promise_type;
238 using clock = typename shared_future_type::clock;
239 using time_point = typename shared_future_type::time_point;
240 using value_tuple_type = typename shared_future_type::value_tuple_type;
241 private:
242 promise_type _promise;
243 shared_future_type _shared_future;
244 static constexpr bool copy_noexcept = future_type::copy_noexcept;
245 public:
246 shared_promise(const shared_promise&) = delete;
247 shared_promise(shared_promise&&) = default; // noexcept, based on the respective promise and shared_future constructors
248 shared_promise& operator=(shared_promise&&) = default; // noexcept, based on the respective promise and shared_future constructors
249 shared_promise() : _promise(), _shared_future(_promise.get_future()) {
250 }
251
252 /// \brief Gets new future associated with this promise.
253 /// If the promise is not resolved before timeout the returned future will resolve with \ref timed_out_error.
254 /// This instance doesn't have to be kept alive until the returned future resolves.
255 future_type get_shared_future(time_point timeout = time_point::max()) const noexcept {
256 return _shared_future.get_future(timeout);
257 }
258
259 /// \brief Sets the shared_promise's value (as tuple; by copying), same as normal promise
260 void set_value(const value_tuple_type& result) noexcept(copy_noexcept) {
261 _promise.set_value(result);
262 }
263
264 /// \brief Sets the shared_promise's value (as tuple; by moving), same as normal promise
265 void set_value(value_tuple_type&& result) noexcept {
266 _promise.set_value(std::move(result));
267 }
268
269 /// \brief Sets the shared_promise's value (variadic), same as normal promise
270 template <typename... A>
271 void set_value(A&&... a) noexcept {
272 _promise.set_value(std::forward<A>(a)...);
273 }
274
275 /// \brief Marks the shared_promise as failed, same as normal promise
276 void set_exception(std::exception_ptr ex) noexcept {
277 _promise.set_exception(std::move(ex));
278 }
279
280 /// \brief Marks the shared_promise as failed, same as normal promise
281 template<typename Exception>
282 void set_exception(Exception&& e) noexcept {
283 set_exception(make_exception_ptr(std::forward<Exception>(e)));
284 }
285
286 /// \brief Returns true if the underlying future is available (ready or failed)
287 bool available() const noexcept {
288 return _shared_future.available();
289 }
290
291 /// \brief Returns true if the underlying future is failed
292 bool failed() const noexcept {
293 return _shared_future.failed();
294 }
295 };
296
297 /// @}
298
299 }