]> git.proxmox.com Git - ceph.git/blob - ceph/src/crimson/common/errorator.h
update ceph source to reef 18.2.1
[ceph.git] / ceph / src / crimson / common / errorator.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
2 // vim: ts=8 sw=2 smarttab expandtab
3
4 #pragma once
5
6 #include <exception>
7 #include <system_error>
8
9 #include <seastar/core/future-util.hh>
10
11 #include "crimson/common/utility.h"
12 #include "include/ceph_assert.h"
13
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
24 namespace crimson {
25
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;
33
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);
39 }
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;
45 }
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 }
59 }
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;
76 if (f.failed()) {
77 return f;
78 }
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 }
85 }
86 return seastar::make_ready_future<>();
87 }
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
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
99 template<typename AsyncAction>
100 inline auto repeat(AsyncAction action) {
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(
116 [action = std::move(action)] (auto stop) mutable {
117 if (stop == seastar::stop_iteration::yes) {
118 return errorator_t::template make_ready_future<>();
119 }
120 return ::crimson::repeat(
121 std::move(action));
122 });
123 }
124 }
125 }
126
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
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
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
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
169 static auto exception_ptr() {
170 return make().to_exception_ptr();
171 }
172
173 template<class Func>
174 static auto handle(Func&& func) {
175 return [
176 func = std::forward<Func>(func)
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>) {
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) {
246 return [
247 func = std::forward<Func>(func)
248 ] (stateful_error_t<ErrorT>&& e) mutable -> decltype(auto) {
249 if constexpr (std::is_invocable_v<Func>) {
250 return std::invoke(std::forward<Func>(func));
251 }
252 try {
253 std::rethrow_exception(e.ep);
254 } catch (const ErrorT& obj) {
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)>) {
258 return std::invoke(std::forward<Func>(func), obj);
259 }
260 }
261 ceph_abort_msg("exception type mismatch -- impossible!");
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)));
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))));
345 } else {
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))));
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),
362 // perfect forwarding in lambda's closure isn't available in C++17
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(
377 std::is_invocable_v<FuncHead, decltype(args)...> ||
378 (sizeof...(FuncTail) > 0),
379 "composition is not exhaustive");
380 }
381 };
382 }
383
384 template <class ValueT>
385 struct errorated_future_marker{};
386
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
400 template <class... AllowedErrors>
401 struct errorator {
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>
427 class [[nodiscard]] _future {};
428 template <class ValueT>
429 class [[nodiscard]] _future<::crimson::errorated_future_marker<ValueT>>
430 : private seastar::future<ValueT> {
431 using base_t = seastar::future<ValueT>;
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
439 // any `seastar::futurize` specialization must be able to access the base.
440 // see : `satisfy_with_result_of()` far below.
441 template <typename>
442 friend struct seastar::futurize;
443
444 template <typename T1, typename T2, typename... More>
445 friend auto seastar::internal::do_with_impl(T1&& rv1, T2&& rv2, More&&... more);
446
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.
477 using _next_errorator = std::conditional_t<
478 is_error_v<ErrorVisitorRetsHeadT> &&
479 !step_errorator::template contains_once_v<ErrorVisitorRetsHeadT>,
480 typename step_errorator::template extend<ErrorVisitorRetsHeadT>,
481 step_errorator>;
482 using maybe_head_ertr = get_errorator_t<ErrorVisitorRetsHeadT>;
483 using next_errorator =
484 typename _next_errorator::template extend_ertr<maybe_head_ertr>;
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
512 protected:
513 using base_t::get_exception;
514 public:
515 using errorator_type = ::crimson::errorator<AllowedErrors...>;
516 using promise_type = seastar::promise<ValueT>;
517
518 using base_t::available;
519 using base_t::failed;
520 // need this because of the legacy in PG::do_osd_ops().
521 using base_t::handle_exception_type;
522
523 [[gnu::always_inline]]
524 _future(base_t&& base)
525 : base_t(std::move(base)) {
526 }
527
528 base_t to_base() && {
529 return std::move(*this);
530 }
531
532 template <class... A>
533 [[gnu::always_inline]]
534 _future(ready_future_marker, A&&... a)
535 : base_t(::seastar::make_ready_future<ValueT>(std::forward<A>(a)...)) {
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<
549 ::crimson::errorated_future_marker<ValueT>>::errorator_type>>
550 operator ErroratedFuture<errorated_future_marker<ValueT>> () && {
551 using dest_errorator_t = \
552 typename ErroratedFuture<
553 ::crimson::errorated_future_marker<ValueT>>::errorator_type;
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(
591 seastar::make_exception_future<ValueT>(
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");
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");
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;
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.
673 return futurator_t::invoke(std::forward<ValueFuncT>(valfunc),
674 std::move(future).get());
675 }
676 });
677 }
678
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 }
694
695 template <class FuncT>
696 _future finally(FuncT &&func) {
697 return this->then_wrapped(
698 [func = std::forward<FuncT>(func)](auto &&result) mutable noexcept {
699 if constexpr (seastar::InvokeReturnsAnyFuture<FuncT>) {
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 });
715 }
716
717 _future<::crimson::errorated_future_marker<void>>
718 discard_result() noexcept {
719 return safe_then([](auto&&) {});
720 }
721
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
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
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 = \
768 typename return_errorator_t::template futurize<::seastar::future<ValueT>>;
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 }
780
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 }
797 template <class T>
798 auto _forward_to(T&& pr) {
799 return base_t::forward_to(std::forward<T>(pr));
800 }
801 template<typename Iterator, typename AsyncAction>
802 friend inline auto ::crimson::do_for_each(Iterator begin,
803 Iterator end,
804 AsyncAction action);
805
806 template <typename Iterator, typename AsyncAction, typename FutureT>
807 friend class ::crimson::do_for_each_state;
808
809 template<typename AsyncAction>
810 friend inline auto ::crimson::repeat(AsyncAction action);
811
812 template <typename Result>
813 friend class ::seastar::future;
814
815 // let seastar::do_with_impl to up-cast us to seastar::future.
816 template<typename T, typename F>
817 friend inline auto ::seastar::internal::do_with_impl(T&& rvalue, F&& f);
818 template<typename T1, typename T2, typename T3_or_F, typename... More>
819 friend inline auto ::seastar::internal::do_with_impl(T1&& rv1, T2&& rv2, T3_or_F&& rv3, More&&... more);
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;
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));
843 static_assert(std::is_invocable_v<decltype(handler), ErrorT>);
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.
857 template <class ValueT=void>
858 using future = _future<::crimson::errorated_future_marker<ValueT>>;
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>...>
872 void operator()(ErrorT&&) {
873 static_assert(contains_once_v<std::decay_t<ErrorT>>,
874 "discarding disallowed ErrorT");
875 }
876 };
877
878 template <typename T>
879 static future<T> make_errorator_future(seastar::future<T>&& fut) {
880 return std::move(fut);
881 }
882
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&&) {
895 static_assert(contains_once_v<std::decay_t<ErrorT>>,
896 "discarding disallowed ErrorT");
897 if (msg) {
898 ceph_abort_msg(msg);
899 } else {
900 ceph_abort();
901 }
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
910 // get a new errorator by extending current one with new errors
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
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
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)...);
954 }
955
956 template <typename T=void>
957 static
958 future<T> make_exception_future2(std::exception_ptr&& ex) noexcept {
959 return future<T>(exception_future_marker(), std::move(ex));
960 }
961 template <typename T=void>
962 static
963 future<T> make_exception_future2(seastar::future_state_base&& state) noexcept {
964 return future<T>(exception_future_marker(), std::move(state));
965 }
966 template <typename T=void, typename Exception>
967 static
968 future<T> make_exception_future2(Exception&& ex) noexcept {
969 return make_exception_future2<T>(std::make_exception_ptr(std::forward<Exception>(ex)));
970 }
971
972 static auto now() {
973 return make_ready_future<>();
974 }
975
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 }
992 private:
993 template <class T>
994 class futurize {
995 using vanilla_futurize = seastar::futurize<T>;
996
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<>;
1007 };
1008
1009 public:
1010 using type =
1011 typename stored_to_future<typename vanilla_futurize::value_type>::type;
1012
1013 template <class Func, class... Args>
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 }
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,
1038 class ValueT>
1039 class futurize<ErroratedFutureT<::crimson::errorated_future_marker<ValueT>>> {
1040 public:
1041 using type = ::crimson::errorator<AllowedErrors...>::future<ValueT>;
1042
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));
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) {
1064 return ::crimson::errorator<AllowedErrors...>::make_exception_future2<ValueT>(std::forward<Arg>(arg));
1065 }
1066 };
1067
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
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;
1122 template<typename, typename>
1123 friend class ::crimson::interruptible::interruptible_future_detail;
1124 }; // class errorator, generic template
1125
1126 // no errors? errorator<>::future is plain seastar::future then!
1127 template <>
1128 class errorator<> {
1129 public:
1130 template <class ValueT=void>
1131 using future = ::seastar::futurize_t<ValueT>;
1132
1133 template <class T>
1134 using futurize = ::seastar::futurize<T>;
1135
1136 // get a new errorator by extending current one with errors
1137 template <class... NewAllowedErrors>
1138 using extend = errorator<NewAllowedErrors...>;
1139
1140 // get a new errorator by extending current one with another errorator
1141 template <class E>
1142 using extend_ertr = E;
1143
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.
1178 template <int ErrorV>
1179 inline std::error_code ec = std::error_code(ErrorV, std::generic_category());
1180
1181 template <int ErrorV>
1182 using ct_error_code = unthrowable_wrapper<const std::error_code&, ec<ErrorV>>;
1183
1184 namespace ct_error {
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)>;
1191 using operation_not_supported =
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)>;
1195 using erange =
1196 ct_error_code<static_cast<int>(std::errc::result_out_of_range)>;
1197 using ebadf =
1198 ct_error_code<static_cast<int>(std::errc::bad_file_descriptor)>;
1199 using enospc =
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)>;
1202 using eagain =
1203 ct_error_code<static_cast<int>(std::errc::resource_unavailable_try_again)>;
1204 using file_too_large =
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)>;
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>
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 }
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
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
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
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>>> {
1292 using errorator_type = typename Container<
1293 ::crimson::errorated_future_marker<Value>>::errorator_type;
1294
1295 using type = typename errorator_type::template future<Value>;
1296 using value_type = seastar::internal::future_stored_type_t<Value>;
1297
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
1310 template<typename Func, typename... FuncArgs>
1311 [[gnu::always_inline]]
1312 static inline type invoke(Func&& func, FuncArgs&&... args) noexcept {
1313 try {
1314 return func(std::forward<FuncArgs>(args)...);
1315 } catch (...) {
1316 return make_exception_future(std::current_exception());
1317 }
1318 }
1319
1320 template <class Func>
1321 [[gnu::always_inline]]
1322 static type invoke(Func&& func, seastar::internal::monostate) noexcept {
1323 try {
1324 return func();
1325 } catch (...) {
1326 return make_exception_future(std::current_exception());
1327 }
1328 }
1329
1330 template <typename Arg>
1331 [[gnu::always_inline]]
1332 static type make_exception_future(Arg&& arg) {
1333 return errorator_type::template make_exception_future2<Value>(std::forward<Arg>(arg));
1334 }
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;
1350 };
1351
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>;
1356 };
1357
1358 } // namespace seastar