leaf: No error_id loaded when catching an non-leaf::exception
This one took a while to track down, but I have narrowed it down to this small snippet. This is admittedly convoluted in appearance, but I was able to run into this behavior on my own with a much more spread-out code.
Here are two test cases that exhibit two related (but unexpected(?)) behaviors:
Error Object is Dropped:
auto fun = [] {
return leaf::try_catch([]() -> std::pair<int, int> {
auto augment = leaf::on_error(info2{1729}); // [1]
leaf::try_catch(
[] {
throw my_exception(12);
},
[](const my_exception& e) {
leaf::current_error().load(info1{42}); // [2]
throw;
});
// unreachable
}, [](const my_exception& e, info1* v1, info2* v2) {
// Return the pair of {info1, info2}
return std::make_pair(v1 ? v1->value : -1, v2 ? v2->value : -1);
});
};
auto pair = fun();
BOOST_TEST_EQ(pair.first, 42);
BOOST_TEST_EQ(pair.second, 1729);
pair = fun();
BOOST_TEST_EQ(pair.first, 42);
BOOST_TEST_EQ(pair.second, 1729);
In this case
- the bare
throw my_exceptiondoes not initialize a newerror_idin the current thread. - The handler will now try to
.load()into the current in-flight error at[2].- Since there is no new
error_idin flight, it will attachinfo1to whatevererror_idjust happened to be loaded in the current thread (Possibly just throwing theinfoaway).
- Since there is no new
- The exception is then re-thrown with a bare
throw;. Still, no newerror_idis generated. - The
augmentobject’s destructor at[1]will detect a new exception in-flight, but also detect that no newerror_idhas been created. It will then callnew_error()(viaerror_monitor::check_id()) and attach aninfo2to that error. The value ofinfo1is now inaccessible to the intended handler immediately below.
Additional quirk: If one moves the on_error object into the innermost throwing-lambda expression, then it’s destructor will call new_error() (as expected!) before the exception is caught and this code will work.
Result differences:
auto fun = [](bool use_leaf_exception) {
return leaf::try_catch([&]() -> std::pair<int, int> {
auto augment = leaf::on_error(info2{1729}); // [1]
leaf::try_catch(
[&] {
if (use_leaf_exception) {
throw leaf::exception(my_exception(12));
} else {
throw my_exception(12);
}
},
[](const my_exception& e) {
leaf::current_error().load(info1{42}); // [2]
throw;
});
// unreachable
}, [](const my_exception& e, info1* v1, info2* v2) {
// Return the pair of {info1, info2}
return std::make_pair(v1 ? v1->value : -1, v2 ? v2->value : -1);
});
};
auto pair = fun(false);
BOOST_TEST_EQ(pair.first, 42);
BOOST_TEST_EQ(pair.second, 1729);
pair = fun(true);
BOOST_TEST_EQ(pair.first, 42);
BOOST_TEST_EQ(pair.second, 1729);
As a side effect of the prior quirk, the result will differ depending on whether leaf::exception() is called. In this case, if use_leaf_exception is true, then the correct result appears, otherwise it will fail
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 15 (6 by maintainers)
@vector-of-bool I’m closing this but feel free to reopen it if you think I’ve missed something.