1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
7 #include <system_error>
9 #include <seastar/core/future-util.hh>
11 #include "include/ceph_assert.h"
15 template<typename Iterator
, typename AsyncAction
>
16 inline auto do_for_each(Iterator begin
, Iterator end
, AsyncAction action
) {
18 ::seastar::futurize
<std::invoke_result_t
<AsyncAction
, decltype(*begin
)>>;
21 return futurator::type::errorator_type::template make_ready_future
<>();
24 auto f
= futurator::invoke(action
, *begin
);
29 if (!f
.available() || seastar::need_preempt()) {
30 return std::move(f
)._then(
31 [ action
= std::move(action
),
32 begin
= std::move(begin
),
35 return ::crimson::do_for_each(std::move(begin
),
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
));
50 template<typename AsyncAction
>
51 inline auto do_until(AsyncAction action
) {
53 typename ::seastar::futurize_t
<std::invoke_result_t
<AsyncAction
>>::errorator_type
;
56 auto f
= ::seastar::futurize_invoke(action
);
58 return errorator_t::template make_exception_future2
<>(
61 } else if (f
.available()) {
62 if (auto done
= f
.get0()) {
63 return errorator_t::template make_ready_future
<>();
66 return std::move(f
)._then(
67 [action
= std::move(action
)] (auto &&done
) mutable {
69 return errorator_t::template make_ready_future
<>();
71 return ::crimson::do_until(
78 // define the interface between error types and errorator
79 template <class ConcreteErrorT
>
81 static constexpr const std::type_info
& get_exception_ptr_type_info() {
82 return ConcreteErrorT::exception_ptr_type_info();
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();
90 decltype(auto) static from_exception_ptr(std::exception_ptr ep
) {
91 return ConcreteErrorT::from_exception_ptr(std::move(ep
));
94 template <class... AllowedErrorsT
>
95 friend struct errorator
;
97 template <class ErrorVisitorT
, class FuturatorT
>
98 friend class maybe_handle_error_t
;
101 template <class Func
>
102 static decltype(auto) handle(Func
&& func
) {
103 return ConcreteErrorT::handle(std::forward
<Func
>(func
));
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
{};
120 static auto handle(Func
&& func
) {
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
);
127 return std::invoke(std::forward
<Func
>(func
));
132 struct pass_further
{
133 decltype(auto) operator()(const unthrowable_wrapper
& e
) {
139 decltype(auto) operator()(const unthrowable_wrapper
&) {
145 // can be used only to initialize the `instance` member
146 explicit unthrowable_wrapper() = default;
148 // implement the errorable interface
149 struct throwable_carrier
{};
150 static std::exception_ptr carrier_instance
;
152 static constexpr const std::type_info
& exception_ptr_type_info() {
153 return typeid(throwable_carrier
);
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
162 return carrier_instance
;
164 static const auto& from_exception_ptr(std::exception_ptr
) {
168 friend class error_t
<unthrowable_wrapper
<ErrorT
, ErrorV
>>;
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
>({});
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
)...)) {
185 static auto handle(Func
&& func
) {
186 static_assert(std::is_invocable_v
<Func
, ErrorT
>);
188 func
= std::forward
<Func
>(func
)
189 ] (stateful_error_t
<ErrorT
>&& e
) mutable -> decltype(auto) {
191 std::rethrow_exception(e
.ep
);
192 } catch (const ErrorT
& obj
) {
193 return std::invoke(std::forward
<Func
>(func
), obj
);
195 ceph_abort_msg("exception type mismatch – impossible!");
200 std::exception_ptr ep
;
202 explicit stateful_error_t(std::exception_ptr ep
) : ep(std::move(ep
)) {}
204 static constexpr const std::type_info
& exception_ptr_type_info() {
205 return typeid(ErrorT
);
207 auto to_exception_ptr() const {
210 static stateful_error_t
<ErrorT
> from_exception_ptr(std::exception_ptr ep
) {
211 return stateful_error_t
<ErrorT
>(std::move(ep
));
214 friend class error_t
<stateful_error_t
<ErrorT
>>;
218 template <class T
> struct always_false
: std::false_type
{};
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
;
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
)) {
234 template <class ErrorT
>
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.
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.
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();
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
)));
273 static_assert(_impl::always_false
<return_t
>::value
,
274 "return of Error Visitor is not assignable to future");
275 // do nothing with `ep`.
280 auto get_result() && {
281 return std::move(result
);
285 template <class FuncHead
, class... FuncTail
>
286 static constexpr auto composer(FuncHead
&& head
, FuncTail
&&... tail
) {
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
...>,
300 return std::invoke(std::move(next
),
301 std::forward
<decltype(args
)>(args
)...);
304 std::is_invocable_v
<FuncHead
, decltype(args
)...> ||
305 (sizeof...(FuncTail
) > 0),
306 "composition is not exhaustive");
311 template <class ValueT
>
312 struct errorated_future_marker
{};
314 template <class... AllowedErrors
>
317 static inline constexpr bool is_error_v
= std::is_base_of_v
<error_t
<T
>, T
>;
319 static_assert((... && is_error_v
<AllowedErrors
>),
320 "errorator expects presence of ::is_error in all error types");
322 template <class ErrorT
>
323 struct contains_once
{
324 static constexpr bool value
=
325 (0 + ... + std::is_same_v
<ErrorT
, AllowedErrors
>) == 1;
327 template <class... Errors
>
328 struct contains_once
<errorator
<Errors
...>> {
329 static constexpr bool value
= (... && contains_once
<Errors
>::value
);
332 static constexpr bool contains_once_v
= contains_once
<T
>::value
;
334 static_assert((... && contains_once_v
<AllowedErrors
>),
335 "no error type in errorator can be duplicated");
337 struct ready_future_marker
{};
338 struct exception_future_marker
{};
341 // see the comment for `using future = _future` below.
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
;
355 // any `seastar::futurize` specialization must be able to access the base.
356 // see : `satisfy_with_result_of()` far below.
358 friend class seastar::futurize
;
360 template <typename T1
, typename T2
, typename
... More
>
361 friend auto seastar::internal::do_with_impl(T1
&& rv1
, T2
&& rv2
, More
&&... more
);
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
<>;
369 template <class FutureT
>
370 struct get_errorator
<FutureT
,
371 std::void_t
<typename
FutureT::errorator_type
>> {
372 using type
= typename
FutureT::errorator_type
;
375 using get_errorator_t
= typename get_errorator
<T
>::type
;
377 template <class ValueFuncErroratorT
, class... ErrorVisitorRetsT
>
378 struct make_errorator
{
379 // NOP. The generic template.
381 template <class... ValueFuncAllowedErrors
,
382 class ErrorVisitorRetsHeadT
,
383 class... ErrorVisitorRetsTailT
>
384 struct make_errorator
<errorator
<ValueFuncAllowedErrors
...>,
385 ErrorVisitorRetsHeadT
,
386 ErrorVisitorRetsTailT
...> {
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
>,
400 using type
= typename make_errorator
<next_errorator
,
401 ErrorVisitorRetsTailT
...>::type
;
403 // finish the recursion
404 template <class... ValueFuncAllowedErrors
>
405 struct make_errorator
<errorator
<ValueFuncAllowedErrors
...>> {
406 using type
= ::crimson::errorator
<ValueFuncAllowedErrors
...>;
408 template <class... Args
>
409 using make_errorator_t
= typename make_errorator
<Args
...>::type
;
411 using base_t::base_t
;
413 template <class Futurator
, class Future
, class ErrorVisitor
>
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()
421 (maybe_handle_error
.template handle
<AllowedErrors
>() , ...);
422 return std::move(maybe_handle_error
).get_result();
426 using errorator_type
= ::crimson::errorator
<AllowedErrors
...>;
427 using promise_type
= seastar::promise
<ValueT
>;
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
;
434 [[gnu::always_inline
]]
435 _future(base_t
&& base
)
436 : base_t(std::move(base
)) {
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
)...)) {
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
))) {
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
))) {
453 template <template <class...> class ErroratedFuture
,
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);
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:
470 // "This is done as if executing the following code:
474 // return std::current_exception();
477 // the "as if" is absolutely crucial because modern GCCs employ optimized
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.
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...
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
>>
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");
504 template <class ValueFuncT
, class ErrorVisitorT
>
505 auto safe_then(ValueFuncT
&& valfunc
, ErrorVisitorT
&& errfunc
) {
506 static_assert((... && std::is_invocable_v
<ErrorVisitorT
,
508 "provided Error Visitor is not exhaustive");
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
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
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
));
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:
558 // if (__builtin_expect(future.failed(), false)) {
559 // ea25: 48 83 bd c8 fe ff ff cmpq $0x2,-0x138(%rbp)
561 // ea2d: 0f 87 f0 05 00 00 ja f023 <ceph::osd::
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::
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());
587 * Only valid within a seastar_thread. Ignores errorator protections
588 * and throws any contained exceptions.
590 * Should really only be used within test code
591 * (see test/crimson/gtest_seastar.h).
593 auto &&unsafe_get() {
594 return seastar::future
<ValueT
>::get();
597 return seastar::future
<ValueT
>::get0();
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
);
617 return std::move(result
);
622 // taking ErrorFuncOne and ErrorFuncTwo separately from ErrorFuncTail
624 template <class ValueFunc
,
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);
632 std::forward
<ValueFunc
>(value_func
),
633 composer(std::forward
<ErrorFuncHead
>(error_func_head
),
634 std::forward
<ErrorFuncTail
>(error_func_tail
)...));
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
{});
643 template <class Func
>
644 void then(Func
&&) = delete;
646 template <class ErrorVisitorT
>
647 auto handle_error(ErrorVisitorT
&& errfunc
) {
648 static_assert((... && std::is_invocable_v
<ErrorVisitorT
,
650 "provided Error Visitor is not exhaustive");
651 using return_errorator_t
= make_errorator_t
<
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
));
663 return typename
futurator_t::type
{ std::move(future
) };
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
)...));
678 // for ::crimson::do_for_each
679 template <class Func
>
680 auto _then(Func
&& func
) {
681 return base_t::then(std::forward
<Func
>(func
));
683 template<typename Iterator
, typename AsyncAction
>
684 friend inline auto ::crimson::do_for_each(Iterator begin
,
688 template<typename AsyncAction
>
689 friend inline auto ::crimson::do_until(AsyncAction action
);
691 template <typename Result
>
692 friend class ::seastar::future
;
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
);
703 template <typename T
>
704 using EnableIf
= typename
std::enable_if
<contains_once_v
<std::decay_t
<T
>>, Enabler
>::type
;
706 template <typename ErrorFunc
>
707 struct all_same_way_t
{
709 all_same_way_t(ErrorFunc
&&error_func
)
710 : func(std::forward
<ErrorFunc
>(error_func
)) {}
712 template <typename ErrorT
, EnableIf
<ErrorT
>...>
713 decltype(auto) operator()(ErrorT
&& e
) {
714 using decayed_t
= std::decay_t
<decltype(e
)>;
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
));
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
>>;
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
);
745 template <class ErrorT
, EnableIf
<ErrorT
>...>
746 void operator()(ErrorT
&&) {
747 static_assert(contains_once_v
<std::decay_t
<ErrorT
>>,
748 "discarding disallowed ErrorT");
752 // assert_all{ "TODO" };
754 const char* const msg
= nullptr;
756 template <std::size_t N
>
757 assert_all(const char (&msg
)[N
])
760 assert_all() = default;
762 template <class ErrorT
, EnableIf
<ErrorT
>...>
763 void operator()(ErrorT
&&) {
764 static_assert(contains_once_v
<std::decay_t
<ErrorT
>>,
765 "discarding disallowed ErrorT");
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
)};
779 // get a new errorator by extending current one with new error
780 template <class... NewAllowedErrorsT
>
781 using extend
= errorator
<AllowedErrors
..., NewAllowedErrorsT
...>;
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
>
788 // 1st: generic NOP template
790 template <class OtherAllowedErrorsHead
,
791 class... OtherAllowedErrorsTail
>
792 struct unify
<errorator
<OtherAllowedErrorsHead
,
793 OtherAllowedErrorsTail
...>> {
795 // 2nd: specialization for errorators with non-empty error set.
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
...>;
807 using type
= typename
step_errorator::template unify
<rest_errorator
>::type
;
809 template <class... EmptyPack
>
810 struct unify
<errorator
<EmptyPack
...>> {
811 // 3rd: recursion finisher
812 static_assert(sizeof...(EmptyPack
) == 0);
813 using type
= errorator
<AllowedErrors
...>;
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
)...);
821 template <typename T
=void>
823 future
<T
> make_exception_future2(std::exception_ptr
&& ex
) noexcept
{
824 return future
<T
>(exception_future_marker(), std::move(ex
));
826 template <typename T
=void>
828 future
<T
> make_exception_future2(seastar::future_state_base
&& state
) noexcept
{
829 return future
<T
>(exception_future_marker(), std::move(state
));
831 template <typename T
=void, typename Exception
>
833 future
<T
> make_exception_future2(Exception
&& ex
) noexcept
{
834 return make_exception_future2
<T
>(std::make_exception_ptr(std::forward
<Exception
>(ex
)));
838 return make_ready_future
<>();
842 template <class T
, class = std::void_t
<T
>>
844 using vanilla_futurize
= seastar::futurize
<T
>;
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
>;
854 struct stored_to_future
<seastar::internal::monostate
, Dummy
> {
855 using type
= future
<>;
860 typename stored_to_future
<typename
vanilla_futurize::value_type
>::type
;
862 template <class Func
, class... Args
>
863 static type
invoke(Func
&& func
, Args
&&... args
) {
865 return vanilla_futurize::invoke(std::forward
<Func
>(func
),
866 std::forward
<Args
>(args
)...);
868 return make_exception_future(std::current_exception());
872 template <class Func
>
873 static type
invoke(Func
&& func
, seastar::internal::monostate
) {
875 return vanilla_futurize::invoke(std::forward
<Func
>(func
));
877 return make_exception_future(std::current_exception());
881 template <typename Arg
>
882 static type
make_exception_future(Arg
&& arg
) {
883 return vanilla_futurize::make_exception_future(std::forward
<Arg
>(arg
));
886 template <template <class...> class ErroratedFutureT
,
888 class futurize
<ErroratedFutureT
<::crimson::errorated_future_marker
<ValueT
>>,
890 typename ErroratedFutureT
<
891 ::crimson::errorated_future_marker
<ValueT
>>::errorator_type
>> {
893 using type
= ::crimson::errorator
<AllowedErrors
...>::future
<ValueT
>;
895 template <class Func
, class... Args
>
896 static type
apply(Func
&& func
, std::tuple
<Args
...>&& args
) {
898 return ::seastar::futurize_apply(std::forward
<Func
>(func
),
899 std::forward
<std::tuple
<Args
...>>(args
));
901 return make_exception_future(std::current_exception());
905 template <class Func
, class... Args
>
906 static type
invoke(Func
&& func
, Args
&&... args
) {
908 return ::seastar::futurize_invoke(std::forward
<Func
>(func
),
909 std::forward
<Args
>(args
)...);
911 return make_exception_future(std::current_exception());
915 template <class Func
>
916 static type
invoke(Func
&& func
, seastar::internal::monostate
) {
918 return ::seastar::futurize_invoke(std::forward
<Func
>(func
));
920 return make_exception_future(std::current_exception());
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
));
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();
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.
943 friend class errorator
;
944 }; // class errorator, generic template
946 // no errors? errorator<>::future is plain seastar::future then!
950 template <class ValueT
>
951 using future
= ::seastar::future
<ValueT
>;
954 using futurize
= ::seastar::futurize
<T
>;
956 // get a new errorator by extending current one with new error
957 template <class... NewAllowedErrors
>
958 using extend
= errorator
<NewAllowedErrors
...>;
960 // errorator with empty error set never contains any error
962 static constexpr bool contains_once_v
= false;
963 }; // class errorator, <> specialization
966 template <class ErroratorOne
,
968 class... FurtherErrators
>
969 struct compound_errorator
{
971 // generic template. Empty `FurtherErrators` are handled by
972 // the specialization below.
973 static_assert(sizeof...(FurtherErrators
) > 0);
975 typename compound_errorator
<ErroratorOne
, ErroratorTwo
>::type
;
979 typename compound_errorator
<step
, FurtherErrators
...>::type
;
981 template <class ErroratorOne
,
983 struct compound_errorator
<ErroratorOne
, ErroratorTwo
> {
984 // specialization for empty `FurtherErrators` arg pack
986 typename
ErroratorOne::template unify
<ErroratorTwo
>::type
;
988 template <class... Args
>
989 using compound_errorator_t
= typename compound_errorator
<Args
...>::type
;
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
);
997 template <std::errc ErrorV
>
998 using ct_error_code
= unthrowable_wrapper
<const std::error_code
&, ec
<ErrorV
>>;
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
>;
1012 ct_error_code
<std::errc::result_out_of_range
>;
1014 ct_error_code
<std::errc::bad_file_descriptor
>;
1016 ct_error_code
<std::errc::no_space_on_device
>;
1017 using value_too_large
= ct_error_code
<std::errc::value_too_large
>;
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
>;
1024 struct pass_further_all
{
1025 template <class ErrorT
>
1026 decltype(auto) operator()(ErrorT
&& e
) {
1027 return std::forward
<ErrorT
>(e
);
1031 struct discard_all
{
1032 template <class ErrorT
>
1033 void operator()(ErrorT
&&) {
1038 const char* const msg
= nullptr;
1040 template <std::size_t N
>
1041 assert_all(const char (&msg
)[N
])
1044 assert_all() = default;
1046 template <class ErrorT
>
1047 void operator()(ErrorT
&&) {
1056 template <class ErrorFunc
>
1057 static decltype(auto) all_same_way(ErrorFunc
&& error_func
) {
1059 error_func
= std::forward
<ErrorFunc
>(error_func
)
1060 ] (auto&& e
) mutable -> decltype(auto) {
1061 using decayed_t
= std::decay_t
<decltype(e
)>;
1063 decayed_t::error_t::handle(std::forward
<ErrorFunc
>(error_func
));
1064 return std::invoke(std::move(handler
), std::forward
<decltype(e
)>(e
));
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
>;
1073 } // namespace crimson
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.
1082 // Container is a placeholder for errorator::_future<> template
1083 template <template <class> class Container
,
1085 struct futurize
<Container
<::crimson::errorated_future_marker
<Value
>>> {
1086 using errorator_type
= typename Container
<
1087 ::crimson::errorated_future_marker
<Value
>>::errorator_type
;
1089 using type
= typename
errorator_type::template future
<Value
>;
1090 using value_type
= seastar::internal::future_stored_type_t
<Value
>;
1092 template<typename Func
, typename
... FuncArgs
>
1093 [[gnu::always_inline
]]
1094 static inline type
invoke(Func
&& func
, FuncArgs
&&... args
) noexcept
{
1096 return func(std::forward
<FuncArgs
>(args
)...);
1098 return make_exception_future(std::current_exception());
1102 template <class Func
>
1103 [[gnu::always_inline
]]
1104 static type
invoke(Func
&& func
, seastar::internal::monostate
) noexcept
{
1108 return make_exception_future(std::current_exception());
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
));
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()`
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
));
1130 template <typename U
>
1131 friend class future
;
1134 template <template <class> class Container
,
1136 struct continuation_base_from_future
<Container
<::crimson::errorated_future_marker
<Value
>>> {
1137 using type
= continuation_base
<Value
>;
1140 } // namespace seastar