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