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
19 * Copyright (C) 2015 Cloudius Systems, Ltd.
24 #include <seastar/core/apply.hh>
25 #include <seastar/core/future.hh>
38 // Given a future type, find the corresponding continuation_base.
39 template <typename Future>
40 struct continuation_base_from_future;
42 template <typename... T>
43 struct continuation_base_from_future<future<T...>> {
44 using type = continuation_base<T...>;
47 template <typename HeldState, typename Future>
48 class do_with_state final : public continuation_base_from_future<Future>::type {
50 typename Future::promise_type _pr;
52 explicit do_with_state(HeldState&& held) : _held(std::move(held)) {}
53 virtual void run_and_dispose() noexcept override {
54 _pr.set_urgent_state(std::move(this->_state));
61 return _pr.get_future();
68 /// \addtogroup future-util
71 /// do_with() holds an object alive for the duration until a future
72 /// completes, and allow the code involved in making the future
73 /// complete to have easy access to this object.
75 /// do_with() takes two arguments: The first is an temporary object (rvalue),
76 /// the second is a function returning a future (a so-called "promise").
77 /// The function is given (a moved copy of) this temporary object, by
78 /// reference, and it is ensured that the object will not be destructed until
79 /// the completion of the future returned by the function.
81 /// do_with() returns a future which resolves to whatever value the given future
82 /// (returned by the given function) resolves to. This returned value must not
83 /// contain references to the temporary object, as at that point the temporary
86 /// \param rvalue a temporary value to protect while \c f is running
87 /// \param f a callable, accepting an lvalue reference of the same type
88 /// as \c rvalue, that will be accessible while \c f runs
89 /// \return whatever \c f returns
90 template<typename T, typename F>
92 auto do_with(T&& rvalue, F&& f) {
93 auto task = std::make_unique<internal::do_with_state<T, std::result_of_t<F(T&)>>>(std::forward<T>(rvalue));
94 auto fut = f(task->data());
95 if (fut.available()) {
98 auto ret = task->get_future();
99 internal::set_callback(fut, task.release());
104 template <typename Tuple, size_t... Idx>
107 cherry_pick_tuple(std::index_sequence<Idx...>, Tuple&& tuple) {
108 return std::make_tuple(std::get<Idx>(std::forward<Tuple>(tuple))...);
112 /// Executes the function \c func making sure the lock \c lock is taken,
113 /// and later on properly released.
115 /// \param lock the lock, which is any object having providing a lock() / unlock() semantics.
116 /// Caller must make sure that it outlives \ref func.
117 /// \param func function to be executed
118 /// \returns whatever \c func returns
119 template<typename Lock, typename Func>
121 auto with_lock(Lock& lock, Func&& func) {
122 return lock.lock().then([func = std::forward<Func>(func)] () mutable {
124 }).then_wrapped([&lock] (auto&& fut) {
126 return std::move(fut);
130 /// Multiple argument variant of \ref do_with(T&& rvalue, F&& f).
132 /// This is the same as \ref do_with(T&& tvalue, F&& f), but accepts
133 /// two or more rvalue parameters, which are held in memory while
134 /// \c f executes. \c f will be called with all arguments as
135 /// reference parameters.
136 template <typename T1, typename T2, typename T3_or_F, typename... More>
139 do_with(T1&& rv1, T2&& rv2, T3_or_F&& rv3, More&&... more) {
140 auto all = std::forward_as_tuple(
141 std::forward<T1>(rv1),
142 std::forward<T2>(rv2),
143 std::forward<T3_or_F>(rv3),
144 std::forward<More>(more)...);
145 constexpr size_t nr = std::tuple_size<decltype(all)>::value - 1;
146 using idx = std::make_index_sequence<nr>;
147 auto&& just_values = cherry_pick_tuple(idx(), std::move(all));
148 auto&& just_func = std::move(std::get<nr>(std::move(all)));
149 using value_tuple = std::remove_reference_t<decltype(just_values)>;
150 using ret_type = decltype(apply(just_func, just_values));
151 auto task = std::make_unique<internal::do_with_state<value_tuple, ret_type>>(std::move(just_values));
152 auto fut = apply(just_func, task->data());
153 if (fut.available()) {
156 auto ret = task->get_future();
157 internal::set_callback(fut, task.release());