@@ -209,26 +209,16 @@ struct unthrowable_wrapper : error_t<unthrowable_wrapper<ErrorT, ErrorV>> {
209209
210210 class assert_failure {
211211 const char * const msg = nullptr ;
212- std::function<void ()> pre_assert;
213212 public:
214- template <std::size_t N>
215- assert_failure (const char (&msg)[N])
213+ assert_failure (const char * msg)
216214 : msg(msg) {
217215 }
218216 assert_failure () = default ;
219- template <typename Func>
220- assert_failure (Func&& f)
221- : pre_assert(std::forward<Func>(f)) {}
222217
223- no_touch_error_marker operator ()(const unthrowable_wrapper&) {
224- if (pre_assert) {
225- pre_assert ();
226- }
227- if (msg) {
228- ceph_abort (msg);
229- } else {
230- ceph_abort ();
231- }
218+ no_touch_error_marker operator ()(const unthrowable_wrapper& raw_error) {
219+ handle ([this ] (auto && error_v) {
220+ ceph_abort_msgf (" %s: %s" , msg ? msg : " " , error_v.message ().c_str ());
221+ })(raw_error);
232222 return no_touch_error_marker{};
233223 }
234224 };
@@ -299,34 +289,24 @@ struct stateful_error_t : error_t<stateful_error_t<ErrorT>> {
299289
300290 class assert_failure {
301291 const char * const msg = nullptr ;
302- std::function<void (const ErrorT&)> pre_assert;
303292 public:
304- template <std::size_t N>
305- assert_failure (const char (&msg)[N])
293+ assert_failure (const char * msg)
306294 : msg(msg) {
307295 }
308296 assert_failure () = default ;
309- template <typename Func>
310- assert_failure (Func&& f)
311- : pre_assert(std::forward<Func>(f)) {}
312-
313- no_touch_error_marker operator ()(stateful_error_t <ErrorT>&& e) {
314- if (pre_assert) {
315- try {
316- std::rethrow_exception (e.ep );
317- } catch (const ErrorT& err) {
318- pre_assert (err);
319- }
320- }
321- if (msg) {
322- ceph_abort (msg);
323- } else {
324- ceph_abort ();
325- }
297+
298+ no_touch_error_marker operator ()(stateful_error_t <ErrorT>&& raw_error) {
299+ handle ([this ] (auto && error_v) {
300+ ceph_abort_msgf (" %s: %s" , msg ? msg : " " , error_v.message ().c_str ());
301+ })(std::move (raw_error));
326302 return no_touch_error_marker{};
327303 }
328304 };
329305
306+ auto exception_ptr () {
307+ return ep;
308+ }
309+
330310private:
331311 std::exception_ptr ep;
332312
@@ -356,6 +336,10 @@ class maybe_handle_error_t {
356336 ErrorVisitorT errfunc;
357337
358338public:
339+ // NOTE: `__cxa_exception_type()` is an extension of the language.
340+ // It should be available both in GCC and Clang but a fallback
341+ // (based on `std::rethrow_exception()` and `catch`) can be made
342+ // to handle other platforms if necessary.
359343 maybe_handle_error_t (ErrorVisitorT&& errfunc, std::exception_ptr ep)
360344 : type_info(*ep.__cxa_exception_type()),
361345 result (FuturatorT::make_exception_future(std::move(ep))),
@@ -366,11 +350,34 @@ class maybe_handle_error_t {
366350 void handle () {
367351 static_assert (std::is_invocable<ErrorVisitorT, ErrorT>::value,
368352 " provided Error Visitor is not exhaustive" );
353+
354+ // Forbid any error handlers that are returning void.
355+ // See: https://tracker.ceph.com/issues/69406
369356 using return_t = std::invoke_result_t <ErrorVisitorT, ErrorT>;
370357 static_assert (!std::is_same_v<return_t , void >,
371358 " error handlers mustn't return void" );
359+
360+ // The code below checks for exact match only while
361+ // `catch` would allow to match against a base class as well.
362+ // However, this shouldn't be a big issue for `errorator` as
363+ // ErrorVisitorT are already checked for exhaustiveness at compile-time.
364+ // TODO: why/when is this possible?
365+ if (type_info != ErrorT::error_t::get_exception_ptr_type_info ()) {
366+ return ;
367+ }
368+
369+ auto ep = take_exception_from_future ();
370+
371+ // Any assert_* handler we have:
372+ // assert_failure, assert_all and assert_all_func_t
373+ // are expected to return void since we actually abort in them.
374+ // This is why we need a way to diffreciate between them and between
375+ // non-aborting error handlers (e.g handle) - for that we use the dedicated
376+ // label of: no_touch_error_marker. Otherwise we would fail the above
377+ // static assertion.
372378 if constexpr (std::is_same_v<return_t , no_touch_error_marker>) {
373- return ;
379+ std::ignore = std::invoke (std::forward<ErrorVisitorT>(errfunc),
380+ ErrorT::error_t::from_exception_ptr (std::move (ep)));
374381 } else {
375382 // In C++ throwing an exception isn't the sole way to signal
376383 // error with it. This approach nicely fits cold, infrequent cases
@@ -382,34 +389,32 @@ class maybe_handle_error_t {
382389 // pointee's type with `__cxa_exception_type()` instead of costly
383390 // re-throwing (via `std::rethrow_exception()`) and matching with
384391 // `catch`. The limitation here is lack of support for hierarchies
385- // of exceptions. The code below checks for exact match only while
386- // `catch` would allow to match against a base class as well.
387- // However, this shouldn't be a big issue for `errorator` as Error
388- // Visitors are already checked for exhaustiveness at compile-time.
389- //
390- // NOTE: `__cxa_exception_type()` is an extension of the language.
391- // It should be available both in GCC and Clang but a fallback
392- // (based on `std::rethrow_exception()` and `catch`) can be made
393- // to handle other platforms if necessary.
394- if (type_info == ErrorT::error_t::get_exception_ptr_type_info ()) {
395- // set `state::invalid` in internals of `seastar::future` to not
396- // call `report_failed_future()` during `operator=()`.
397- [[maybe_unused]] auto &&ep = std::move (result).get_exception ();
398- if constexpr (std::is_assignable_v<decltype (result), return_t >) {
399- result = std::invoke (std::forward<ErrorVisitorT>(errfunc),
400- ErrorT::error_t::from_exception_ptr (std::move (ep)));
401- } else {
402- result = FuturatorT::invoke (
403- std::forward<ErrorVisitorT>(errfunc),
404- ErrorT::error_t::from_exception_ptr (std::move (ep)));
405- }
392+ // of exceptions.
393+
394+ // TODO: add missing explanation
395+ if constexpr (std::is_assignable_v<decltype (result), return_t >) {
396+ result = std::invoke (std::forward<ErrorVisitorT>(errfunc),
397+ ErrorT::error_t::from_exception_ptr (std::move (ep)));
398+ } else {
399+ result = FuturatorT::invoke (
400+ std::forward<ErrorVisitorT>(errfunc),
401+ ErrorT::error_t::from_exception_ptr (std::move (ep)));
406402 }
407403 }
408404 }
409405
410406 auto get_result () && {
411407 return std::move (result);
412408 }
409+
410+ // seastar::future::get_exception()&& calls take_exception() internally.
411+ // This will result in the future state to be "state::invalid".
412+ // That way when using seastar::future `operator=()`,
413+ // report_failed_future() won't be called.
414+ std::exception_ptr take_exception_from_future () {
415+ auto && ep = std::move (result).get_exception ();
416+ return ep;
417+ }
413418};
414419
415420template <class FuncHead , class ... FuncTail>
@@ -901,10 +906,8 @@ struct errorator {
901906 template <typename ErrorT, EnableIf<ErrorT>...>
902907 decltype (auto ) operator ()(ErrorT&& e) {
903908 using decayed_t = std::decay_t <decltype (e)>;
904- auto && handler =
905- decayed_t::error_t::handle (std::forward<ErrorFunc>(func));
906- static_assert (std::is_invocable_v<decltype (handler), ErrorT>);
907- return std::invoke (std::move (handler), std::forward<ErrorT>(e));
909+ return decayed_t::error_t::handle (std::forward<ErrorFunc>(func))
910+ (std::forward<ErrorT>(e));
908911 }
909912 };
910913
@@ -935,25 +938,22 @@ struct errorator {
935938 return std::move (fut);
936939 }
937940
938- // assert_all{ "TODO" };
939941 class assert_all {
940942 const char * const msg = nullptr ;
941943 public:
942- template <std::size_t N>
943- assert_all (const char (&msg)[N])
944+ assert_all (const char * msg)
944945 : msg(msg) {
945946 }
946947 assert_all () = default ;
947948
948949 template <class ErrorT , EnableIf<ErrorT>...>
949- no_touch_error_marker operator ()(ErrorT&&) {
950- static_assert (contains_once_v<std::decay_t <ErrorT>>,
950+ no_touch_error_marker operator ()(ErrorT&& raw_error) {
951+ using decayed_t = std::decay_t <ErrorT>;
952+ static_assert (contains_once_v<decayed_t >,
951953 " discarding disallowed ErrorT" );
952- if (msg) {
953- ceph_abort_msg (msg);
954- } else {
955- ceph_abort ();
956- }
954+ decayed_t::error_t::handle ([this ] (auto && error_v) {
955+ ceph_abort_msgf (" %s: %s" , msg ? msg : " " , error_v.message ().c_str ());
956+ })(std::forward<ErrorT>(raw_error));
957957 return no_touch_error_marker{};
958958 }
959959 };
@@ -969,8 +969,8 @@ struct errorator {
969969 static_assert (contains_once_v<std::decay_t <ErrorT>>,
970970 " discarding disallowed ErrorT" );
971971 try {
972- std::rethrow_exception (e.ep );
973- } catch (const typename ErrorT::error_type_t & err) {
972+ std::rethrow_exception (e.exception_ptr () );
973+ } catch (const typename std:: decay_t < ErrorT> ::error_type_t & err) {
974974 f (err);
975975 }
976976 ceph_abort ();
@@ -1306,26 +1306,18 @@ namespace ct_error {
13061306
13071307 class assert_all {
13081308 const char * const msg = nullptr ;
1309- std::function<void ()> pre_assert;
13101309 public:
1311- template <std::size_t N>
1312- assert_all (const char (&msg)[N])
1310+ assert_all (const char * msg)
13131311 : msg(msg) {
13141312 }
13151313 assert_all () = default ;
1316- assert_all (std::function<void ()> &&f)
1317- : pre_assert(std::move(f)) {}
13181314
13191315 template <class ErrorT >
1320- no_touch_error_marker operator ()(ErrorT&&) {
1321- if (pre_assert) {
1322- pre_assert ();
1323- }
1324- if (msg) {
1325- ceph_abort (msg);
1326- } else {
1327- ceph_abort ();
1328- }
1316+ no_touch_error_marker operator ()(ErrorT&& raw_error) {
1317+ using decayed_t = std::decay_t <ErrorT>;
1318+ decayed_t::error_t::handle ([this ] (auto && error_v) {
1319+ ceph_abort_msgf (" %s: %s" , msg ? msg : " " , error_v.message ().c_str ());
1320+ })(std::forward<ErrorT>(raw_error));
13291321 return no_touch_error_marker{};
13301322 }
13311323 };
@@ -1336,9 +1328,8 @@ namespace ct_error {
13361328 error_func = std::forward<ErrorFunc>(error_func)
13371329 ] (auto && e) mutable -> decltype (auto ) {
13381330 using decayed_t = std::decay_t <decltype (e)>;
1339- auto && handler =
1340- decayed_t::error_t::handle (std::forward<ErrorFunc>(error_func));
1341- return std::invoke (std::move (handler), std::forward<decltype (e)>(e));
1331+ return decayed_t::error_t::handle (std::forward<ErrorFunc>(error_func))
1332+ (std::forward<decltype (e)>(e));
13421333 };
13431334 };
13441335}
0 commit comments