]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/include/seastar/core/do_with.hh
import 15.2.0 Octopus source
[ceph.git] / ceph / src / seastar / include / seastar / core / do_with.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 * Copyright (C) 2015 Cloudius Systems, Ltd.
20 */
21
22 #pragma once
23
24 #include <seastar/core/apply.hh>
25 #include <seastar/core/future.hh>
26 #include <utility>
27 #include <memory>
28 #include <tuple>
29
30 namespace seastar {
31
32
33 /// \cond internal
34
35 namespace internal {
36
37
38 // Given a future type, find the corresponding continuation_base.
39 template <typename Future>
40 struct continuation_base_from_future;
41
42 template <typename... T>
43 struct continuation_base_from_future<future<T...>> {
44 using type = continuation_base<T...>;
45 };
46
47 template <typename HeldState, typename Future>
48 class do_with_state final : public continuation_base_from_future<Future>::type {
49 HeldState _held;
50 typename Future::promise_type _pr;
51 public:
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));
55 delete this;
56 }
57 HeldState& data() {
58 return _held;
59 }
60 Future get_future() {
61 return _pr.get_future();
62 }
63 };
64
65 }
66 /// \endcond
67
68 /// \addtogroup future-util
69 /// @{
70
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.
74 ///
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.
80 ///
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
84 /// is destructed.
85 ///
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>
91 inline
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()) {
96 return fut;
97 }
98 auto ret = task->get_future();
99 internal::set_callback(fut, task.release());
100 return ret;
101 }
102
103 /// \cond internal
104 template <typename Tuple, size_t... Idx>
105 inline
106 auto
107 cherry_pick_tuple(std::index_sequence<Idx...>, Tuple&& tuple) {
108 return std::make_tuple(std::get<Idx>(std::forward<Tuple>(tuple))...);
109 }
110 /// \endcond
111
112 /// Executes the function \c func making sure the lock \c lock is taken,
113 /// and later on properly released.
114 ///
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>
120 inline
121 auto with_lock(Lock& lock, Func&& func) {
122 return lock.lock().then([func = std::forward<Func>(func)] () mutable {
123 return func();
124 }).then_wrapped([&lock] (auto&& fut) {
125 lock.unlock();
126 return std::move(fut);
127 });
128 }
129
130 /// Multiple argument variant of \ref do_with(T&& rvalue, F&& f).
131 ///
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>
137 inline
138 auto
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()) {
154 return fut;
155 }
156 auto ret = task->get_future();
157 internal::set_callback(fut, task.release());
158 return ret;
159 }
160
161 /// @}
162
163 }