1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
2 // vim: ts=8 sw=2 smarttab expandtab
7 #include <system_error>
9 #include <seastar/core/future-util.hh>
11 #include "crimson/common/utility.h"
12 #include "include/ceph_assert.h"
14 namespace crimson::interruptible
{
16 template <typename
, typename
>
17 class parallel_for_each_state
;
19 template <typename
, typename
>
20 class interruptible_future_detail
;
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
<> {
32 seastar::promise
<> _pr
;
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);
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
));
46 while (_begin
!= _end
) {
47 auto f
= seastar::futurize_invoke(_action
, *_begin
);
50 f
._forward_to(std::move(_pr
));
53 if (!f
.available() || seastar::need_preempt()) {
55 seastar::internal::set_callback(std::move(f
), this);
62 task
* waiting_task() noexcept override
{
63 return _pr
.waiting_task();
65 FutureT
get_future() {
66 return _pr
.get_future();
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
);
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();
86 return seastar::make_ready_future
<>();
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
));
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
));
99 template<typename AsyncAction
>
100 inline auto repeat(AsyncAction action
) {
102 typename ::seastar::futurize_t
<std::invoke_result_t
<AsyncAction
>>::errorator_type
;
105 auto f
= ::seastar::futurize_invoke(action
);
107 return errorator_t::template make_exception_future2
<>(
110 } else if (f
.available()) {
111 if (auto done
= f
.get0()) {
112 return errorator_t::template make_ready_future
<>();
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
<>();
120 return ::crimson::repeat(
127 // define the interface between error types and errorator
128 template <class ConcreteErrorT
>
130 static constexpr const std::type_info
& get_exception_ptr_type_info() {
131 return ConcreteErrorT::exception_ptr_type_info();
134 decltype(auto) static from_exception_ptr(std::exception_ptr ep
) {
135 return ConcreteErrorT::from_exception_ptr(std::move(ep
));
138 template <class... AllowedErrorsT
>
139 friend struct errorator
;
141 template <class ErrorVisitorT
, class FuturatorT
>
142 friend class maybe_handle_error_t
;
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();
151 template <class Func
>
152 static decltype(auto) handle(Func
&& func
) {
153 return ConcreteErrorT::handle(std::forward
<Func
>(func
));
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
{};
169 static auto exception_ptr() {
170 return make().to_exception_ptr();
174 static auto handle(Func
&& func
) {
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
),
183 std::move(raw_error
));
184 } else if constexpr (std::is_invocable_v
<Func
, ErrorT
>) {
185 return std::invoke(std::forward
<Func
>(func
), ErrorV
);
187 return std::invoke(std::forward
<Func
>(func
));
192 struct pass_further
{
193 decltype(auto) operator()(const unthrowable_wrapper
& e
) {
199 decltype(auto) operator()(const unthrowable_wrapper
&) {
205 // can be used only to initialize the `instance` member
206 explicit unthrowable_wrapper() = default;
208 // implement the errorable interface
209 struct throwable_carrier
{};
210 static std::exception_ptr carrier_instance
;
212 static constexpr const std::type_info
& exception_ptr_type_info() {
213 return typeid(throwable_carrier
);
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
222 return carrier_instance
;
224 static const auto& from_exception_ptr(std::exception_ptr
) {
228 friend class error_t
<unthrowable_wrapper
<ErrorT
, ErrorV
>>;
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
>({});
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
)...)) {
245 static auto handle(Func
&& func
) {
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
));
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
);
261 ceph_abort_msg("exception type mismatch -- impossible!");
266 std::exception_ptr ep
;
268 explicit stateful_error_t(std::exception_ptr ep
) : ep(std::move(ep
)) {}
270 static constexpr const std::type_info
& exception_ptr_type_info() {
271 return typeid(ErrorT
);
273 auto to_exception_ptr() const {
276 static stateful_error_t
<ErrorT
> from_exception_ptr(std::exception_ptr ep
) {
277 return stateful_error_t
<ErrorT
>(std::move(ep
));
280 friend class error_t
<stateful_error_t
<ErrorT
>>;
284 template <class T
> struct always_false
: std::false_type
{};
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
;
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
)) {
300 template <class ErrorT
>
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.
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.
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();
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
))));
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
))));
353 auto get_result() && {
354 return std::move(result
);
358 template <class FuncHead
, class... FuncTail
>
359 static constexpr auto composer(FuncHead
&& head
, FuncTail
&&... tail
) {
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
...>,
373 return std::invoke(std::move(next
),
374 std::forward
<decltype(args
)>(args
)...);
377 std::is_invocable_v
<FuncHead
, decltype(args
)...> ||
378 (sizeof...(FuncTail
) > 0),
379 "composition is not exhaustive");
384 template <class ValueT
>
385 struct errorated_future_marker
{};
387 template <class... AllowedErrors
>
388 class parallel_for_each_state
;
391 static inline constexpr bool is_error_v
= std::is_base_of_v
<error_t
<T
>, T
>;
393 template <typename
... AllowedErrors
>
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
;
400 template <class... AllowedErrors
>
403 static_assert((... && is_error_v
<AllowedErrors
>),
404 "errorator expects presence of ::is_error in all error types");
406 template <class ErrorT
>
407 struct contains_once
{
408 static constexpr bool value
=
409 (0 + ... + std::is_same_v
<ErrorT
, AllowedErrors
>) == 1;
411 template <class... Errors
>
412 struct contains_once
<errorator
<Errors
...>> {
413 static constexpr bool value
= (... && contains_once
<Errors
>::value
);
416 static constexpr bool contains_once_v
= contains_once
<T
>::value
;
418 static_assert((... && contains_once_v
<AllowedErrors
>),
419 "no error type in errorator can be duplicated");
421 struct ready_future_marker
{};
422 struct exception_future_marker
{};
425 // see the comment for `using future = _future` below.
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
;
439 // any `seastar::futurize` specialization must be able to access the base.
440 // see : `satisfy_with_result_of()` far below.
442 friend struct seastar::futurize
;
444 template <typename T1
, typename T2
, typename
... More
>
445 friend auto seastar::internal::do_with_impl(T1
&& rv1
, T2
&& rv2
, More
&&... more
);
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
<>;
453 template <class FutureT
>
454 struct get_errorator
<FutureT
,
455 std::void_t
<typename
FutureT::errorator_type
>> {
456 using type
= typename
FutureT::errorator_type
;
459 using get_errorator_t
= typename get_errorator
<T
>::type
;
461 template <class ValueFuncErroratorT
, class... ErrorVisitorRetsT
>
462 struct make_errorator
{
463 // NOP. The generic template.
465 template <class... ValueFuncAllowedErrors
,
466 class ErrorVisitorRetsHeadT
,
467 class... ErrorVisitorRetsTailT
>
468 struct make_errorator
<errorator
<ValueFuncAllowedErrors
...>,
469 ErrorVisitorRetsHeadT
,
470 ErrorVisitorRetsTailT
...> {
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
>,
482 using maybe_head_ertr
= get_errorator_t
<ErrorVisitorRetsHeadT
>;
483 using next_errorator
=
484 typename
_next_errorator::template extend_ertr
<maybe_head_ertr
>;
487 using type
= typename make_errorator
<next_errorator
,
488 ErrorVisitorRetsTailT
...>::type
;
490 // finish the recursion
491 template <class... ValueFuncAllowedErrors
>
492 struct make_errorator
<errorator
<ValueFuncAllowedErrors
...>> {
493 using type
= ::crimson::errorator
<ValueFuncAllowedErrors
...>;
495 template <class... Args
>
496 using make_errorator_t
= typename make_errorator
<Args
...>::type
;
498 using base_t::base_t
;
500 template <class Futurator
, class Future
, class ErrorVisitor
>
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()
508 (maybe_handle_error
.template handle
<AllowedErrors
>() , ...);
509 return std::move(maybe_handle_error
).get_result();
513 using base_t::get_exception
;
515 using errorator_type
= ::crimson::errorator
<AllowedErrors
...>;
516 using promise_type
= seastar::promise
<ValueT
>;
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
;
523 [[gnu::always_inline
]]
524 _future(base_t
&& base
)
525 : base_t(std::move(base
)) {
528 base_t
to_base() && {
529 return std::move(*this);
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
)...)) {
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
))) {
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
))) {
546 template <template <class...> class ErroratedFuture
,
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);
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:
563 // "This is done as if executing the following code:
567 // return std::current_exception();
570 // the "as if" is absolutely crucial because modern GCCs employ optimized
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.
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...
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
>>
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");
597 template <class ValueFuncT
, class ErrorVisitorT
>
598 auto safe_then(ValueFuncT
&& valfunc
, ErrorVisitorT
&& errfunc
) {
599 static_assert((... && std::is_invocable_v
<ErrorVisitorT
,
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
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
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
));
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:
653 // if (__builtin_expect(future.failed(), false)) {
654 // ea25: 48 83 bd c8 fe ff ff cmpq $0x2,-0x138(%rbp)
656 // ea2d: 0f 87 f0 05 00 00 ja f023 <ceph::osd::
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::
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());
682 * Only valid within a seastar_thread. Ignores errorator protections
683 * and throws any contained exceptions.
685 * Should really only be used within test code
686 * (see test/crimson/gtest_seastar.h).
688 auto &&unsafe_get() {
689 return seastar::future
<ValueT
>::get();
692 return seastar::future
<ValueT
>::get0();
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
);
712 return std::move(result
);
717 _future
<::crimson::errorated_future_marker
<void>>
718 discard_result() noexcept
{
719 return safe_then([](auto&&) {});
722 // taking ErrorFuncOne and ErrorFuncTwo separately from ErrorFuncTail
724 template <class ValueFunc
,
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);
732 std::forward
<ValueFunc
>(value_func
),
733 composer(std::forward
<ErrorFuncHead
>(error_func_head
),
734 std::forward
<ErrorFuncTail
>(error_func_tail
)...));
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
{});
743 template <class ValueFunc
,
745 auto safe_then_unpack(ValueFunc
&& value_func
,
746 ErrorFuncs
&&... error_funcs
) {
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
));
752 std::forward
<ErrorFuncs
>(error_funcs
)...
756 template <class Func
>
757 void then(Func
&&) = delete;
759 template <class ErrorVisitorT
>
760 auto handle_error(ErrorVisitorT
&& errfunc
) {
761 static_assert((... && std::is_invocable_v
<ErrorVisitorT
,
763 "provided Error Visitor is not exhaustive");
764 using return_errorator_t
= make_errorator_t
<
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
));
776 return typename
futurator_t::type
{ std::move(future
) };
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
)...));
792 // for ::crimson::do_for_each
793 template <class Func
>
794 auto _then(Func
&& func
) {
795 return base_t::then(std::forward
<Func
>(func
));
798 auto _forward_to(T
&& pr
) {
799 return base_t::forward_to(std::forward
<T
>(pr
));
801 template<typename Iterator
, typename AsyncAction
>
802 friend inline auto ::crimson::do_for_each(Iterator begin
,
806 template <typename Iterator
, typename AsyncAction
, typename FutureT
>
807 friend class ::crimson::do_for_each_state
;
809 template<typename AsyncAction
>
810 friend inline auto ::crimson::repeat(AsyncAction action
);
812 template <typename Result
>
813 friend class ::seastar::future
;
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
;
829 template <typename T
>
830 using EnableIf
= typename
std::enable_if
<contains_once_v
<std::decay_t
<T
>>, Enabler
>::type
;
832 template <typename ErrorFunc
>
833 struct all_same_way_t
{
835 all_same_way_t(ErrorFunc
&&error_func
)
836 : func(std::forward
<ErrorFunc
>(error_func
)) {}
838 template <typename ErrorT
, EnableIf
<ErrorT
>...>
839 decltype(auto) operator()(ErrorT
&& e
) {
840 using decayed_t
= std::decay_t
<decltype(e
)>;
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
));
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
>>;
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
);
871 template <class ErrorT
, EnableIf
<ErrorT
>...>
872 void operator()(ErrorT
&&) {
873 static_assert(contains_once_v
<std::decay_t
<ErrorT
>>,
874 "discarding disallowed ErrorT");
878 template <typename T
>
879 static future
<T
> make_errorator_future(seastar::future
<T
>&& fut
) {
880 return std::move(fut
);
883 // assert_all{ "TODO" };
885 const char* const msg
= nullptr;
887 template <std::size_t N
>
888 assert_all(const char (&msg
)[N
])
891 assert_all() = default;
893 template <class ErrorT
, EnableIf
<ErrorT
>...>
894 void operator()(ErrorT
&&) {
895 static_assert(contains_once_v
<std::decay_t
<ErrorT
>>,
896 "discarding disallowed ErrorT");
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
)};
910 // get a new errorator by extending current one with new errors
911 template <class... NewAllowedErrorsT
>
912 using extend
= errorator
<AllowedErrors
..., NewAllowedErrorsT
...>;
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
>
919 // 1st: generic NOP template
921 template <class OtherAllowedErrorsHead
,
922 class... OtherAllowedErrorsTail
>
923 struct unify
<errorator
<OtherAllowedErrorsHead
,
924 OtherAllowedErrorsTail
...>> {
926 // 2nd: specialization for errorators with non-empty error set.
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
...>;
938 using type
= typename
step_errorator::template unify
<rest_errorator
>::type
;
940 template <class... EmptyPack
>
941 struct unify
<errorator
<EmptyPack
...>> {
942 // 3rd: recursion finisher
943 static_assert(sizeof...(EmptyPack
) == 0);
944 using type
= errorator
<AllowedErrors
...>;
947 // get a new errorator by extending current one with another errorator
949 using extend_ertr
= typename unify
<E
>::type
;
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
)...);
956 template <typename T
=void>
958 future
<T
> make_exception_future2(std::exception_ptr
&& ex
) noexcept
{
959 return future
<T
>(exception_future_marker(), std::move(ex
));
961 template <typename T
=void>
963 future
<T
> make_exception_future2(seastar::future_state_base
&& state
) noexcept
{
964 return future
<T
>(exception_future_marker(), std::move(state
));
966 template <typename T
=void, typename Exception
>
968 future
<T
> make_exception_future2(Exception
&& ex
) noexcept
{
969 return make_exception_future2
<T
>(std::make_exception_ptr(std::forward
<Exception
>(ex
)));
973 return make_ready_future
<>();
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
),
981 std::forward
<Func
>(func
));
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
...>(
990 std::forward
<Func
>(func
));
995 using vanilla_futurize
= seastar::futurize
<T
>;
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
>;
1004 template <int Dummy
>
1005 struct stored_to_future
<seastar::internal::monostate
, Dummy
> {
1006 using type
= future
<>;
1011 typename stored_to_future
<typename
vanilla_futurize::value_type
>::type
;
1013 template <class Func
, class... Args
>
1014 static type
invoke(Func
&& func
, Args
&&... args
) {
1016 return vanilla_futurize::invoke(std::forward
<Func
>(func
),
1017 std::forward
<Args
>(args
)...);
1019 return make_exception_future(std::current_exception());
1023 template <class Func
>
1024 static type
invoke(Func
&& func
, seastar::internal::monostate
) {
1026 return vanilla_futurize::invoke(std::forward
<Func
>(func
));
1028 return make_exception_future(std::current_exception());
1032 template <typename Arg
>
1033 static type
make_exception_future(Arg
&& arg
) {
1034 return vanilla_futurize::make_exception_future(std::forward
<Arg
>(arg
));
1037 template <template <class...> class ErroratedFutureT
,
1039 class futurize
<ErroratedFutureT
<::crimson::errorated_future_marker
<ValueT
>>> {
1041 using type
= ::crimson::errorator
<AllowedErrors
...>::future
<ValueT
>;
1043 template <class Func
, class... Args
>
1044 static type
invoke(Func
&& func
, Args
&&... args
) {
1046 return ::seastar::futurize_invoke(std::forward
<Func
>(func
),
1047 std::forward
<Args
>(args
)...);
1049 return make_exception_future(std::current_exception());
1053 template <class Func
>
1054 static type
invoke(Func
&& func
, seastar::internal::monostate
) {
1056 return ::seastar::futurize_invoke(std::forward
<Func
>(func
));
1058 return make_exception_future(std::current_exception());
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
));
1068 template <typename InterruptCond
, typename FutureType
>
1070 ::crimson::interruptible::interruptible_future_detail
<
1071 InterruptCond
, FutureType
>> {
1073 using type
= ::crimson::interruptible::interruptible_future_detail
<
1074 InterruptCond
, typename futurize
<FutureType
>::type
>;
1076 template <typename Func
, typename
... Args
>
1077 static type
invoke(Func
&& func
, Args
&&... args
) {
1079 return ::seastar::futurize_invoke(std::forward
<Func
>(func
),
1080 std::forward
<Args
>(args
)...);
1082 return seastar::futurize
<
1083 ::crimson::interruptible::interruptible_future_detail
<
1084 InterruptCond
, FutureType
>>::make_exception_future(
1085 std::current_exception());
1088 template <typename Func
>
1089 static type
invoke(Func
&& func
, seastar::internal::monostate
) {
1091 return ::seastar::futurize_invoke(std::forward
<Func
>(func
));
1093 return seastar::futurize
<
1094 ::crimson::interruptible::interruptible_future_detail
<
1095 InterruptCond
, FutureType
>>::make_exception_future(
1096 std::current_exception());
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
));
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();
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.
1121 friend class errorator
;
1122 template<typename
, typename
>
1123 friend class ::crimson::interruptible::interruptible_future_detail
;
1124 }; // class errorator, generic template
1126 // no errors? errorator<>::future is plain seastar::future then!
1130 template <class ValueT
=void>
1131 using future
= ::seastar::futurize_t
<ValueT
>;
1134 using futurize
= ::seastar::futurize
<T
>;
1136 // get a new errorator by extending current one with errors
1137 template <class... NewAllowedErrors
>
1138 using extend
= errorator
<NewAllowedErrors
...>;
1140 // get a new errorator by extending current one with another errorator
1142 using extend_ertr
= E
;
1144 // errorator with empty error set never contains any error
1146 static constexpr bool contains_once_v
= false;
1147 }; // class errorator, <> specialization
1150 template <class ErroratorOne
,
1152 class... FurtherErrators
>
1153 struct compound_errorator
{
1155 // generic template. Empty `FurtherErrators` are handled by
1156 // the specialization below.
1157 static_assert(sizeof...(FurtherErrators
) > 0);
1159 typename compound_errorator
<ErroratorOne
, ErroratorTwo
>::type
;
1163 typename compound_errorator
<step
, FurtherErrators
...>::type
;
1165 template <class ErroratorOne
,
1167 struct compound_errorator
<ErroratorOne
, ErroratorTwo
> {
1168 // specialization for empty `FurtherErrators` arg pack
1170 typename
ErroratorOne::template unify
<ErroratorTwo
>::type
;
1172 template <class... Args
>
1173 using compound_errorator_t
= typename compound_errorator
<Args
...>::type
;
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());
1181 template <int ErrorV
>
1182 using ct_error_code
= unthrowable_wrapper
<const std::error_code
&, ec
<ErrorV
>>;
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
)>;
1196 ct_error_code
<static_cast<int>(std::errc::result_out_of_range
)>;
1198 ct_error_code
<static_cast<int>(std::errc::bad_file_descriptor
)>;
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
)>;
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
)>;
1216 struct pass_further_all
{
1217 template <class ErrorT
>
1218 decltype(auto) operator()(ErrorT
&& e
) {
1219 return std::forward
<ErrorT
>(e
);
1223 struct discard_all
{
1224 template <class ErrorT
>
1225 void operator()(ErrorT
&&) {
1230 const char* const msg
= nullptr;
1232 template <std::size_t N
>
1233 assert_all(const char (&msg
)[N
])
1236 assert_all() = default;
1238 template <class ErrorT
>
1239 void operator()(ErrorT
&&) {
1248 template <class ErrorFunc
>
1249 static decltype(auto) all_same_way(ErrorFunc
&& error_func
) {
1251 error_func
= std::forward
<ErrorFunc
>(error_func
)
1252 ] (auto&& e
) mutable -> decltype(auto) {
1253 using decayed_t
= std::decay_t
<decltype(e
)>;
1255 decayed_t::error_t::handle(std::forward
<ErrorFunc
>(error_func
));
1256 return std::invoke(std::move(handler
), std::forward
<decltype(e
)>(e
));
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
>;
1265 template <typename F
>
1266 struct is_errorated_future
{
1267 static constexpr bool value
= false;
1269 template <template <class...> class ErroratedFutureT
,
1271 struct is_errorated_future
<
1272 ErroratedFutureT
<::crimson::errorated_future_marker
<ValueT
>>
1274 static constexpr bool value
= true;
1276 template <typename T
>
1277 constexpr bool is_errorated_future_v
= is_errorated_future
<T
>::value
;
1279 } // namespace crimson
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.
1288 // Container is a placeholder for errorator::_future<> template
1289 template <template <class> class Container
,
1291 struct futurize
<Container
<::crimson::errorated_future_marker
<Value
>>> {
1292 using errorator_type
= typename Container
<
1293 ::crimson::errorated_future_marker
<Value
>>::errorator_type
;
1295 using type
= typename
errorator_type::template future
<Value
>;
1296 using value_type
= seastar::internal::future_stored_type_t
<Value
>;
1298 template<typename Func
, typename
... FuncArgs
>
1299 [[gnu::always_inline
]]
1300 static type
apply(Func
&& func
, std::tuple
<FuncArgs
...>&& args
) noexcept
{
1303 std::forward
<Func
>(func
),
1304 std::forward
<std::tuple
<FuncArgs
...>>(args
));
1306 return make_exception_future(std::current_exception());
1310 template<typename Func
, typename
... FuncArgs
>
1311 [[gnu::always_inline
]]
1312 static inline type
invoke(Func
&& func
, FuncArgs
&&... args
) noexcept
{
1314 return func(std::forward
<FuncArgs
>(args
)...);
1316 return make_exception_future(std::current_exception());
1320 template <class Func
>
1321 [[gnu::always_inline
]]
1322 static type
invoke(Func
&& func
, seastar::internal::monostate
) noexcept
{
1326 return make_exception_future(std::current_exception());
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
));
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()`
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
));
1348 template <typename U
>
1349 friend class future
;
1352 template <template <class> class Container
,
1354 struct continuation_base_from_future
<Container
<::crimson::errorated_future_marker
<Value
>>> {
1355 using type
= continuation_base
<Value
>;
1358 } // namespace seastar