]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*- |
2 | // vim: ts=8 sw=2 smarttab expandtab | |
9f95a23c TL |
3 | |
4 | #pragma once | |
5 | ||
6 | #include <exception> | |
7 | #include <system_error> | |
8 | ||
9 | #include <seastar/core/future-util.hh> | |
10 | ||
20effc67 | 11 | #include "crimson/common/utility.h" |
f67539c2 TL |
12 | #include "include/ceph_assert.h" |
13 | ||
20effc67 TL |
14 | namespace crimson::interruptible { |
15 | ||
16 | template <typename, typename> | |
17 | class parallel_for_each_state; | |
18 | ||
19 | template <typename, typename> | |
20 | class interruptible_future_detail; | |
21 | ||
22 | } | |
23 | ||
9f95a23c TL |
24 | namespace crimson { |
25 | ||
1e59de90 TL |
26 | // crimson::do_for_each_state is the mirror of seastar::do_for_each_state with FutureT |
27 | template <typename Iterator, typename AsyncAction, typename FutureT> | |
28 | class do_for_each_state final : public seastar::continuation_base<> { | |
29 | Iterator _begin; | |
30 | Iterator _end; | |
31 | AsyncAction _action; | |
32 | seastar::promise<> _pr; | |
9f95a23c | 33 | |
1e59de90 TL |
34 | public: |
35 | do_for_each_state(Iterator begin, Iterator end, AsyncAction action, | |
36 | FutureT&& first_unavailable) | |
37 | : _begin(std::move(begin)), _end(std::move(end)), _action(std::move(action)) { | |
38 | seastar::internal::set_callback(std::move(first_unavailable), this); | |
9f95a23c | 39 | } |
1e59de90 TL |
40 | virtual void run_and_dispose() noexcept override { |
41 | std::unique_ptr<do_for_each_state> zis(this); | |
42 | if (_state.failed()) { | |
43 | _pr.set_urgent_state(std::move(_state)); | |
44 | return; | |
9f95a23c | 45 | } |
1e59de90 TL |
46 | while (_begin != _end) { |
47 | auto f = seastar::futurize_invoke(_action, *_begin); | |
48 | ++_begin; | |
49 | if (f.failed()) { | |
50 | f._forward_to(std::move(_pr)); | |
51 | return; | |
52 | } | |
53 | if (!f.available() || seastar::need_preempt()) { | |
54 | _state = {}; | |
55 | seastar::internal::set_callback(std::move(f), this); | |
56 | zis.release(); | |
57 | return; | |
58 | } | |
9f95a23c | 59 | } |
1e59de90 TL |
60 | _pr.set_value(); |
61 | } | |
62 | task* waiting_task() noexcept override { | |
63 | return _pr.waiting_task(); | |
64 | } | |
65 | FutureT get_future() { | |
66 | return _pr.get_future(); | |
67 | } | |
68 | }; | |
69 | ||
70 | template<typename Iterator, typename AsyncAction, | |
71 | typename FutureT = std::invoke_result_t<AsyncAction, typename Iterator::reference>> | |
72 | inline FutureT do_for_each_impl(Iterator begin, Iterator end, AsyncAction action) { | |
73 | while (begin != end) { | |
74 | auto f = seastar::futurize_invoke(action, *begin); | |
75 | ++begin; | |
9f95a23c TL |
76 | if (f.failed()) { |
77 | return f; | |
78 | } | |
1e59de90 TL |
79 | if (!f.available() || seastar::need_preempt()) { |
80 | // s will be freed by run_and_dispose() | |
81 | auto* s = new crimson::do_for_each_state<Iterator, AsyncAction, FutureT>{ | |
82 | std::move(begin), std::move(end), std::move(action), std::move(f)}; | |
83 | return s->get_future(); | |
84 | } | |
9f95a23c | 85 | } |
1e59de90 | 86 | return seastar::make_ready_future<>(); |
9f95a23c | 87 | } |
1e59de90 TL |
88 | |
89 | template<typename Iterator, typename AsyncAction> | |
90 | inline auto do_for_each(Iterator begin, Iterator end, AsyncAction action) { | |
91 | return ::crimson::do_for_each_impl(begin, end, std::move(action)); | |
92 | } | |
93 | ||
9f95a23c TL |
94 | template<typename Container, typename AsyncAction> |
95 | inline auto do_for_each(Container& c, AsyncAction action) { | |
96 | return ::crimson::do_for_each(std::begin(c), std::end(c), std::move(action)); | |
97 | } | |
98 | ||
f67539c2 | 99 | template<typename AsyncAction> |
20effc67 | 100 | inline auto repeat(AsyncAction action) { |
f67539c2 TL |
101 | using errorator_t = |
102 | typename ::seastar::futurize_t<std::invoke_result_t<AsyncAction>>::errorator_type; | |
103 | ||
104 | while (true) { | |
105 | auto f = ::seastar::futurize_invoke(action); | |
106 | if (f.failed()) { | |
107 | return errorator_t::template make_exception_future2<>( | |
108 | f.get_exception() | |
109 | ); | |
110 | } else if (f.available()) { | |
111 | if (auto done = f.get0()) { | |
112 | return errorator_t::template make_ready_future<>(); | |
113 | } | |
114 | } else { | |
115 | return std::move(f)._then( | |
20effc67 TL |
116 | [action = std::move(action)] (auto stop) mutable { |
117 | if (stop == seastar::stop_iteration::yes) { | |
f67539c2 TL |
118 | return errorator_t::template make_ready_future<>(); |
119 | } | |
20effc67 | 120 | return ::crimson::repeat( |
f67539c2 TL |
121 | std::move(action)); |
122 | }); | |
123 | } | |
124 | } | |
125 | } | |
126 | ||
9f95a23c TL |
127 | // define the interface between error types and errorator |
128 | template <class ConcreteErrorT> | |
129 | class error_t { | |
130 | static constexpr const std::type_info& get_exception_ptr_type_info() { | |
131 | return ConcreteErrorT::exception_ptr_type_info(); | |
132 | } | |
133 | ||
9f95a23c TL |
134 | decltype(auto) static from_exception_ptr(std::exception_ptr ep) { |
135 | return ConcreteErrorT::from_exception_ptr(std::move(ep)); | |
136 | } | |
137 | ||
138 | template <class... AllowedErrorsT> | |
139 | friend struct errorator; | |
140 | ||
141 | template <class ErrorVisitorT, class FuturatorT> | |
142 | friend class maybe_handle_error_t; | |
143 | ||
1e59de90 TL |
144 | protected: |
145 | std::exception_ptr to_exception_ptr() const { | |
146 | const auto* concrete_error = static_cast<const ConcreteErrorT*>(this); | |
147 | return concrete_error->to_exception_ptr(); | |
148 | } | |
149 | ||
9f95a23c TL |
150 | public: |
151 | template <class Func> | |
152 | static decltype(auto) handle(Func&& func) { | |
153 | return ConcreteErrorT::handle(std::forward<Func>(func)); | |
154 | } | |
155 | }; | |
156 | ||
157 | // unthrowable_wrapper ensures compilation failure when somebody | |
158 | // would like to `throw make_error<...>)()` instead of returning. | |
159 | // returning allows for the compile-time verification of future's | |
160 | // AllowedErrorsV and also avoid the burden of throwing. | |
161 | template <class ErrorT, ErrorT ErrorV> | |
162 | struct unthrowable_wrapper : error_t<unthrowable_wrapper<ErrorT, ErrorV>> { | |
163 | unthrowable_wrapper(const unthrowable_wrapper&) = delete; | |
164 | [[nodiscard]] static const auto& make() { | |
165 | static constexpr unthrowable_wrapper instance{}; | |
166 | return instance; | |
167 | } | |
168 | ||
1e59de90 TL |
169 | static auto exception_ptr() { |
170 | return make().to_exception_ptr(); | |
171 | } | |
172 | ||
9f95a23c TL |
173 | template<class Func> |
174 | static auto handle(Func&& func) { | |
175 | return [ | |
176 | func = std::forward<Func>(func) | |
1e59de90 TL |
177 | ] (const unthrowable_wrapper& raw_error) mutable -> decltype(auto) { |
178 | if constexpr (std::is_invocable_v<Func, ErrorT, decltype(raw_error)>) { | |
179 | // check whether the handler wants to take the raw error object which | |
180 | // would be the case if it wants conditionally handle-or-pass-further. | |
181 | return std::invoke(std::forward<Func>(func), | |
182 | ErrorV, | |
183 | std::move(raw_error)); | |
184 | } else if constexpr (std::is_invocable_v<Func, ErrorT>) { | |
9f95a23c TL |
185 | return std::invoke(std::forward<Func>(func), ErrorV); |
186 | } else { | |
187 | return std::invoke(std::forward<Func>(func)); | |
188 | } | |
189 | }; | |
190 | } | |
191 | ||
192 | struct pass_further { | |
193 | decltype(auto) operator()(const unthrowable_wrapper& e) { | |
194 | return e; | |
195 | } | |
196 | }; | |
197 | ||
198 | struct discard { | |
199 | decltype(auto) operator()(const unthrowable_wrapper&) { | |
200 | } | |
201 | }; | |
202 | ||
203 | ||
204 | private: | |
205 | // can be used only to initialize the `instance` member | |
206 | explicit unthrowable_wrapper() = default; | |
207 | ||
208 | // implement the errorable interface | |
209 | struct throwable_carrier{}; | |
210 | static std::exception_ptr carrier_instance; | |
211 | ||
212 | static constexpr const std::type_info& exception_ptr_type_info() { | |
213 | return typeid(throwable_carrier); | |
214 | } | |
215 | auto to_exception_ptr() const { | |
216 | // error codes don't need to instantiate `std::exception_ptr` each | |
217 | // time as the code is actually a part of the type itself. | |
218 | // `std::make_exception_ptr()` on modern enough GCCs is quite cheap | |
219 | // (see the Gleb Natapov's patch eradicating throw/catch there), | |
220 | // but using one instance per type boils down the overhead to just | |
221 | // ref-counting. | |
222 | return carrier_instance; | |
223 | } | |
224 | static const auto& from_exception_ptr(std::exception_ptr) { | |
225 | return make(); | |
226 | } | |
227 | ||
228 | friend class error_t<unthrowable_wrapper<ErrorT, ErrorV>>; | |
229 | }; | |
230 | ||
231 | template <class ErrorT, ErrorT ErrorV> | |
232 | std::exception_ptr unthrowable_wrapper<ErrorT, ErrorV>::carrier_instance = \ | |
233 | std::make_exception_ptr< | |
234 | unthrowable_wrapper<ErrorT, ErrorV>::throwable_carrier>({}); | |
235 | ||
236 | ||
237 | template <class ErrorT> | |
238 | struct stateful_error_t : error_t<stateful_error_t<ErrorT>> { | |
239 | template <class... Args> | |
240 | explicit stateful_error_t(Args&&... args) | |
241 | : ep(std::make_exception_ptr<ErrorT>(std::forward<Args>(args)...)) { | |
242 | } | |
243 | ||
244 | template<class Func> | |
245 | static auto handle(Func&& func) { | |
9f95a23c TL |
246 | return [ |
247 | func = std::forward<Func>(func) | |
248 | ] (stateful_error_t<ErrorT>&& e) mutable -> decltype(auto) { | |
1e59de90 TL |
249 | if constexpr (std::is_invocable_v<Func>) { |
250 | return std::invoke(std::forward<Func>(func)); | |
251 | } | |
9f95a23c TL |
252 | try { |
253 | std::rethrow_exception(e.ep); | |
254 | } catch (const ErrorT& obj) { | |
1e59de90 TL |
255 | if constexpr (std::is_invocable_v<Func, decltype(obj), decltype(e)>) { |
256 | return std::invoke(std::forward<Func>(func), obj, e); | |
257 | } else if constexpr (std::is_invocable_v<Func, decltype(obj)>) { | |
20effc67 | 258 | return std::invoke(std::forward<Func>(func), obj); |
1e59de90 | 259 | } |
9f95a23c | 260 | } |
20effc67 | 261 | ceph_abort_msg("exception type mismatch -- impossible!"); |
9f95a23c TL |
262 | }; |
263 | } | |
264 | ||
265 | private: | |
266 | std::exception_ptr ep; | |
267 | ||
268 | explicit stateful_error_t(std::exception_ptr ep) : ep(std::move(ep)) {} | |
269 | ||
270 | static constexpr const std::type_info& exception_ptr_type_info() { | |
271 | return typeid(ErrorT); | |
272 | } | |
273 | auto to_exception_ptr() const { | |
274 | return ep; | |
275 | } | |
276 | static stateful_error_t<ErrorT> from_exception_ptr(std::exception_ptr ep) { | |
277 | return stateful_error_t<ErrorT>(std::move(ep)); | |
278 | } | |
279 | ||
280 | friend class error_t<stateful_error_t<ErrorT>>; | |
281 | }; | |
282 | ||
283 | namespace _impl { | |
284 | template <class T> struct always_false : std::false_type {}; | |
285 | }; | |
286 | ||
287 | template <class ErrorVisitorT, class FuturatorT> | |
288 | class maybe_handle_error_t { | |
289 | const std::type_info& type_info; | |
290 | typename FuturatorT::type result; | |
291 | ErrorVisitorT errfunc; | |
292 | ||
293 | public: | |
294 | maybe_handle_error_t(ErrorVisitorT&& errfunc, std::exception_ptr ep) | |
295 | : type_info(*ep.__cxa_exception_type()), | |
296 | result(FuturatorT::make_exception_future(std::move(ep))), | |
297 | errfunc(std::forward<ErrorVisitorT>(errfunc)) { | |
298 | } | |
299 | ||
300 | template <class ErrorT> | |
301 | void handle() { | |
302 | static_assert(std::is_invocable<ErrorVisitorT, ErrorT>::value, | |
303 | "provided Error Visitor is not exhaustive"); | |
304 | // In C++ throwing an exception isn't the sole way to signal | |
305 | // error with it. This approach nicely fits cold, infrequent cases | |
306 | // but when applied to a hot one, it will likely hurt performance. | |
307 | // | |
308 | // Alternative approach is to create `std::exception_ptr` on our | |
309 | // own and place it in the future via `make_exception_future()`. | |
310 | // When it comes to handling, the pointer can be interrogated for | |
311 | // pointee's type with `__cxa_exception_type()` instead of costly | |
312 | // re-throwing (via `std::rethrow_exception()`) and matching with | |
313 | // `catch`. The limitation here is lack of support for hierarchies | |
314 | // of exceptions. The code below checks for exact match only while | |
315 | // `catch` would allow to match against a base class as well. | |
316 | // However, this shouldn't be a big issue for `errorator` as Error | |
317 | // Visitors are already checked for exhaustiveness at compile-time. | |
318 | // | |
319 | // NOTE: `__cxa_exception_type()` is an extension of the language. | |
320 | // It should be available both in GCC and Clang but a fallback | |
321 | // (based on `std::rethrow_exception()` and `catch`) can be made | |
322 | // to handle other platforms if necessary. | |
323 | if (type_info == ErrorT::error_t::get_exception_ptr_type_info()) { | |
324 | // set `state::invalid` in internals of `seastar::future` to not | |
325 | // call `report_failed_future()` during `operator=()`. | |
326 | [[maybe_unused]] auto&& ep = std::move(result).get_exception(); | |
327 | ||
328 | using return_t = std::invoke_result_t<ErrorVisitorT, ErrorT>; | |
329 | if constexpr (std::is_assignable_v<decltype(result), return_t>) { | |
330 | result = std::invoke(std::forward<ErrorVisitorT>(errfunc), | |
331 | ErrorT::error_t::from_exception_ptr(std::move(ep))); | |
332 | } else if constexpr (std::is_same_v<return_t, void>) { | |
333 | // void denotes explicit discarding | |
334 | // execute for the sake a side effects. Typically this boils down | |
335 | // to throwing an exception by the handler. | |
336 | std::invoke(std::forward<ErrorVisitorT>(errfunc), | |
337 | ErrorT::error_t::from_exception_ptr(std::move(ep))); | |
1e59de90 TL |
338 | } else if constexpr (seastar::Future<decltype(result)>) { |
339 | // result is seastar::future but return_t is e.g. int. If so, | |
340 | // the else clause cannot be used as seastar::future lacks | |
341 | // errorator_type member. | |
342 | result = seastar::make_ready_future<return_t>( | |
343 | std::invoke(std::forward<ErrorVisitorT>(errfunc), | |
344 | ErrorT::error_t::from_exception_ptr(std::move(ep)))); | |
9f95a23c | 345 | } else { |
20effc67 TL |
346 | result = FuturatorT::type::errorator_type::template make_ready_future<return_t>( |
347 | std::invoke(std::forward<ErrorVisitorT>(errfunc), | |
348 | ErrorT::error_t::from_exception_ptr(std::move(ep)))); | |
9f95a23c TL |
349 | } |
350 | } | |
351 | } | |
352 | ||
353 | auto get_result() && { | |
354 | return std::move(result); | |
355 | } | |
356 | }; | |
357 | ||
358 | template <class FuncHead, class... FuncTail> | |
359 | static constexpr auto composer(FuncHead&& head, FuncTail&&... tail) { | |
360 | return [ | |
361 | head = std::forward<FuncHead>(head), | |
f67539c2 | 362 | // perfect forwarding in lambda's closure isn't available in C++17 |
9f95a23c TL |
363 | // using tuple as workaround; see: https://stackoverflow.com/a/49902823 |
364 | tail = std::make_tuple(std::forward<FuncTail>(tail)...) | |
365 | ] (auto&&... args) mutable -> decltype(auto) { | |
366 | if constexpr (std::is_invocable_v<FuncHead, decltype(args)...>) { | |
367 | return std::invoke(std::forward<FuncHead>(head), | |
368 | std::forward<decltype(args)>(args)...); | |
369 | } else if constexpr (sizeof...(FuncTail) > 0) { | |
370 | using next_composer_t = decltype(composer<FuncTail...>); | |
371 | auto&& next = std::apply<next_composer_t>(composer<FuncTail...>, | |
372 | std::move(tail)); | |
373 | return std::invoke(std::move(next), | |
374 | std::forward<decltype(args)>(args)...); | |
375 | } else { | |
376 | static_assert( | |
f67539c2 TL |
377 | std::is_invocable_v<FuncHead, decltype(args)...> || |
378 | (sizeof...(FuncTail) > 0), | |
379 | "composition is not exhaustive"); | |
9f95a23c TL |
380 | } |
381 | }; | |
382 | } | |
383 | ||
f67539c2 | 384 | template <class ValueT> |
9f95a23c TL |
385 | struct errorated_future_marker{}; |
386 | ||
20effc67 TL |
387 | template <class... AllowedErrors> |
388 | class parallel_for_each_state; | |
389 | ||
390 | template <class T> | |
391 | static inline constexpr bool is_error_v = std::is_base_of_v<error_t<T>, T>; | |
392 | ||
393 | template <typename... AllowedErrors> | |
394 | struct errorator; | |
395 | ||
396 | template <typename Iterator, typename Func, typename... AllowedErrors> | |
397 | static inline typename errorator<AllowedErrors...>::template future<> | |
398 | parallel_for_each(Iterator first, Iterator last, Func&& func) noexcept; | |
399 | ||
9f95a23c TL |
400 | template <class... AllowedErrors> |
401 | struct errorator { | |
9f95a23c TL |
402 | |
403 | static_assert((... && is_error_v<AllowedErrors>), | |
404 | "errorator expects presence of ::is_error in all error types"); | |
405 | ||
406 | template <class ErrorT> | |
407 | struct contains_once { | |
408 | static constexpr bool value = | |
409 | (0 + ... + std::is_same_v<ErrorT, AllowedErrors>) == 1; | |
410 | }; | |
411 | template <class... Errors> | |
412 | struct contains_once<errorator<Errors...>> { | |
413 | static constexpr bool value = (... && contains_once<Errors>::value); | |
414 | }; | |
415 | template <class T> | |
416 | static constexpr bool contains_once_v = contains_once<T>::value; | |
417 | ||
418 | static_assert((... && contains_once_v<AllowedErrors>), | |
419 | "no error type in errorator can be duplicated"); | |
420 | ||
421 | struct ready_future_marker{}; | |
422 | struct exception_future_marker{}; | |
423 | ||
424 | private: | |
425 | // see the comment for `using future = _future` below. | |
426 | template <class> | |
20effc67 | 427 | class [[nodiscard]] _future {}; |
f67539c2 | 428 | template <class ValueT> |
20effc67 | 429 | class [[nodiscard]] _future<::crimson::errorated_future_marker<ValueT>> |
f67539c2 TL |
430 | : private seastar::future<ValueT> { |
431 | using base_t = seastar::future<ValueT>; | |
9f95a23c TL |
432 | // we need the friendship for the sake of `get_exception() &&` when |
433 | // `safe_then()` is going to return an errorated future as a result of | |
434 | // chaining. In contrast to `seastar::future`, errorator<T...>::future` | |
435 | // has this member private. | |
436 | template <class ErrorVisitor, class Futurator> | |
437 | friend class maybe_handle_error_t; | |
438 | ||
f67539c2 TL |
439 | // any `seastar::futurize` specialization must be able to access the base. |
440 | // see : `satisfy_with_result_of()` far below. | |
441 | template <typename> | |
20effc67 | 442 | friend struct seastar::futurize; |
f67539c2 TL |
443 | |
444 | template <typename T1, typename T2, typename... More> | |
445 | friend auto seastar::internal::do_with_impl(T1&& rv1, T2&& rv2, More&&... more); | |
446 | ||
9f95a23c TL |
447 | template <class, class = std::void_t<>> |
448 | struct get_errorator { | |
449 | // generic template for non-errorated things (plain types and | |
450 | // vanilla seastar::future as well). | |
451 | using type = errorator<>; | |
452 | }; | |
453 | template <class FutureT> | |
454 | struct get_errorator<FutureT, | |
455 | std::void_t<typename FutureT::errorator_type>> { | |
456 | using type = typename FutureT::errorator_type; | |
457 | }; | |
458 | template <class T> | |
459 | using get_errorator_t = typename get_errorator<T>::type; | |
460 | ||
461 | template <class ValueFuncErroratorT, class... ErrorVisitorRetsT> | |
462 | struct make_errorator { | |
463 | // NOP. The generic template. | |
464 | }; | |
465 | template <class... ValueFuncAllowedErrors, | |
466 | class ErrorVisitorRetsHeadT, | |
467 | class... ErrorVisitorRetsTailT> | |
468 | struct make_errorator<errorator<ValueFuncAllowedErrors...>, | |
469 | ErrorVisitorRetsHeadT, | |
470 | ErrorVisitorRetsTailT...> { | |
471 | private: | |
472 | using step_errorator = errorator<ValueFuncAllowedErrors...>; | |
473 | // add ErrorVisitorRetsHeadT only if 1) it's an error type and | |
474 | // 2) isn't already included in the errorator's error set. | |
475 | // It's enough to negate contains_once_v as any errorator<...> | |
476 | // type is already guaranteed to be free of duplications. | |
20effc67 | 477 | using _next_errorator = std::conditional_t< |
9f95a23c TL |
478 | is_error_v<ErrorVisitorRetsHeadT> && |
479 | !step_errorator::template contains_once_v<ErrorVisitorRetsHeadT>, | |
480 | typename step_errorator::template extend<ErrorVisitorRetsHeadT>, | |
481 | step_errorator>; | |
20effc67 TL |
482 | using maybe_head_ertr = get_errorator_t<ErrorVisitorRetsHeadT>; |
483 | using next_errorator = | |
484 | typename _next_errorator::template extend_ertr<maybe_head_ertr>; | |
9f95a23c TL |
485 | |
486 | public: | |
487 | using type = typename make_errorator<next_errorator, | |
488 | ErrorVisitorRetsTailT...>::type; | |
489 | }; | |
490 | // finish the recursion | |
491 | template <class... ValueFuncAllowedErrors> | |
492 | struct make_errorator<errorator<ValueFuncAllowedErrors...>> { | |
493 | using type = ::crimson::errorator<ValueFuncAllowedErrors...>; | |
494 | }; | |
495 | template <class... Args> | |
496 | using make_errorator_t = typename make_errorator<Args...>::type; | |
497 | ||
498 | using base_t::base_t; | |
499 | ||
500 | template <class Futurator, class Future, class ErrorVisitor> | |
501 | [[gnu::noinline]] | |
502 | static auto _safe_then_handle_errors(Future&& future, | |
503 | ErrorVisitor&& errfunc) { | |
504 | maybe_handle_error_t<ErrorVisitor, Futurator> maybe_handle_error( | |
505 | std::forward<ErrorVisitor>(errfunc), | |
506 | std::move(future).get_exception() | |
507 | ); | |
508 | (maybe_handle_error.template handle<AllowedErrors>() , ...); | |
509 | return std::move(maybe_handle_error).get_result(); | |
510 | } | |
511 | ||
20effc67 TL |
512 | protected: |
513 | using base_t::get_exception; | |
9f95a23c TL |
514 | public: |
515 | using errorator_type = ::crimson::errorator<AllowedErrors...>; | |
f67539c2 | 516 | using promise_type = seastar::promise<ValueT>; |
9f95a23c TL |
517 | |
518 | using base_t::available; | |
519 | using base_t::failed; | |
f67539c2 TL |
520 | // need this because of the legacy in PG::do_osd_ops(). |
521 | using base_t::handle_exception_type; | |
9f95a23c TL |
522 | |
523 | [[gnu::always_inline]] | |
524 | _future(base_t&& base) | |
525 | : base_t(std::move(base)) { | |
526 | } | |
527 | ||
1e59de90 TL |
528 | base_t to_base() && { |
529 | return std::move(*this); | |
530 | } | |
531 | ||
9f95a23c TL |
532 | template <class... A> |
533 | [[gnu::always_inline]] | |
534 | _future(ready_future_marker, A&&... a) | |
f67539c2 | 535 | : base_t(::seastar::make_ready_future<ValueT>(std::forward<A>(a)...)) { |
9f95a23c TL |
536 | } |
537 | [[gnu::always_inline]] | |
538 | _future(exception_future_marker, ::seastar::future_state_base&& state) noexcept | |
539 | : base_t(::seastar::futurize<base_t>::make_exception_future(std::move(state))) { | |
540 | } | |
541 | [[gnu::always_inline]] | |
542 | _future(exception_future_marker, std::exception_ptr&& ep) noexcept | |
543 | : base_t(::seastar::futurize<base_t>::make_exception_future(std::move(ep))) { | |
544 | } | |
545 | ||
546 | template <template <class...> class ErroratedFuture, | |
547 | class = std::void_t< | |
548 | typename ErroratedFuture< | |
f67539c2 TL |
549 | ::crimson::errorated_future_marker<ValueT>>::errorator_type>> |
550 | operator ErroratedFuture<errorated_future_marker<ValueT>> () && { | |
9f95a23c TL |
551 | using dest_errorator_t = \ |
552 | typename ErroratedFuture< | |
f67539c2 | 553 | ::crimson::errorated_future_marker<ValueT>>::errorator_type; |
9f95a23c TL |
554 | static_assert(dest_errorator_t::template contains_once_v<errorator_type>, |
555 | "conversion is possible to more-or-eq errorated future!"); | |
556 | return static_cast<base_t&&>(*this); | |
557 | } | |
558 | ||
559 | // initialize future as failed without throwing. `make_exception_future()` | |
560 | // internally uses `std::make_exception_ptr()`. cppreference.com shouldn't | |
561 | // be misinterpreted when it says: | |
562 | // | |
563 | // "This is done as if executing the following code: | |
564 | // try { | |
565 | // throw e; | |
566 | // } catch(...) { | |
567 | // return std::current_exception(); | |
568 | // }", | |
569 | // | |
570 | // the "as if" is absolutely crucial because modern GCCs employ optimized | |
571 | // path for it. See: | |
572 | // * https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=cce8e59224e18858749a2324bce583bcfd160d6c, | |
573 | // * https://gcc.gnu.org/ml/gcc-patches/2016-08/msg00373.html. | |
574 | // | |
575 | // This behavior, combined with `__cxa_exception_type()` for inspecting | |
576 | // exception's type, allows for throw/catch-free handling of stateless | |
577 | // exceptions (which is fine for error codes). Stateful jumbos would be | |
578 | // actually a bit harder as `_M_get()` is private, and thus rethrowing is | |
579 | // necessary to get to the state inside. However, it's not unthinkable to | |
580 | // see another extension bringing operator*() to the exception pointer... | |
581 | // | |
582 | // TODO: we don't really need to `make_exception_ptr` each time. It still | |
583 | // allocates memory underneath while can be replaced with single instance | |
584 | // per type created on start-up. | |
585 | template <class ErrorT, | |
586 | class DecayedT = std::decay_t<ErrorT>, | |
587 | bool IsError = is_error_v<DecayedT>, | |
588 | class = std::enable_if_t<IsError>> | |
589 | _future(ErrorT&& e) | |
590 | : base_t( | |
f67539c2 | 591 | seastar::make_exception_future<ValueT>( |
9f95a23c TL |
592 | errorator_type::make_exception_ptr(e))) { |
593 | static_assert(errorator_type::contains_once_v<DecayedT>, | |
594 | "ErrorT is not enlisted in errorator"); | |
595 | } | |
596 | ||
597 | template <class ValueFuncT, class ErrorVisitorT> | |
598 | auto safe_then(ValueFuncT&& valfunc, ErrorVisitorT&& errfunc) { | |
599 | static_assert((... && std::is_invocable_v<ErrorVisitorT, | |
600 | AllowedErrors>), | |
601 | "provided Error Visitor is not exhaustive"); | |
aee94f69 TL |
602 | static_assert(std::is_void_v<ValueT> ? std::is_invocable_v<ValueFuncT> |
603 | : std::is_invocable_v<ValueFuncT, ValueT>, | |
604 | "Value Func is not invocable with future's value"); | |
f67539c2 TL |
605 | using value_func_result_t = |
606 | typename std::conditional_t<std::is_void_v<ValueT>, | |
607 | std::invoke_result<ValueFuncT>, | |
608 | std::invoke_result<ValueFuncT, ValueT>>::type; | |
9f95a23c TL |
609 | // recognize whether there can be any error coming from the Value |
610 | // Function. | |
611 | using value_func_errorator_t = get_errorator_t<value_func_result_t>; | |
612 | // mutate the Value Function's errorator to harvest errors coming | |
613 | // from the Error Visitor. Yes, it's perfectly fine to fail error | |
614 | // handling at one step and delegate even broader set of issues | |
615 | // to next continuation. | |
616 | using return_errorator_t = make_errorator_t< | |
617 | value_func_errorator_t, | |
618 | std::decay_t<std::invoke_result_t<ErrorVisitorT, AllowedErrors>>...>; | |
619 | // OK, now we know about all errors next continuation must take | |
620 | // care about. If Visitor handled everything and the Value Func | |
621 | // doesn't return any, we'll finish with errorator<>::future | |
622 | // which is just vanilla seastar::future – that's it, next cont | |
623 | // finally could use `.then()`! | |
624 | using futurator_t = \ | |
625 | typename return_errorator_t::template futurize<value_func_result_t>; | |
626 | // `seastar::futurize`, used internally by `then_wrapped()`, would | |
627 | // wrap any non-`seastar::future` type coming from Value Func into | |
628 | // `seastar::future`. As we really don't want to end with things | |
629 | // like `seastar::future<errorator::future<...>>`, we need either: | |
630 | // * convert the errorated future into plain in the lambda below | |
631 | // and back here or | |
632 | // * specialize the `seastar::futurize<T>` to get proper kind of | |
633 | // future directly from `::then_wrapped()`. | |
634 | // As C++17 doesn't guarantee copy elision when non-same types are | |
635 | // involved while examination of assemblies from GCC 8.1 confirmed | |
636 | // extra copying, switch to the second approach has been made. | |
637 | return this->then_wrapped( | |
638 | [ valfunc = std::forward<ValueFuncT>(valfunc), | |
639 | errfunc = std::forward<ErrorVisitorT>(errfunc) | |
640 | ] (auto&& future) mutable noexcept { | |
641 | if (__builtin_expect(future.failed(), false)) { | |
642 | return _safe_then_handle_errors<futurator_t>( | |
643 | std::move(future), std::forward<ErrorVisitorT>(errfunc)); | |
644 | } else { | |
645 | // NOTE: using `seastar::future::get()` here is a bit bloaty | |
646 | // as the method rechecks availability of future's value and, | |
647 | // if it's unavailable, does the `::do_wait()` path (yes, it | |
648 | // targets `seastar::thread`). Actually this is dead code as | |
649 | // `then_wrapped()` executes the lambda only when the future | |
650 | // is available (which means: failed or ready). However, GCC | |
651 | // hasn't optimized it out: | |
652 | // | |
653 | // if (__builtin_expect(future.failed(), false)) { | |
654 | // ea25: 48 83 bd c8 fe ff ff cmpq $0x2,-0x138(%rbp) | |
655 | // ea2c: 02 | |
656 | // ea2d: 0f 87 f0 05 00 00 ja f023 <ceph::osd:: | |
657 | // ... | |
658 | // /// If get() is called in a \ref seastar::thread context, | |
659 | // /// then it need not be available; instead, the thread will | |
660 | // /// be paused until the future becomes available. | |
661 | // [[gnu::always_inline]] | |
662 | // std::tuple<T...> get() { | |
663 | // if (!_state.available()) { | |
664 | // ea3a: 0f 85 1b 05 00 00 jne ef5b <ceph::osd:: | |
665 | // } | |
666 | // ... | |
667 | // | |
668 | // I don't perceive this as huge issue. Though, it cannot be | |
669 | // claimed errorator has 0 overhead on hot path. The perfect | |
670 | // solution here would be mark the `::get_available_state()` | |
671 | // as `protected` and use dedicated `get_value()` exactly as | |
672 | // `::then()` already does. | |
f67539c2 TL |
673 | return futurator_t::invoke(std::forward<ValueFuncT>(valfunc), |
674 | std::move(future).get()); | |
9f95a23c TL |
675 | } |
676 | }); | |
677 | } | |
678 | ||
f67539c2 TL |
679 | /** |
680 | * unsafe_thread_get | |
681 | * | |
682 | * Only valid within a seastar_thread. Ignores errorator protections | |
683 | * and throws any contained exceptions. | |
684 | * | |
685 | * Should really only be used within test code | |
686 | * (see test/crimson/gtest_seastar.h). | |
687 | */ | |
688 | auto &&unsafe_get() { | |
689 | return seastar::future<ValueT>::get(); | |
690 | } | |
691 | auto unsafe_get0() { | |
692 | return seastar::future<ValueT>::get0(); | |
693 | } | |
9f95a23c | 694 | |
f67539c2 TL |
695 | template <class FuncT> |
696 | _future finally(FuncT &&func) { | |
9f95a23c | 697 | return this->then_wrapped( |
f67539c2 | 698 | [func = std::forward<FuncT>(func)](auto &&result) mutable noexcept { |
1e59de90 | 699 | if constexpr (seastar::InvokeReturnsAnyFuture<FuncT>) { |
f67539c2 TL |
700 | return ::seastar::futurize_invoke(std::forward<FuncT>(func)).then_wrapped( |
701 | [result = std::move(result)](auto&& f_res) mutable { | |
702 | // TODO: f_res.failed() | |
703 | (void)f_res.discard_result(); | |
704 | return std::move(result); | |
705 | }); | |
706 | } else { | |
707 | try { | |
708 | func(); | |
709 | } catch (...) { | |
710 | // TODO: rethrow | |
711 | } | |
712 | return std::move(result); | |
713 | } | |
714 | }); | |
9f95a23c TL |
715 | } |
716 | ||
20effc67 TL |
717 | _future<::crimson::errorated_future_marker<void>> |
718 | discard_result() noexcept { | |
719 | return safe_then([](auto&&) {}); | |
720 | } | |
721 | ||
9f95a23c TL |
722 | // taking ErrorFuncOne and ErrorFuncTwo separately from ErrorFuncTail |
723 | // to avoid SFINAE | |
724 | template <class ValueFunc, | |
725 | class ErrorFuncHead, | |
726 | class... ErrorFuncTail> | |
727 | auto safe_then(ValueFunc&& value_func, | |
728 | ErrorFuncHead&& error_func_head, | |
729 | ErrorFuncTail&&... error_func_tail) { | |
730 | static_assert(sizeof...(ErrorFuncTail) > 0); | |
731 | return safe_then( | |
732 | std::forward<ValueFunc>(value_func), | |
733 | composer(std::forward<ErrorFuncHead>(error_func_head), | |
734 | std::forward<ErrorFuncTail>(error_func_tail)...)); | |
735 | } | |
736 | ||
737 | template <class ValueFunc> | |
738 | auto safe_then(ValueFunc&& value_func) { | |
739 | return safe_then(std::forward<ValueFunc>(value_func), | |
740 | errorator_type::pass_further{}); | |
741 | } | |
742 | ||
20effc67 TL |
743 | template <class ValueFunc, |
744 | class... ErrorFuncs> | |
745 | auto safe_then_unpack(ValueFunc&& value_func, | |
746 | ErrorFuncs&&... error_funcs) { | |
747 | return safe_then( | |
748 | [value_func=std::move(value_func)] (ValueT&& tuple) mutable { | |
749 | assert_moveable(value_func); | |
750 | return std::apply(std::move(value_func), std::move(tuple)); | |
751 | }, | |
752 | std::forward<ErrorFuncs>(error_funcs)... | |
753 | ); | |
754 | } | |
755 | ||
9f95a23c TL |
756 | template <class Func> |
757 | void then(Func&&) = delete; | |
758 | ||
759 | template <class ErrorVisitorT> | |
760 | auto handle_error(ErrorVisitorT&& errfunc) { | |
761 | static_assert((... && std::is_invocable_v<ErrorVisitorT, | |
762 | AllowedErrors>), | |
763 | "provided Error Visitor is not exhaustive"); | |
764 | using return_errorator_t = make_errorator_t< | |
765 | errorator<>, | |
766 | std::decay_t<std::invoke_result_t<ErrorVisitorT, AllowedErrors>>...>; | |
767 | using futurator_t = \ | |
f67539c2 | 768 | typename return_errorator_t::template futurize<::seastar::future<ValueT>>; |
9f95a23c TL |
769 | return this->then_wrapped( |
770 | [ errfunc = std::forward<ErrorVisitorT>(errfunc) | |
771 | ] (auto&& future) mutable noexcept { | |
772 | if (__builtin_expect(future.failed(), false)) { | |
773 | return _safe_then_handle_errors<futurator_t>( | |
774 | std::move(future), std::forward<ErrorVisitorT>(errfunc)); | |
775 | } else { | |
776 | return typename futurator_t::type{ std::move(future) }; | |
777 | } | |
778 | }); | |
779 | } | |
20effc67 | 780 | |
9f95a23c TL |
781 | template <class ErrorFuncHead, |
782 | class... ErrorFuncTail> | |
783 | auto handle_error(ErrorFuncHead&& error_func_head, | |
784 | ErrorFuncTail&&... error_func_tail) { | |
785 | static_assert(sizeof...(ErrorFuncTail) > 0); | |
786 | return this->handle_error( | |
787 | composer(std::forward<ErrorFuncHead>(error_func_head), | |
788 | std::forward<ErrorFuncTail>(error_func_tail)...)); | |
789 | } | |
790 | ||
791 | private: | |
792 | // for ::crimson::do_for_each | |
793 | template <class Func> | |
794 | auto _then(Func&& func) { | |
795 | return base_t::then(std::forward<Func>(func)); | |
796 | } | |
1e59de90 TL |
797 | template <class T> |
798 | auto _forward_to(T&& pr) { | |
799 | return base_t::forward_to(std::forward<T>(pr)); | |
800 | } | |
9f95a23c TL |
801 | template<typename Iterator, typename AsyncAction> |
802 | friend inline auto ::crimson::do_for_each(Iterator begin, | |
803 | Iterator end, | |
804 | AsyncAction action); | |
805 | ||
1e59de90 TL |
806 | template <typename Iterator, typename AsyncAction, typename FutureT> |
807 | friend class ::crimson::do_for_each_state; | |
808 | ||
f67539c2 | 809 | template<typename AsyncAction> |
20effc67 | 810 | friend inline auto ::crimson::repeat(AsyncAction action); |
f67539c2 TL |
811 | |
812 | template <typename Result> | |
9f95a23c TL |
813 | friend class ::seastar::future; |
814 | ||
f67539c2 | 815 | // let seastar::do_with_impl to up-cast us to seastar::future. |
9f95a23c | 816 | template<typename T, typename F> |
f67539c2 | 817 | friend inline auto ::seastar::internal::do_with_impl(T&& rvalue, F&& f); |
9f95a23c | 818 | template<typename T1, typename T2, typename T3_or_F, typename... More> |
f67539c2 | 819 | friend inline auto ::seastar::internal::do_with_impl(T1&& rv1, T2&& rv2, T3_or_F&& rv3, More&&... more); |
20effc67 TL |
820 | template<typename, typename> |
821 | friend class ::crimson::interruptible::interruptible_future_detail; | |
822 | friend class ::crimson::parallel_for_each_state<AllowedErrors...>; | |
823 | template <typename IC, typename FT> | |
824 | friend class ::crimson::interruptible::parallel_for_each_state; | |
9f95a23c TL |
825 | }; |
826 | ||
827 | class Enabler {}; | |
828 | ||
829 | template <typename T> | |
830 | using EnableIf = typename std::enable_if<contains_once_v<std::decay_t<T>>, Enabler>::type; | |
831 | ||
832 | template <typename ErrorFunc> | |
833 | struct all_same_way_t { | |
834 | ErrorFunc func; | |
835 | all_same_way_t(ErrorFunc &&error_func) | |
836 | : func(std::forward<ErrorFunc>(error_func)) {} | |
837 | ||
838 | template <typename ErrorT, EnableIf<ErrorT>...> | |
839 | decltype(auto) operator()(ErrorT&& e) { | |
840 | using decayed_t = std::decay_t<decltype(e)>; | |
841 | auto&& handler = | |
842 | decayed_t::error_t::handle(std::forward<ErrorFunc>(func)); | |
f67539c2 | 843 | static_assert(std::is_invocable_v<decltype(handler), ErrorT>); |
9f95a23c TL |
844 | return std::invoke(std::move(handler), std::forward<ErrorT>(e)); |
845 | } | |
846 | }; | |
847 | ||
848 | public: | |
849 | // HACK: `errorated_future_marker` and `_future` is just a hack to | |
850 | // specialize `seastar::futurize` for category of class templates: | |
851 | // `future<...>` from distinct errorators. Such tricks are usually | |
852 | // performed basing on SFINAE and `std::void_t` to check existence | |
853 | // of a trait/member (`future<...>::errorator_type` in our case). | |
854 | // Unfortunately, this technique can't be applied as the `futurize` | |
855 | // lacks the optional parameter. The problem looks awfully similar | |
856 | // to following SO item: https://stackoverflow.com/a/38860413. | |
f67539c2 TL |
857 | template <class ValueT=void> |
858 | using future = _future<::crimson::errorated_future_marker<ValueT>>; | |
9f95a23c TL |
859 | |
860 | // the visitor that forwards handling of all errors to next continuation | |
861 | struct pass_further { | |
862 | template <class ErrorT, EnableIf<ErrorT>...> | |
863 | decltype(auto) operator()(ErrorT&& e) { | |
864 | static_assert(contains_once_v<std::decay_t<ErrorT>>, | |
865 | "passing further disallowed ErrorT"); | |
866 | return std::forward<ErrorT>(e); | |
867 | } | |
868 | }; | |
869 | ||
870 | struct discard_all { | |
871 | template <class ErrorT, EnableIf<ErrorT>...> | |
f67539c2 TL |
872 | void operator()(ErrorT&&) { |
873 | static_assert(contains_once_v<std::decay_t<ErrorT>>, | |
874 | "discarding disallowed ErrorT"); | |
875 | } | |
876 | }; | |
877 | ||
20effc67 TL |
878 | template <typename T> |
879 | static future<T> make_errorator_future(seastar::future<T>&& fut) { | |
880 | return std::move(fut); | |
881 | } | |
882 | ||
f67539c2 TL |
883 | // assert_all{ "TODO" }; |
884 | class assert_all { | |
885 | const char* const msg = nullptr; | |
886 | public: | |
887 | template <std::size_t N> | |
888 | assert_all(const char (&msg)[N]) | |
889 | : msg(msg) { | |
890 | } | |
891 | assert_all() = default; | |
892 | ||
893 | template <class ErrorT, EnableIf<ErrorT>...> | |
894 | void operator()(ErrorT&&) { | |
9f95a23c TL |
895 | static_assert(contains_once_v<std::decay_t<ErrorT>>, |
896 | "discarding disallowed ErrorT"); | |
f67539c2 TL |
897 | if (msg) { |
898 | ceph_abort_msg(msg); | |
899 | } else { | |
900 | ceph_abort(); | |
901 | } | |
9f95a23c TL |
902 | } |
903 | }; | |
904 | ||
905 | template <class ErrorFunc> | |
906 | static decltype(auto) all_same_way(ErrorFunc&& error_func) { | |
907 | return all_same_way_t<ErrorFunc>{std::forward<ErrorFunc>(error_func)}; | |
908 | }; | |
909 | ||
20effc67 | 910 | // get a new errorator by extending current one with new errors |
9f95a23c TL |
911 | template <class... NewAllowedErrorsT> |
912 | using extend = errorator<AllowedErrors..., NewAllowedErrorsT...>; | |
913 | ||
914 | // get a new errorator by summing and deduplicating error set of | |
915 | // the errorator `unify<>` is applied on with another errorator | |
916 | // provided as template parameter. | |
917 | template <class OtherErroratorT> | |
918 | struct unify { | |
919 | // 1st: generic NOP template | |
920 | }; | |
921 | template <class OtherAllowedErrorsHead, | |
922 | class... OtherAllowedErrorsTail> | |
923 | struct unify<errorator<OtherAllowedErrorsHead, | |
924 | OtherAllowedErrorsTail...>> { | |
925 | private: | |
926 | // 2nd: specialization for errorators with non-empty error set. | |
927 | // | |
928 | // split error set of other errorator, passed as template param, | |
929 | // into head and tail. Mix error set of this errorator with head | |
930 | // of the other one only if it isn't already present in the set. | |
931 | using step_errorator = std::conditional_t< | |
932 | contains_once_v<OtherAllowedErrorsHead> == false, | |
933 | errorator<AllowedErrors..., OtherAllowedErrorsHead>, | |
934 | errorator<AllowedErrors...>>; | |
935 | using rest_errorator = errorator<OtherAllowedErrorsTail...>; | |
936 | ||
937 | public: | |
938 | using type = typename step_errorator::template unify<rest_errorator>::type; | |
939 | }; | |
940 | template <class... EmptyPack> | |
941 | struct unify<errorator<EmptyPack...>> { | |
942 | // 3rd: recursion finisher | |
943 | static_assert(sizeof...(EmptyPack) == 0); | |
944 | using type = errorator<AllowedErrors...>; | |
945 | }; | |
946 | ||
20effc67 TL |
947 | // get a new errorator by extending current one with another errorator |
948 | template <class E> | |
949 | using extend_ertr = typename unify<E>::type; | |
950 | ||
f67539c2 TL |
951 | template <typename T=void, typename... A> |
952 | static future<T> make_ready_future(A&&... value) { | |
953 | return future<T>(ready_future_marker(), std::forward<A>(value)...); | |
9f95a23c TL |
954 | } |
955 | ||
f67539c2 | 956 | template <typename T=void> |
9f95a23c | 957 | static |
f67539c2 TL |
958 | future<T> make_exception_future2(std::exception_ptr&& ex) noexcept { |
959 | return future<T>(exception_future_marker(), std::move(ex)); | |
9f95a23c | 960 | } |
f67539c2 | 961 | template <typename T=void> |
9f95a23c | 962 | static |
f67539c2 TL |
963 | future<T> make_exception_future2(seastar::future_state_base&& state) noexcept { |
964 | return future<T>(exception_future_marker(), std::move(state)); | |
9f95a23c | 965 | } |
f67539c2 | 966 | template <typename T=void, typename Exception> |
9f95a23c | 967 | static |
f67539c2 TL |
968 | future<T> make_exception_future2(Exception&& ex) noexcept { |
969 | return make_exception_future2<T>(std::make_exception_ptr(std::forward<Exception>(ex))); | |
9f95a23c TL |
970 | } |
971 | ||
972 | static auto now() { | |
973 | return make_ready_future<>(); | |
974 | } | |
975 | ||
20effc67 TL |
976 | template <typename Container, typename Func> |
977 | static inline auto parallel_for_each(Container&& container, Func&& func) noexcept { | |
978 | return crimson::parallel_for_each<decltype(std::begin(container)), Func, AllowedErrors...>( | |
979 | std::begin(container), | |
980 | std::end(container), | |
981 | std::forward<Func>(func)); | |
982 | } | |
983 | ||
984 | template <typename Iterator, typename Func> | |
985 | static inline errorator<AllowedErrors...>::future<> | |
986 | parallel_for_each(Iterator first, Iterator last, Func&& func) noexcept { | |
987 | return crimson::parallel_for_each<Iterator, Func, AllowedErrors...>( | |
988 | first, | |
989 | last, | |
990 | std::forward<Func>(func)); | |
991 | } | |
9f95a23c | 992 | private: |
20effc67 | 993 | template <class T> |
9f95a23c TL |
994 | class futurize { |
995 | using vanilla_futurize = seastar::futurize<T>; | |
996 | ||
f67539c2 TL |
997 | // explicit specializations for nested type is not allowed unless both |
998 | // the member template and the enclosing template are specialized. see | |
999 | // section temp.expl.spec, N4659 | |
1000 | template <class Stored, int Dummy = 0> | |
1001 | struct stored_to_future { | |
1002 | using type = future<Stored>; | |
1003 | }; | |
1004 | template <int Dummy> | |
1005 | struct stored_to_future <seastar::internal::monostate, Dummy> { | |
1006 | using type = future<>; | |
9f95a23c TL |
1007 | }; |
1008 | ||
1009 | public: | |
1010 | using type = | |
f67539c2 | 1011 | typename stored_to_future<typename vanilla_futurize::value_type>::type; |
9f95a23c TL |
1012 | |
1013 | template <class Func, class... Args> | |
f67539c2 TL |
1014 | static type invoke(Func&& func, Args&&... args) { |
1015 | try { | |
1016 | return vanilla_futurize::invoke(std::forward<Func>(func), | |
1017 | std::forward<Args>(args)...); | |
1018 | } catch (...) { | |
1019 | return make_exception_future(std::current_exception()); | |
1020 | } | |
1021 | } | |
1022 | ||
1023 | template <class Func> | |
1024 | static type invoke(Func&& func, seastar::internal::monostate) { | |
1025 | try { | |
1026 | return vanilla_futurize::invoke(std::forward<Func>(func)); | |
1027 | } catch (...) { | |
1028 | return make_exception_future(std::current_exception()); | |
1029 | } | |
9f95a23c TL |
1030 | } |
1031 | ||
1032 | template <typename Arg> | |
1033 | static type make_exception_future(Arg&& arg) { | |
1034 | return vanilla_futurize::make_exception_future(std::forward<Arg>(arg)); | |
1035 | } | |
1036 | }; | |
1037 | template <template <class...> class ErroratedFutureT, | |
f67539c2 | 1038 | class ValueT> |
20effc67 | 1039 | class futurize<ErroratedFutureT<::crimson::errorated_future_marker<ValueT>>> { |
9f95a23c | 1040 | public: |
f67539c2 | 1041 | using type = ::crimson::errorator<AllowedErrors...>::future<ValueT>; |
9f95a23c | 1042 | |
f67539c2 TL |
1043 | template <class Func, class... Args> |
1044 | static type invoke(Func&& func, Args&&... args) { | |
1045 | try { | |
1046 | return ::seastar::futurize_invoke(std::forward<Func>(func), | |
1047 | std::forward<Args>(args)...); | |
1048 | } catch (...) { | |
1049 | return make_exception_future(std::current_exception()); | |
1050 | } | |
1051 | } | |
1052 | ||
1053 | template <class Func> | |
1054 | static type invoke(Func&& func, seastar::internal::monostate) { | |
1055 | try { | |
1056 | return ::seastar::futurize_invoke(std::forward<Func>(func)); | |
9f95a23c TL |
1057 | } catch (...) { |
1058 | return make_exception_future(std::current_exception()); | |
1059 | } | |
1060 | } | |
1061 | ||
1062 | template <typename Arg> | |
1063 | static type make_exception_future(Arg&& arg) { | |
f67539c2 | 1064 | return ::crimson::errorator<AllowedErrors...>::make_exception_future2<ValueT>(std::forward<Arg>(arg)); |
9f95a23c TL |
1065 | } |
1066 | }; | |
1067 | ||
20effc67 TL |
1068 | template <typename InterruptCond, typename FutureType> |
1069 | class futurize< | |
1070 | ::crimson::interruptible::interruptible_future_detail< | |
1071 | InterruptCond, FutureType>> { | |
1072 | public: | |
1073 | using type = ::crimson::interruptible::interruptible_future_detail< | |
1074 | InterruptCond, typename futurize<FutureType>::type>; | |
1075 | ||
1076 | template <typename Func, typename... Args> | |
1077 | static type invoke(Func&& func, Args&&... args) { | |
1078 | try { | |
1079 | return ::seastar::futurize_invoke(std::forward<Func>(func), | |
1080 | std::forward<Args>(args)...); | |
1081 | } catch(...) { | |
1082 | return seastar::futurize< | |
1083 | ::crimson::interruptible::interruptible_future_detail< | |
1084 | InterruptCond, FutureType>>::make_exception_future( | |
1085 | std::current_exception()); | |
1086 | } | |
1087 | } | |
1088 | template <typename Func> | |
1089 | static type invoke(Func&& func, seastar::internal::monostate) { | |
1090 | try { | |
1091 | return ::seastar::futurize_invoke(std::forward<Func>(func)); | |
1092 | } catch(...) { | |
1093 | return seastar::futurize< | |
1094 | ::crimson::interruptible::interruptible_future_detail< | |
1095 | InterruptCond, FutureType>>::make_exception_future( | |
1096 | std::current_exception()); | |
1097 | } | |
1098 | } | |
1099 | template <typename Arg> | |
1100 | static type make_exception_future(Arg&& arg) { | |
1101 | return ::seastar::futurize< | |
1102 | ::crimson::interruptible::interruptible_future_detail< | |
1103 | InterruptCond, FutureType>>::make_exception_future( | |
1104 | std::forward<Arg>(arg)); | |
1105 | } | |
1106 | }; | |
1107 | ||
9f95a23c TL |
1108 | template <class ErrorT> |
1109 | static std::exception_ptr make_exception_ptr(ErrorT&& e) { | |
1110 | // calling via interface class due to encapsulation and friend relations. | |
1111 | return e.error_t<std::decay_t<ErrorT>>::to_exception_ptr(); | |
1112 | } | |
1113 | ||
1114 | // needed because of: | |
1115 | // * return_errorator_t::template futurize<...> in `safe_then()`, | |
1116 | // * conversion to `std::exception_ptr` in `future::future(ErrorT&&)`. | |
1117 | // the friendship with all errorators is an idea from Kefu to fix build | |
1118 | // issues on GCC 9. This version likely fixes some access violation bug | |
1119 | // we were exploiting before. | |
1120 | template <class...> | |
1121 | friend class errorator; | |
20effc67 TL |
1122 | template<typename, typename> |
1123 | friend class ::crimson::interruptible::interruptible_future_detail; | |
9f95a23c TL |
1124 | }; // class errorator, generic template |
1125 | ||
1126 | // no errors? errorator<>::future is plain seastar::future then! | |
1127 | template <> | |
1128 | class errorator<> { | |
1129 | public: | |
20effc67 TL |
1130 | template <class ValueT=void> |
1131 | using future = ::seastar::futurize_t<ValueT>; | |
9f95a23c TL |
1132 | |
1133 | template <class T> | |
1134 | using futurize = ::seastar::futurize<T>; | |
1135 | ||
20effc67 | 1136 | // get a new errorator by extending current one with errors |
9f95a23c TL |
1137 | template <class... NewAllowedErrors> |
1138 | using extend = errorator<NewAllowedErrors...>; | |
1139 | ||
20effc67 TL |
1140 | // get a new errorator by extending current one with another errorator |
1141 | template <class E> | |
1142 | using extend_ertr = E; | |
1143 | ||
9f95a23c TL |
1144 | // errorator with empty error set never contains any error |
1145 | template <class T> | |
1146 | static constexpr bool contains_once_v = false; | |
1147 | }; // class errorator, <> specialization | |
1148 | ||
1149 | ||
1150 | template <class ErroratorOne, | |
1151 | class ErroratorTwo, | |
1152 | class... FurtherErrators> | |
1153 | struct compound_errorator { | |
1154 | private: | |
1155 | // generic template. Empty `FurtherErrators` are handled by | |
1156 | // the specialization below. | |
1157 | static_assert(sizeof...(FurtherErrators) > 0); | |
1158 | using step = | |
1159 | typename compound_errorator<ErroratorOne, ErroratorTwo>::type; | |
1160 | ||
1161 | public: | |
1162 | using type = | |
1163 | typename compound_errorator<step, FurtherErrators...>::type; | |
1164 | }; | |
1165 | template <class ErroratorOne, | |
1166 | class ErroratorTwo> | |
1167 | struct compound_errorator<ErroratorOne, ErroratorTwo> { | |
1168 | // specialization for empty `FurtherErrators` arg pack | |
1169 | using type = | |
1170 | typename ErroratorOne::template unify<ErroratorTwo>::type; | |
1171 | }; | |
1172 | template <class... Args> | |
1173 | using compound_errorator_t = typename compound_errorator<Args...>::type; | |
1174 | ||
1175 | // this is conjunction of two nasty features: C++14's variable template | |
1176 | // and inline global variable of C++17. The latter is crucial to ensure | |
1177 | // the variable will get the same address across all translation units. | |
1e59de90 TL |
1178 | template <int ErrorV> |
1179 | inline std::error_code ec = std::error_code(ErrorV, std::generic_category()); | |
9f95a23c | 1180 | |
1e59de90 | 1181 | template <int ErrorV> |
9f95a23c TL |
1182 | using ct_error_code = unthrowable_wrapper<const std::error_code&, ec<ErrorV>>; |
1183 | ||
1184 | namespace ct_error { | |
1e59de90 TL |
1185 | using enoent = ct_error_code<static_cast<int>(std::errc::no_such_file_or_directory)>; |
1186 | using enodata = ct_error_code<static_cast<int>(std::errc::no_message_available)>; | |
1187 | using invarg = ct_error_code<static_cast<int>(std::errc::invalid_argument)>; | |
1188 | using input_output_error = ct_error_code<static_cast<int>(std::errc::io_error)>; | |
1189 | using object_corrupted = ct_error_code<static_cast<int>(std::errc::illegal_byte_sequence)>; | |
1190 | using permission_denied = ct_error_code<static_cast<int>(std::errc::permission_denied)>; | |
9f95a23c | 1191 | using operation_not_supported = |
1e59de90 TL |
1192 | ct_error_code<static_cast<int>(std::errc::operation_not_supported)>; |
1193 | using not_connected = ct_error_code<static_cast<int>(std::errc::not_connected)>; | |
1194 | using timed_out = ct_error_code<static_cast<int>(std::errc::timed_out)>; | |
f67539c2 | 1195 | using erange = |
1e59de90 | 1196 | ct_error_code<static_cast<int>(std::errc::result_out_of_range)>; |
f67539c2 | 1197 | using ebadf = |
1e59de90 | 1198 | ct_error_code<static_cast<int>(std::errc::bad_file_descriptor)>; |
f67539c2 | 1199 | using enospc = |
1e59de90 TL |
1200 | ct_error_code<static_cast<int>(std::errc::no_space_on_device)>; |
1201 | using value_too_large = ct_error_code<static_cast<int>(std::errc::value_too_large)>; | |
f67539c2 | 1202 | using eagain = |
1e59de90 | 1203 | ct_error_code<static_cast<int>(std::errc::resource_unavailable_try_again)>; |
f67539c2 | 1204 | using file_too_large = |
1e59de90 TL |
1205 | ct_error_code<static_cast<int>(std::errc::file_too_large)>; |
1206 | using address_in_use = ct_error_code<static_cast<int>(std::errc::address_in_use)>; | |
1207 | using address_not_available = ct_error_code<static_cast<int>(std::errc::address_not_available)>; | |
1208 | using ecanceled = ct_error_code<static_cast<int>(std::errc::operation_canceled)>; | |
1209 | using einprogress = ct_error_code<static_cast<int>(std::errc::operation_in_progress)>; | |
1210 | using enametoolong = ct_error_code<static_cast<int>(std::errc::filename_too_long)>; | |
1211 | using eexist = ct_error_code<static_cast<int>(std::errc::file_exists)>; | |
1212 | using edquot = ct_error_code<int(122)>; | |
1213 | constexpr int cmp_fail_error_value = 4095; | |
1214 | using cmp_fail = ct_error_code<int(cmp_fail_error_value)>; | |
9f95a23c TL |
1215 | |
1216 | struct pass_further_all { | |
1217 | template <class ErrorT> | |
1218 | decltype(auto) operator()(ErrorT&& e) { | |
1219 | return std::forward<ErrorT>(e); | |
1220 | } | |
1221 | }; | |
1222 | ||
1223 | struct discard_all { | |
1224 | template <class ErrorT> | |
f67539c2 TL |
1225 | void operator()(ErrorT&&) { |
1226 | } | |
1227 | }; | |
1228 | ||
1229 | class assert_all { | |
1230 | const char* const msg = nullptr; | |
1231 | public: | |
1232 | template <std::size_t N> | |
1233 | assert_all(const char (&msg)[N]) | |
1234 | : msg(msg) { | |
1235 | } | |
1236 | assert_all() = default; | |
1237 | ||
1238 | template <class ErrorT> | |
1239 | void operator()(ErrorT&&) { | |
1240 | if (msg) { | |
1241 | ceph_abort(msg); | |
1242 | } else { | |
1243 | ceph_abort(); | |
1244 | } | |
9f95a23c TL |
1245 | } |
1246 | }; | |
1247 | ||
1248 | template <class ErrorFunc> | |
1249 | static decltype(auto) all_same_way(ErrorFunc&& error_func) { | |
1250 | return [ | |
1251 | error_func = std::forward<ErrorFunc>(error_func) | |
1252 | ] (auto&& e) mutable -> decltype(auto) { | |
1253 | using decayed_t = std::decay_t<decltype(e)>; | |
1254 | auto&& handler = | |
1255 | decayed_t::error_t::handle(std::forward<ErrorFunc>(error_func)); | |
1256 | return std::invoke(std::move(handler), std::forward<decltype(e)>(e)); | |
1257 | }; | |
1258 | }; | |
1259 | } | |
1260 | ||
1261 | using stateful_errc = stateful_error_t<std::errc>; | |
1262 | using stateful_errint = stateful_error_t<int>; | |
1263 | using stateful_ec = stateful_error_t<std::error_code>; | |
1264 | ||
1e59de90 TL |
1265 | template <typename F> |
1266 | struct is_errorated_future { | |
1267 | static constexpr bool value = false; | |
1268 | }; | |
1269 | template <template <class...> class ErroratedFutureT, | |
1270 | class ValueT> | |
1271 | struct is_errorated_future< | |
1272 | ErroratedFutureT<::crimson::errorated_future_marker<ValueT>> | |
1273 | > { | |
1274 | static constexpr bool value = true; | |
1275 | }; | |
1276 | template <typename T> | |
1277 | constexpr bool is_errorated_future_v = is_errorated_future<T>::value; | |
1278 | ||
9f95a23c TL |
1279 | } // namespace crimson |
1280 | ||
1281 | ||
1282 | // open the `seastar` namespace to specialize `futurize`. This is not | |
1283 | // pretty for sure. I just hope it's not worse than e.g. specializing | |
1284 | // `hash` in the `std` namespace. The justification is copy avoidance | |
1285 | // in `future<...>::safe_then()`. See the comments there for details. | |
1286 | namespace seastar { | |
1287 | ||
f67539c2 TL |
1288 | // Container is a placeholder for errorator::_future<> template |
1289 | template <template <class> class Container, | |
1290 | class Value> | |
1291 | struct futurize<Container<::crimson::errorated_future_marker<Value>>> { | |
9f95a23c | 1292 | using errorator_type = typename Container< |
f67539c2 | 1293 | ::crimson::errorated_future_marker<Value>>::errorator_type; |
9f95a23c | 1294 | |
f67539c2 TL |
1295 | using type = typename errorator_type::template future<Value>; |
1296 | using value_type = seastar::internal::future_stored_type_t<Value>; | |
9f95a23c | 1297 | |
20effc67 TL |
1298 | template<typename Func, typename... FuncArgs> |
1299 | [[gnu::always_inline]] | |
1300 | static type apply(Func&& func, std::tuple<FuncArgs...>&& args) noexcept { | |
1301 | try { | |
1302 | return std::apply( | |
1303 | std::forward<Func>(func), | |
1304 | std::forward<std::tuple<FuncArgs...>>(args)); | |
1305 | } catch (...) { | |
1306 | return make_exception_future(std::current_exception()); | |
1307 | } | |
1308 | } | |
1309 | ||
9f95a23c TL |
1310 | template<typename Func, typename... FuncArgs> |
1311 | [[gnu::always_inline]] | |
f67539c2 | 1312 | static inline type invoke(Func&& func, FuncArgs&&... args) noexcept { |
9f95a23c | 1313 | try { |
f67539c2 | 1314 | return func(std::forward<FuncArgs>(args)...); |
9f95a23c TL |
1315 | } catch (...) { |
1316 | return make_exception_future(std::current_exception()); | |
1317 | } | |
1318 | } | |
1319 | ||
f67539c2 TL |
1320 | template <class Func> |
1321 | [[gnu::always_inline]] | |
1322 | static type invoke(Func&& func, seastar::internal::monostate) noexcept { | |
9f95a23c | 1323 | try { |
f67539c2 | 1324 | return func(); |
9f95a23c | 1325 | } catch (...) { |
f67539c2 | 1326 | return make_exception_future(std::current_exception()); |
9f95a23c TL |
1327 | } |
1328 | } | |
1329 | ||
1330 | template <typename Arg> | |
1331 | [[gnu::always_inline]] | |
1332 | static type make_exception_future(Arg&& arg) { | |
f67539c2 | 1333 | return errorator_type::template make_exception_future2<Value>(std::forward<Arg>(arg)); |
9f95a23c | 1334 | } |
f67539c2 TL |
1335 | |
1336 | private: | |
1337 | template<typename PromiseT, typename Func> | |
1338 | static void satisfy_with_result_of(PromiseT&& pr, Func&& func) { | |
1339 | // this may use the protected variant of `seastar::future::forward_to()` | |
1340 | // because: | |
1341 | // 1. `seastar::future` established a friendship with with all | |
1342 | // specializations of `seastar::futurize`, including this | |
1343 | // one (we're in the `seastar` namespace!) WHILE | |
1344 | // 2. any errorated future declares now the friendship with any | |
1345 | // `seastar::futurize<...>`. | |
1346 | func().forward_to(std::move(pr)); | |
1347 | } | |
1348 | template <typename U> | |
1349 | friend class future; | |
9f95a23c TL |
1350 | }; |
1351 | ||
f67539c2 TL |
1352 | template <template <class> class Container, |
1353 | class Value> | |
1354 | struct continuation_base_from_future<Container<::crimson::errorated_future_marker<Value>>> { | |
1355 | using type = continuation_base<Value>; | |
9f95a23c | 1356 | }; |
9f95a23c TL |
1357 | |
1358 | } // namespace seastar |