Skip to content

Commit 7e91398

Browse files
authored
Merge pull request ceph#62699 from Matan-B/wip-matanb-crimson-ignore-abort-v2
crimson/common/errorator: rework aborts error handlers Reviewed-by: Yingxin Cheng <[email protected]>
2 parents 3375baa + a697a18 commit 7e91398

File tree

16 files changed

+240
-209
lines changed

16 files changed

+240
-209
lines changed

src/crimson/common/errorator.h

Lines changed: 81 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
330310
private:
331311
std::exception_ptr ep;
332312

@@ -356,6 +336,10 @@ class maybe_handle_error_t {
356336
ErrorVisitorT errfunc;
357337

358338
public:
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

415420
template <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
}

src/crimson/os/cyanstore/cyan_store.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -194,11 +194,8 @@ class CyanStore final : public FuturizedStore {
194194
return shard_stores.invoke_on_all(
195195
[](auto &local_store) {
196196
return local_store.mount().handle_error(
197-
crimson::stateful_ec::assert_failure([](const auto& ec) {
198-
crimson::get_logger(ceph_subsys_cyanstore).error(
199-
"error mounting cyanstore: ({}) {}",
200-
ec.value(), ec.message());
201-
}));
197+
crimson::stateful_ec::assert_failure(
198+
fmt::format("error mounting cyanstore").c_str()));
202199
});
203200
}
204201

src/crimson/os/seastore/onode_manager/staged-fltree/node.cc

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -428,9 +428,8 @@ eagain_ifuture<Ref<Node>> Node::load_root(context_t c, RootNodeTracker& root_tra
428428
return c.nm.get_super(c.t, root_tracker
429429
).handle_error_interruptible(
430430
eagain_iertr::pass_further{},
431-
crimson::ct_error::input_output_error::assert_failure([FNAME, c] {
432-
ERRORT("EIO during get_super()", c.t);
433-
})
431+
crimson::ct_error::input_output_error::assert_failure(fmt::format(
432+
"{} EIO during get_super()", FNAME).c_str())
434433
).si_then([c, &root_tracker, FNAME](auto&& _super) {
435434
assert(_super);
436435
auto root_addr = _super->get_root_laddr();
@@ -691,26 +690,8 @@ eagain_ifuture<Ref<Node>> Node::load(
691690
return c.nm.read_extent(c.t, addr
692691
).handle_error_interruptible(
693692
eagain_iertr::pass_further{},
694-
crimson::ct_error::input_output_error::assert_failure(
695-
[FNAME, c, addr, expect_is_level_tail] {
696-
ERRORT("EIO -- addr={}, is_level_tail={}",
697-
c.t, addr, expect_is_level_tail);
698-
}),
699-
crimson::ct_error::invarg::assert_failure(
700-
[FNAME, c, addr, expect_is_level_tail] {
701-
ERRORT("EINVAL -- addr={}, is_level_tail={}",
702-
c.t, addr, expect_is_level_tail);
703-
}),
704-
crimson::ct_error::enoent::assert_failure(
705-
[FNAME, c, addr, expect_is_level_tail] {
706-
ERRORT("ENOENT -- addr={}, is_level_tail={}",
707-
c.t, addr, expect_is_level_tail);
708-
}),
709-
crimson::ct_error::erange::assert_failure(
710-
[FNAME, c, addr, expect_is_level_tail] {
711-
ERRORT("ERANGE -- addr={}, is_level_tail={}",
712-
c.t, addr, expect_is_level_tail);
713-
})
693+
crimson::ct_error::assert_all(fmt::format(
694+
"{} -- addr={}, is_level_tail={}", FNAME, addr, expect_is_level_tail).c_str())
714695
).si_then([FNAME, c, addr, expect_is_level_tail](auto extent)
715696
-> eagain_ifuture<Ref<Node>> {
716697
assert(extent);
@@ -2145,9 +2126,8 @@ eagain_ifuture<Ref<LeafNode>> LeafNode::allocate_root(
21452126
return c.nm.get_super(c.t, root_tracker
21462127
).handle_error_interruptible(
21472128
eagain_iertr::pass_further{},
2148-
crimson::ct_error::input_output_error::assert_failure([FNAME, c] {
2149-
ERRORT("EIO during get_super()", c.t);
2150-
})
2129+
crimson::ct_error::input_output_error::assert_failure(fmt::format(
2130+
"{} EIO during get_super()", FNAME).c_str())
21512131
).si_then([c, root](auto&& super) {
21522132
assert(super);
21532133
root->make_root_new(c, std::move(super));

0 commit comments

Comments
 (0)