CXX-3065 guard against use of REQUIRE within APM callbacks #1171
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
Resolves CXX-3065. Verified by this patch.
Followup to #1015:
This PR implements a workaround for such assertions in our test suite.
Exceptions in APM Callbacks
The "[sdam_monitoring]" test cases (two total) can be used to demonstrate the problem by deliberately inverting one of the assertion conditions to trigger assertion failure:
Executing
test_driver -r compact --order rand --rng-seed 0 "[sdam_monitoring]"
produces the following output (slightly edited for brevity):The test suite prematurely terminates after running only the "Heartbeat failed event" test case due to the QoI check added in #1015. The "SDAM Monitoring" test case is not executed.
Exception Guards
A new
bsoncxx/test/exception_guard.hh
component is added (for testing only!) providing a mechanism to store-and-rethrow exceptions thrown within an "exception guard region" to allow for graceful return from APM callback functions while still propagating the thrown exception.This feature is achieved using
std::exception_ptr
:This pattern is abstracted by exception guard macros:
An additional
BSONCXX_TEST_EXCEPTION_GUARD_RESET()
macro is provided to reset the exception guard state for testing and reuse (this is not expected to be used often).The utility includes some additional features such as basic thread-safety and logging of ignored exceptions (only the first exception is caught and checked). See the new test code for a demonstration of its features.
Supporting the storage of multiple exceptions was considered overkill considering the purpose this utility is meant to fulfill (a workaround specifically for exceptions expected to be thrown from APM callbacks). It is not expected to be used for other purposes.
Note
Catch2 is not thread-safe. This utility does not add support thread-safe assertions.
BSONCXX_TEST_EXCEPTION_GUARD_CHECK()
is not strictly necessary (since Catch tracks test case failure internally regardless of how the exception is handled), but helps avoid the unnecessary and noisy execution of further assertions for an already-failed test case. The check also provides an opportunity to log any additionally-thrown exceptions which were ignored by the exception guard.Applying this pattern to the "[sdam_monitoring]" case described above produces the following output:
Importantly, there is no premature termination, and although it is not apparent from this compact output, the other "SDAM Monitoring" test case is executed with success. The QoI check is no longer triggered.
CHECK vs. REQUIRE
In most cases, this new utility is not used. As noted in #1015 (review),
CHECK
is a non-throwing alternative toREQUIRE
which still marks the test case as failed. This PR favors changingREQUIRE*
->CHECK*
when able over using the new exception guard utility, as this is arguably the more "correct" use of APM callbacks (which, again, shouldn't be throwing exceptions).However, in the interest of avoiding significant refactors, this is only done in obvious cases where the assertion does not appear to be guarding code (e.g. a null check prior to dereference). This PR does not attempt to eliminate
REQUIRE
entirely from APM callbacks.Miscellaneous
<bsoncxx/test/catch.hh>
(which definesStringMaker
specializations for our types) instead of directly including the Catch2 header.SYSTEM
to thetarget_include_directories()
command importing mnmlstc/core headers to avoid irrelevant warnings-as-errors.