-
Notifications
You must be signed in to change notification settings - Fork 547
CXX-3320 migrate instance and logger to mongocxx::v1 #1469
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
instance& operator=(instance const&) = delete; | ||
|
||
/// | ||
/// Initialize the mongoc library with unstructured log messages disabled. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated documentation to clarify that this all applies to "unstructured log handling" to leave open the design space for "structured log handling" support by the C++ Driver.
Documentation also increased references to specific mongoc API to clarify behavior + defer much of the behavioral documentation to mongoc.
/// | ||
/// This class is not moveable. | ||
/// | ||
instance(instance&&) = delete; | ||
|
||
/// | ||
/// This class is not moveable. | ||
/// | ||
instance& operator=(instance&&) = delete; | ||
|
||
/// | ||
/// This class is not copyable. | ||
/// | ||
instance(instance const&) = delete; | ||
|
||
/// | ||
/// This class is not copyable. | ||
/// | ||
instance& operator=(instance const&) = delete; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Relative breaking change: instance
is made immovable as well as uncopyable. Allowing instance
to be moved-from does not seem to be well-motivated.
mongoc_log_level_t log_level, | ||
char const* domain, | ||
char const* message, | ||
void* user_data) noexcept { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small relative change: added noexcept
for consistency with other C API callback functions.
"mongocxx", | ||
MONGOCXX_VERSION_STRING, | ||
"CXX=" MONGOCXX_COMPILER_ID " " MONGOCXX_COMPILER_VERSION " stdcxx=" STDCXX " / "); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor optimization: avoid allocations, given all fields are literals, by concatenating everything into a single string literal.
src/mongocxx/test/subprocess.cpp
Outdated
// Use `std::exit()` and `std::terminate()` to prevent continued execution of the Catch2 test suite. | ||
try { | ||
fn(); | ||
std::exit(EXIT_SUCCESS); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you checked that these uses of exit
are correct in the child process? Usually, with a fork()
without exec()
, you want to use _Exit
instead of exit()
to prevent cleanup handlers from executing in the weird intermediate state of the child process. (Using _Exit
may or may not also avoid Valgrind weirdness.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Usually, with a
fork()
withoutexec()
, you want to use_Exit
instead ofexit()
Good call. Converted std::exit()
into std::_Exit()
within the subprocess.
Using
_Exit
may or may not also avoid Valgrind weirdness.
Alas, this doesn't entirely address Valgrind weirdness, as the abnormal termination of a subprocess still produces noisy diagnostic messages due to "still reachable" allocations in its exit summary (one for each terminated subprocess). There doesn't seem to be a straightforward way to suppress the diagnostic messages of the subprocesses only while still performing at-exit leak checks. Given the limited use of this pattern, I think I would prefer defer memchecking of subprocesses to ASAN/UBSAN task coverage instead and keep the --trace-children=no
Valgrind flag for test_instance
(fwiw it doesn't apply to any other test executables).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with minor comments. I like the new overload to simplify enabling default logging.
how important are log messages which may be emitted by
mongoc_init()
?
I see two possible logs:
I expect neither is likely. (2) might be improved by making handshake non-global (CDRIVER-4142) (errors could be returned on the first client operation).
For backward compatibility,
v_noabi::instance
's custom log handler constructor preserves the "register handler aftermongoc_init()
" behavior.
If it helps simplify: I expect having v_noabi
register the log handler before mongoc_init
(to match v1
) is unlikely to harm consumers (some added unlikely-to-occur logs)
src/mongocxx/test/subprocess.hh
Outdated
// @returns The exit code of the subprocess (`*is_signal` is `false`) or the signal used to kill the subprocess | ||
// (`*is_signal` is `true`) . |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// @returns The exit code of the subprocess (`*is_signal` is `false`) or the signal used to kill the subprocess | |
// (`*is_signal` is `true`) . | |
// @returns The exit code of the subprocess (`*is_signal_ptr` is `false`) or the signal used to kill the subprocess | |
// (`*is_signal_ptr` is `true`) . |
src/mongocxx/test/subprocess.cpp
Outdated
CHECK_SUBPROCESS([] { | ||
// Try to silence noisy Catch2 output. | ||
(void)::close(1); // stdout | ||
(void)::close(2); // stderr | ||
|
||
SKIP("subprocess"); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CHECK_SUBPROCESS([] { | |
// Try to silence noisy Catch2 output. | |
(void)::close(1); // stdout | |
(void)::close(2); // stderr | |
SKIP("subprocess"); | |
}); | |
CHECK_SUBPROCESS([] { | |
// Try to silence noisy Catch2 output. | |
(void)::close(1); // stdout | |
(void)::close(2); // stderr | |
SKIP("subprocess"); | |
}, &is_signal); |
src/mongocxx/test/v1/logger.cpp
Outdated
|
||
} // namespace | ||
|
||
// mongocxx::v1::logger must not unnecessarily mpose special requirements on derived classes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// mongocxx::v1::logger must not unnecessarily mpose special requirements on derived classes. | |
// mongocxx::v1::logger must not unnecessarily impose special requirements on derived classes. |
/// | ||
/// Cleanup the mongocxx (and mongoc) library. | ||
/// | ||
/// Calls [`mongoc_init()`](https://mongoc.org/libmongoc/current/mongoc_cleanup.html). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Calls [`mongoc_init()`](https://mongoc.org/libmongoc/current/mongoc_cleanup.html). | |
/// Calls [`mongoc_cleanup()`](https://mongoc.org/libmongoc/current/mongoc_cleanup.html). |
static_assert(!std::is_move_constructible<instance>::value, "bsoncxx::v1::instance must be non-moveable"); | ||
static_assert(!std::is_copy_constructible<instance>::value, "bsoncxx::v1::instance must be non-copyable"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static_assert(!std::is_move_constructible<instance>::value, "bsoncxx::v1::instance must be non-moveable"); | |
static_assert(!std::is_copy_constructible<instance>::value, "bsoncxx::v1::instance must be non-copyable"); | |
static_assert(!std::is_move_constructible<instance>::value, "mongocxx::v1::instance must be non-moveable"); | |
static_assert(!std::is_copy_constructible<instance>::value, "mongocxx::v1::instance must be non-copyable"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it helps simplify: I expect having v_noabi register the log handler before mongoc_init (to match v1) is unlikely to harm consumers (some added unlikely-to-occur logs)
Simplified the v_noabi implementation as suggested.
|
||
// Inform the user that a custom log handler has been registered. | ||
// Cannot use mocked `libmongoc::log()` due to varargs. | ||
mongoc_log(MONGOC_LOG_LEVEL_INFO, "mongocxx", "libmongoc logging callback enabled"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to keep this informational log message?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am slightly in favor of keeping for v_noabi
. The API examples expect it, and it might avoid a surprise to consumers. I expect it is not much burden for us to keep it in v_noabi
. But I do not think it is needed for v1
.
Related to CXX-3320. Due to the unique nature of
mongocxx::v_noabi::instance
as a global singleton-like object, this class is migrated in full fromv_noabi
tov1
as a single, standalone PR. Related typesv_noabi::logger
andv_noabi::exception
are also minimally migrated as part of this PR to support theinstance
class API.First, concerning
v_noabi::logger
: due to having no observable difference in behavior inv1
,v_noabi::logger
is merely a redeclaration ofv1::logger
in thev_noabi
namespace! 🎉 This also avoids complications with trying to support compatibility between both av_noabi::logger
andv1::logger
class given their polymorphic nature.The
v_noabi::instance
class is not so fortunate. Its migration is complicated primary by the realization that there are two unresolvable deficiencies with thev_noabi::instance
API:mongoc_init()
has already been invoked.As mentioned by mongoc docs:
That is, the initialization behavior is currently defined as follows (pseudocode):
The
v1::instance
object proposes fixing these issues as follows:v1::default_handler
tag type by which the user can request using the mongoc default log handler.mongoc_init()
so that all log messages are handled properly.That is, the initialization behavior is changed to the following:
For backward compatibility,Update: per feedback, simplified the v_noabi implementation to also register the custom unstructured log handler before mongoc initialization.v_noabi::instance
's custom log handler constructor preserves the "register handler aftermongoc_init()
" behavior. If we do not consider this behavior to be worth preserving (how important are log messages which may be emitted bymongoc_init()
?), we can simplify the implementation ofv_noabi::instance
's initialization. (Related: CXX-1029)The
v_noabi::instance
still supports the (deprecated)::instance()
helper function for backward compatibility. The implementation of::instance()
remains specific tov_noabi
: the implementationv1::instance
is entirely unaware of::instance()
support. In place of thecurrent_instance
"sentinel" value,v1::instance
instead utilizes a simplestd::atomic_int
counter to track and report exceptions given multiple instance objects.Much of the rest of this PR involves refactors to the test suite for the
instance
andlogger
classes. Thetest_logging
executable is removed in favor of grouping allinstance
object testing totest_instance
. All other test executables can (in theory) be grouped into a single test executable without concern for "multiple instance object" exceptions.The fork-based pattern to test
instance
objects used by the examples API runner (#1216) is repurposed here for compatibility with the Catch2 test suite asmongocxx::test::subprocess(op)
. This helper function accepts a singleop: void()
invocable that is executed within a forked subprocess. The result of the subprocess, including termination, is translated into well-behaved Catch test assertions. The Catch test suite reporter is reused by the subprocess to support capturing values and evaluating assertions. However, usingSECTION()
is not supported due to non-trivial control flow, either withinop
or in the parentTEST_CASE()
.This enabled writing test cases asserting proper log message handling behavior, including default log message handler behavior, by capturing log output emitted by the subprocess (using POSIX pipe API in the
capture_stderr
class). Note this is only supported on non-Windows platforms. Unfortunately, the use ofstd::exit()
andstd::terminate()
in the subprocess confusesvalgrind
, which (understandably) reports memory leaks due to the abnormal process termination method. Therefore, leak detection in subprocesses is disabled for thetest_instance
executable using--trace-children=no
.