]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/async/completion.h
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / common / async / completion.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2018 Red Hat
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15 #ifndef CEPH_ASYNC_COMPLETION_H
16 #define CEPH_ASYNC_COMPLETION_H
17
18 #include <memory>
19
20 #include "bind_handler.h"
21 #include "forward_handler.h"
22
23 namespace ceph::async {
24
25 /**
26 * Abstract completion handler interface for use with boost::asio.
27 *
28 * Memory management is performed using the Handler's 'associated allocator',
29 * which carries the additional requirement that its memory be released before
30 * the Handler is invoked. This allows memory allocated for one asynchronous
31 * operation to be reused in its continuation. Because of this requirement, any
32 * calls to invoke the completion must first release ownership of it. To enforce
33 * this, the static functions defer()/dispatch()/post() take the completion by
34 * rvalue-reference to std::unique_ptr<Completion>, i.e. std::move(completion).
35 *
36 * Handlers may also have an 'associated executor', so the calls to defer(),
37 * dispatch(), and post() are forwarded to that executor. If there is no
38 * associated executor (which is generally the case unless one was bound with
39 * boost::asio::bind_executor()), the executor passed to Completion::create()
40 * is used as a default.
41 *
42 * Example use:
43 *
44 * // declare a Completion type with Signature = void(int, string)
45 * using MyCompletion = ceph::async::Completion<void(int, string)>;
46 *
47 * // create a completion with the given callback:
48 * std::unique_ptr<MyCompletion> c;
49 * c = MyCompletion::create(ex, [] (int a, const string& b) {});
50 *
51 * // bind arguments to the callback and post to its associated executor:
52 * MyCompletion::post(std::move(c), 5, "hello");
53 *
54 *
55 * Additional user data may be stored along with the Completion to take
56 * advantage of the handler allocator optimization. This is accomplished by
57 * specifying its type in the template parameter T. For example, the type
58 * Completion<void(), int> contains a public member variable 'int user_data'.
59 * Any additional arguments to Completion::create() will be forwarded to type
60 * T's constructor.
61 *
62 * If the AsBase<T> type tag is used, as in Completion<void(), AsBase<T>>,
63 * the Completion will inherit from T instead of declaring it as a member
64 * variable.
65 *
66 * When invoking the completion handler via defer(), dispatch(), or post(),
67 * care must be taken when passing arguments that refer to user data, because
68 * its memory is destroyed prior to invocation. In such cases, the user data
69 * should be moved/copied out of the Completion first.
70 */
71 template <typename Signature, typename T = void>
72 class Completion;
73
74
75 /// type tag for UserData
76 template <typename T> struct AsBase {};
77
78 namespace detail {
79
80 /// optional user data to be stored with the Completion
81 template <typename T>
82 struct UserData {
83 T user_data;
84 template <typename ...Args>
85 UserData(Args&& ...args)
86 : user_data(std::forward<Args>(args)...)
87 {}
88 };
89 // AsBase specialization inherits from T
90 template <typename T>
91 struct UserData<AsBase<T>> : public T {
92 template <typename ...Args>
93 UserData(Args&& ...args)
94 : T(std::forward<Args>(args)...)
95 {}
96 };
97 // void specialization
98 template <>
99 class UserData<void> {};
100
101 } // namespace detail
102
103
104 // template specialization to pull the Signature's args apart
105 template <typename T, typename ...Args>
106 class Completion<void(Args...), T> : public detail::UserData<T> {
107 protected:
108 // internal interfaces for type-erasure on the Handler/Executor. uses
109 // tuple<Args...> to provide perfect forwarding because you can't make
110 // virtual function templates
111 virtual void destroy_defer(std::tuple<Args...>&& args) = 0;
112 virtual void destroy_dispatch(std::tuple<Args...>&& args) = 0;
113 virtual void destroy_post(std::tuple<Args...>&& args) = 0;
114 virtual void destroy() = 0;
115
116 // constructor is protected, use create(). any constructor arguments are
117 // forwarded to UserData
118 template <typename ...TArgs>
119 Completion(TArgs&& ...args)
120 : detail::UserData<T>(std::forward<TArgs>(args)...)
121 {}
122 public:
123 virtual ~Completion() = default;
124
125 // use the virtual destroy() interface on delete. this allows the derived
126 // class to manage its memory using Handler allocators, without having to use
127 // a custom Deleter for std::unique_ptr<>
128 static void operator delete(void *p) {
129 static_cast<Completion*>(p)->destroy();
130 }
131
132 /// completion factory function that uses the handler's associated allocator.
133 /// any additional arguments are forwared to T's constructor
134 template <typename Executor1, typename Handler, typename ...TArgs>
135 static std::unique_ptr<Completion>
136 create(const Executor1& ex1, Handler&& handler, TArgs&& ...args);
137
138 /// take ownership of the completion, bind any arguments to the completion
139 /// handler, then defer() it on its associated executor
140 template <typename ...Args2>
141 static void defer(std::unique_ptr<Completion>&& c, Args2&&...args);
142
143 /// take ownership of the completion, bind any arguments to the completion
144 /// handler, then dispatch() it on its associated executor
145 template <typename ...Args2>
146 static void dispatch(std::unique_ptr<Completion>&& c, Args2&&...args);
147
148 /// take ownership of the completion, bind any arguments to the completion
149 /// handler, then post() it to its associated executor
150 template <typename ...Args2>
151 static void post(std::unique_ptr<Completion>&& c, Args2&&...args);
152 };
153
154 namespace detail {
155
156 // concrete Completion that knows how to invoke the completion handler. this
157 // observes all of the 'Requirements on asynchronous operations' specified by
158 // the C++ Networking TS
159 template <typename Executor1, typename Handler, typename T, typename ...Args>
160 class CompletionImpl final : public Completion<void(Args...), T> {
161 // use Handler's associated executor (or Executor1 by default) for callbacks
162 using Executor2 = boost::asio::associated_executor_t<Handler, Executor1>;
163 // maintain work on both executors
164 using Work1 = boost::asio::executor_work_guard<Executor1>;
165 using Work2 = boost::asio::executor_work_guard<Executor2>;
166 std::pair<Work1, Work2> work;
167 Handler handler;
168
169 // use Handler's associated allocator
170 using Alloc2 = boost::asio::associated_allocator_t<Handler>;
171 using Traits2 = std::allocator_traits<Alloc2>;
172 using RebindAlloc2 = typename Traits2::template rebind_alloc<CompletionImpl>;
173 using RebindTraits2 = std::allocator_traits<RebindAlloc2>;
174
175 // placement new for the handler allocator
176 static void* operator new(size_t, RebindAlloc2 alloc2) {
177 return RebindTraits2::allocate(alloc2, 1);
178 }
179 // placement delete for when the constructor throws during placement new
180 static void operator delete(void *p, RebindAlloc2 alloc2) {
181 RebindTraits2::deallocate(alloc2, static_cast<CompletionImpl*>(p), 1);
182 }
183
184 static auto bind_and_forward(Handler&& h, std::tuple<Args...>&& args) {
185 return forward_handler(CompletionHandler{std::move(h), std::move(args)});
186 }
187
188 void destroy_defer(std::tuple<Args...>&& args) override {
189 auto w = std::move(work);
190 auto f = bind_and_forward(std::move(handler), std::move(args));
191 RebindAlloc2 alloc2 = boost::asio::get_associated_allocator(handler);
192 RebindTraits2::destroy(alloc2, this);
193 RebindTraits2::deallocate(alloc2, this, 1);
194 w.second.get_executor().defer(std::move(f), alloc2);
195 }
196 void destroy_dispatch(std::tuple<Args...>&& args) override {
197 auto w = std::move(work);
198 auto f = bind_and_forward(std::move(handler), std::move(args));
199 RebindAlloc2 alloc2 = boost::asio::get_associated_allocator(handler);
200 RebindTraits2::destroy(alloc2, this);
201 RebindTraits2::deallocate(alloc2, this, 1);
202 w.second.get_executor().dispatch(std::move(f), alloc2);
203 }
204 void destroy_post(std::tuple<Args...>&& args) override {
205 auto w = std::move(work);
206 auto f = bind_and_forward(std::move(handler), std::move(args));
207 RebindAlloc2 alloc2 = boost::asio::get_associated_allocator(handler);
208 RebindTraits2::destroy(alloc2, this);
209 RebindTraits2::deallocate(alloc2, this, 1);
210 w.second.get_executor().post(std::move(f), alloc2);
211 }
212 void destroy() override {
213 RebindAlloc2 alloc2 = boost::asio::get_associated_allocator(handler);
214 RebindTraits2::destroy(alloc2, this);
215 RebindTraits2::deallocate(alloc2, this, 1);
216 }
217
218 // constructor is private, use create(). extra constructor arguments are
219 // forwarded to UserData
220 template <typename ...TArgs>
221 CompletionImpl(const Executor1& ex1, Handler&& handler, TArgs&& ...args)
222 : Completion<void(Args...), T>(std::forward<TArgs>(args)...),
223 work(ex1, boost::asio::make_work_guard(handler, ex1)),
224 handler(std::move(handler))
225 {}
226
227 public:
228 template <typename ...TArgs>
229 static auto create(const Executor1& ex, Handler&& handler, TArgs&& ...args) {
230 auto alloc2 = boost::asio::get_associated_allocator(handler);
231 using Ptr = std::unique_ptr<CompletionImpl>;
232 return Ptr{new (alloc2) CompletionImpl(ex, std::move(handler),
233 std::forward<TArgs>(args)...)};
234 }
235
236 static void operator delete(void *p) {
237 static_cast<CompletionImpl*>(p)->destroy();
238 }
239 };
240
241 } // namespace detail
242
243
244 template <typename T, typename ...Args>
245 template <typename Executor1, typename Handler, typename ...TArgs>
246 std::unique_ptr<Completion<void(Args...), T>>
247 Completion<void(Args...), T>::create(const Executor1& ex,
248 Handler&& handler, TArgs&& ...args)
249 {
250 using Impl = detail::CompletionImpl<Executor1, Handler, T, Args...>;
251 return Impl::create(ex, std::forward<Handler>(handler),
252 std::forward<TArgs>(args)...);
253 }
254
255 template <typename T, typename ...Args>
256 template <typename ...Args2>
257 void Completion<void(Args...), T>::defer(std::unique_ptr<Completion>&& ptr,
258 Args2&& ...args)
259 {
260 auto c = ptr.release();
261 c->destroy_defer(std::make_tuple(std::forward<Args2>(args)...));
262 }
263
264 template <typename T, typename ...Args>
265 template <typename ...Args2>
266 void Completion<void(Args...), T>::dispatch(std::unique_ptr<Completion>&& ptr,
267 Args2&& ...args)
268 {
269 auto c = ptr.release();
270 c->destroy_dispatch(std::make_tuple(std::forward<Args2>(args)...));
271 }
272
273 template <typename T, typename ...Args>
274 template <typename ...Args2>
275 void Completion<void(Args...), T>::post(std::unique_ptr<Completion>&& ptr,
276 Args2&& ...args)
277 {
278 auto c = ptr.release();
279 c->destroy_post(std::make_tuple(std::forward<Args2>(args)...));
280 }
281
282
283 /// completion factory function that uses the handler's associated allocator.
284 /// any additional arguments are forwared to T's constructor
285 template <typename Signature, typename T, typename Executor1,
286 typename Handler, typename ...TArgs>
287 std::unique_ptr<Completion<Signature, T>>
288 create_completion(const Executor1& ex, Handler&& handler, TArgs&& ...args)
289 {
290 return Completion<Signature, T>::create(ex, std::forward<Handler>(handler),
291 std::forward<TArgs>(args)...);
292 }
293
294 /// take ownership of the completion, bind any arguments to the completion
295 /// handler, then defer() it on its associated executor
296 template <typename Signature, typename T, typename ...Args>
297 void defer(std::unique_ptr<Completion<Signature, T>>&& ptr, Args&& ...args)
298 {
299 Completion<Signature, T>::defer(std::move(ptr), std::forward<Args>(args)...);
300 }
301
302 /// take ownership of the completion, bind any arguments to the completion
303 /// handler, then dispatch() it on its associated executor
304 template <typename Signature, typename T, typename ...Args>
305 void dispatch(std::unique_ptr<Completion<Signature, T>>&& ptr, Args&& ...args)
306 {
307 Completion<Signature, T>::dispatch(std::move(ptr), std::forward<Args>(args)...);
308 }
309
310 /// take ownership of the completion, bind any arguments to the completion
311 /// handler, then post() it to its associated executor
312 template <typename Signature, typename T, typename ...Args>
313 void post(std::unique_ptr<Completion<Signature, T>>&& ptr, Args&& ...args)
314 {
315 Completion<Signature, T>::post(std::move(ptr), std::forward<Args>(args)...);
316 }
317
318 } // namespace ceph::async
319
320 #endif // CEPH_ASYNC_COMPLETION_H