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.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
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
20 * Copyright (C) 2015 ScyllaDB
25 #include <seastar/core/future.hh>
26 #include <seastar/core/expiring_fifo.hh>
30 /// \addtogroup future-module
33 /// Changes the clock used by shared_future<> and shared_promise<> when passed as the first template parameter.
34 template<typename Clock>
39 template <typename... T>
40 struct future_option_traits;
42 template <typename Clock, typename... T>
43 struct future_option_traits<with_clock<Clock>, T...> {
44 using clock_type = Clock;
46 template<template <typename...> class Class>
48 using type = Class<T...>;
52 template <typename... T>
53 struct future_option_traits {
54 using clock_type = lowres_clock;
56 template<template <typename...> class Class>
58 using type = Class<T...>;
64 /// \brief Like \ref future except the result can be waited for by many fibers.
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.
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.
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.
79 /// shared_future can be copied at any time and all copies will resolve with the same value.
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.
84 /// The types in the parameter pack T must all be copy-constructible.
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.
93 /// shared_future<with_clock<manual_clock>, int> sf(std::move(f));
94 /// future<int> f2 = sf;
96 template<typename... T>
98 template <typename... U> friend class shared_promise;
99 using options = future_option_traits<T...>;
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;
107 using promise_expiry = typename future_option_traits<T...>::template parametrize<promise_expiry>::type;
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;
116 // Don't warn if the shared future is exceptional. Any
117 // warnings will be reported by the futures returned by
119 if (_original_future.failed()) {
120 _original_future.ignore_ready_future();
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()) {
129 _peers.front().set_exception(state.get_exception());
134 auto& p = _peers.front();
136 p.set_value(state.get_value());
138 p.set_exception(std::current_exception());
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()) {
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));
162 _peers.push_back(std::move(p), timeout);
164 } else if (_original_future.failed()) {
165 return future_type(exception_future_marker(), std::exception_ptr(_original_future._state.get_exception()));
167 return future_type(ready_future_marker(), _original_future._state.get_value());
171 bool available() const noexcept {
172 return _original_future.available();
175 bool failed() const noexcept {
176 return _original_future.failed();
180 lw_shared_ptr<shared_state> _state;
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))) { }
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
192 /// \brief Creates a new \c future which will resolve with the result of this shared_future
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.
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);
202 /// \brief Returns true if the future is available (ready or failed)
204 /// \note This object must be in a valid state.
205 bool available() const noexcept {
206 return _state->available();
209 /// \brief Returns true if the future is failed
211 /// \note This object must be in a valid state.
212 bool failed() const noexcept {
213 return _state->failed();
216 /// \brief Equivalent to \ref get_future()
217 operator future_type() const noexcept {
221 /// \brief Returns true if the instance is in valid state
222 bool valid() const noexcept {
227 /// \brief Like \ref promise except that its counterpart is \ref shared_future instead of \ref future
229 /// When the shared_promise is made ready, every waiter is also made ready.
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 {
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;
242 promise_type _promise;
243 shared_future_type _shared_future;
244 static constexpr bool copy_noexcept = future_type::copy_noexcept;
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()) {
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);
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);
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));
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)...);
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));
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)));
286 /// \brief Returns true if the underlying future is available (ready or failed)
287 bool available() const noexcept {
288 return _shared_future.available();
291 /// \brief Returns true if the underlying future is failed
292 bool failed() const noexcept {
293 return _shared_future.failed();