summaryrefslogtreecommitdiff
path: root/src/mongo/util
diff options
context:
space:
mode:
authorLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-11 15:07:35 -0300
committerLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-11 15:07:35 -0300
commit4cb8841196d0625dfa3825aa326f071cd27c7b8b (patch)
tree1682a647d4463397c119183369ae6f750d5fdcff /src/mongo/util
parentaa03c6362cbaa767638e6eed9b031d86dd2643d1 (diff)
parent8f0827553e09872941945a093b647a4211a9db7f (diff)
Update upstream source from tag 'upstream/6.0.0'master
Update to upstream version '6.0.0' with Debian dir 5604a80ec1c96ca76f25f40d78e6ef855abec322
Diffstat (limited to 'src/mongo/util')
-rw-r--r--src/mongo/util/SConscript32
-rw-r--r--src/mongo/util/assert_util.h48
-rw-r--r--src/mongo/util/assert_util_test.cpp2
-rw-r--r--src/mongo/util/bufreader.h13
-rw-r--r--src/mongo/util/concurrency/SConscript1
-rw-r--r--src/mongo/util/concurrency/thread_name.cpp241
-rw-r--r--src/mongo/util/concurrency/thread_name.h162
-rw-r--r--src/mongo/util/concurrency/thread_pool.h7
-rw-r--r--src/mongo/util/concurrency/ticketholder.cpp3
-rw-r--r--src/mongo/util/concurrency/ticketholder_bm.cpp93
-rw-r--r--src/mongo/util/concurrency/ticketholder_test.cpp82
-rw-r--r--src/mongo/util/dns_query_posix-impl.h8
-rw-r--r--src/mongo/util/errno_util.cpp18
-rw-r--r--src/mongo/util/errno_util.h56
-rw-r--r--src/mongo/util/future.h16
-rw-r--r--src/mongo/util/future_impl.h18
-rw-r--r--src/mongo/util/future_test_utils.h6
-rw-r--r--src/mongo/util/future_test_valid.cpp285
-rw-r--r--src/mongo/util/immutable/README.md35
-rw-r--r--src/mongo/util/immutable/SConscript29
-rw-r--r--src/mongo/util/immutable/details/map.h233
-rw-r--r--src/mongo/util/immutable/details/memory_policy.h63
-rw-r--r--src/mongo/util/immutable/details/set.h110
-rw-r--r--src/mongo/util/immutable/immutable_absl_comparison_bm.cpp168
-rw-r--r--src/mongo/util/immutable/immutable_ordered_test.cpp1336
-rw-r--r--src/mongo/util/immutable/immutable_std_comparison_bm.cpp168
-rw-r--r--src/mongo/util/immutable/immutable_unordered_test.cpp234
-rw-r--r--src/mongo/util/immutable/immutable_vector_test.cpp126
-rw-r--r--src/mongo/util/immutable/map.h358
-rw-r--r--src/mongo/util/immutable/set.h199
-rw-r--r--src/mongo/util/immutable/unordered_map.h65
-rw-r--r--src/mongo/util/immutable/unordered_set.h65
-rw-r--r--src/mongo/util/immutable/vector.h61
-rw-r--r--src/mongo/util/interruptible.h101
-rw-r--r--src/mongo/util/interruptible_test.cpp61
-rw-r--r--src/mongo/util/murmur3.h114
-rw-r--r--src/mongo/util/net/SConscript8
-rw-r--r--src/mongo/util/net/hostandport.cpp7
-rw-r--r--src/mongo/util/net/hostandport.h6
-rw-r--r--src/mongo/util/net/hostname_canonicalization.cpp6
-rw-r--r--src/mongo/util/net/http_client_curl.cpp100
-rw-r--r--src/mongo/util/net/http_client_options.cpp36
-rw-r--r--src/mongo/util/net/http_client_options.h46
-rw-r--r--src/mongo/util/net/http_client_options.idl38
-rw-r--r--src/mongo/util/net/ocsp/ocsp_manager.cpp1
-rw-r--r--src/mongo/util/net/openssl_init.cpp59
-rw-r--r--src/mongo/util/net/ssl_manager.cpp4
-rw-r--r--src/mongo/util/net/ssl_manager_apple.cpp57
-rw-r--r--src/mongo/util/net/ssl_manager_openssl.cpp65
-rw-r--r--src/mongo/util/net/ssl_manager_test.cpp1
-rw-r--r--src/mongo/util/net/ssl_manager_windows.cpp30
-rw-r--r--src/mongo/util/net/ssl_options.h1
-rw-r--r--src/mongo/util/net/ssl_options_server.cpp20
-rw-r--r--src/mongo/util/net/ssl_options_server.idl7
-rw-r--r--src/mongo/util/net/ssl_parameters.idl2
-rw-r--r--src/mongo/util/net/ssl_types.h1
-rw-r--r--src/mongo/util/pin_code_segments.cpp115
-rw-r--r--src/mongo/util/pin_code_segments_params.idl42
-rw-r--r--src/mongo/util/processinfo.h23
-rw-r--r--src/mongo/util/processinfo_freebsd.cpp6
-rw-r--r--src/mongo/util/processinfo_linux.cpp113
-rw-r--r--src/mongo/util/processinfo_openbsd.cpp6
-rw-r--r--src/mongo/util/processinfo_osx.cpp10
-rw-r--r--src/mongo/util/processinfo_solaris.cpp52
-rw-r--r--src/mongo/util/processinfo_test.cpp38
-rw-r--r--src/mongo/util/processinfo_unknown.cpp4
-rw-r--r--src/mongo/util/processinfo_windows.cpp89
-rw-r--r--src/mongo/util/procparser.cpp105
-rw-r--r--src/mongo/util/procparser.h18
-rw-r--r--src/mongo/util/procparser_test.cpp133
-rw-r--r--src/mongo/util/scoped_unlock.h75
-rw-r--r--src/mongo/util/scoped_unlock_test.cpp78
-rw-r--r--src/mongo/util/shared_buffer_fragment.h74
-rw-r--r--src/mongo/util/shared_buffer_fragment_builder.cpp79
-rw-r--r--src/mongo/util/shared_buffer_test.cpp426
-rw-r--r--src/mongo/util/signal_handlers_synchronous.cpp56
-rw-r--r--src/mongo/util/stacktrace.cpp18
-rw-r--r--src/mongo/util/stacktrace.h104
-rw-r--r--src/mongo/util/stacktrace_posix.cpp26
-rw-r--r--src/mongo/util/stacktrace_test.cpp58
-rw-r--r--src/mongo/util/stacktrace_threads.cpp13
-rw-r--r--src/mongo/util/stacktrace_windows.cpp56
-rw-r--r--src/mongo/util/str_escape.cpp120
-rw-r--r--src/mongo/util/str_escape.h74
-rw-r--r--src/mongo/util/string_map.h8
-rw-r--r--src/mongo/util/tick_source_bm.cpp50
-rw-r--r--src/mongo/util/uuid.h5
-rw-r--r--src/mongo/util/version/releases.h.tpl18
-rw-r--r--src/mongo/util/version/releases.yml21
89 files changed, 802 insertions, 6423 deletions
diff --git a/src/mongo/util/SConscript b/src/mongo/util/SConscript
index 429b0dfcb42..724ba71b17d 100644
--- a/src/mongo/util/SConscript
+++ b/src/mongo/util/SConscript
@@ -33,7 +33,7 @@ def fmtBuildInfo(data):
fmtStr(env.subst(obj['value'])),
fmtBool(obj['inBuildInfo']),
fmtBool(obj['inVersion']))
- return ',\n'.join([fmtObj(obj) for _, obj in data.items()])
+ return ',\n'.join([fmtObj(obj) for _,obj in data.items()])
buildInfoInitializer = fmtBuildInfo(env['MONGO_BUILDINFO_ENVIRONMENT_DATA'])
@@ -83,7 +83,6 @@ env.SConscript(
dirs=[
'cmdline_utils',
'concurrency',
- 'immutable',
'net',
'options_parser',
'version',
@@ -564,14 +563,6 @@ env.Benchmark(
],
)
-env.Benchmark(
- target='tick_source_bm',
- source=[
- 'tick_source_bm.cpp',
- ],
- LIBDEPS=[],
-)
-
env.Library(
target='future_util',
source=[
@@ -582,21 +573,6 @@ env.Library(
],
)
-if env.TargetOSIs('linux'):
- env.Library(
- target='pin_code_segments',
- source=[
- 'pin_code_segments.cpp',
- 'pin_code_segments_params.idl',
- ],
- LIBDEPS=[
- '$BUILD_DIR/mongo/base',
- ],
- LIBDEPS_PRIVATE=[
- '$BUILD_DIR/mongo/idl/server_parameter',
- ],
- )
-
env.Benchmark(
target='hash_table_bm',
source='hash_table_bm.cpp',
@@ -716,7 +692,6 @@ icuEnv.CppUnitTest(
'future_test_future_int.cpp',
'future_test_future_move_only.cpp',
'future_test_future_void.cpp',
- 'future_test_valid.cpp',
'future_test_promise_int.cpp',
'future_test_promise_void.cpp',
'future_test_shared_future.cpp',
@@ -725,7 +700,6 @@ icuEnv.CppUnitTest(
'hierarchical_acquisition_test.cpp',
'icu_test.cpp',
'static_immortal_test.cpp',
- 'interruptible_test.cpp',
'invalidating_lru_cache_test.cpp',
'itoa_test.cpp',
'latch_analyzer_test.cpp' if get_option('use-diagnostic-latches') == 'on' else [],
@@ -745,8 +719,6 @@ icuEnv.CppUnitTest(
'registry_list_test.cpp',
'represent_as_test.cpp',
'safe_num_test.cpp',
- 'scoped_unlock_test.cpp',
- 'shared_buffer_test.cpp',
'secure_zero_memory_test.cpp',
'signal_handlers_synchronous_test.cpp' if not env.TargetOSIs('windows') else [],
'str_test.cpp',
@@ -779,7 +751,6 @@ icuEnv.CppUnitTest(
'processinfo',
'procparser' if env.TargetOSIs('linux') else [],
'progress_meter',
- 'regex_util',
'safe_num',
'secure_zero_memory',
'summation',
@@ -881,3 +852,4 @@ env.Benchmark(
'processinfo',
],
)
+
diff --git a/src/mongo/util/assert_util.h b/src/mongo/util/assert_util.h
index d03605e6192..4a9e7479443 100644
--- a/src/mongo/util/assert_util.h
+++ b/src/mongo/util/assert_util.h
@@ -150,19 +150,6 @@ public:
};
/**
- * Use `throwTransactionTooLargeForCache()` instead of throwing
- * `TransactionTooLargeForCache` directly.
- */
-class TransactionTooLargeForCacheException final : public DBException {
-public:
- TransactionTooLargeForCacheException(const Status& status) : DBException(status) {}
-
-private:
- void defineOnlyInFinalSubclassToPreventSlicing() final {}
-};
-
-
-/**
* The base class of all DBExceptions for codes of the given ErrorCategory to allow catching by
* category.
*/
@@ -178,8 +165,6 @@ protected:
}
};
-class WriteConflictException;
-class TemporarilyUnavailableException;
/**
* This namespace contains implementation details for our error handling code and should not be used
@@ -218,21 +203,6 @@ struct ExceptionForDispatcher<code, CategoryList<categories...>> {
ExceptionForImpl<code, ExceptionForCat<categories>...>>;
};
-template <>
-struct ExceptionForDispatcher<ErrorCodes::WriteConflict> {
- using type = WriteConflictException;
-};
-
-template <>
-struct ExceptionForDispatcher<ErrorCodes::TemporarilyUnavailable> {
- using type = TemporarilyUnavailableException;
-};
-
-template <>
-struct ExceptionForDispatcher<ErrorCodes::TransactionTooLargeForCache> {
- using type = TransactionTooLargeForCacheException;
-};
-
} // namespace error_details
@@ -700,21 +670,3 @@ Status exceptionToStatus() noexcept;
* Like `MONGO_UNREACHABLE`, but triggers a `tassert` instead of an `invariant`
*/
#define MONGO_UNREACHABLE_TASSERT(msgid) tasserted(msgid, "Hit a MONGO_UNREACHABLE_TASSERT!")
-
-/**
- * Produces an invariant failure if executed. Subset of MONGO_UNREACHABLE, but specifically
- * to indicate that the program has reached a function that is unimplemented and should be
- * unreachable from production.
- * Example:
- *
- * void myFuncToDo() {
- * MONGO_UNIMPLEMENTED;
- * }
- */
-#define MONGO_UNIMPLEMENTED \
- ::mongo::invariantFailed("Hit a MONGO_UNIMPLEMENTED!", __FILE__, __LINE__);
-
-/**
- * Like `MONGO_UNIMPLEMENTED`, but triggers a `tassert` instead of an `invariant`
- */
-#define MONGO_UNIMPLEMENTED_TASSERT(msgid) tasserted(msgid, "Hit a MONGO_UNIMPLEMENTED_TASSERT!")
diff --git a/src/mongo/util/assert_util_test.cpp b/src/mongo/util/assert_util_test.cpp
index 68e45dff3e4..d4740ea966e 100644
--- a/src/mongo/util/assert_util_test.cpp
+++ b/src/mongo/util/assert_util_test.cpp
@@ -144,7 +144,7 @@ TEST(AssertUtils, UassertNamedCodeWithTwoCategories) {
}
MONGO_STATIC_ASSERT(!error_details::isNamedCode<19999>);
-// ExceptionFor<ErrorCodes::Error19999)> invalidType; // Must not compile.
+// ExceptionFor<ErrorCodes::Error(19999)> invalidType; // Must not compile.
TEST(AssertUtils, UassertNumericCode) {
ASSERT_CATCHES(19999, DBException);
diff --git a/src/mongo/util/bufreader.h b/src/mongo/util/bufreader.h
index b8d931add9a..8c30070bada 100644
--- a/src/mongo/util/bufreader.h
+++ b/src/mongo/util/bufreader.h
@@ -106,11 +106,6 @@ public:
invariant(_pos >= _start);
}
- /** back up to beginging of buffer */
- void rewindToStart() {
- _pos = _start;
- }
-
/** return current position pointer, and advance by len */
const void* skip(unsigned len) {
ConstDataRangeCursor cdrc(_pos, _end);
@@ -129,14 +124,6 @@ public:
s = readCStr().toString();
}
- /**
- * Return a view of the next len bytes and advance by len.
- */
- StringData readBytes(size_t len) {
- // Note: the call to skip() includes a check that at least 'len' bytes remain in the buffer.
- return StringData(reinterpret_cast<const char*>(skip(len)), len);
- }
-
const void* pos() {
return _pos;
}
diff --git a/src/mongo/util/concurrency/SConscript b/src/mongo/util/concurrency/SConscript
index 3d4460e782a..7ac61ec0025 100644
--- a/src/mongo/util/concurrency/SConscript
+++ b/src/mongo/util/concurrency/SConscript
@@ -29,7 +29,6 @@ env.Library('ticketholder',
['ticketholder.cpp'],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
- '$BUILD_DIR/mongo/db/commands/test_commands_enabled',
'$BUILD_DIR/mongo/db/service_context',
'$BUILD_DIR/third_party/shim_boost',
])
diff --git a/src/mongo/util/concurrency/thread_name.cpp b/src/mongo/util/concurrency/thread_name.cpp
index 5109b079c6d..1a950c24bfa 100644
--- a/src/mongo/util/concurrency/thread_name.cpp
+++ b/src/mongo/util/concurrency/thread_name.cpp
@@ -44,6 +44,10 @@
#include <mach/thread_info.h>
#endif
#endif
+#if defined(__linux__)
+#include <sys/syscall.h>
+#include <sys/types.h>
+#endif
#include <fmt/format.h>
@@ -51,18 +55,13 @@
#include "mongo/config.h"
#include "mongo/logv2/log.h"
#include "mongo/platform/atomic_word.h"
-#include "mongo/platform/process_id.h"
-#include "mongo/util/thread_context.h"
+#include "mongo/util/str.h"
namespace mongo {
using namespace fmt::literals;
namespace {
-bool isMainThread() {
- return ProcessId::getCurrent() == ProcessId::getCurrentThreadId();
-}
-
#ifdef _WIN32
// From https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
// Note: The thread name is only set for the thread if the debugger is attached.
@@ -93,15 +92,49 @@ void setWindowsThreadName(DWORD dwThreadID, const char* threadName) {
}
#endif
-void setOSThreadName(const std::string& threadName) {
+constexpr auto kMainId = size_t{0};
+
+auto makeAnonymousThreadName() {
+ static auto gNextAnonymousId = AtomicWord<size_t>{kMainId};
+ auto id = gNextAnonymousId.fetchAndAdd(1);
+ if (id == kMainId) {
+ // The first thread name should always be "main".
+ return make_intrusive<ThreadName>("main");
+ } else {
+ return make_intrusive<ThreadName>("thread{}"_format(id));
+ }
+}
+
+struct ThreadNameSconce {
+ ThreadNameSconce() : cachedPtr(makeAnonymousThreadName()) {
+ // Note that we're not setting the thread name here. It will log differently, but appear the
+ // same in top and like.
+ }
+
+ // At any given time, either cachedPtr or activePtr can be valid, but not both.
+ boost::intrusive_ptr<ThreadName> activePtr;
+ boost::intrusive_ptr<ThreadName> cachedPtr;
+};
+
+auto getSconce = ThreadContext::declareDecoration<ThreadNameSconce>();
+auto& getThreadName(const boost::intrusive_ptr<ThreadContext>& context) {
+ auto& sconce = getSconce(context.get());
+ if (sconce.activePtr) {
+ return sconce.activePtr;
+ }
+
+ return sconce.cachedPtr;
+}
+
+void setOSThreadName(StringData threadName) {
#if defined(_WIN32)
// Naming should not be expensive compared to thread creation and connection set up, but if
// testing shows otherwise we should make this depend on DEBUG again.
- setWindowsThreadName(GetCurrentThreadId(), threadName.c_str());
+ setWindowsThreadName(GetCurrentThreadId(), threadName.rawData());
#elif defined(__APPLE__)
// Maximum thread name length on OS X is MAXTHREADNAMESIZE (64 characters). This assumes
// OS X 10.6 or later.
- std::string threadNameCopy = threadName;
+ std::string threadNameCopy = threadName.toString();
if (threadNameCopy.size() > MAXTHREADNAMESIZE) {
threadNameCopy.resize(MAXTHREADNAMESIZE - 4);
threadNameCopy += "...";
@@ -117,137 +150,95 @@ void setOSThreadName(const std::string& threadName) {
// Do not set thread name on the main() thread. Setting the name on main thread breaks
// pgrep/pkill since these programs base this name on /proc/*/status which displays the thread
// name, not the executable name.
- if (isMainThread())
- return;
- // Maximum thread name length supported on Linux is 16 including the null terminator.
- // Ideally we use short and descriptive thread names that fit: this helps for log
- // readability as well. Still, as the limit is so low and a few current names exceed the
- // limit, it's best to shorten long names.
- static constexpr size_t kMaxThreadNameLength = 16 - 1;
- boost::optional<std::string> shortNameBuf;
- const char* truncName = threadName.c_str();
- if (threadName.size() > kMaxThreadNameLength) {
- StringData sd = threadName;
- shortNameBuf = "{}.{}"_format(sd.substr(0, 7), sd.substr(sd.size() - 7));
- truncName = shortNameBuf->c_str();
- }
-
- int error = pthread_setname_np(pthread_self(), truncName);
- if (error) {
- LOGV2(23103,
- "Ignoring error from setting thread name: {error}",
- "Ignoring error from setting thread name",
- "error"_attr = errnoWithDescription(error));
+ if (getpid() != syscall(SYS_gettid)) {
+ // Maximum thread name length supported on Linux is 16 including the null terminator.
+ // Ideally we use short and descriptive thread names that fit: this helps for log
+ // readability as well. Still, as the limit is so low and a few current names exceed the
+ // limit, it's best to shorten long names.
+ int error = 0;
+ if (threadName.size() > 15) {
+ std::string shortName = str::stream()
+ << threadName.substr(0, 7) << '.' << threadName.substr(threadName.size() - 7);
+ error = pthread_setname_np(pthread_self(), shortName.c_str());
+ } else {
+ error = pthread_setname_np(pthread_self(), threadName.rawData());
+ }
+
+ if (error) {
+ LOGV2(23103,
+ "Ignoring error from setting thread name: {error}",
+ "Ignoring error from setting thread name",
+ "error"_attr = errnoWithDescription(error));
+ }
}
#endif
}
-/**
- * Manages the relationship of our high-level ThreadNameRef strings to
- * the thread local context, and efficiently notifying the OS of name
- * changes. We try to apply temporary names to threads to make them
- * meaningful representations of the kind of work the thread is doing.
- * But sharing these names with the OS is slow and name length is limited. So
- * ThreadNameInfo is an auxiliary resource to the OS thread name, available to
- * the LOGV2 system and to GDB.
- *
- * ThreadNameInfo is a decoration of ThreadContext.
- * ThreadContext are held by thread_local storage and so threads started
- * after server initialization will have an associated ThreadNameInfo.
- *
- * A name is "active" when it has been pushed to the OS by `setHandle`. The
- * association can be abandoned by calling `release`. This doesn't affect the
- * OS, but indicates that the name binding is abandoned and shouldn't be
- * preserved by returning it from subsequent `setHandle` calls. We do however
- * retain the inactive reference in hopes of perhaps identifying redundant
- * `setHandle` calls that would set the OS thread name to the same value it
- * already has.
- *
- * Upon construction, a ThreadNameInfo has an inactive unique name that
- * the OS doesn't know about yet. A push/pop style call sequence of
- * `h=getHandle()` then (eventually) `setHandle(h)` can make this name
- * the active (known to the OS) thread name.
- */
-class ThreadNameInfo {
-public:
- /** Returns the thread name ref, whether it's active or not. */
- const ThreadNameRef& getHandle() const {
- return _h;
- }
+} // namespace
- /**
- * Changes the thread name ref to `name`, marking it active,
- * and updating the OS thread name if necessary.
- *
- * If there was a previous active thread name, it is returned so that
- * callers can perhaps restore it and implement a temporary rename.
- * Inactive thread names are considered abandoned and are not returned.
- */
- ThreadNameRef setHandle(ThreadNameRef name) {
- bool alreadyActive = std::exchange(_active, true);
- if (name == _h)
- return {};
- auto old = std::exchange(_h, std::move(name));
- setOSThreadName(*_h);
- if (alreadyActive)
- return old;
- return {};
- }
+ThreadName::Id ThreadName::_nextId() {
+ static auto gNextId = AtomicWord<Id>{0};
+ return gNextId.fetchAndAdd(1);
+}
- /**
- * Mark the current ThreadNameRef as inactive. This is only a marking and
- * does not affect the OS thread name. The ThreadNameRef is retained
- * so that redundant setHandle calls can be recognized and elided.
- */
- void release() {
- _active = false;
+StringData ThreadName::getStaticString() {
+ auto& context = ThreadContext::get();
+ if (!context) {
+ // Use a static fallback to avoid allocations. This is the string that will be used before
+ // initializers run in main a.k.a. pre-init.
+ static constexpr auto kFallback = "-"_sd;
+ return kFallback;
}
- /**
- * Get a pointer to this thread's ThreadNameInfo.
- * Returns null if there's no ThreadContext to get it from.
- */
- static ThreadNameInfo* forThisThread() {
- auto& context = ThreadContext::get();
- return context ? &_decoration(*context) : nullptr;
- }
+ return getThreadName(context)->toString();
+}
-private:
- inline static auto _decoration = ThreadContext::declareDecoration<ThreadNameInfo>();
-
- /**
- * Main thread always gets "main". Other threads are sequentially
- * named as "thread1", "thread2", etc.
- */
- static std::string _makeAnonymousThreadName() {
- if (isMainThread())
- return "main";
- static AtomicWord<uint64_t> next{1};
- return "thread{}"_format(next.fetchAndAdd(1));
- }
+boost::intrusive_ptr<ThreadName> ThreadName::get(boost::intrusive_ptr<ThreadContext> context) {
+ return getThreadName(context);
+}
- ThreadNameRef _h{_makeAnonymousThreadName()};
- bool _active = false;
-};
+boost::intrusive_ptr<ThreadName> ThreadName::set(boost::intrusive_ptr<ThreadContext> context,
+ boost::intrusive_ptr<ThreadName> name) {
+ invariant(name);
-} // namespace
+ auto& sconce = getSconce(context.get());
-ThreadNameRef getThreadNameRef() {
- if (auto info = ThreadNameInfo::forThisThread())
- return info->getHandle();
- return {};
-}
+ if (sconce.activePtr) {
+ invariant(!sconce.cachedPtr);
+ if (*sconce.activePtr == *name) {
+ // The name was already set, skip setting it to the OS thread name.
+ return {};
+ } else {
+ // Replace the current active name with the new one, and set the OS thread name.
+ setOSThreadName(name->toString());
+ return std::exchange(sconce.activePtr, name);
+ }
+ } else if (sconce.cachedPtr) {
+ if (*sconce.cachedPtr == *name) {
+ // The name was cached, set it as active and skip setting it to the OS thread name.
+ sconce.activePtr = std::exchange(sconce.cachedPtr, {});
+ return {};
+ } else {
+ // The new name is different than the cached name, set the active, reset the cached, and
+ // set the OS thread name.
+ setOSThreadName(name->toString());
-ThreadNameRef setThreadNameRef(ThreadNameRef name) {
- invariant(name);
- if (auto info = ThreadNameInfo::forThisThread())
- return info->setHandle(std::move(name));
- return {};
+ sconce.activePtr = name;
+ sconce.cachedPtr.reset();
+ return {};
+ }
+ }
+
+ MONGO_UNREACHABLE;
}
-void releaseThreadNameRef() {
- if (auto info = ThreadNameInfo::forThisThread())
- info->release();
+void ThreadName::release(boost::intrusive_ptr<ThreadContext> context) {
+ auto& sconce = getSconce(context.get());
+ if (sconce.activePtr) {
+ sconce.cachedPtr = std::exchange(sconce.activePtr, {});
+ }
}
+ThreadName::ThreadName(StringData name) : _id(_nextId()), _storage(name.toString()){};
+
} // namespace mongo
diff --git a/src/mongo/util/concurrency/thread_name.h b/src/mongo/util/concurrency/thread_name.h
index f4bc4582a3c..2efd004263a 100644
--- a/src/mongo/util/concurrency/thread_name.h
+++ b/src/mongo/util/concurrency/thread_name.h
@@ -29,141 +29,103 @@
#pragma once
-#include <memory>
#include <string>
#include "mongo/base/string_data.h"
-#include "mongo/util/static_immortal.h"
+#include "mongo/util/intrusive_counter.h"
+#include "mongo/util/thread_context.h"
namespace mongo {
/**
- * A nullable handle pinning a ref-counted immutable string.
- * Copies of a ThreadNameString refer to the same string object.
- * Equality comparisons consider only that string's identity, not its value.
- *
- * This class is just a kind of refcounted string handle and does not itself
- * interact with the OS or with thread storage.
- *
- * Presents a pointer-like API with `get()`, and dereference operators, and
- * explicit bool conversion. Dereferencing yields a reference to a
- * string value if nonempty. Dereferencing an empty reference is allowed and
- * yields the singleton string value "-".
- *
- * Copyable and movable, with the usual refcounting semantics. Copies refer
- * to the same string and will compare equal to each other.
+ * ThreadName is a uniquely identifyable, immutable, ref-counted string.
*
+ * This class is used for three purposes:
+ * - Setting the official thread name with the OS.
+ * - Populating the "ctx" field for log lines.
+ * - Providing a thread name to gdb.
*/
-class ThreadNameRef {
+class ThreadName : public RefCountable {
public:
- /** An empty ref (empty refs still stringify as "-"). */
- ThreadNameRef() = default;
+ using Id = size_t;
- /** A ref to the string value `name`. */
- explicit ThreadNameRef(std::string name)
- : _ptr{std::make_shared<std::string>(std::move(name))} {}
+ /**
+ * Create a new instance.
+ *
+ * Note that this does not set it to be the official one for the thread.
+ */
+ explicit ThreadName(StringData name);
+ ThreadName(const ThreadName&) = delete;
+ ThreadName(ThreadName&&) = delete;
/**
- * Dereferences this. If nonempty, returns its string value.
- * Otherwise, returns a singleton "-" string.
+ * Get the official ThreadName for the current thread via the ThreadContext.
*/
- const std::string* get() const {
- if (_ptr)
- return &*_ptr;
- static const StaticImmortal whenEmpty = std::string("-");
- return &*whenEmpty;
- }
+ static boost::intrusive_ptr<ThreadName> get(boost::intrusive_ptr<ThreadContext> context);
- const std::string* operator->() const {
- return get();
- }
+ /**
+ * Set the official ThreadName for the current thread via the ThreadContext.
+ *
+ * Note that this also will set the OS thread name if the name is different from the current
+ * one.
+ *
+ * If a different non-anonymous thread name was previously set, this returns that name. If the
+ * given name was already set, a previous name was released, or the initial name was set, this
+ * returns an empty pointer.
+ */
+ static boost::intrusive_ptr<ThreadName> set(boost::intrusive_ptr<ThreadContext> context,
+ boost::intrusive_ptr<ThreadName> name);
- const std::string& operator*() const {
- return *get();
- }
+ /**
+ * Release the current thread name.
+ *
+ * This does not unset the OS thread name or change the current storage. Instead, this marks the
+ * current name as available for reuse or replacement.
+ */
+ static void release(boost::intrusive_ptr<ThreadContext> context);
- /** Returns true if nonempty. */
- explicit operator bool() const {
- return !!_ptr;
- }
+ /**
+ * Get a string for the current thread without new allocations.
+ *
+ * In pre-init, this returns "-". That value will mostly be associated with the main thread.
+ * If a thread is somehow started in pre-init and dodges our ThreadSafetyContext checks, it will
+ * also return "-" for this function.
+ */
+ static StringData getStaticString();
- operator StringData() const {
- return **this;
+ StringData toString() const {
+ return _storage;
}
- /**
- * Two ThreadNameRef are equal if and only if they are copies of the same
- * original ThreadNameRef object. Equality of string value is insufficient.
- */
- friend bool operator==(const ThreadNameRef& a, const ThreadNameRef& b) noexcept {
- return a._ptr == b._ptr;
+ friend bool operator==(const ThreadName& lhs, const ThreadName& rhs) noexcept {
+ return lhs._id == rhs._id;
}
- friend bool operator!=(const ThreadNameRef& a, const ThreadNameRef& b) noexcept {
- return !(a == b);
+ friend bool operator!=(const ThreadName& lhs, const ThreadName& rhs) noexcept {
+ return lhs._id != rhs._id;
}
private:
- std::shared_ptr<const std::string> _ptr;
-};
-
-/**
- * Returns the name reference attached to current thread. Returns an empty
- * ThreadNameRef if current thread has no ThreadContext. The empty ThreadNameRef
- * still has a valid string value of "-".
- *
- * This string is not limited in length, so it will be a better name
- * than the name the OS uses to refer to the same thread.
- */
-ThreadNameRef getThreadNameRef();
+ static Id _nextId();
-/**
- * Swaps in a new active name, returns the old one if it was active.
- *
- * The active thread name is used for:
- * - Setting the thread name in the OS. As an optimization, clearing
- * the thread name in the OS is performed lazily.
- * - Populating the "ctx" field for log lines.
- * - Providing a thread name to GDB.
- *
- * Has no effect if there is no `ThreadContext` for this thread.
- */
-ThreadNameRef setThreadNameRef(ThreadNameRef name);
-
-/**
- * Marks the ThreadNameRef attached to the current thread as inactive.
- * - The inactive thread name remains attached to the thread.
- * - The thread name according to the OS is not changed.
- * - A subsequent `setThreadNameRef` call will not return it.
- * - An immediately subsequent `setThreadNameRef` call with the same name will
- * cheaply reactivate it, saving two OS thread rename operations.
- * This is an optimization on the assumption that a thread name will be
- * temporarily set to the same `ThreadNameRef` repeatedly, so setting it and
- * resetting it with the OS on each change would be wasteful.
- *
- * Has no effect if there is no `ThreadContext` for this thread.
- */
-void releaseThreadNameRef();
+ const Id _id;
+ const std::string _storage;
+};
/**
* Sets the name of the current thread.
*/
-inline void setThreadName(std::string name) {
- setThreadNameRef(ThreadNameRef{std::move(name)});
+inline void setThreadName(StringData name) {
+ ThreadName::set(ThreadContext::get(), make_intrusive<ThreadName>(name));
}
/**
- * Returns current thread's name, as previously set, or "main", or
- * "thread#" if no name was previously set.
- *
- * Before the ThreadContext API is initialized, this returns "-". That value
- * will mostly be associated with the main thread, or threads that were started
- * before ThreadContext API initialization.
- *
- * Used by the MongoDB GDB pretty printer extentions in `gdb/mongo.py`.
+ * Retrieves the name of the current thread, as previously set, or "thread#" if no name was
+ * previously set. The returned StringData is always null terminated so it is safe to pass to APIs
+ * that expect c-strings.
*/
inline StringData getThreadName() {
- return *getThreadNameRef();
+ return ThreadName::get(ThreadContext::get())->toString();
}
} // namespace mongo
diff --git a/src/mongo/util/concurrency/thread_pool.h b/src/mongo/util/concurrency/thread_pool.h
index 1cbd6b8b263..29acd9e09c0 100644
--- a/src/mongo/util/concurrency/thread_pool.h
+++ b/src/mongo/util/concurrency/thread_pool.h
@@ -147,11 +147,6 @@ public:
// from ThreadPoolInterface
void startup() override;
void shutdown() override;
-
- /**
- * Joins all scheduled tasks. Can also spawn a free thread that ignores maxThread options to
- * execute pending tasks.
- */
void join() override;
/**
@@ -163,8 +158,6 @@ public:
*
* May be called multiple times, by multiple threads. May not be called by a task in the thread
* pool.
- *
- * Not safe to use when shutdown can be called concurrently.
*/
void waitForIdle();
diff --git a/src/mongo/util/concurrency/ticketholder.cpp b/src/mongo/util/concurrency/ticketholder.cpp
index f0ff7fdb4ce..30523f97f0f 100644
--- a/src/mongo/util/concurrency/ticketholder.cpp
+++ b/src/mongo/util/concurrency/ticketholder.cpp
@@ -31,7 +31,6 @@
#include "mongo/platform/basic.h"
-#include "mongo/db/commands/test_commands_enabled.h"
#include "mongo/db/service_context.h"
#include "mongo/util/concurrency/admission_context.h"
#include "mongo/util/concurrency/ticket.h"
@@ -151,7 +150,7 @@ void SemaphoreTicketHolder::release(AdmissionContext* admCtx, Ticket&& ticket) {
Status SemaphoreTicketHolder::resize(int newSize) {
stdx::lock_guard<Latch> lk(_resizeMutex);
- if (newSize < 5 && !getTestCommandsEnabled())
+ if (newSize < 5)
return Status(ErrorCodes::BadValue,
str::stream() << "Minimum value for semaphore is 5; given " << newSize);
diff --git a/src/mongo/util/concurrency/ticketholder_bm.cpp b/src/mongo/util/concurrency/ticketholder_bm.cpp
index 2e40e65a270..be4c3034d44 100644
--- a/src/mongo/util/concurrency/ticketholder_bm.cpp
+++ b/src/mongo/util/concurrency/ticketholder_bm.cpp
@@ -42,7 +42,7 @@ namespace mongo {
namespace {
static int kTickets = 128;
-static int kThreadMin = 16;
+static int kThreadMin = 8;
static int kThreadMax = 1024;
static TicketHolder::WaitMode waitMode = TicketHolder::WaitMode::kUninterruptible;
@@ -69,6 +69,86 @@ public:
};
template <class TicketHolderImpl>
+void BM_tryAcquire(benchmark::State& state) {
+ static std::unique_ptr<TicketHolderFixture<TicketHolderImpl>> p;
+ if (state.thread_index == 0) {
+ p = std::make_unique<TicketHolderFixture<TicketHolderImpl>>(state.threads);
+ }
+ double attempted = 0, acquired = 0;
+ for (auto _ : state) {
+ AdmissionContext admCtx;
+ auto ticket = p->ticketHolder->tryAcquire(&admCtx);
+ state.PauseTiming();
+ sleepmicros(1);
+ attempted++;
+ if (ticket) {
+ acquired++;
+ p->ticketHolder->release(&admCtx, std::move(*ticket));
+ }
+ state.ResumeTiming();
+ }
+ state.counters["Attempted"] = attempted;
+ state.counters["Acquired"] = acquired;
+}
+
+BENCHMARK_TEMPLATE(BM_tryAcquire, SemaphoreTicketHolder)->ThreadRange(kThreadMin, kThreadMax);
+
+BENCHMARK_TEMPLATE(BM_tryAcquire, FifoTicketHolder)->ThreadRange(kThreadMin, kThreadMax);
+
+template <class TicketHolderImpl>
+void BM_acquire(benchmark::State& state) {
+ static std::unique_ptr<TicketHolderFixture<TicketHolderImpl>> p;
+ if (state.thread_index == 0) {
+ p = std::make_unique<TicketHolderFixture<TicketHolderImpl>>(state.threads);
+ }
+ double acquired = 0;
+ for (auto _ : state) {
+ AdmissionContext admCtx;
+ auto opCtx = p->opCtxs[state.thread_index].get();
+ auto ticket = p->ticketHolder->waitForTicket(opCtx, &admCtx, waitMode);
+ state.PauseTiming();
+ sleepmicros(1);
+ p->ticketHolder->release(&admCtx, std::move(ticket));
+ acquired++;
+ state.ResumeTiming();
+ }
+ state.counters["Acquired"] = benchmark::Counter(acquired, benchmark::Counter::kIsRate);
+ state.counters["AcquiredPerThread"] =
+ benchmark::Counter(acquired, benchmark::Counter::kAvgThreadsRate);
+}
+
+BENCHMARK_TEMPLATE(BM_acquire, SemaphoreTicketHolder)->ThreadRange(kThreadMin, kThreadMax);
+
+BENCHMARK_TEMPLATE(BM_acquire, FifoTicketHolder)->ThreadRange(kThreadMin, kThreadMax);
+
+template <class TicketHolderImpl>
+void BM_release(benchmark::State& state) {
+ static std::unique_ptr<TicketHolderFixture<TicketHolderImpl>> p;
+ if (state.thread_index == 0) {
+ p = std::make_unique<TicketHolderFixture<TicketHolderImpl>>(state.threads);
+ }
+ double acquired = 0;
+ for (auto _ : state) {
+ AdmissionContext admCtx;
+ auto opCtx = p->opCtxs[state.thread_index].get();
+ state.PauseTiming();
+ auto ticket = p->ticketHolder->waitForTicket(opCtx, &admCtx, waitMode);
+ sleepmicros(1);
+ state.ResumeTiming();
+ p->ticketHolder->release(&admCtx, std::move(ticket));
+ acquired++;
+ }
+ state.counters["Acquired"] = benchmark::Counter(acquired, benchmark::Counter::kIsRate);
+ state.counters["AcquiredPerThread"] =
+ benchmark::Counter(acquired, benchmark::Counter::kAvgThreadsRate);
+}
+
+BENCHMARK_TEMPLATE(BM_release, SemaphoreTicketHolder)->ThreadRange(kThreadMin, kThreadMax);
+
+BENCHMARK_TEMPLATE(BM_release, FifoTicketHolder)->ThreadRange(kThreadMin, kThreadMax);
+
+
+template <class TicketHolderImpl>
void BM_acquireAndRelease(benchmark::State& state) {
static std::unique_ptr<TicketHolderFixture<TicketHolderImpl>> p;
if (state.thread_index == 0) {
@@ -91,14 +171,9 @@ void BM_acquireAndRelease(benchmark::State& state) {
}
BENCHMARK_TEMPLATE(BM_acquireAndRelease, SemaphoreTicketHolder)
- ->Threads(kThreadMin)
- ->Threads(kTickets)
- ->Threads(kThreadMax);
-
-BENCHMARK_TEMPLATE(BM_acquireAndRelease, FifoTicketHolder)
- ->Threads(kThreadMin)
- ->Threads(kTickets)
- ->Threads(kThreadMax);
+ ->ThreadRange(kThreadMin, kThreadMax);
+
+BENCHMARK_TEMPLATE(BM_acquireAndRelease, FifoTicketHolder)->ThreadRange(kThreadMin, kThreadMax);
} // namespace
} // namespace mongo
diff --git a/src/mongo/util/concurrency/ticketholder_test.cpp b/src/mongo/util/concurrency/ticketholder_test.cpp
index 2d0a04f88cc..5eba4fd046a 100644
--- a/src/mongo/util/concurrency/ticketholder_test.cpp
+++ b/src/mongo/util/concurrency/ticketholder_test.cpp
@@ -153,6 +153,88 @@ private:
TicketHolder* _holder;
};
+TEST_F(TicketHolderTest, FifoBasicMetrics) {
+ ServiceContext serviceContext;
+ serviceContext.setTickSource(std::make_unique<TickSourceMock<Microseconds>>());
+ auto tickSource = dynamic_cast<TickSourceMock<Microseconds>*>(serviceContext.getTickSource());
+ FifoTicketHolder holder(1, &serviceContext);
+ Stats stats(&holder);
+ AdmissionContext admCtx;
+
+ auto ticket =
+ holder.waitForTicket(_opCtx.get(), &admCtx, TicketHolder::WaitMode::kInterruptible);
+
+ unittest::Barrier barrier(2);
+ stdx::thread waiting([this, &holder, &barrier]() {
+ auto client = this->getServiceContext()->makeClient("waiting");
+ auto opCtx = client->makeOperationContext();
+ AdmissionContext admCtx;
+
+ auto ticket =
+ holder.waitForTicket(opCtx.get(), &admCtx, TicketHolder::WaitMode::kInterruptible);
+ barrier.countDownAndWait();
+ holder.release(&admCtx, std::move(ticket));
+ });
+
+ while (holder.queued() == 0) {
+ // Wait for thread to start waiting.
+ }
+
+ {
+ // Test that the metrics eventually converge to the following set of values. There can be
+ // cases where the values are incorrect for brief periods of time due to optimistic
+ // concurrency.
+ auto deadline = Date_t::now() + Milliseconds{100};
+ while (true) {
+ try {
+ ASSERT_EQ(stats["out"], 1);
+ ASSERT_EQ(stats["available"], 0);
+ ASSERT_EQ(stats["addedToQueue"], 1);
+ ASSERT_EQ(stats["queueLength"], 1);
+ break;
+ } catch (...) {
+ if (Date_t::now() > deadline) {
+ throw;
+ }
+ // Sleep to allow other threads to process and converge the metrics.
+ stdx::this_thread::sleep_for(Milliseconds{1}.toSystemDuration());
+ }
+ }
+ }
+ tickSource->advance(Microseconds(100));
+ holder.release(&admCtx, std::move(ticket));
+
+ while (holder.queued() > 0) {
+ // Wait for thread to take ticket.
+ }
+
+ tickSource->advance(Microseconds(200));
+ barrier.countDownAndWait();
+
+ waiting.join();
+
+ ASSERT_EQ(admCtx.getAdmissions(), 1);
+ ASSERT_EQ(stats["out"], 0);
+ ASSERT_EQ(stats["available"], 1);
+ ASSERT_EQ(stats["addedToQueue"], 1);
+ ASSERT_EQ(stats["removedFromQueue"], 1);
+ ASSERT_EQ(stats["queueLength"], 0);
+ ASSERT_EQ(stats["totalTimeQueuedMicros"], 100);
+ ASSERT_EQ(stats["startedProcessing"], 2);
+ ASSERT_EQ(stats["finishedProcessing"], 2);
+ ASSERT_EQ(stats["processing"], 0);
+ ASSERT_EQ(stats["totalTimeProcessingMicros"], 300);
+ ASSERT_EQ(stats["canceled"], 0);
+ ASSERT_EQ(stats["newAdmissions"], 2);
+
+ // Retake ticket.
+ ticket = holder.waitForTicket(_opCtx.get(), &admCtx, TicketHolder::WaitMode::kInterruptible);
+ holder.release(&admCtx, std::move(ticket));
+
+ ASSERT_EQ(admCtx.getAdmissions(), 2);
+ ASSERT_EQ(stats["newAdmissions"], 2);
+}
+
TEST_F(TicketHolderTest, FifoCanceled) {
ServiceContext serviceContext;
serviceContext.setTickSource(std::make_unique<TickSourceMock<Microseconds>>());
diff --git a/src/mongo/util/dns_query_posix-impl.h b/src/mongo/util/dns_query_posix-impl.h
index d6ef7f6764f..93431114f7f 100644
--- a/src/mongo/util/dns_query_posix-impl.h
+++ b/src/mongo/util/dns_query_posix-impl.h
@@ -54,7 +54,6 @@
#include <boost/noncopyable.hpp>
-#include "mongo/platform/mutex.h"
#include "mongo/util/duration.h"
namespace mongo {
@@ -366,19 +365,12 @@ public:
}
DNSQueryState() : _state() {
- // res_ninit may modify the global conf object creating a data race when multiple instances
- // of DNSQueryState are created concurrently.
- stdx::lock_guard<Latch> lk(_staticMutex);
res_ninit(&_state);
}
private:
struct __res_state _state;
- static Mutex _staticMutex;
};
-
-Mutex DNSQueryState::_staticMutex = MONGO_MAKE_LATCH("DNSQueryState::_staticMutex");
-
} // namespace
} // namespace dns
} // namespace mongo
diff --git a/src/mongo/util/errno_util.cpp b/src/mongo/util/errno_util.cpp
index 73aac860c8e..6e75b4b511a 100644
--- a/src/mongo/util/errno_util.cpp
+++ b/src/mongo/util/errno_util.cpp
@@ -27,34 +27,16 @@
* it in the license file.
*/
-#include "mongo/platform/basic.h"
-
#include "mongo/util/errno_util.h"
#include <cerrno>
#include <fmt/format.h>
#include <system_error>
-#ifdef _WIN32
-#include <errhandlingapi.h>
-#include <winsock2.h>
-#endif
-
namespace mongo {
using namespace fmt::literals;
-#ifdef _WIN32
-namespace errno_util_win32_detail {
-int gle() {
- return GetLastError();
-}
-int wsaGle() {
- return WSAGetLastError();
-}
-} // namespace errno_util_win32_detail
-#endif
-
std::string errorMessage(std::error_code ec) {
std::string r = ec.message();
bool vague = false;
diff --git a/src/mongo/util/errno_util.h b/src/mongo/util/errno_util.h
index bbc12c3b40f..0464c38a409 100644
--- a/src/mongo/util/errno_util.h
+++ b/src/mongo/util/errno_util.h
@@ -29,7 +29,6 @@
#pragma once
-#include <cstdlib>
#include <system_error>
#include <utility>
@@ -37,36 +36,6 @@
namespace mongo {
-#ifdef _WIN32
-namespace errno_util_win32_detail {
-int gle();
-int wsaGle();
-} // namespace errno_util_win32_detail
-#endif
-
-/**
- * Returns category to use for POSIX errno error codes.
- * On POSIX, `errno` codes are the `std::system_category`.
- * On Windows, the `errno` codes are the `std::generic_category`.
- */
-inline const std::error_category& posixCategory() {
-#ifdef _WIN32
- return std::generic_category();
-#else
- return std::system_category();
-#endif
-}
-
-/** Wraps POSIX `errno` value in an appropriate `std::error_code`. */
-inline std::error_code posixError(int e) {
- return std::error_code(e, posixCategory());
-}
-
-/** Wraps `e` in a `std::error_code` with `std::system_category`. */
-inline std::error_code systemError(int e) {
- return std::error_code(e, std::system_category());
-}
-
/**
* Returns `{errno, std::generic_category()}`.
* Windows has both Windows errors and POSIX errors. That is, there's a
@@ -79,39 +48,22 @@ inline std::error_code systemError(int e) {
* On POSIX systems, `std::system_category` is potentially a superset of
* `std::generic_category`, so `lastSystemError` should be preferred for
* handling system errors.
- *
- * Guaranteed to not modify `errno`.
*/
inline std::error_code lastPosixError() {
- return posixError(errno);
+ return std::error_code(errno, std::generic_category());
}
/**
* On POSIX, returns `{errno, std::system_category()}`.
* On Windows, returns `{GetLastError(), std::system_category()}`, but see `lastPosixError`.
- *
- * Guaranteed to not modify the system error code variable.
*/
inline std::error_code lastSystemError() {
#ifdef _WIN32
- return systemError(errno_util_win32_detail::gle());
-#else
- return systemError(errno);
-#endif
-}
-
-/**
- * Portable wrapper for socket API calls. On POSIX platforms this is just
- * `lastSystemError`. On Windows, Winsock API callers must query last error with
- * `WSAGetLastError` instead of `GetLastError`. The Winsock errors can use the
- * same error code category as other Windows API calls.
- */
-inline std::error_code lastSocketError() {
-#ifdef _WIN32
- return systemError(errno_util_win32_detail::wsaGle());
+ int e = GetLastError();
#else
- return lastSystemError();
+ int e = errno;
#endif
+ return std::error_code(e, std::system_category());
}
/**
diff --git a/src/mongo/util/future.h b/src/mongo/util/future.h
index cfdb724a101..5d36bb39861 100644
--- a/src/mongo/util/future.h
+++ b/src/mongo/util/future.h
@@ -181,16 +181,6 @@ public:
}
/**
- * Returns whether this SemiFuture can or will be able to access a deferred status or value.
- *
- * NOTE: valid() will still return true if the value inside of the future is moved from. This
- * should not be used as a way to determine usage validity until SERVER-66036.
- */
- bool valid() const {
- return _impl.valid();
- }
-
- /**
* Returns when the Semifuture isReady().
*
* Throws if the interruptible passed is interrupted (explicitly or via deadline).
@@ -326,7 +316,6 @@ public:
using SemiFuture<T>::SemiFuture; // Constructors.
using SemiFuture<T>::share;
using SemiFuture<T>::isReady;
- using SemiFuture<T>::valid;
using SemiFuture<T>::wait;
using SemiFuture<T>::waitNoThrow;
using SemiFuture<T>::get;
@@ -605,7 +594,6 @@ public:
using value_type = T;
using SemiFuture<T>::share;
using SemiFuture<T>::isReady;
- using SemiFuture<T>::valid;
using SemiFuture<T>::wait;
using SemiFuture<T>::waitNoThrow;
using SemiFuture<T>::get;
@@ -982,10 +970,6 @@ public:
return _shared.isReady();
}
- bool valid() const {
- return _shared.valid();
- }
-
void wait(Interruptible* interruptible = Interruptible::notInterruptible()) const {
_shared.wait(interruptible);
}
diff --git a/src/mongo/util/future_impl.h b/src/mongo/util/future_impl.h
index b1ef0ffd955..6fe25a51fb5 100644
--- a/src/mongo/util/future_impl.h
+++ b/src/mongo/util/future_impl.h
@@ -654,10 +654,6 @@ public:
return _shared->state.load(std::memory_order_acquire) == SSBState::kFinished;
}
- bool valid() const {
- return _shared != nullptr;
- }
-
void wait(Interruptible* interruptible) const {
_shared->wait(interruptible);
}
@@ -761,10 +757,6 @@ public:
return _inner.isReady();
}
- bool valid() const {
- return _inner.valid();
- }
-
void wait(Interruptible* interruptible) const {
_inner.wait(interruptible);
}
@@ -832,16 +824,6 @@ public:
return _immediate || _shared.isReady();
}
- /**
- * Returns whether the Future has or can eventually have access to a deferred value or status.
- *
- * NOTE: this does not return whether that deferred value is itself valid. It could have been
- * moved from.
- */
- bool valid() const {
- return _immediate || _shared.valid();
- }
-
void wait(Interruptible* interruptible) const {
if (_immediate)
return;
diff --git a/src/mongo/util/future_test_utils.h b/src/mongo/util/future_test_utils.h
index ec7bd783030..8bbecfd1faa 100644
--- a/src/mongo/util/future_test_utils.h
+++ b/src/mongo/util/future_test_utils.h
@@ -71,6 +71,12 @@ class DummyInterruptible final : public Interruptible {
// Must be implemented because it's called by Interruptible::waitForConditionOrInterrupt.
return Status::OK();
}
+ IgnoreInterruptsState pushIgnoreInterrupts() override {
+ MONGO_UNREACHABLE;
+ }
+ void popIgnoreInterrupts(IgnoreInterruptsState iis) override {
+ MONGO_UNREACHABLE;
+ }
DeadlineState pushArtificialDeadline(Date_t deadline, ErrorCodes::Error error) override {
MONGO_UNREACHABLE;
}
diff --git a/src/mongo/util/future_test_valid.cpp b/src/mongo/util/future_test_valid.cpp
deleted file mode 100644
index 2ca85a19f26..00000000000
--- a/src/mongo/util/future_test_valid.cpp
+++ /dev/null
@@ -1,285 +0,0 @@
-/**
- * Copyright (C) 2022-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/util/future.h"
-
-#include "mongo/stdx/thread.h"
-#include "mongo/unittest/death_test.h"
-#include "mongo/unittest/unittest.h"
-#include "mongo/util/future_test_utils.h"
-
-#include <boost/optional.hpp>
-
-namespace mongo {
-namespace {
-
-/**
- * TODO(SERVER-66036): Expand testing to ensure that `valid()` semantics are in line with "valid
- * usage" semantics.
- */
-
-/**
- * These tests validate the postconditions of operations on the 4 future types:
- * - Future
- * - SemiFuture
- * - SharedSemiFuture
- * - ExecutorFuture
- *
- * TODO(SERVER-66036): use FUTURE_SUCCESS_TEST in all helpers for better coverage of ExecutorFuture.
- */
-
-/** Asserts that the Future is still valid() after `func`. */
-template <typename TestFunc>
-void assertFutureValidAfter(const TestFunc& func) {
- FUTURE_SUCCESS_TEST([] { return 0; },
- [func](auto&& fut) {
- func(std::move(fut));
- ASSERT_TRUE(fut.valid());
- });
-}
-
-/** Asserts that `func` returns a valid() future while making the input Future non-valid(). */
-template <DoExecutorFuture doExecutorFuture = kDoExecutorFuture, typename TestFunc>
-void assertFutureTransfersValid(const TestFunc& func) {
- // TODO(SERVER-66036): use FUTURE_SUCCESS_TEST once moves from _immediate have the same
- // semantics as moves from SharedState
- auto [promise, fut] = makePromiseFuture<int>();
- promise.emplaceValue(0);
- auto otherFut = func(std::move(fut));
- ASSERT_FALSE(fut.valid()); // NOLINT
- ASSERT_TRUE(otherFut.valid());
-}
-
-TEST(FutureValid, ValidAtStart) {
- FUTURE_SUCCESS_TEST([] { return 0; }, [](auto&& fut) { ASSERT_TRUE(fut.valid()); });
-}
-
-// TODO SERVER-64948: this test is only needed if the lvalue& getter is kept around.
-TEST(FutureValid, ValidAfterGetLvalue) {
- assertFutureValidAfter([](auto&& fut) { [[maybe_unused]] auto val = fut.get(); });
-}
-
-TEST(FutureValid, ValidAfterGetConstLvalue) {
- assertFutureValidAfter([](const auto& fut) { [[maybe_unused]] auto val = fut.get(); });
-}
-
-TEST(FutureValid, ValidAfterGetNoThrowConstLvalue) {
- assertFutureValidAfter([](const auto& fut) { [[maybe_unused]] auto val = fut.getNoThrow(); });
-}
-
-TEST(FutureValid, ValidAfterWait) {
- assertFutureValidAfter([](auto&& fut) { fut.wait(); });
-}
-
-TEST(FutureValid, ValidAfterWaitNoThrow) {
- assertFutureValidAfter([](auto&& fut) { [[maybe_unused]] auto status = fut.waitNoThrow(); });
-}
-
-TEST(FutureValid, ThenRunOnTransfersValid) {
- assertFutureTransfersValid([](auto&& fut) {
- auto exec = InlineQueuedCountingExecutor::make();
- return std::move(fut).thenRunOn(exec);
- });
-}
-
-TEST(FutureValid, MoveTransfersValid) {
- assertFutureTransfersValid([](auto&& fut) { return std::move(fut); });
-}
-
-TEST(FutureValid, SemiTransfersValid) {
- assertFutureTransfersValid([](auto&& fut) { return std::move(fut).semi(); });
-}
-
-TEST(FutureValid, ShareTransfersValid) {
- assertFutureTransfersValid([](auto&& fut) { return std::move(fut).share(); });
-}
-
-/** Asserts that the SemiFuture is still valid() after `func`. */
-template <typename TestFunc>
-void assertSemiFutureValidAfter(const TestFunc& func) {
- FUTURE_SUCCESS_TEST([] { return 0; },
- [func](auto&& fut) {
- auto semiFut = std::move(fut).semi();
- func(std::move(semiFut));
- ASSERT_TRUE(semiFut.valid());
- });
-}
-
-/* Asserts that `func` returns a valid() future while making the input SemiFuture non-valid(). */
-template <typename TestFunc>
-void assertSemiFutureTransfersValid(const TestFunc& func) {
- // TODO(SERVER-66036): use FUTURE_SUCCESS_TEST once moves from _immediate have the same
- // semantics as moves from SharedState
- auto [promise, fut] = makePromiseFuture<int>();
- promise.emplaceValue(0);
- auto semiFut = std::move(fut).semi();
- auto otherFut = func(std::move(semiFut));
- ASSERT_FALSE(semiFut.valid()); // NOLINT
- ASSERT_TRUE(otherFut.valid());
-}
-
-// TODO SERVER-64948: this test is only needed if the lvalue& getter is kept around.
-TEST(SemiFutureValid, ValidAfterGetLvalue) {
- assertSemiFutureValidAfter([](auto&& fut) { [[maybe_unused]] auto val = fut.get(); });
-}
-
-TEST(SemiFutureValid, ValidAfterGetConstLvalue) {
- assertSemiFutureValidAfter([](const auto& fut) { [[maybe_unused]] auto val = fut.get(); });
-}
-
-TEST(SemiFutureValid, ValidAfterGetNoThrowConstLvalue) {
- assertSemiFutureValidAfter(
- [](const auto& fut) { [[maybe_unused]] auto val = fut.getNoThrow(); });
-}
-
-TEST(SemiFutureValid, ValidAfterWait) {
- assertSemiFutureValidAfter([](auto&& fut) { fut.wait(); });
-}
-
-TEST(SemiFutureValid, ValidAfterWaitNoThrow) {
- assertSemiFutureValidAfter(
- [](auto&& fut) { [[maybe_unused]] auto status = fut.waitNoThrow(); });
-}
-
-TEST(SemiFutureValid, ThenRunOnTransfersValid) {
- assertSemiFutureTransfersValid([](auto&& fut) {
- auto exec = InlineQueuedCountingExecutor::make();
- return std::move(fut).thenRunOn(exec);
- });
-}
-
-TEST(SemiFutureValid, MoveTransfersValid) {
- assertSemiFutureTransfersValid([](auto&& fut) { return std::move(fut); });
-}
-
-TEST(SemiFutureValid, SemiTransfersValid) {
- assertSemiFutureTransfersValid([](auto&& fut) { return std::move(fut).semi(); });
-}
-
-TEST(SemiFutureValid, ShareTransfersValid) {
- assertSemiFutureTransfersValid([](auto&& fut) { return std::move(fut).share(); });
-}
-
-TEST(SemiFutureValid, UnsafeToInlineFutureTransfersValid) {
- assertSemiFutureTransfersValid(
- [](auto&& fut) { return std::move(fut).unsafeToInlineFuture(); });
-}
-
-/** Asserts that the SharedSemiFuture is still valid() after `func`. */
-template <typename TestFunc>
-void assertSharedSemiFutureValidAfter(const TestFunc& func) {
- FUTURE_SUCCESS_TEST([] { return 0; },
- [func](auto&& fut) {
- auto sharedFut = std::move(fut).share();
- func(std::move(sharedFut));
- ASSERT_TRUE(sharedFut.valid());
- });
-}
-
-/**
- * Asserts that `func` returns a valid() future while making the input SharedSemiFuture non-valid().
- */
-template <typename TestFunc>
-void assertSharedSemiFutureTransfersValid(const TestFunc& func) {
- // TODO(SERVER-66036): use FUTURE_SUCCESS_TEST once moves from _immediate have the same
- // semantics as moves from SharedState
- auto [promise, fut] = makePromiseFuture<int>();
- promise.emplaceValue(0);
- auto sharedFut = std::move(fut).share();
- auto otherFut = func(std::move(sharedFut));
- ASSERT_FALSE(sharedFut.valid()); // NOLINT
- ASSERT_TRUE(otherFut.valid());
-}
-
-/** Asserts that `func` returns a valid() Future and keeps the input SharedSemiFuture valid(). */
-template <typename TestFunc>
-void assertSharedSemiFutureSplits(const TestFunc& func) {
- // TODO(SERVER-66036): use FUTURE_SUCCESS_TEST once moves from _immediate have the same
- // semantics as moves from SharedState
- auto [promise, fut] = makePromiseFuture<int>();
- promise.emplaceValue(0);
- auto sharedFut = std::move(fut).share();
- auto otherFut = func(std::move(sharedFut));
- ASSERT_TRUE(sharedFut.valid()); // NOLINT
- ASSERT_TRUE(otherFut.valid());
-}
-
-TEST(SharedSemiFutureValid, ValidAfterGetLvalue) {
- assertSharedSemiFutureValidAfter([](auto&& fut) { [[maybe_unused]] auto val = fut.get(); });
-}
-
-TEST(SharedSemiFutureValid, ValidAfterGetConstLvalue) {
- assertSharedSemiFutureValidAfter(
- [](const auto& fut) { [[maybe_unused]] auto val = fut.get(); });
-}
-
-TEST(SharedSemiFutureValid, ValidAfterGetNoThrowLvalue) {
- assertSharedSemiFutureValidAfter(
- [](auto&& fut) { [[maybe_unused]] auto val = fut.getNoThrow(); });
-}
-
-TEST(SharedSemiFutureValid, ValidAfterGetNoThrowConstLvalue) {
- assertSharedSemiFutureValidAfter(
- [](const auto& fut) { [[maybe_unused]] auto val = fut.getNoThrow(); });
-}
-
-TEST(SharedSemiFutureValid, ValidAfterWait) {
- assertSharedSemiFutureValidAfter([](auto&& fut) { fut.wait(); });
-}
-
-TEST(SharedSemiFutureValid, ValidAfterWaitNoThrow) {
- assertSharedSemiFutureValidAfter(
- [](auto&& fut) { [[maybe_unused]] auto status = fut.waitNoThrow(); });
-}
-
-TEST(SharedSemiFutureValid, ValidAfterThenRunOn) {
- assertSharedSemiFutureSplits([](auto&& fut) {
- auto exec = InlineQueuedCountingExecutor::make();
- return fut.thenRunOn(exec);
- });
-}
-
-TEST(SharedSemiFutureValid, MoveTransfersValid) {
- assertSharedSemiFutureTransfersValid([](auto&& fut) { return std::move(fut); });
-}
-
-TEST(SharedSemiFutureValid, SemiRetainsValid) {
- assertSharedSemiFutureSplits([](auto&& fut) { return std::move(fut).semi(); });
-}
-
-TEST(SharedSemiFutureValid, SplitRetainsValid) {
- assertSharedSemiFutureSplits([](auto&& fut) { return std::move(fut).split(); });
-}
-
-TEST(SharedSemiFutureValid, UnsafeToInlineFutureRetainsValid) {
- assertSharedSemiFutureSplits([](auto&& fut) { return std::move(fut).unsafeToInlineFuture(); });
-}
-
-} // namespace
-} // namespace mongo
diff --git a/src/mongo/util/immutable/README.md b/src/mongo/util/immutable/README.md
deleted file mode 100644
index 74f5e07259d..00000000000
--- a/src/mongo/util/immutable/README.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# Immutable Containers
-
-This folder contains a number of _immutable_ container classes. Sometimes called _persistent data
-structures_ in the literature, these classes provide interfaces similar to STL containers with one
-key difference. Operations which "modify" the container are `const` and return a modified copy of
-the container rather than modifying it in-place. This makes the containers implicitly thread-safe to
-read and write, but external synchronization will still be needed in many cases to address isolation
-and serializability concerns (i.e.
-[MVCC](https://en.wikipedia.org/wiki/Multiversion_concurrency_control)).
-
-## When To Use Immutable Containers
-
-If the container will be copied frequently, e.g. to support a copy-on-write pattern, consider using
-an immutable container. Otherwise, a standard container may make more sense.
-
-## Supported Containers
-
-The currently supported containers are all based on classes from the
-[`immer`](https://sinusoid.es/immer/) library.
-
- - [`immutable::map`](map.h): ordered map interface backed by `immer::flex_vector`
- - [`immutable::set`](set.h): ordered set interface backed by `immer::flex_vector`
- - [`immutable::unordered_map`](unordered_map.h): typedef for `immer:map`
- - [`immutable::unordered_set`](unordered_set.h): typedef for `immer:set`
- - [`immutable::vector`](vector.h): typedef for `immer::vector`
-
-Both ordered and unordered map and set variants support heterogeneous lookup.
-
-## A Note on Performance
-
-The internal implementations of these containers are optimized to support an internal copy-on-write
-pattern so that copies and modifications are $O(log(n))$ or even $O(1)$. However, the constants on
-these runtime guarantees, as well as those for lookups, are typically worse than those of the
-corresponding STL or Abseil containers. For this reason, they should not be considered a
-general-purpose drop-in replacement.
diff --git a/src/mongo/util/immutable/SConscript b/src/mongo/util/immutable/SConscript
deleted file mode 100644
index 72e517d93c5..00000000000
--- a/src/mongo/util/immutable/SConscript
+++ /dev/null
@@ -1,29 +0,0 @@
-# -*- mode: python -*-
-
-Import("env")
-
-env = env.Clone()
-
-env.CppUnitTest(
- target='immutable_test',
- source=[
- 'immutable_ordered_test.cpp',
- 'immutable_unordered_test.cpp',
- 'immutable_vector_test.cpp',
- ],
- LIBDEPS=[],
-)
-
-env.Benchmark(
- target='immutable_absl_comparison_bm',
- source=[
- 'immutable_absl_comparison_bm.cpp',
- ],
-)
-
-env.Benchmark(
- target='immutable_std_comparison_bm',
- source=[
- 'immutable_std_comparison_bm.cpp',
- ],
-)
diff --git a/src/mongo/util/immutable/details/map.h b/src/mongo/util/immutable/details/map.h
deleted file mode 100644
index a66db19b412..00000000000
--- a/src/mongo/util/immutable/details/map.h
+++ /dev/null
@@ -1,233 +0,0 @@
-/**
- * Copyright (C) 2023-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#pragma once
-
-#include <algorithm>
-
-namespace mongo::immutable::details::map {
-
-template <typename map, typename SearchKey>
-bool equal(const typename map::key_type& a, const SearchKey& b) {
- return !typename map::comp{}(a, b) && !typename map::comp{}(b, a);
-}
-
-template <typename map, typename SearchKey>
-[[nodiscard]] typename map::iterator lower_bound(const typename map::storage_type& storage,
- const SearchKey& key) {
- return std::lower_bound(storage.begin(),
- storage.end(),
- key,
- [](const typename map::value_type& a, const SearchKey& b) -> bool {
- return typename map::comp{}(a.first, b);
- });
-}
-
-template <typename map, typename SearchKey>
-[[nodiscard]] typename map::iterator find(const typename map::storage_type& storage,
- const SearchKey& key) {
- auto it = lower_bound<map>(storage, key);
- if (it != storage.end() && equal<map>(it->first, key)) {
- return it;
- }
-
- return storage.end();
-}
-
-template <typename map, typename S, typename K, typename V>
-[[nodiscard]] typename map::storage_type insert(S&& storage, K&& key, V&& value) {
- auto it = lower_bound<map>(storage, key);
- if (it != storage.end() && equal<map>(it->first, key)) {
- return std::forward<S>(storage);
- }
- if (it == storage.end()) {
- return std::forward<S>(storage).push_back(
- std::make_pair(std::forward<K>(key), std::forward<V>(value)));
- }
- return std::forward<S>(storage).insert(
- it.index(), std::make_pair(std::forward<K>(key), std::forward<V>(value)));
-}
-
-template <typename map, typename S, typename K, typename V>
-[[nodiscard]] typename map::storage_type insert(S&& storage,
- typename map::iterator it,
- K&& key,
- V&& value) {
- if (it != storage.end() && equal<map>(it->first, key)) {
- return std::forward<S>(storage);
- }
-
- if (it == storage.end()) {
- if (typename map::comp{}(storage[storage.size() - 1].first, key)) {
- return std::forward<S>(storage).push_back(
- std::make_pair(std::forward<K>(key), std::forward<V>(value)));
- }
- return insert<map>(std::forward<S>(storage), std::forward<K>(key), std::forward<V>(value));
- }
-
- if (typename map::comp{}(key, it->first) &&
- (it.index() == 0 || typename map::comp{}((it - 1)->first, key))) {
- return std::forward<S>(storage).insert(
- it.index(), std::make_pair(std::forward<K>(key), std::forward<V>(value)));
- }
-
- return insert<map>(std::forward<S>(storage), std::forward<K>(key), std::forward<V>(value));
-}
-
-template <typename map, typename S, typename K, typename U>
-[[nodiscard]] typename map::storage_type update(S&& storage, K&& key, U&& valueUpdate) {
- auto it = lower_bound<map>(storage, key);
- if (it == storage.end()) {
- return std::forward<S>(storage).push_back(
- std::make_pair(std::forward<K>(key), valueUpdate(typename map::default_value{}())));
- }
-
- if (equal<map>(it->first, key)) {
- return std::forward<S>(storage).set(
- it.index(), std::make_pair(std::forward<K>(key), valueUpdate(it->second)));
- }
-
- return insert<map>(std::forward<S>(storage),
- std::forward<K>(key),
- valueUpdate(typename map::default_value{}()));
-}
-
-template <typename map, typename S, typename K, typename U>
-[[nodiscard]] typename map::storage_type update(S&& storage,
- typename map::iterator it,
- K&& key,
- U&& valueUpdate) {
- if (it == storage.end()) {
- if (typename map::comp{}(storage[storage.size() - 1].first, key)) {
- return std::forward<S>(storage).push_back(
- std::make_pair(std::forward<K>(key), valueUpdate(typename map::default_value{}())));
- }
- return update<map>(
- std::forward<S>(storage), std::forward<K>(key), std::forward<U>(valueUpdate));
- }
-
- if (equal<map>(it->first, key)) {
- return std::forward<S>(storage).set(
- it.index(), std::make_pair(std::forward<K>(key), valueUpdate(it->second)));
- }
-
- if (typename map::comp{}(key, it->first) &&
- (it.index() == 0 || typename map::comp{}((it - 1)->first, key))) {
- return std::forward<S>(storage).insert(
- it.index(),
- std::make_pair(std::forward<K>(key), valueUpdate(typename map::default_value{}())));
- }
-
- return insert<map>(std::forward<S>(storage),
- std::forward<K>(key),
- valueUpdate(typename map::default_value{}()));
-}
-
-template <typename map, typename S, typename K, typename U>
-[[nodiscard]] typename map::storage_type update_if_exists(S&& storage, K&& key, U&& valueUpdate) {
- auto it = find<map>(storage, key);
- if (it == storage.end()) {
- return std::forward<S>(storage);
- }
- return std::forward<S>(storage).set(
- it.index(), std::make_pair(std::forward<K>(key), valueUpdate(it->second)));
-}
-
-template <typename map, typename S, typename K, typename U>
-[[nodiscard]] typename map::storage_type update_if_exists(S&& storage,
- typename map::iterator it,
- K&& key,
- U&& valueUpdate) {
- if (it == storage.end() || !equal<map>(it->first, key)) {
- return update_if_exists<map>(
- std::forward<S>(storage), std::forward<K>(key), std::forward<U>(valueUpdate));
- }
- return std::forward<S>(storage).set(
- it.index(), std::make_pair(std::forward<K>(key), valueUpdate(it->second)));
-}
-
-template <typename map, typename S, typename K, typename V>
-[[nodiscard]] typename map::storage_type set(S&& storage, K&& key, V&& value) {
- auto it = lower_bound<map>(storage, key);
- if (it == storage.end()) {
- return std::forward<S>(storage).push_back(
- std::make_pair(std::forward<K>(key), std::forward<V>(value)));
- } else if (!equal<map>(it->first, key)) {
- return std::forward<S>(storage).insert(
- it.index(), std::make_pair(std::forward<K>(key), std::forward<V>(value)));
- }
- return std::forward<S>(storage).set(
- it.index(), std::make_pair(std::forward<K>(key), std::forward<V>(value)));
-}
-
-template <typename map, typename S, typename K, typename V>
-[[nodiscard]] typename map::storage_type set(S&& storage,
- typename map::iterator it,
- K&& key,
- V&& value) {
- if (it == storage.end()) {
- if (typename map::comp{}(storage[storage.size() - 1].first, key)) {
- return std::forward<S>(storage).push_back(
- std::make_pair(std::forward<K>(key), std::forward<V>(value)));
- }
- return set<map>(std::forward<S>(storage), std::forward<K>(key), std::forward<V>(value));
- }
-
- if (equal<map>(it->first, key)) {
- return std::forward<S>(storage).set(
- it.index(), std::make_pair(std::forward<K>(key), std::forward<V>(value)));
- }
-
- if (typename map::comp{}(key, it->first) &&
- (it.index() == 0 || typename map::comp{}((it - 1)->first, key))) {
- return std::forward<S>(storage).insert(
- it.index(), std::make_pair(std::forward<K>(key), std::forward<V>(value)));
- }
-
- return set<map>(std::forward<S>(storage), std::forward<K>(key), std::forward<V>(value));
-}
-
-template <typename map, typename S, typename K>
-[[nodiscard]] typename map::storage_type erase(S&& storage, K&& key) {
- auto it = find<map>(storage, key);
- if (it == storage.end()) {
- return std::forward<S>(storage);
- }
- return std::forward<S>(storage).erase(it.index());
-}
-
-template <typename map, typename S, typename K>
-[[nodiscard]] typename map::storage_type erase(S&& storage, typename map::iterator it, K&& key) {
- if (it == storage.end() || !equal<map>(it->first, key)) {
- return erase<map>(std::forward<S>(storage), std::forward<K>(key));
- }
- return std::forward<S>(storage).erase(it.index());
-}
-
-} // namespace mongo::immutable::details::map
diff --git a/src/mongo/util/immutable/details/memory_policy.h b/src/mongo/util/immutable/details/memory_policy.h
deleted file mode 100644
index 01923f032b7..00000000000
--- a/src/mongo/util/immutable/details/memory_policy.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * Copyright (C) 2023-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#pragma once
-
-#include <immer/memory_policy.hpp>
-
-namespace mongo::immutable::detail {
-
-// Memory allocations using regular new/delete operators
-using HeapPolicy = immer::heap_policy<immer::cpp_heap>;
-
-// Refcounting using atomics for thread safety
-using RefcountPolicy = immer::refcount_policy;
-
-// We are not using any features from immer that requires locking. Use 'void' as the lock type which
-// would fail compilation if locking was needed anywhere.
-using LockPolicy = void;
-
-// No transience policy (this is just used for garbage collection)
-using TransiencePolicy = immer::no_transience_policy;
-
-using MemoryPolicy = immer::memory_policy<HeapPolicy,
- RefcountPolicy,
- LockPolicy,
- TransiencePolicy,
- /*PreferFewerBiggerObjects*/ true,
- /*UseTransientRValues*/ true>;
-
-// Verify that the recommended settings for our memory policy is as expected. We need to investigate
-// if any of these fire during a library upgrade.
-static_assert(
- std::is_same<immer::get_transience_policy_t<RefcountPolicy>, TransiencePolicy>::value);
-static_assert(immer::get_prefer_fewer_bigger_objects_v<HeapPolicy> == true);
-static_assert(immer::get_use_transient_rvalues_v<RefcountPolicy> == true);
-
-} // namespace mongo::immutable::detail
diff --git a/src/mongo/util/immutable/details/set.h b/src/mongo/util/immutable/details/set.h
deleted file mode 100644
index 491412e696f..00000000000
--- a/src/mongo/util/immutable/details/set.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/**
- * Copyright (C) 2023-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#pragma once
-
-#include <algorithm>
-
-namespace mongo::immutable::details::set {
-
-template <typename set, typename SearchKey>
-bool equal(const typename set::key_type& a, const SearchKey& b) {
- return !typename set::comp{}(a, b) && !typename set::comp{}(b, a);
-}
-
-template <typename set, class SearchKey>
-[[nodiscard]] typename set::iterator lower_bound(const typename set::storage_type& storage,
- const SearchKey& key) {
- return std::lower_bound(storage.begin(), storage.end(), key, typename set::comp{});
-}
-
-template <typename set, typename SearchKey>
-[[nodiscard]] typename set::iterator find(const typename set::storage_type& storage,
- const SearchKey& key) {
- auto it = lower_bound<set>(storage, key);
- if (it != storage.end() && equal<set>(*it, key)) {
- return it;
- }
-
- return storage.end();
-}
-
-template <typename set, typename S, typename K>
-[[nodiscard]] typename set::storage_type insert(S&& storage, K&& key) {
- auto it = lower_bound<set>(storage, key);
- if (it == storage.end()) {
- return std::forward<S>(storage).push_back(std::forward<K>(key));
- }
-
- if (equal<set>(*it, key)) {
- return std::forward<S>(storage);
- }
-
- return std::forward<S>(storage).insert(it.index(), std::forward<K>(key));
-}
-
-template <typename set, typename S, typename K>
-[[nodiscard]] typename set::storage_type insert(S&& storage, typename set::iterator it, K&& key) {
- if (it != storage.end() && equal<set>(*it, key)) {
- return std::forward<S>(storage);
- }
-
- if (it == storage.end()) {
- if (typename set::comp{}(storage[storage.size() - 1], key)) {
- return std::forward<S>(storage).push_back(std::forward<K>(key));
- }
- return insert<set>(std::forward<S>(storage), std::forward<K>(key));
- }
-
- if (typename set::comp{}(key, *it) &&
- (it.index() == 0 || typename set::comp{}(*(it - 1), key))) {
- return std::forward<S>(storage).insert(it.index(), std::forward<K>(key));
- }
-
- return insert<set>(std::forward<S>(storage), std::forward<K>(key));
-}
-
-template <typename set, typename S, typename K>
-[[nodiscard]] typename set::storage_type erase(S&& storage, K&& key) {
- auto it = find<set>(storage, key);
- if (it == storage.end()) {
- return std::forward<S>(storage);
- }
- return std::forward<S>(storage).erase(it.index());
-}
-
-template <typename set, typename S, typename K>
-[[nodiscard]] typename set::storage_type erase(S&& storage, typename set::iterator it, K&& key) {
- if (it == storage.end() || !equal<set>(*it, key)) {
- return erase<set>(std::forward<S>(storage), std::forward<K>(key));
- }
- return std::forward<S>(storage).erase(it.index());
-}
-
-} // namespace mongo::immutable::details::set
diff --git a/src/mongo/util/immutable/immutable_absl_comparison_bm.cpp b/src/mongo/util/immutable/immutable_absl_comparison_bm.cpp
deleted file mode 100644
index 78358aae0ff..00000000000
--- a/src/mongo/util/immutable/immutable_absl_comparison_bm.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/**
- * Copyright (C) 2023-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include <benchmark/benchmark.h>
-
-#include "mongo/stdx/unordered_map.h"
-#include "mongo/util/immutable/unordered_map.h"
-
-namespace mongo {
-
-static void BM_absl_insert_op(benchmark::State& state) {
- stdx::unordered_map<int, int> map;
-
- int64_t i = 0;
- for (; i < state.range(0); i++) {
- map[i] = i;
- }
-
- for (auto _ : state) {
- map[i] = i;
- i++;
- benchmark::ClobberMemory();
- }
-}
-
-static void BM_absl_copy_op(benchmark::State& state) {
- stdx::unordered_map<int, int> map;
-
- int64_t i = 0;
- for (; i < state.range(0); i++) {
- map[i] = i;
- }
-
- stdx::unordered_map<int, int> mapCopy;
- for (auto _ : state) {
- mapCopy = map;
- benchmark::ClobberMemory();
- }
-}
-
-static void BM_absl_find_op(benchmark::State& state) {
- stdx::unordered_map<int, int> map;
-
- int64_t i = 0;
- for (; i < state.range(0); i++) {
- map[i] = i;
- }
-
- for (auto _ : state) {
- benchmark::DoNotOptimize(map.find(i - 1));
- benchmark::ClobberMemory();
- }
-}
-
-static void BM_absl_copy_and_insert_op(benchmark::State& state) {
- stdx::unordered_map<int, int> map;
-
- int64_t i = 0;
- for (; i < state.range(0); i++) {
- map[i] = i;
- }
-
- stdx::unordered_map<int, int> mapCopy;
- for (auto _ : state) {
- mapCopy = map;
- mapCopy[i] = i;
- benchmark::ClobberMemory();
- }
-}
-
-static void BM_immutable_insert_op(benchmark::State& state) {
- immutable::unordered_map<int, int> map;
-
- int64_t i = 0;
- for (; i < state.range(0); i++) {
- map = std::move(map).set(i, i);
- }
-
- for (auto _ : state) {
- map = std::move(map).set(i, i);
- i++;
- benchmark::ClobberMemory();
- }
-}
-
-static void BM_immutable_copy_op(benchmark::State& state) {
- immutable::unordered_map<int, int> map;
-
- int64_t i = 0;
- for (; i < state.range(0); i++) {
- map = std::move(map).set(i, i);
- }
-
- immutable::unordered_map<int, int> mapCopy;
- for (auto _ : state) {
- mapCopy = map;
- benchmark::ClobberMemory();
- }
-}
-
-static void BM_immutable_find_op(benchmark::State& state) {
- immutable::unordered_map<int, int> map;
-
- int64_t i = 0;
- for (; i < state.range(0); i++) {
- map = std::move(map).set(i, i);
- }
-
- for (auto _ : state) {
- benchmark::DoNotOptimize(map.find(i - 1));
- benchmark::ClobberMemory();
- }
-}
-
-static void BM_immutable_copy_and_insert_op(benchmark::State& state) {
- immutable::unordered_map<int, int> map;
-
- int64_t i = 0;
- for (; i < state.range(0); i++) {
- map = std::move(map).set(i, i);
- }
-
- immutable::unordered_map<int, int> mapCopy;
- for (auto _ : state) {
- mapCopy = map.set(i, i);
- benchmark::ClobberMemory();
- }
-}
-
-// Run with varying container sizes: [ 8, 16, 32, 64, 128, 256, 512, 1024, 2k, 4k, 8k ].
-BENCHMARK(BM_absl_insert_op)->RangeMultiplier(2)->Range(8, 8 << 10);
-BENCHMARK(BM_immutable_insert_op)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-BENCHMARK(BM_absl_copy_op)->RangeMultiplier(2)->Range(8, 8 << 10);
-BENCHMARK(BM_immutable_copy_op)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-BENCHMARK(BM_absl_find_op)->RangeMultiplier(2)->Range(8, 8 << 10);
-BENCHMARK(BM_immutable_find_op)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-BENCHMARK(BM_absl_copy_and_insert_op)->RangeMultiplier(2)->Range(8, 8 << 10);
-BENCHMARK(BM_immutable_copy_and_insert_op)->RangeMultiplier(2)->Range(8, 8 << 10);
-} // namespace mongo
diff --git a/src/mongo/util/immutable/immutable_ordered_test.cpp b/src/mongo/util/immutable/immutable_ordered_test.cpp
deleted file mode 100644
index 2b9308d5caf..00000000000
--- a/src/mongo/util/immutable/immutable_ordered_test.cpp
+++ /dev/null
@@ -1,1336 +0,0 @@
-/**
- * Copyright (C) 2023-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-
-#include "mongo/unittest/unittest.h"
-
-#include "mongo/util/immutable/map.h"
-#include "mongo/util/immutable/set.h"
-#include "mongo/util/string_map.h"
-#include <stdexcept>
-
-#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kTest
-
-namespace mongo {
-namespace {
-
-class UserDefinedKey {
-public:
- UserDefinedKey() = default;
- explicit UserDefinedKey(int val) : a(val) {}
-
- bool operator==(const UserDefinedKey& rhs) const {
- return a == rhs.a;
- }
-
- bool operator<(const UserDefinedKey& rhs) const {
- return a < rhs.a;
- }
-
- std::string toString() const {
- return std::to_string(a);
- }
-
-private:
- int a = 0;
-};
-
-
-class Incomparable {
- friend struct CompareIncomparable;
-
-public:
- Incomparable() = default;
- explicit Incomparable(int val) : a(val) {}
-
- bool operator==(const Incomparable&) = delete;
- bool operator<(const Incomparable&) = delete;
-
- std::string toString() const {
- return std::to_string(a);
- }
-
-private:
- int a = 0;
-};
-
-struct CompareIncomparable {
- bool operator()(const Incomparable& a, const Incomparable& b) const {
- return a.a < b.a;
- }
-
- // Pair comparator needed for some testing macros to function properly for both maps and sets.
- bool operator()(const std::pair<Incomparable, int>& a,
- const std::pair<Incomparable, int>& b) const {
- return a.first.a < b.first.a;
- }
-};
-
-struct StringCompare {
- bool operator()(const std::string& a, const std::string& b) const {
- return a < b;
- }
- bool operator()(const std::string& a, const StringData& b) const {
- return a < b;
- }
- bool operator()(const StringData& a, const std::string& b) const {
- return a < b;
- }
- bool operator()(const std::string& a, const char* b) const {
- return a < b;
- }
- bool operator()(const char* a, const std::string& b) const {
- return a < b;
- }
-};
-
-template <typename C, typename L>
-void ensureContainerInvariants(const C& container, L&& less) {
- size_t visited = 0;
- for (auto it = container.begin(); it != container.end(); ++it) {
- if (++visited > 1) {
- ASSERT(less(*(it - 1), *it));
- }
- }
- ASSERT_EQ(visited, container.size());
-}
-
-
-template <typename C>
-void ensureContainerInvariants(const C& container) {
- size_t visited = 0;
- for (auto it = container.begin(); it != container.end(); ++it) {
- if (++visited > 1) {
- ASSERT(*(it - 1) < *it);
- }
- }
- ASSERT_EQ(visited, container.size());
-}
-
-template <typename C>
-void ensureContainerInvariants(const std::tuple<C, C, C, C>& containers) {
- ensureContainerInvariants(std::get<0>(containers));
- ensureContainerInvariants(std::get<1>(containers));
- ensureContainerInvariants(std::get<2>(containers));
- ensureContainerInvariants(std::get<3>(containers));
-}
-
-template <typename C>
-void ensureContainerInvariants(std::initializer_list<const C> containers) {
- for (auto& c : containers) {
- ensureContainerInvariants(c);
- }
-}
-
-template <typename K, typename V, typename M = immutable::map<K, V>>
-std::tuple<M, M, M, M> init_maps() {
- return std::make_tuple(M{}, M{}, M{}, M{});
-}
-
-template <typename K, typename S = immutable::set<K>>
-std::tuple<S, S, S, S> init_sets() {
- return std::make_tuple(S{}, S{}, S{}, S{});
-}
-
-std::ostream& operator<<(std::ostream& s, const UserDefinedKey& k) {
- return s << k.toString();
-}
-
-std::ostream& operator<<(std::ostream& s, const Incomparable& k) {
- return s << k.toString();
-}
-
-template <typename T, typename U>
-std::ostream& operator<<(std::ostream& s, const std::pair<T, U> pair) {
- return s << "(" << pair.first << "," << pair.second << ")";
-}
-
-template <typename C>
-struct ContainerWrapper {
- ContainerWrapper(const C& c) : container{c} {}
- const C& container;
-};
-template <typename T>
-std::ostream& operator<<(std::ostream& str, const ContainerWrapper<T>& wrapper) {
- str << "{";
- bool first = true;
- for (auto& el : wrapper.container) {
- if (first) {
- first = false;
- } else {
- str << ", ";
- }
- str << el;
- }
- str << "}";
- return str;
-}
-
-#define ASSERT_CONTAINS(containers, k) \
- { \
- ASSERT_TRUE(std::get<0>(containers).contains(k)) \
- << ContainerWrapper{std::get<0>(containers)}; \
- ASSERT_TRUE(std::get<1>(containers).contains(k)) \
- << ContainerWrapper{std::get<0>(containers)}; \
- ASSERT_TRUE(std::get<2>(containers).contains(k)) \
- << ContainerWrapper{std::get<0>(containers)}; \
- ASSERT_TRUE(std::get<3>(containers).contains(k)) \
- << ContainerWrapper{std::get<0>(containers)}; \
- }
-
-#define ASSERT_NOT_CONTAINS(containers, k) \
- { \
- ASSERT_FALSE(std::get<0>(containers).contains(k)) \
- << ContainerWrapper{std::get<0>(containers)}; \
- ASSERT_FALSE(std::get<1>(containers).contains(k)) \
- << ContainerWrapper{std::get<0>(containers)}; \
- ASSERT_FALSE(std::get<2>(containers).contains(k)) \
- << ContainerWrapper{std::get<0>(containers)}; \
- ASSERT_FALSE(std::get<3>(containers).contains(k)) \
- << ContainerWrapper{std::get<0>(containers)}; \
- }
-
-#define ASSERT_VALUE_EQ(its, v) \
- { \
- ASSERT_EQ(std::get<0>(its)->second, v); \
- ASSERT_EQ(std::get<1>(its)->second, v); \
- ASSERT_EQ(std::get<2>(its)->second, v); \
- ASSERT_EQ(std::get<3>(its)->second, v); \
- }
-
-#define MUTATE_KV(containers, fn, k, v) \
- [&]() { \
- auto _k1 = k; \
- auto _v1 = v; \
- auto _m1 = std::get<0>(containers).fn(_k1, _v1); \
- ensureContainerInvariants(_m1); \
- \
- auto _k2 = k; \
- auto _v2 = v; \
- auto _m2 = std::move(std::get<1>(containers)).fn(_k2, _v2); \
- ensureContainerInvariants(_m2); \
- \
- auto _k3 = k; \
- auto _v3 = v; \
- auto _m3 = std::get<2>(containers).fn(std::move(_k3), std::move(_v3)); \
- ensureContainerInvariants(_m3); \
- \
- auto _k4 = k; \
- auto _v4 = v; \
- auto _m4 = std::move(std::get<3>(containers)).fn(std::move(_k4), std::move(_v4)); \
- ensureContainerInvariants(_m4); \
- \
- return std::make_tuple(_m1, _m2, _m3, _m4); \
- }();
-
-#define MUTATE_K(containers, fn, k) \
- ([&]() { \
- auto _k1 = k; \
- auto _m1 = std::get<0>(containers).fn(_k1); \
- ensureContainerInvariants(_m1); \
- \
- auto _k2 = k; \
- auto _m2 = std::move(std::get<1>(containers)).fn(_k2); \
- ensureContainerInvariants(_m2); \
- \
- auto _k3 = k; \
- auto _m3 = std::get<2>(containers).fn(std::move(_k3)); \
- ensureContainerInvariants(_m3); \
- \
- auto _k4 = k; \
- auto _m4 = std::move(std::get<3>(containers)).fn(std::move(_k4)); \
- ensureContainerInvariants(_m4); \
- \
- return std::make_tuple(_m1, _m2, _m3, _m4); \
- }());
-
-#define MUTATE_IT_KV(containers, fn, its, k, v) \
- [&]() { \
- auto _k1 = k; \
- auto _v1 = v; \
- auto _m1 = std::get<0>(containers).fn(std::get<0>(its), _k1, _v1); \
- ensureContainerInvariants(_m1); \
- \
- auto _k2 = k; \
- auto _v2 = v; \
- auto _m2 = std::move(std::get<1>(containers)).fn(std::get<1>(its), _k2, _v2); \
- ensureContainerInvariants(_m2); \
- \
- auto _k3 = k; \
- auto _v3 = v; \
- auto _m3 = std::get<2>(containers).fn(std::get<2>(its), std::move(_k3), std::move(_v3)); \
- ensureContainerInvariants(_m3); \
- \
- auto _k4 = k; \
- auto _v4 = v; \
- auto _m4 = std::move(std::get<3>(containers)) \
- .fn(std::get<3>(its), std::move(_k4), std::move(_v4)); \
- ensureContainerInvariants(_m4); \
- \
- return std::make_tuple(_m1, _m2, _m3, _m4); \
- }();
-
-#define MUTATE_IT_K(containers, fn, its, k) \
- [&]() { \
- auto _k1 = k; \
- auto _m1 = std::get<0>(containers).fn(std::get<0>(its), _k1); \
- ensureContainerInvariants(_m1); \
- \
- auto _k2 = k; \
- auto _m2 = std::move(std::get<1>(containers)).fn(std::get<1>(its), _k2); \
- ensureContainerInvariants(_m2); \
- \
- auto _k3 = k; \
- auto _m3 = std::get<2>(containers).fn(std::get<2>(its), std::move(_k3)); \
- ensureContainerInvariants(_m3); \
- \
- auto _k4 = k; \
- auto _m4 = std::move(std::get<3>(containers)).fn(std::get<3>(its), std::move(_k4)); \
- ensureContainerInvariants(_m4); \
- \
- return std::make_tuple(_m1, _m2, _m3, _m4); \
- }();
-
-#define SEARCH(containers, fn, k) \
- [&]() { \
- auto _i1 = std::get<0>(containers).fn(k); \
- auto _i2 = std::get<1>(containers).fn(k); \
- auto _i3 = std::get<2>(containers).fn(k); \
- auto _i4 = std::get<3>(containers).fn(k); \
- return std::make_tuple(_i1, _i2, _i3, _i4); \
- }();
-
-#define END(containers) \
- [&]() { \
- auto _i1 = std::get<0>(containers).end(); \
- auto _i2 = std::get<1>(containers).end(); \
- auto _i3 = std::get<2>(containers).end(); \
- auto _i4 = std::get<3>(containers).end(); \
- return std::make_tuple(_i1, _i2, _i3, _i4); \
- }();
-
-TEST(ImmutableMap, Basic) {
- // Insert some values and verify that the data structure is behaving as expected
- immutable::map<int, int> v0;
- auto v1 = v0.set(1, 2);
- // Record the pointer to the value '1', verify that this doesn't change after performing more
- // inserts
- auto v1Val = v1.find(1);
-
- // Create distinct branches of the history from v1. v0 and v1 should be unaffected
- auto v2 = v1.update_if_exists(1, [](int v) { return v += 1; });
- auto v3 = v1.set(2, 3);
-
- // Verify that values are as expected
- ASSERT_EQ(v0.size(), 0);
-
- ASSERT_EQ(v1.size(), 1);
- ASSERT_TRUE(v1.contains(1));
- ASSERT_EQ(v1.find(1)->second, 2);
-
- ASSERT_EQ(v2.size(), 1);
- ASSERT_TRUE(v2.contains(1));
- ASSERT_EQ(v2.find(1)->second, 3);
- ASSERT_FALSE(v2.contains(2));
-
- ASSERT_EQ(v3.size(), 2);
- ASSERT_TRUE(v3.contains(1));
- ASSERT_EQ(v3.find(1)->second, 2);
- ASSERT_TRUE(v3.contains(2));
- ASSERT_EQ(v3.find(2)->second, 3);
-
- // Verify that v1's value did not change
- ASSERT(v1.find(1) == v1Val);
-
- // Verify that erase works as expected, and preserves history.
- auto v4 = v3.erase(1).erase(2);
- ASSERT_FALSE(v4.contains(1));
- ASSERT_FALSE(v4.contains(2));
- ASSERT_TRUE(v3.contains(1));
- ASSERT_EQ(v3.find(1)->second, 2);
- ASSERT_TRUE(v3.contains(2));
- ASSERT_EQ(v3.find(2)->second, 3);
- ASSERT_TRUE(v2.contains(1));
- ASSERT_EQ(v2.find(1)->second, 3);
- ASSERT_FALSE(v2.contains(2));
- ASSERT_TRUE(v1.contains(1));
- ASSERT_EQ(v1.find(1)->second, 2);
- ASSERT_FALSE(v1.contains(2));
-
- ensureContainerInvariants({v0, v1, v2, v3, v4});
-}
-
-TEST(ImmutableMap, UserDefinedType) {
- immutable::map<UserDefinedKey, int> v0;
- auto v1 = v0.set(UserDefinedKey(1), 2);
- ASSERT(v1.find(UserDefinedKey(1)) != v1.end());
- ASSERT_EQ(v1.find(UserDefinedKey(1))->second, 2);
-
- ensureContainerInvariants({v0, v1});
-}
-
-TEST(ImmutableMap, IncomparableType) {
- immutable::map<Incomparable, int, CompareIncomparable> v0;
- auto v1 = v0.set(Incomparable(1), 2);
- ASSERT(v1.find(Incomparable(1)) != v1.end());
- ASSERT_EQ(v1.find(Incomparable(1))->second, 2);
-
- ensureContainerInvariants(v0, CompareIncomparable{});
- ensureContainerInvariants(v1, CompareIncomparable{});
-}
-
-TEST(ImmutableMap, HeterogeneousLookup) {
- immutable::map<std::string, int, StringCompare> v0;
- auto v1 = v0.set("str", 1);
-
- // Lookup using StringData without the need to convert to string.
- ASSERT(v1.find("str"_sd) != v1.end());
-
- ensureContainerInvariants({v0, v1});
-}
-
-TEST(ImmutableMap, Accessors) {
- immutable::map<int, int> v0;
- auto v1 = v0.insert(1, 1).insert(2, 2).insert(3, 3);
-
- ASSERT_EQ(v1[1], 1);
- ASSERT_EQ(v1[2], 2);
- ASSERT_EQ(v1[3], 3);
- ASSERT_EQ(v1.at(1), 1);
- ASSERT_EQ(v1.at(2), 2);
- ASSERT_EQ(v1.at(3), 3);
-
- // Handling of missing elements
- ASSERT_EQ(v1[4], 0);
- ASSERT_THROWS(v1.at(4), std::out_of_range);
-
- ensureContainerInvariants({v0, v1});
-}
-
-TEST(ImmutableMap, Bounds) {
- immutable::map<int, int> map;
- constexpr int numKeys = 100;
- for (int i = 0; i < numKeys; ++i) {
- map = map.set(2 * i, 2 * i);
- ensureContainerInvariants(map);
- }
-
- for (int i = 0; i < numKeys - 1; ++i) {
- auto lowerExact = map.lower_bound(2 * i);
- ASSERT(lowerExact != map.end() && lowerExact->first == 2 * i);
- auto upperExact = map.upper_bound(2 * i);
- ASSERT(upperExact != map.end() && upperExact->first == 2 * (i + 1));
-
- auto lowerNear = map.lower_bound(2 * i + 1);
- ASSERT(lowerNear != map.end() && lowerNear->first == 2 * (i + 1));
- auto upperNear = map.upper_bound(2 * i + 1);
- ASSERT(upperNear != map.end() && upperNear->first == 2 * (i + 1));
- }
-}
-
-TEST(ImmutableMap, Iteration) {
- immutable::map<int, int> map;
- constexpr int numKeys = 100;
- for (int i = 0; i < numKeys; ++i) {
- map = map.set(2 * i, 2 * i);
- ensureContainerInvariants(map);
- }
-
- auto map0 = map;
- auto it0 = map0.begin();
-
- for (int i = 0; i < numKeys; ++i) {
- map = map.set(2 * i + 1, 2 * i + 1);
- ensureContainerInvariants(map);
- }
-
- auto map1 = map;
- auto it1 = map1.begin();
-
- for (int i = 0; i < numKeys; ++i) {
- ASSERT(it0 != map0.end());
- ASSERT_EQ(it0->first, 2 * i);
- ++it0;
-
- ASSERT(it1 != map1.end());
- ASSERT_EQ(it1->first, 2 * i);
- ++it1;
-
- ASSERT(it1 != map1.end());
- ASSERT_EQ(it1->first, 2 * i + 1);
- ++it1;
- }
- ASSERT(it0 == map0.end());
- ASSERT(it1 == map1.end());
-}
-
-TEST(ImmutableMap, Insert) {
- auto v0 = init_maps<int, int>();
-
- // Populate an initial set of values.
- auto v1 = MUTATE_KV(v0, insert, 1, 1);
- auto v2 = MUTATE_KV(v1, insert, 2, 2);
- auto v3 = MUTATE_KV(v2, insert, 3, 3);
- auto v4 = MUTATE_KV(v3, insert, 5, 5);
- ASSERT_CONTAINS(v4, 1);
- ASSERT_CONTAINS(v4, 2);
- ASSERT_CONTAINS(v4, 3);
- ASSERT_CONTAINS(v4, 5);
-
- // Insert of existing key is a noop.
- auto v5 = MUTATE_KV(v4, insert, 1, 2);
- ASSERT_CONTAINS(v5, 1);
- auto i5 = SEARCH(v5, find, 1);
- ASSERT_VALUE_EQ(i5, 1);
-
- // Insert at beginning works.
- auto v6 = MUTATE_KV(v4, insert, 0, 0);
- ASSERT_CONTAINS(v6, 0);
- auto i6 = SEARCH(v6, find, 0);
- ASSERT_VALUE_EQ(i6, 0);
-
- // Insert at end works.
- auto v7 = MUTATE_KV(v4, insert, 6, 6);
- ASSERT_CONTAINS(v7, 6);
- auto i7 = SEARCH(v7, find, 6);
- ASSERT_VALUE_EQ(i7, 6);
-
- // Insert in middle works.
- auto v8 = MUTATE_KV(v4, insert, 4, 4);
- ASSERT_CONTAINS(v8, 4);
- auto i8 = SEARCH(v8, find, 4);
- ASSERT_VALUE_EQ(i8, 4);
-}
-
-TEST(ImmutableMap, InsertViaIterator) {
- auto v0 = init_maps<int, int>();
-
- // Populate an initial set of values.
- auto v1 = MUTATE_KV(v0, insert, 1, 1);
- auto v2 = MUTATE_KV(v1, insert, 2, 2);
- auto v3 = MUTATE_KV(v2, insert, 3, 3);
- auto v4 = MUTATE_KV(v3, insert, 6, 6);
- ASSERT_CONTAINS(v4, 1);
- ASSERT_CONTAINS(v4, 2);
- ASSERT_CONTAINS(v4, 3);
- ASSERT_CONTAINS(v4, 6);
-
- // Giving the iterator for an existing element does a noop.
-
- auto it4_2 = SEARCH(v4, find, 2);
- auto v5 = MUTATE_IT_KV(v4, insert, it4_2, 2, 5);
- auto it5_2 = SEARCH(v5, find, 2);
- ASSERT_VALUE_EQ(it5_2, 2);
-
- // Giving end() as hint works appropriately whether hint is accurate or not.
-
- auto end5 = END(v5);
- auto v6 = MUTATE_IT_KV(v5, insert, end5, 2, 5);
- auto it6_2 = SEARCH(v6, find, 2);
- ASSERT_VALUE_EQ(it6_2, 2);
-
- auto end6 = END(v6);
- auto v7 = MUTATE_IT_KV(v6, insert, end6, 4, 4);
- ASSERT_NOT_CONTAINS(v6, 4);
- ASSERT_CONTAINS(v7, 4);
- auto it7_4 = SEARCH(v7, find, 4);
- ASSERT_VALUE_EQ(it7_4, 4);
-
- auto end7 = END(v7);
- auto v8 = MUTATE_IT_KV(v7, insert, end7, 7, 7);
- ASSERT_NOT_CONTAINS(v7, 7);
- ASSERT_CONTAINS(v8, 7);
- auto it8_7 = SEARCH(v8, find, 7);
- ASSERT_VALUE_EQ(it8_7, 7);
-
- // Giving hint from lower_bound works for both existing and new entries.
-
- auto lb8_2 = SEARCH(v8, lower_bound, 2);
- auto v9 = MUTATE_IT_KV(v8, insert, lb8_2, 2, 5);
- auto it9_2 = SEARCH(v9, find, 2);
- ASSERT_VALUE_EQ(it9_2, 2);
-
- auto lb9_0 = SEARCH(v9, lower_bound, 0);
- auto v10 = MUTATE_IT_KV(v9, insert, lb9_0, 0, 0);
- ASSERT_NOT_CONTAINS(v9, 0);
- ASSERT_CONTAINS(v10, 0);
- auto it10_0 = SEARCH(v10, find, 0);
- ASSERT_VALUE_EQ(it10_0, 0);
-
- auto lb10_5 = SEARCH(v10, lower_bound, 5);
- auto v11 = MUTATE_IT_KV(v10, insert, lb10_5, 5, 5);
- ASSERT_NOT_CONTAINS(v10, 5);
- ASSERT_CONTAINS(v11, 5);
- auto it11_5 = SEARCH(v11, find, 5);
- ASSERT_VALUE_EQ(it11_5, 5);
-
- auto lb11_8 = SEARCH(v11, lower_bound, 8);
- auto v12 = MUTATE_IT_KV(v11, insert, lb11_8, 8, 8);
- ASSERT_NOT_CONTAINS(v11, 8);
- ASSERT_CONTAINS(v12, 8);
- auto it12_8 = SEARCH(v12, find, 8);
- ASSERT_VALUE_EQ(it12_8, 8);
-}
-
-TEST(ImmutableMap, Set) {
- auto v0 = init_maps<int, int>();
-
- // Populate an initial set of values.
- auto v1 = MUTATE_KV(v0, insert, 1, 1);
- auto v2 = MUTATE_KV(v1, insert, 2, 2);
- auto v3 = MUTATE_KV(v2, insert, 3, 3);
- auto v4 = MUTATE_KV(v3, insert, 5, 5);
- ASSERT_CONTAINS(v4, 1);
- ASSERT_CONTAINS(v4, 2);
- ASSERT_CONTAINS(v4, 3);
- ASSERT_CONTAINS(v4, 5);
-
- // Set on existing key updates value.
- auto v5 = MUTATE_KV(v4, set, 1, 2);
- ASSERT_CONTAINS(v5, 1);
- auto i5 = SEARCH(v5, find, 1);
- ASSERT_VALUE_EQ(i5, 2);
-
- // Set to insert at beginning works.
- auto v6 = MUTATE_KV(v4, set, 0, 0);
- ASSERT_CONTAINS(v6, 0);
- auto i6 = SEARCH(v6, find, 0);
- ASSERT_VALUE_EQ(i6, 0);
-
- // Set to insert at end works.
- auto v7 = MUTATE_KV(v4, set, 6, 6);
- ASSERT_CONTAINS(v7, 6);
- auto i7 = SEARCH(v7, find, 6);
- ASSERT_VALUE_EQ(i7, 6);
-
- // Set to insert in middle works.
- auto v8 = MUTATE_KV(v4, set, 4, 4);
- ASSERT_CONTAINS(v8, 4);
- auto i8 = SEARCH(v8, find, 4);
- ASSERT_VALUE_EQ(i8, 4);
-}
-
-TEST(ImmutableMap, SetViaIterator) {
- auto v0 = init_maps<int, int>();
-
- // Populate an initial set of values.
- auto v1 = MUTATE_KV(v0, insert, 1, 1);
- auto v2 = MUTATE_KV(v1, insert, 2, 2);
- auto v3 = MUTATE_KV(v2, insert, 3, 3);
- auto v4 = MUTATE_KV(v3, insert, 6, 6);
- ASSERT_CONTAINS(v4, 1);
- ASSERT_CONTAINS(v4, 2);
- ASSERT_CONTAINS(v4, 3);
- ASSERT_CONTAINS(v4, 6);
-
- // Giving the iterator for an existing element updates the value.
-
- auto it4_2 = SEARCH(v4, find, 2);
- auto v5 = MUTATE_IT_KV(v4, set, it4_2, 2, 5);
- auto it5_2 = SEARCH(v5, find, 2);
- ASSERT_VALUE_EQ(it5_2, 5);
-
- // Giving end() as hint works appropriately whether hint is accurate or not.
-
- auto end5 = END(v5);
- auto v6 = MUTATE_IT_KV(v5, set, end5, 2, 7);
- auto it6_2 = SEARCH(v6, find, 2);
- ASSERT_VALUE_EQ(it6_2, 7);
-
- auto end6 = END(v6);
- auto v7 = MUTATE_IT_KV(v6, set, end6, 4, 4);
- ASSERT_NOT_CONTAINS(v6, 4);
- ASSERT_CONTAINS(v7, 4);
- auto it7_4 = SEARCH(v7, find, 4);
- ASSERT_VALUE_EQ(it7_4, 4);
-
- auto end7 = END(v7);
- auto v8 = MUTATE_IT_KV(v7, set, end7, 7, 7);
- ASSERT_NOT_CONTAINS(v7, 7);
- ASSERT_CONTAINS(v8, 7);
- auto it8_7 = SEARCH(v8, find, 7);
- ASSERT_VALUE_EQ(it8_7, 7);
-
- // Giving hint from lower_bound works for both existing and new entries.
-
- auto lb8_2 = SEARCH(v8, lower_bound, 2);
- auto v9 = MUTATE_IT_KV(v8, set, lb8_2, 2, 9);
- auto it9_2 = SEARCH(v9, find, 2);
- ASSERT_VALUE_EQ(it9_2, 9);
-
- auto lb9_0 = SEARCH(v9, lower_bound, 0);
- auto v10 = MUTATE_IT_KV(v9, set, lb9_0, 0, 0);
- ASSERT_NOT_CONTAINS(v9, 0);
- ASSERT_CONTAINS(v10, 0);
- auto it10_0 = SEARCH(v10, find, 0);
- ASSERT_VALUE_EQ(it10_0, 0);
-
- auto lb10_5 = SEARCH(v10, lower_bound, 5);
- auto v11 = MUTATE_IT_KV(v10, set, lb10_5, 5, 5);
- ASSERT_NOT_CONTAINS(v10, 5);
- ASSERT_CONTAINS(v11, 5);
- auto it11_5 = SEARCH(v11, find, 5);
- ASSERT_VALUE_EQ(it11_5, 5);
-
- auto lb11_8 = SEARCH(v11, lower_bound, 8);
- auto v12 = MUTATE_IT_KV(v11, set, lb11_8, 8, 8);
- ASSERT_NOT_CONTAINS(v11, 8);
- ASSERT_CONTAINS(v12, 8);
- auto it12_8 = SEARCH(v12, find, 8);
- ASSERT_VALUE_EQ(it12_8, 8);
-}
-
-TEST(ImmutableMap, Update) {
- auto v0 = init_maps<int, int>();
-
- // Populate an initial set of values.
- auto v1 = MUTATE_KV(v0, insert, 1, 1);
- auto v2 = MUTATE_KV(v1, insert, 2, 2);
- auto v3 = MUTATE_KV(v2, insert, 3, 3);
- auto v4 = MUTATE_KV(v3, insert, 5, 5);
- ASSERT_CONTAINS(v4, 1);
- ASSERT_CONTAINS(v4, 2);
- ASSERT_CONTAINS(v4, 3);
- ASSERT_CONTAINS(v4, 5);
-
- // Update on existing key updates value.
- auto v5 = MUTATE_KV(v4, update, 1, [](int x) { return x + 1; });
- ASSERT_CONTAINS(v5, 1);
- auto i5 = SEARCH(v5, find, 1);
- ASSERT_VALUE_EQ(i5, 2);
-
- // Update to insert at beginning works.
- auto v6 = MUTATE_KV(v4, update, 0, [](int x) { return x + 1; });
- ASSERT_CONTAINS(v6, 0);
- auto i6 = SEARCH(v6, find, 0);
- ASSERT_VALUE_EQ(i6, 1);
-
- // Update to insert at end works.
- auto v7 = MUTATE_KV(v4, update, 6, [](int x) { return x + 1; });
- ASSERT_CONTAINS(v7, 6);
- auto i7 = SEARCH(v7, find, 6);
- ASSERT_VALUE_EQ(i7, 1);
-
- // Update to insert in middle works.
- auto v8 = MUTATE_KV(v4, update, 4, [](int x) { return x + 1; });
- ASSERT_CONTAINS(v8, 4);
- auto i8 = SEARCH(v8, find, 4);
- ASSERT_VALUE_EQ(i8, 1);
-}
-
-TEST(ImmutableMap, UpdateViaIterator) {
- auto v0 = init_maps<int, int>();
-
- // Populate an initial set of values.
- auto v1 = MUTATE_KV(v0, insert, 1, 1);
- auto v2 = MUTATE_KV(v1, insert, 2, 2);
- auto v3 = MUTATE_KV(v2, insert, 3, 3);
- auto v4 = MUTATE_KV(v3, insert, 6, 6);
- ASSERT_CONTAINS(v4, 1);
- ASSERT_CONTAINS(v4, 2);
- ASSERT_CONTAINS(v4, 3);
- ASSERT_CONTAINS(v4, 6);
-
- // Giving the iterator for an existing element updates the value.
-
- auto it4_2 = SEARCH(v4, find, 2);
- auto v5 = MUTATE_IT_KV(v4, update, it4_2, 2, [](int x) { return x + 1; });
- auto it5_2 = SEARCH(v5, find, 2);
- ASSERT_VALUE_EQ(it5_2, 3);
-
- // Giving end() as hint works appropriately whether hint is accurate or not.
-
- auto end5 = END(v5);
- auto v6 = MUTATE_IT_KV(v5, update, end5, 2, [](int x) { return x + 1; });
- auto it6_2 = SEARCH(v6, find, 2);
- ASSERT_VALUE_EQ(it6_2, 4);
-
- auto end6 = END(v6);
- auto v7 = MUTATE_IT_KV(v6, update, end6, 4, [](int x) { return x + 1; });
- ASSERT_NOT_CONTAINS(v6, 4);
- ASSERT_CONTAINS(v7, 4);
- auto it7_4 = SEARCH(v7, find, 4);
- ASSERT_VALUE_EQ(it7_4, 1);
-
- auto end7 = END(v7);
- auto v8 = MUTATE_IT_KV(v7, update, end7, 7, [](int x) { return x + 1; });
- ASSERT_NOT_CONTAINS(v7, 7);
- ASSERT_CONTAINS(v8, 7);
- auto it8_7 = SEARCH(v8, find, 7);
- ASSERT_VALUE_EQ(it8_7, 1);
-
- // Giving hint from lower_bound works for both existing and new entries.
-
- auto lb8_2 = SEARCH(v8, lower_bound, 2);
- auto v9 = MUTATE_IT_KV(v8, update, lb8_2, 2, [](int x) { return x + 1; });
- auto it9_2 = SEARCH(v9, find, 2);
- ASSERT_VALUE_EQ(it9_2, 5);
-
- auto lb9_0 = SEARCH(v9, lower_bound, 0);
- auto v10 = MUTATE_IT_KV(v9, update, lb9_0, 0, [](int x) { return x + 1; });
- ASSERT_NOT_CONTAINS(v9, 0);
- ASSERT_CONTAINS(v10, 0);
- auto it10_0 = SEARCH(v10, find, 0);
- ASSERT_VALUE_EQ(it10_0, 1);
-
- auto lb10_5 = SEARCH(v10, lower_bound, 5);
- auto v11 = MUTATE_IT_KV(v10, update, lb10_5, 5, [](int x) { return x + 1; });
- ASSERT_NOT_CONTAINS(v10, 5);
- ASSERT_CONTAINS(v11, 5);
- auto it11_5 = SEARCH(v11, find, 5);
- ASSERT_VALUE_EQ(it11_5, 1);
-
- auto lb11_8 = SEARCH(v11, lower_bound, 8);
- auto v12 = MUTATE_IT_KV(v11, update, lb11_8, 8, [](int x) { return x + 1; });
- ASSERT_NOT_CONTAINS(v11, 8);
- ASSERT_CONTAINS(v12, 8);
- auto it12_8 = SEARCH(v12, find, 8);
- ASSERT_VALUE_EQ(it12_8, 1);
-}
-
-TEST(ImmutableMap, UpdateIfExists) {
- auto v0 = init_maps<int, int>();
-
- // Populate an initial set of values.
- auto v1 = MUTATE_KV(v0, insert, 1, 1);
- auto v2 = MUTATE_KV(v1, insert, 2, 2);
- auto v3 = MUTATE_KV(v2, insert, 3, 3);
- auto v4 = MUTATE_KV(v3, insert, 5, 5);
- ASSERT_CONTAINS(v4, 1);
- ASSERT_CONTAINS(v4, 2);
- ASSERT_CONTAINS(v4, 3);
- ASSERT_CONTAINS(v4, 5);
-
- // Update on existing key updates value.
- auto v5 = MUTATE_KV(v4, update_if_exists, 1, [](int x) { return x + 1; });
- ASSERT_CONTAINS(v5, 1);
- auto i5 = SEARCH(v5, find, 1);
- ASSERT_VALUE_EQ(i5, 2);
-
- // Update to insert at beginning does nothing.
- auto v6 = MUTATE_KV(v4, update_if_exists, 0, [](int x) { return x + 1; });
- ASSERT_NOT_CONTAINS(v6, 0);
-
- // Update to insert at end does nothing.
- auto v7 = MUTATE_KV(v4, update_if_exists, 6, [](int x) { return x + 1; });
- ASSERT_NOT_CONTAINS(v7, 6);
-
- // Update to insert in middle does nothing.
- auto v8 = MUTATE_KV(v4, update_if_exists, 4, [](int x) { return x + 1; });
- ASSERT_NOT_CONTAINS(v8, 4);
-}
-
-TEST(ImmutableMap, UpdateIfExistsViaIterator) {
- auto v0 = init_maps<int, int>();
-
- // Populate an initial set of values.
- auto v1 = MUTATE_KV(v0, insert, 1, 1);
- auto v2 = MUTATE_KV(v1, insert, 2, 2);
- auto v3 = MUTATE_KV(v2, insert, 3, 3);
- auto v4 = MUTATE_KV(v3, insert, 6, 6);
- ASSERT_CONTAINS(v4, 1);
- ASSERT_CONTAINS(v4, 2);
- ASSERT_CONTAINS(v4, 3);
- ASSERT_CONTAINS(v4, 6);
-
- // Giving the iterator for an existing element updates the value.
-
- auto it4_2 = SEARCH(v4, find, 2);
- auto v5 = MUTATE_IT_KV(v4, update_if_exists, it4_2, 2, [](int x) { return x + 1; });
- auto it5_2 = SEARCH(v5, find, 2);
- ASSERT_VALUE_EQ(it5_2, 3);
-
- // Giving end() as hint works appropriately whether hint is accurate or not.
-
- auto end5 = END(v5);
- auto v6 = MUTATE_IT_KV(v5, update_if_exists, end5, 2, [](int x) { return x + 1; });
- auto it6_2 = SEARCH(v6, find, 2);
- ASSERT_VALUE_EQ(it6_2, 4);
-
- auto end6 = END(v6);
- auto v7 = MUTATE_IT_KV(v6, update_if_exists, end6, 4, [](int x) { return x + 1; });
- ASSERT_NOT_CONTAINS(v7, 4);
-
- auto end7 = END(v7);
- auto v8 = MUTATE_IT_KV(v7, update_if_exists, end7, 7, [](int x) { return x + 1; });
- ASSERT_NOT_CONTAINS(v8, 7);
-
- // Giving hint from lower_bound works for both existing and non-existing entries.
-
- auto lb8_2 = SEARCH(v8, lower_bound, 2);
- auto v9 = MUTATE_IT_KV(v8, update_if_exists, lb8_2, 2, [](int x) { return x + 1; });
- auto it9_2 = SEARCH(v9, find, 2);
- ASSERT_VALUE_EQ(it9_2, 5);
-
- auto lb9_0 = SEARCH(v9, lower_bound, 0);
- auto v10 = MUTATE_IT_KV(v9, update_if_exists, lb9_0, 0, [](int x) { return x + 1; });
- ASSERT_NOT_CONTAINS(v10, 0);
-
- auto lb10_5 = SEARCH(v10, lower_bound, 5);
- auto v11 = MUTATE_IT_KV(v10, update_if_exists, lb10_5, 5, [](int x) { return x + 1; });
- ASSERT_NOT_CONTAINS(v11, 5);
-
- auto lb11_8 = SEARCH(v11, lower_bound, 8);
- auto v12 = MUTATE_IT_KV(v11, update_if_exists, lb11_8, 8, [](int x) { return x + 1; });
- ASSERT_NOT_CONTAINS(v12, 8);
-}
-
-TEST(ImmutableMap, Erase) {
- auto v0 = init_maps<int, int>();
-
- // Populate an initial set of values.
- auto v1 = MUTATE_KV(v0, insert, 1, 1);
- auto v2 = MUTATE_KV(v1, insert, 2, 2);
- auto v3 = MUTATE_KV(v2, insert, 3, 3);
- auto v4 = MUTATE_KV(v3, insert, 5, 5);
- ASSERT_CONTAINS(v4, 1);
- ASSERT_CONTAINS(v4, 2);
- ASSERT_CONTAINS(v4, 3);
- ASSERT_CONTAINS(v4, 5);
-
- // Erase on existing key removes value.
- auto v5 = MUTATE_K(v4, erase, 1);
- ASSERT_NOT_CONTAINS(v5, 1);
-
- // Erase on non-existent key does nothing.
- auto v6 = MUTATE_K(v5, erase, 0);
- ASSERT_NOT_CONTAINS(v6, 0);
- ASSERT_CONTAINS(v6, 2);
- ASSERT_CONTAINS(v6, 3);
- ASSERT_CONTAINS(v6, 5);
-}
-
-TEST(ImmutableMap, EraseViaIterator) {
- auto v0 = init_maps<int, int>();
-
- // Populate an initial set of values.
- auto v1 = MUTATE_KV(v0, insert, 1, 1);
- auto v2 = MUTATE_KV(v1, insert, 2, 2);
- auto v3 = MUTATE_KV(v2, insert, 3, 3);
- auto v4 = MUTATE_KV(v3, insert, 5, 5);
- auto v5 = MUTATE_KV(v4, insert, 6, 6);
- ASSERT_CONTAINS(v5, 1);
- ASSERT_CONTAINS(v5, 2);
- ASSERT_CONTAINS(v5, 3);
- ASSERT_CONTAINS(v5, 5);
- ASSERT_CONTAINS(v5, 6);
-
- // Giving the iterator for an existing element erases the value.
-
- auto it5_2 = SEARCH(v5, find, 2);
- auto v6 = MUTATE_IT_K(v5, erase, it5_2, 2);
- ASSERT_NOT_CONTAINS(v6, 2);
-
- // Giving end() as hint works appropriately whether hint is accurate or not.
-
- auto end6 = END(v6);
- auto v7 = MUTATE_IT_K(v6, erase, end6, 3);
- ASSERT_NOT_CONTAINS(v7, 3);
-
- auto end7 = END(v7);
- auto v8 = MUTATE_IT_K(v7, erase, end7, 4);
- ASSERT_NOT_CONTAINS(v8, 4);
-
- auto end8 = END(v8);
- auto v9 = MUTATE_IT_K(v8, erase, end8, 7);
- ASSERT_NOT_CONTAINS(v9, 7);
-
- // Giving hint from lower_bound works for both existing and non-existing entries.
-
- auto lb9_5 = SEARCH(v9, lower_bound, 5);
- auto v10 = MUTATE_IT_K(v9, erase, lb9_5, 5);
- ASSERT_NOT_CONTAINS(v10, 5);
-
- auto lb10_0 = SEARCH(v10, lower_bound, 0);
- auto v11 = MUTATE_IT_K(v10, erase, lb10_0, 0);
-
- auto lb11_4 = SEARCH(v10, lower_bound, 4);
- auto v12 = MUTATE_IT_K(v11, erase, lb11_4, 4);
-
- auto lb12_7 = SEARCH(v12, lower_bound, 7);
- auto v13 = MUTATE_IT_K(v12, erase, lb12_7, 7);
-
- ASSERT_CONTAINS(v13, 1);
- ASSERT_CONTAINS(v13, 6);
- ASSERT_NOT_CONTAINS(v13, 0);
- ASSERT_NOT_CONTAINS(v13, 2);
- ASSERT_NOT_CONTAINS(v13, 3);
- ASSERT_NOT_CONTAINS(v13, 4);
- ASSERT_NOT_CONTAINS(v13, 5);
- ASSERT_NOT_CONTAINS(v13, 7);
-}
-
-TEST(ImmutableMap, ExclusiveOwnership) {
- immutable::map<int, int> v0;
-
- auto v1 = v0.set(1, 1);
- auto v2 = v1.set(2, 2);
- auto v3 = v2.set(3, 3);
-
- ASSERT_TRUE(v1.contains(1));
- ASSERT_TRUE(v2.contains(1));
- ASSERT_TRUE(v3.contains(1));
-
- // Claiming exclusive ownership over v3 means v3 will no longer be valid after mutation, but
- // older versions should be unperturbed.
- auto v4 = std::move(v3).erase(1);
- ASSERT_TRUE(v1.contains(1));
- ASSERT_TRUE(v2.contains(1));
- ASSERT_FALSE(v4.contains(1));
-}
-
-TEST(ImmutableSet, Basic) {
- // Insert some values and verify that the data structure is behaving as expected
- immutable::set<int> v0;
- auto v1 = v0.insert(1);
- // Record the iterator for the key '1', verify that this doesn't change after performing
- // more inserts
- auto v1it = v1.find(1);
-
- // Create distinct branches of the history from v1. v0 and v1 should be unaffected
- auto v2 = v1.insert(2);
- auto v3 = v1.insert(3);
-
- // Verify that values are as expected
- ASSERT_EQ(v0.size(), 0);
-
- ASSERT_EQ(v1.size(), 1);
- ASSERT_TRUE(v1.contains(1));
- ASSERT_EQ(*v1.find(1), 1);
- ASSERT_FALSE(v1.contains(2));
- ASSERT_FALSE(v1.contains(3));
-
- ASSERT_EQ(v2.size(), 2);
- ASSERT_TRUE(v2.contains(1));
- ASSERT_EQ(*v2.find(1), 1);
- ASSERT_TRUE(v2.contains(2));
- ASSERT_EQ(*v2.find(2), 2);
- ASSERT_FALSE(v2.contains(3));
-
- ASSERT_EQ(v3.size(), 2);
- ASSERT_TRUE(v3.contains(1));
- ASSERT_EQ(*v3.find(1), 1);
- ASSERT_TRUE(v3.contains(3));
- ASSERT_EQ(*v3.find(3), 3);
- ASSERT_FALSE(v3.contains(2));
-
- // Verify that v1's iterator did not change
- ASSERT(v1.find(1) == v1it);
-
- // Verify that erase works as expected, and preserves history.
- auto v4 = v3.erase(1).erase(3);
- ASSERT_EQ(v4.size(), 0);
- ASSERT_FALSE(v4.contains(1));
- ASSERT_FALSE(v4.contains(2));
- ASSERT_FALSE(v4.contains(3));
- ASSERT_EQ(v3.size(), 2);
- ASSERT_TRUE(v3.contains(1));
- ASSERT_TRUE(v3.contains(3));
- ASSERT_EQ(v2.size(), 2);
- ASSERT_TRUE(v2.contains(1));
- ASSERT_TRUE(v2.contains(2));
- ASSERT_EQ(v1.size(), 1);
- ASSERT_TRUE(v1.contains(1));
-
- ensureContainerInvariants({v0, v1, v2, v3, v4});
-}
-
-TEST(ImmutableSet, UserDefinedType) {
- immutable::set<UserDefinedKey> v0;
- auto v1 = v0.insert(UserDefinedKey(1));
- ASSERT(v1.find(UserDefinedKey(1)) != v1.end());
- ASSERT_EQ(*v1.find(UserDefinedKey(1)), UserDefinedKey(1));
-
- ensureContainerInvariants({v0, v1});
-}
-
-TEST(ImmutableSet, IncomparableType) {
- immutable::set<Incomparable, CompareIncomparable> v0;
- auto v1 = v0.insert(Incomparable(1));
- ASSERT_TRUE(v1.contains(Incomparable(1)));
-
- ensureContainerInvariants(v0, CompareIncomparable{});
- ensureContainerInvariants(v1, CompareIncomparable{});
-}
-
-TEST(ImmutableSet, HeterogeneousLookup) {
- immutable::set<std::string, StringCompare> v0;
- auto v1 = v0.insert("str");
-
- // Lookup using StringData without the need to convert to string.
- ASSERT(v1.find("str"_sd) != v1.end());
-
- ensureContainerInvariants({v0, v1});
-}
-
-TEST(ImmutableSet, Bounds) {
- immutable::set<int> set;
- constexpr int numKeys = 100;
- for (int i = 0; i < numKeys; ++i) {
- set = set.insert(2 * i);
- ensureContainerInvariants(set);
- }
-
- for (int i = 0; i < numKeys - 1; ++i) {
- auto lowerExact = set.lower_bound(2 * i);
- ASSERT(lowerExact != set.end() && *lowerExact == 2 * i);
- auto upperExact = set.upper_bound(2 * i);
- ASSERT(upperExact != set.end() && *upperExact == 2 * (i + 1));
-
- auto lowerNear = set.lower_bound(2 * i + 1);
- ASSERT(lowerNear != set.end() && *lowerNear == 2 * (i + 1));
- auto upperNear = set.upper_bound(2 * i + 1);
- ASSERT(upperNear != set.end() && *upperNear == 2 * (i + 1));
- }
-}
-
-TEST(ImmutableSet, Iteration) {
- immutable::set<int> set;
- constexpr int numKeys = 100;
- for (int i = 0; i < numKeys; ++i) {
- set = set.insert(2 * i);
- ensureContainerInvariants(set);
- }
-
- auto set0 = set;
- auto it0 = set0.begin();
-
- for (int i = 0; i < numKeys; ++i) {
- set = set.insert(2 * i + 1);
- ensureContainerInvariants(set);
- }
-
- auto set1 = set;
- auto it1 = set1.begin();
-
- for (int i = 0; i < numKeys; ++i) {
- ASSERT(it0 != set0.end());
- ASSERT_EQ(*it0, 2 * i);
- ++it0;
-
- ASSERT(it1 != set1.end());
- ASSERT_EQ(*it1, 2 * i);
- ++it1;
-
- ASSERT(it1 != set1.end());
- ASSERT_EQ(*it1, 2 * i + 1);
- ++it1;
- }
- ASSERT(it0 == set0.end());
- ASSERT(it1 == set1.end());
-}
-
-TEST(ImmutableSet, Insert) {
- auto v0 = init_sets<int>();
-
- // Populate an initial set of values.
- auto v1 = MUTATE_K(v0, insert, 1);
- auto v2 = MUTATE_K(v1, insert, 2);
- auto v3 = MUTATE_K(v2, insert, 3);
- auto v4 = MUTATE_K(v3, insert, 5);
- ASSERT_CONTAINS(v4, 1);
- ASSERT_CONTAINS(v4, 2);
- ASSERT_CONTAINS(v4, 3);
- ASSERT_CONTAINS(v4, 5);
-
- // Insert of existing key is a noop.
- auto v5 = MUTATE_K(v4, insert, 1); // enforces no duplicates
- ASSERT_CONTAINS(v5, 1);
-
- // Insert at beginning works.
- auto v6 = MUTATE_K(v4, insert, 0);
- ASSERT_CONTAINS(v6, 0);
-
- // Insert at end works.
- auto v7 = MUTATE_K(v4, insert, 6);
- ASSERT_CONTAINS(v7, 6);
-
- // Insert in middle works.
- auto v8 = MUTATE_K(v4, insert, 4);
- ASSERT_CONTAINS(v8, 4);
-}
-
-TEST(ImmutableSet, InsertViaIterator) {
- auto v0 = init_sets<int>();
-
- // Populate an initial set of values.
- auto v1 = MUTATE_K(v0, insert, 1);
- auto v2 = MUTATE_K(v1, insert, 2);
- auto v3 = MUTATE_K(v2, insert, 3);
- auto v4 = MUTATE_K(v3, insert, 6);
- ASSERT_CONTAINS(v4, 1);
- ASSERT_CONTAINS(v4, 2);
- ASSERT_CONTAINS(v4, 3);
- ASSERT_CONTAINS(v4, 6);
-
- // Giving the iterator for an existing element does a noop.
-
- auto it4_2 = SEARCH(v4, find, 2);
- auto v5 = MUTATE_IT_K(v4, insert, it4_2, 2); // enforces no duplicates
- ASSERT_CONTAINS(v5, 2);
-
- // Giving end() as hint works appropriately whether hint is accurate or not.
-
- auto end5 = END(v5);
- auto v6 = MUTATE_IT_K(v5, insert, end5, 2); // enforces no duplicates
- ASSERT_CONTAINS(v6, 2);
-
- auto end6 = END(v6);
- auto v7 = MUTATE_IT_K(v6, insert, end6, 4);
- ASSERT_CONTAINS(v7, 4);
-
- auto end7 = END(v7);
- auto v8 = MUTATE_IT_K(v7, insert, end7, 7);
- ASSERT_CONTAINS(v8, 7);
-
- // Giving hint from lower_bound works for both existing and new entries.
-
- auto lb8_2 = SEARCH(v8, lower_bound, 2);
- auto v9 = MUTATE_IT_K(v8, insert, lb8_2, 2); // enforces no duplicates
- ASSERT_CONTAINS(v9, 2);
-
- auto lb9_0 = SEARCH(v9, lower_bound, 0);
- auto v10 = MUTATE_IT_K(v9, insert, lb9_0, 0);
- ASSERT_CONTAINS(v10, 0);
-
- auto lb10_5 = SEARCH(v10, lower_bound, 5);
- auto v11 = MUTATE_IT_K(v10, insert, lb10_5, 5);
- ASSERT_CONTAINS(v11, 5);
-
- auto lb11_8 = SEARCH(v11, lower_bound, 8);
- auto v12 = MUTATE_IT_K(v11, insert, lb11_8, 8);
- ASSERT_CONTAINS(v12, 8);
-}
-
-TEST(ImmutableSet, Erase) {
- auto v0 = init_sets<int>();
-
- // Populate an initial set of values.
- auto v1 = MUTATE_K(v0, insert, 1);
- auto v2 = MUTATE_K(v1, insert, 2);
- auto v3 = MUTATE_K(v2, insert, 3);
- auto v4 = MUTATE_K(v3, insert, 5);
- ASSERT_CONTAINS(v4, 1);
- ASSERT_CONTAINS(v4, 2);
- ASSERT_CONTAINS(v4, 3);
- ASSERT_CONTAINS(v4, 5);
-
- // Erase on existing key removes value.
- auto v5 = MUTATE_K(v4, erase, 1);
- ASSERT_NOT_CONTAINS(v5, 1);
-
- // Erase on non-existent key does nothing.
- auto v6 = MUTATE_K(v5, erase, 0);
- ASSERT_NOT_CONTAINS(v6, 0);
- ASSERT_CONTAINS(v6, 2);
- ASSERT_CONTAINS(v6, 3);
- ASSERT_CONTAINS(v6, 5);
-}
-
-TEST(ImmutableSet, EraseViaIterator) {
- auto v0 = init_sets<int>();
-
- // Populate an initial set of values.
- auto v1 = MUTATE_K(v0, insert, 1);
- auto v2 = MUTATE_K(v1, insert, 2);
- auto v3 = MUTATE_K(v2, insert, 3);
- auto v4 = MUTATE_K(v3, insert, 5);
- auto v5 = MUTATE_K(v4, insert, 6);
- ASSERT_CONTAINS(v5, 1);
- ASSERT_CONTAINS(v5, 2);
- ASSERT_CONTAINS(v5, 3);
- ASSERT_CONTAINS(v5, 5);
- ASSERT_CONTAINS(v5, 6);
-
- // Giving the iterator for an existing element erases the value.
-
- auto it5_2 = SEARCH(v5, find, 2);
- auto v6 = MUTATE_IT_K(v5, erase, it5_2, 2);
- ASSERT_NOT_CONTAINS(v6, 2);
-
- // Giving end() as hint works appropriately whether hint is accurate or not.
-
- auto end6 = END(v6);
- auto v7 = MUTATE_IT_K(v6, erase, end6, 3);
- ASSERT_NOT_CONTAINS(v7, 3);
-
- auto end7 = END(v7);
- auto v8 = MUTATE_IT_K(v7, erase, end7, 4);
- ASSERT_NOT_CONTAINS(v8, 4);
-
- auto end8 = END(v8);
- auto v9 = MUTATE_IT_K(v8, erase, end8, 7);
- ASSERT_NOT_CONTAINS(v9, 7);
-
- // Giving hint from lower_bound works for both existing and non-existing entries.
-
- auto lb9_5 = SEARCH(v9, lower_bound, 5);
- auto v10 = MUTATE_IT_K(v9, erase, lb9_5, 5);
- ASSERT_NOT_CONTAINS(v10, 5);
-
- auto lb10_0 = SEARCH(v10, lower_bound, 0);
- auto v11 = MUTATE_IT_K(v10, erase, lb10_0, 0);
-
- auto lb11_4 = SEARCH(v10, lower_bound, 4);
- auto v12 = MUTATE_IT_K(v11, erase, lb11_4, 4);
-
- auto lb12_7 = SEARCH(v12, lower_bound, 7);
- auto v13 = MUTATE_IT_K(v12, erase, lb12_7, 7);
-
- ASSERT_CONTAINS(v13, 1);
- ASSERT_CONTAINS(v13, 6);
- ASSERT_NOT_CONTAINS(v13, 0);
- ASSERT_NOT_CONTAINS(v13, 2);
- ASSERT_NOT_CONTAINS(v13, 3);
- ASSERT_NOT_CONTAINS(v13, 4);
- ASSERT_NOT_CONTAINS(v13, 5);
- ASSERT_NOT_CONTAINS(v13, 7);
-}
-
-TEST(ImmutableSet, ExclusiveOwnership) {
- immutable::set<int> v0;
-
- auto v1 = v0.insert(1);
- auto v2 = v1.insert(2);
- auto v3 = v2.insert(3);
-
- ASSERT_TRUE(v1.contains(1));
- ASSERT_TRUE(v2.contains(1));
- ASSERT_TRUE(v3.contains(1));
-
- // Claiming exclusive ownership over v3 means v3 will no longer be valid after mutation, but
- // older versions should be unperturbed.
- auto v4 = std::move(v3).erase(1);
- ASSERT_TRUE(v1.contains(1));
- ASSERT_TRUE(v2.contains(1));
- ASSERT_FALSE(v4.contains(1));
-}
-
-} // namespace
-} // namespace mongo
diff --git a/src/mongo/util/immutable/immutable_std_comparison_bm.cpp b/src/mongo/util/immutable/immutable_std_comparison_bm.cpp
deleted file mode 100644
index 503984ca1d2..00000000000
--- a/src/mongo/util/immutable/immutable_std_comparison_bm.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/**
- * Copyright (C) 2023-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include <benchmark/benchmark.h>
-
-#include "mongo/util/immutable/map.h"
-#include <map>
-
-namespace mongo {
-
-static void BM_std_insert_op(benchmark::State& state) {
- std::map<int, int> map;
-
- int64_t i = 0;
- for (; i < state.range(0); i++) {
- map[i] = i;
- }
-
- for (auto _ : state) {
- map[i] = i;
- i++;
- benchmark::ClobberMemory();
- }
-}
-
-static void BM_std_copy_op(benchmark::State& state) {
- std::map<int, int> map;
-
- int64_t i = 0;
- for (; i < state.range(0); i++) {
- map[i] = i;
- }
-
- std::map<int, int> mapCopy;
- for (auto _ : state) {
- mapCopy = map;
- benchmark::ClobberMemory();
- }
-}
-
-static void BM_std_find_op(benchmark::State& state) {
- std::map<int, int> map;
-
- int64_t i = 0;
- for (; i < state.range(0); i++) {
- map[i] = i;
- }
-
- for (auto _ : state) {
- benchmark::DoNotOptimize(map.find(i - 1));
- benchmark::ClobberMemory();
- }
-}
-
-static void BM_std_copy_and_insert_op(benchmark::State& state) {
- std::map<int, int> map;
-
- int64_t i = 0;
- for (; i < state.range(0); i++) {
- map[i] = i;
- }
-
- std::map<int, int> mapCopy;
- for (auto _ : state) {
- mapCopy = map;
- mapCopy[i] = i;
- benchmark::ClobberMemory();
- }
-}
-
-static void BM_immutable_insert_op(benchmark::State& state) {
- immutable::map<int, int> map;
-
- int64_t i = 0;
- for (; i < state.range(0); i++) {
- map = std::move(map).set(i, i);
- }
-
- for (auto _ : state) {
- map = std::move(map).set(i, i);
- i++;
- benchmark::ClobberMemory();
- }
-}
-
-static void BM_immutable_copy_op(benchmark::State& state) {
- immutable::map<int, int> map;
-
- int64_t i = 0;
- for (; i < state.range(0); i++) {
- map = std::move(map).set(i, i);
- }
-
- immutable::map<int, int> mapCopy;
- for (auto _ : state) {
- mapCopy = map;
- benchmark::ClobberMemory();
- }
-}
-
-static void BM_immutable_find_op(benchmark::State& state) {
- immutable::map<int, int> map;
-
- int64_t i = 0;
- for (; i < state.range(0); i++) {
- map = std::move(map).set(i, i);
- }
-
- for (auto _ : state) {
- benchmark::DoNotOptimize(map.find(i - 1));
- benchmark::ClobberMemory();
- }
-}
-
-static void BM_immutable_copy_and_insert_op(benchmark::State& state) {
- immutable::map<int, int> map;
-
- int64_t i = 0;
- for (; i < state.range(0); i++) {
- map = std::move(map).set(i, i);
- }
-
- immutable::map<int, int> mapCopy;
- for (auto _ : state) {
- mapCopy = map.set(i, i);
- benchmark::ClobberMemory();
- }
-}
-
-// Run with varying container sizes: [ 8, 16, 32, 64, 128, 256, 512, 1024, 2k, 4k, 8k ].
-BENCHMARK(BM_std_insert_op)->RangeMultiplier(2)->Range(8, 8 << 10);
-BENCHMARK(BM_immutable_insert_op)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-BENCHMARK(BM_std_copy_op)->RangeMultiplier(2)->Range(8, 8 << 10);
-BENCHMARK(BM_immutable_copy_op)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-BENCHMARK(BM_std_find_op)->RangeMultiplier(2)->Range(8, 8 << 10);
-BENCHMARK(BM_immutable_find_op)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-BENCHMARK(BM_std_copy_and_insert_op)->RangeMultiplier(2)->Range(8, 8 << 10);
-BENCHMARK(BM_immutable_copy_and_insert_op)->RangeMultiplier(2)->Range(8, 8 << 10);
-} // namespace mongo
diff --git a/src/mongo/util/immutable/immutable_unordered_test.cpp b/src/mongo/util/immutable/immutable_unordered_test.cpp
deleted file mode 100644
index d6e2aa055dd..00000000000
--- a/src/mongo/util/immutable/immutable_unordered_test.cpp
+++ /dev/null
@@ -1,234 +0,0 @@
-/**
- * Copyright (C) 2023-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-
-#include "mongo/unittest/unittest.h"
-
-#include "mongo/util/immutable/unordered_map.h"
-#include "mongo/util/immutable/unordered_set.h"
-#include "mongo/util/string_map.h"
-
-#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kTest
-
-namespace mongo {
-namespace {
-
-class UserDefinedKey {
-public:
- UserDefinedKey() = default;
- explicit UserDefinedKey(int val) : a(val) {}
-
- bool operator==(const UserDefinedKey& rhs) const {
- return a == rhs.a;
- }
-
- // Use the Abseil hashing framework
- template <typename H>
- friend H AbslHashValue(H h, const UserDefinedKey& obj) {
- return H::combine(std::move(h), obj.a);
- }
-
-private:
- int a = 0;
-};
-
-TEST(ImmutableUnorderedMap, Basic) {
- // Insert some values and verify that the data structure is behaving as expected
- immutable::unordered_map<int, int> v0;
- auto v1 = v0.set(1, 2);
- // Record the pointer to the value '1', verify that this doesn't change after performing more
- // inserts
- auto v1Val = v1.find(1);
-
- // Create distinct branches of the history from v1. v0 and v1 should be unaffected
- auto v2 = v1.update(1, [](int v) { return v += 1; });
- auto v3 = v1.set(2, 3);
-
- // Verify that values are as expected
- ASSERT_EQ(v0.size(), 0);
-
- ASSERT_EQ(v1.size(), 1);
- ASSERT(v1.find(1));
- ASSERT_EQ(*v1.find(1), 2);
-
- ASSERT_EQ(v2.size(), 1);
- ASSERT(v2.find(1));
- ASSERT_EQ(*v2.find(1), 3);
- ASSERT(!v2.find(2));
-
- ASSERT_EQ(v3.size(), 2);
- ASSERT(v3.find(1));
- ASSERT_EQ(*v3.find(1), 2);
- ASSERT(v3.find(2));
- ASSERT_EQ(*v3.find(2), 3);
-
- // Verify that pointer to v1's value did not change
- ASSERT_EQ(v1.find(1), v1Val);
-}
-
-TEST(ImmutableUnorderedMap, UserDefinedType) {
- immutable::unordered_map<UserDefinedKey, int> v0;
- auto v1 = v0.set(UserDefinedKey(1), 2);
- ASSERT(v1.find(UserDefinedKey(1)));
-}
-
-TEST(ImmutableUnorderedMap, HeterogeneousLookup) {
- immutable::unordered_map<std::string, int, StringMapHasher, StringMapEq> v0;
- auto v1 = v0.set("str", 1);
-
- // Lookup using StringData without the need to convert to string.
- ASSERT(v1.find("str"_sd));
-
- // Lookup using pre-hash
- StringMapHashedKey hashedKey = StringMapHasher().hashed_key("str"_sd);
- ASSERT(v1.find(hashedKey));
-}
-
-TEST(ImmutableUnorderedMap, BatchWrite) {
- immutable::unordered_map<int, int> v0;
-
- auto transient = v0.transient();
- transient.set(1, 2);
- transient.set(2, 3);
- immutable::unordered_map<int, int> v1 = transient.persistent();
-
- ASSERT(!v0.find(1));
- ASSERT(!v0.find(2));
- ASSERT(v1.find(1));
- ASSERT(v1.find(2));
-}
-
-TEST(ImmutableUnorderedMap, ExclusiveOwnership) {
- immutable::unordered_map<int, int> v0;
-
- auto v1 = v0.set(1, 1);
- auto v2 = v1.set(2, 2);
- auto v3 = v2.set(3, 3);
-
- ASSERT(v1.find(1));
- ASSERT(v2.find(1));
- ASSERT(v3.find(1));
-
- // Claiming exclusive ownership over v3 means v3 will no longer be valid after mutation, but
- // older versions should be unperturbed.
- auto v4 = std::move(v3).erase(1);
- ASSERT(v1.find(1));
- ASSERT(v2.find(1));
- ASSERT(!v4.find(1));
-}
-
-TEST(ImmutableUnorderedSet, Basic) {
- // Insert some values and verify that the data structure is behaving as expected
- immutable::unordered_set<int> v0;
- auto v1 = v0.insert(1);
- // Record the pointer to the value '1', verify that this doesn't change after performing more
- // inserts
- auto v1Val = v1.find(1);
-
- // Make more versions of the data structure, v2 and v3 are now distinct history branches from
- // v1. v0 and v1 should be unaffected
- auto v2 = v1.insert(2);
- auto v3 = v1.insert(2);
-
- // Verify that values are as expected
- ASSERT_EQ(v0.size(), 0);
-
- ASSERT_EQ(v1.size(), 1);
- ASSERT(v1.find(1));
- ASSERT_EQ(*v1.find(1), 1);
-
- ASSERT_EQ(v2.size(), 2);
- ASSERT(v2.find(1));
- ASSERT_EQ(*v2.find(1), 1);
- ASSERT(v2.find(2));
- ASSERT_EQ(*v2.find(2), 2);
-
- ASSERT_EQ(v3.size(), 2);
- ASSERT(v3.find(1));
- ASSERT_EQ(*v3.find(1), 1);
- ASSERT(v3.find(2));
- ASSERT_EQ(*v3.find(2), 2);
-
- // Verify that pointer to v1's value did not change
- ASSERT_EQ(v1.find(1), v1Val);
- // Key is stored in v2 and v3 with different addresses
- ASSERT_NE(v2.find(2), v3.find(2));
-}
-
-TEST(ImmutableUnorderedSet, UserDefinedType) {
- immutable::unordered_set<UserDefinedKey> v0;
- auto v1 = v0.insert(UserDefinedKey(1));
- ASSERT(v1.find(UserDefinedKey(1)));
-}
-
-TEST(ImmutableUnorderedSet, HeterogeneousLookup) {
- immutable::unordered_set<std::string, StringMapHasher, StringMapEq> v0;
- auto v1 = v0.insert("str");
-
- // Lookup using StringData without the need to convert to string.
- ASSERT(v1.find("str"_sd));
-
- // Lookup using pre-hash
- StringMapHashedKey hashedKey = StringMapHasher().hashed_key("str"_sd);
- ASSERT(v1.find(hashedKey));
-}
-
-TEST(ImmutableUnorderedSet, BatchWrite) {
- immutable::unordered_set<int> v0;
-
- auto transient = v0.transient();
- transient.insert(1);
- transient.insert(2);
- immutable::unordered_set<int> v1 = transient.persistent();
-
- ASSERT(!v0.find(1));
- ASSERT(!v0.find(2));
- ASSERT(v1.find(1));
- ASSERT(v1.find(2));
-}
-
-TEST(ImmutableUnorderedSet, ExclusiveOwnership) {
- immutable::unordered_set<int> v0;
-
- auto v1 = v0.insert(1);
- auto v2 = v1.insert(2);
- auto v3 = v2.insert(3);
-
- ASSERT(v1.find(1));
- ASSERT(v2.find(1));
- ASSERT(v3.find(1));
-
- auto v4 = std::move(v3).erase(1);
- ASSERT(v1.find(1));
- ASSERT(v2.find(1));
- ASSERT(!v4.find(1));
-}
-
-} // namespace
-} // namespace mongo
diff --git a/src/mongo/util/immutable/immutable_vector_test.cpp b/src/mongo/util/immutable/immutable_vector_test.cpp
deleted file mode 100644
index 60329423403..00000000000
--- a/src/mongo/util/immutable/immutable_vector_test.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-/**
- * Copyright (C) 2023-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-
-#include "mongo/unittest/unittest.h"
-
-#include "mongo/util/immutable/vector.h"
-
-#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kTest
-
-namespace mongo {
-namespace {
-
-class UserDefinedType {
-public:
- UserDefinedType() = default;
- explicit UserDefinedType(int val) : a(val) {}
-
- bool operator==(const UserDefinedType& rhs) const {
- return a == rhs.a;
- }
-
-private:
- int a = 0;
-};
-
-TEST(ImmutableUnorderedMap, Basic) {
- // Insert some values and verify that the data structure is behaving as expected
- immutable::vector<int> v0;
- auto v1 = v0.push_back(1);
- // Record the pointer to the value at index '0', verify that this doesn't change after
- // performing additional modifications
- auto* v1Val = &(*v1.begin());
-
- // Create distinct branches of the history from v1. v0 and v1 should be unaffected
- auto v2 = v1.update(0, [](int v) { return v += 1; });
- auto v3 = v1.push_back(3);
- auto v4 = v1.set(0, 4);
-
- // Verify that values are as expected
- ASSERT_EQ(v0.size(), 0);
-
- ASSERT_EQ(v1.size(), 1);
- ASSERT_EQ(v1.at(0), 1);
-
- ASSERT_EQ(v2.size(), 1);
- ASSERT_EQ(v2.at(0), 2);
-
- ASSERT_EQ(v3.size(), 2);
- ASSERT_EQ(v3.at(0), 1);
- ASSERT_EQ(v3.at(1), 3);
-
- ASSERT_EQ(v4.size(), 1);
- ASSERT_EQ(v4.at(0), 4);
-
- // Verify that pointer to v1's value did not change
- ASSERT_EQ(&(*v1.begin()), v1Val);
-}
-
-TEST(ImmutableUnorderedMap, UserDefinedType) {
- immutable::vector<UserDefinedType> v0;
- auto v1 = v0.push_back(UserDefinedType(1));
- ASSERT(v1.at(0) == UserDefinedType(1));
-}
-
-TEST(ImmutableUnorderedMap, BatchWrite) {
- immutable::vector<int> v0;
-
- auto transient = v0.transient();
- transient.push_back(1);
- transient.push_back(2);
- immutable::vector<int> v1 = transient.persistent();
-
- ASSERT_EQ(v0.size(), 0);
- ASSERT_EQ(v1.size(), 2);
- ASSERT_EQ(v1.at(0), 1);
- ASSERT_EQ(v1.at(1), 2);
-}
-
-TEST(ImmutableUnorderedMap, ExclusiveOwnership) {
- immutable::vector<int> v0;
-
- auto v1 = v0.push_back(1);
- auto v2 = v1.push_back(2);
- auto v3 = v2.push_back(3);
-
- ASSERT_EQ(v1.size(), 1);
- ASSERT_EQ(v2.size(), 2);
- ASSERT_EQ(v3.size(), 3);
-
- // Claiming exclusive ownership over v3 means v3 will no longer be valid after mutation, but
- // older versions should be unperturbed.
- auto v4 = std::move(v3).take(0);
- ASSERT_EQ(v1.size(), 1);
- ASSERT_EQ(v2.size(), 2);
- ASSERT_EQ(v4.size(), 0);
-}
-
-} // namespace
-} // namespace mongo
diff --git a/src/mongo/util/immutable/map.h b/src/mongo/util/immutable/map.h
deleted file mode 100644
index 351cfd560da..00000000000
--- a/src/mongo/util/immutable/map.h
+++ /dev/null
@@ -1,358 +0,0 @@
-/**
- * Copyright (C) 2023-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#pragma once
-
-#include <algorithm>
-
-#include <immer/flex_vector.hpp>
-
-#include "mongo/util/immutable/details/map.h"
-#include "mongo/util/immutable/details/memory_policy.h"
-
-
-namespace mongo::immutable {
-
-/**
- * Immutable ordered dictionary.
- *
- * Interfaces that "modify" the map are 'const' and return a new version of the map with the
- * modifications applied and leaves the original version untouched.
- *
- * It is optimized for efficient copies and low memory usage when multiple versions of the map exist
- * simultaneously at the expense of regular lookups not being as efficient as the regular std
- * ordered containers. Suitable for use in code that uses the copy-on-write pattern.
- *
- * Thread-safety: All methods are const, it is safe to perform modifications that result in new
- * versions from multiple threads concurrently.
- *
- * Memory management: Internal memory management is done using reference counting, memory is free'd
- * as references to different versions of the map are released.
- *
- * Built on top of 'immer::flex_vector'.
- * Documentation: 'immer/flex_vector.h' and https://sinusoid.es/immer/
- */
-template <typename Key, typename Value, typename Compare = std::less<Key>>
-class map {
-public:
- using key_type = Key;
- using mapped_type = Value;
- using value_type = std::pair<Key, Value>;
- using storage_type = immer::flex_vector<value_type, detail::MemoryPolicy>;
- using iterator = typename storage_type::iterator;
- using size_type = typename storage_type::size_type;
- using diference_type = std::ptrdiff_t;
- using reference = const value_type&;
- using const_reference = const value_type&;
- using memory_policy_type = detail::MemoryPolicy;
- using comp = Compare;
-
- map() = default;
-
- struct default_value {
- const mapped_type& operator()() const {
- static mapped_type v = mapped_type{};
- return v;
- }
- };
-
- struct error_value {
- const mapped_type& operator()() const {
- throw std::out_of_range{"key not found"};
- }
- };
-
- bool operator==(const map& other) const {
- return _storage == other._storage;
- }
-
- bool operator!=(const map& other) const {
- return !(*this == other);
- }
-
- [[nodiscard]] iterator begin() const {
- return _storage.begin();
- }
-
- [[nodiscard]] iterator end() const {
- return _storage.end();
- }
-
- size_t size() const {
- return _storage.size();
- }
-
- /**
- * Returns a reference to the value associated with 'key' if one exists, otherwise a default
- * constructed Value.
- */
- const mapped_type& operator[](const key_type& key) const {
- auto it = find(key);
- if (it == end()) {
- return default_value{}();
- }
- return it->second;
- }
-
- /**
- * Returns a reference to the value associated with 'key' if one exists, otherwise throws.
- */
- const mapped_type& at(const key_type& key) const {
- auto it = find(key);
- if (it == end()) {
- return error_value{}();
- }
- return it->second;
- }
-
- /**
- * Insert a new 'key' and 'value' pair to the map.
- *
- * Returns the modified map, or the original if 'key' was already contained.
- */
- template <typename K, typename V>
- [[nodiscard]] map insert(K&& key, V&& value) const& {
- return map{details::map::insert<map<key_type, mapped_type, comp>>(
- _storage, std::forward<K>(key), std::forward<V>(value))};
- }
- template <typename K, typename V>
- [[nodiscard]] map insert(K&& key, V&& value) && {
- return map{details::map::insert<map<key_type, mapped_type, comp>>(
- std::move(_storage), std::forward<K>(key), std::forward<V>(value))};
- }
-
- /**
- * Insert a new 'key' and 'value' pair to the map. Uses 'it' as a hint.
- *
- * Returns the modified map, or the original if 'key' was already contained.
- */
- template <typename K, typename V>
- [[nodiscard]] map insert(iterator it, K&& key, V&& value) const& {
- return map{details::map::insert<map<key_type, mapped_type, comp>>(
- _storage, it, std::forward<K>(key), std::forward<V>(value))};
- }
- template <typename K, typename V>
- [[nodiscard]] map insert(iterator it, K&& key, V&& value) && {
- return map{details::map::insert<map<key_type, mapped_type, comp>>(
- std::move(_storage), it, std::forward<K>(key), std::forward<V>(value))};
- }
-
- /**
- * Sets the value associated with 'key' to 'value', inserting the new pair if 'key' does not
- * exist.
- *
- * Returns the modified map.
- */
- template <typename K, typename V>
- [[nodiscard]] map set(K&& key, V&& value) const& {
- return map{details::map::set<map<key_type, mapped_type, comp>>(
- _storage, std::forward<K>(key), std::forward<V>(value))};
- }
- template <typename K, typename V>
- [[nodiscard]] map set(K&& key, V&& value) && {
- return map{details::map::set<map<key_type, mapped_type, comp>>(
- std::move(_storage), std::forward<K>(key), std::forward<V>(value))};
- }
-
- /**
- * Sets the value associated with 'key' to 'value', inserting the new pair if 'key' does not
- * exist. Treats 'it' as a hint.
- *
- * Returns the modified map.
- */
- template <typename K, typename V>
- [[nodiscard]] map set(iterator it, K&& key, V&& value) const& {
- return map{details::map::set<map<key_type, mapped_type, comp>>(
- _storage, it, std::forward<K>(key), std::forward<V>(value))};
- }
- template <typename K, typename V>
- [[nodiscard]] map set(iterator it, K&& key, V&& value) && {
- return map{details::map::set<map<key_type, mapped_type, comp>>(
- std::move(_storage), it, std::forward<K>(key), std::forward<V>(value))};
- }
-
- /**
- * Sets the value associated with 'key' by applying 'valueUpdate' to the existing value, or to a
- * default-constructed value if no entry for 'key' exists.
- *
- * The signature of 'valueUpdate' should be equivalent to
- * std::function<mapped_type(const mapped_type&)>. Returns the modified map.
- */
- template <typename K, typename U>
- [[nodiscard]] map update(K&& key, U&& valueUpdate) const& {
- return map{details::map::update<map<key_type, mapped_type, comp>>(
- _storage, std::forward<K>(key), std::forward<U>(valueUpdate))};
- }
- template <typename K, typename U>
- [[nodiscard]] map update(K&& key, U&& valueUpdate) && {
- return map{details::map::update<map<key_type, mapped_type, comp>>(
- std::move(_storage), std::forward<K>(key), std::forward<U>(valueUpdate))};
- }
-
- /**
- * Updates the value associated with 'key' by applying 'valueUpdate' to the existing value, or
- * to a default-constructed value if no entry for 'key' exists. Uses 'it' as a hint.
- *
- * The signature of 'valueUpdate' should be equivalent to
- * std::function<mapped_type(const mapped_type&)>. Returns the modified map.
- */
- template <typename K, typename U>
- [[nodiscard]] map update(iterator it, K&& key, U&& valueUpdate) const& {
- return map{details::map::update<map<key_type, mapped_type, comp>>(
- _storage, it, std::forward<K>(key), std::forward<U>(valueUpdate))};
- }
- template <typename K, typename U>
- [[nodiscard]] map update(iterator it, K&& key, U&& valueUpdate) && {
- return map{details::map::update<map<key_type, mapped_type, comp>>(
- std::move(_storage), it, std::forward<K>(key), std::forward<U>(valueUpdate))};
- }
-
- /**
- * Updates the value associated with 'key' if it exists by applying 'valueUpdate' to the
- * existing value.
- *
- * The signature of 'valueUpdate' should be equivalent to
- * std::function<mapped_type(const mapped_type&)>. Returns the modified map, or the original if
- * 'key' does not exist.
- */
- template <typename K, typename U>
- [[nodiscard]] map update_if_exists(K&& key, U&& valueUpdate) const& {
- return map{details::map::update_if_exists<map<key_type, mapped_type, comp>>(
- _storage, std::forward<K>(key), std::forward<U>(valueUpdate))};
- }
- template <typename K, typename U>
- [[nodiscard]] map update_if_exists(K&& key, U&& valueUpdate) && {
- return map{details::map::update_if_exists<map<key_type, mapped_type, comp>>(
- std::move(_storage), std::forward<K>(key), std::forward<U>(valueUpdate))};
- }
-
- /**
- * Updates the value associated with 'key' if it exists by applying 'valueUpdate' to the
- * existing value. Uses 'it' as a hint.
- *
- * The signature of 'valueUpdate' should be equivalent to
- * std::function<mapped_type(const mapped_type&)>. Returns the modified map, or the original if
- * 'key' does not exist.
- */
- template <typename K, typename U>
- [[nodiscard]] map update_if_exists(iterator it, K&& key, U&& valueUpdate) const& {
- return map{details::map::update_if_exists<map<key_type, mapped_type, comp>>(
- _storage, it, std::forward<K>(key), std::forward<U>(valueUpdate))};
- }
- template <typename K, typename U>
- [[nodiscard]] map update_if_exists(iterator it, K&& key, U&& valueUpdate) && {
- return map{details::map::update_if_exists<map<key_type, mapped_type, comp>>(
- std::move(_storage), it, std::forward<K>(key), std::forward<U>(valueUpdate))};
- }
-
- /**
- * Removes 'key' and its associated value from the map.
- *
- * Returns the modified map, or the original if 'key' does not exist.
- */
- template <typename K>
- [[nodiscard]] map erase(K&& key) const& {
- return map{
- details::map::erase<map<key_type, mapped_type, comp>>(_storage, std::forward<K>(key))};
- }
- template <typename K>
- [[nodiscard]] map erase(K&& key) && {
- return map{details::map::erase<map<key_type, mapped_type, comp>>(std::move(_storage),
- std::forward<K>(key))};
- }
-
- /**
- * Removes entry assocated with 'it' from the map. 'it' must match 'key' or it will not be
- * erased.
- *
- * Returns the modified map, or the original if 'it' is equal to 'end()'.
- */
- template <typename K>
- [[nodiscard]] map erase(iterator it, K&& key) const& {
- return map{details::map::erase<map<key_type, mapped_type, comp>>(
- _storage, it, std::forward<K>(key))};
- }
- template <typename K>
- [[nodiscard]] map erase(iterator it, K&& key) && {
- return map{details::map::erase<map<key_type, mapped_type, comp>>(
- std::move(_storage), it, std::forward<K>(key))};
- }
-
- /**
- * Returns an iterator to the entry for 'key' if it exists, 'end()' otherwise.
- *
- * Supports heterogeneous lookup.
- */
- template <typename SearchKey>
- [[nodiscard]] iterator find(const SearchKey& key) const {
- return details::map::find<map<key_type, mapped_type, comp>>(_storage, key);
- }
-
- /**
- * Returns true if map contains an entry for 'key'.
- *
- * Supports heterogeneous lookup.
- */
- template <typename SearchKey>
- bool contains(const SearchKey& key) const {
- return find(key) != end();
- }
-
- /**
- * Returns the first the entry greater than or equal to 'key' if one exists, 'end()' otherwise.
- *
- * Supports heterogeneous lookup.
- */
- template <typename SearchKey>
- [[nodiscard]] iterator lower_bound(const SearchKey& key) const {
- return details::map::lower_bound<map<key_type, mapped_type, comp>>(_storage, key);
- }
-
- /**
- * Returns the first the entry strictly greater than 'key' if one exists, 'end()' otherwise.
- *
- * Supports heterogeneous lookup.
- */
- template <typename SearchKey>
- [[nodiscard]] iterator upper_bound(const SearchKey& key) const {
- return std::upper_bound(
- _storage.begin(),
- _storage.end(),
- key,
- [](const SearchKey& a, const value_type& b) -> bool { return comp{}(a, b.first); });
- }
-
-private:
- template <typename S>
- explicit map(S&& s) : _storage{std::forward<S>(s)} {}
-
- storage_type _storage;
-};
-
-} // namespace mongo::immutable
diff --git a/src/mongo/util/immutable/set.h b/src/mongo/util/immutable/set.h
deleted file mode 100644
index 24e5183c34f..00000000000
--- a/src/mongo/util/immutable/set.h
+++ /dev/null
@@ -1,199 +0,0 @@
-/**
- * Copyright (C) 2023-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#pragma once
-
-#include <algorithm>
-
-#include <immer/flex_vector.hpp>
-
-#include "mongo/util/immutable/details/memory_policy.h"
-#include "mongo/util/immutable/details/set.h"
-
-namespace mongo::immutable {
-
-/**
- * Immutable ordered set.
- *
- * Interfaces that "modify" the set are 'const' and return a new version of the set with the
- * modifications applied and leaves the original version untouched.
- *
- * It is optimized for efficient copies and low memory usage when multiple versions of the set exist
- * simultaneously at the expense of regular lookups not being as efficient as the regular std
- * ordered containers. Suitable for use in code that uses the copy-on-write pattern.
- *
- * Thread-safety: All methods are const, it is safe to perform modifications that result in new
- * versions from multiple threads concurrently.
- *
- * Memory management: Internal memory management is done using reference counting, memory is free'd
- * as references to different versions of the set are released.
- *
- * Built on top of 'immer::flex_vector'.
- * Documentation: 'immer/flex_vector.h' and https://sinusoid.es/immer/
- */
-template <typename Key, typename Compare = std::less<Key>>
-class set {
-public:
- using key_type = Key;
- using storage_type = immer::flex_vector<key_type, detail::MemoryPolicy>;
- using iterator = typename storage_type::iterator;
- using size_type = typename storage_type::size_type;
- using diference_type = std::ptrdiff_t;
- using reference = const key_type&;
- using const_reference = const key_type&;
- using memory_policy_type = detail::MemoryPolicy;
- using comp = Compare;
-
- set() = default;
-
- bool operator==(const set& other) const {
- return _storage == other._storage;
- }
-
- bool operator!=(const set& other) const {
- return !(*this == other);
- }
-
- [[nodiscard]] iterator begin() const {
- return _storage.begin();
- }
-
- [[nodiscard]] iterator end() const {
- return _storage.end();
- }
-
- size_t size() const {
- return _storage.size();
- }
-
- /**
- * Insert a new 'key' to the set.
- *
- * Returns the modified set, or the original if 'key' was already contained.
- */
- template <typename K>
- [[nodiscard]] set insert(K&& key) const& {
- return set{details::set::insert<set>(_storage, std::forward<K>(key))};
- }
- template <typename K>
- [[nodiscard]] set insert(K&& key) && {
- return set{details::set::insert<set>(std::move(_storage), std::forward<K>(key))};
- }
-
- /**
- * Insert a new 'key' to the set. Uses 'it' as a hint.
- *
- * Returns the modified set, or the original if 'key' was already contained.
- */
- template <typename K>
- [[nodiscard]] set insert(iterator it, K&& key) const& {
- return set{details::set::insert<set>(_storage, it, std::forward<K>(key))};
- }
- template <typename K>
- [[nodiscard]] set insert(iterator it, K&& key) && {
- return set{details::set::insert<set>(std::move(_storage), it, std::forward<K>(key))};
- }
-
- /**
- * Removes 'key' from the set.
- *
- * Returns the modified set, or the original if 'key' does not exist.
- */
- template <typename K>
- [[nodiscard]] set erase(K&& key) const& {
- return set{details::set::erase<set>(_storage, std::forward<K>(key))};
- }
- template <typename K>
- [[nodiscard]] set erase(K&& key) && {
- return set{details::set::erase<set>(std::move(_storage), std::forward<K>(key))};
- }
-
- /**
- * Removes key associated with 'key' from the set. Uses 'it' as a hint.
- *
- * Returns the modified set, or the original if 'key' does not exist.
- */
- template <typename K>
- [[nodiscard]] set erase(iterator it, K&& key) const& {
- return set{details::set::erase<set>(_storage, it, std::forward<K>(key))};
- }
- template <typename K>
- [[nodiscard]] set erase(iterator it, K&& key) && {
- return set{details::set::erase<set>(std::move(_storage), it, std::forward<K>(key))};
- }
-
- /**
- * Returns an iterator to the element for 'key' if it exists, 'end()' otherwise.
- *
- * Supports heterogeneous lookup.
- */
- template <class SearchKey>
- [[nodiscard]] iterator find(const SearchKey& key) const {
- return details::set::find<set>(_storage, key);
- }
-
- /**
- * Returns true if set contains 'key'.
- *
- * Supports heterogeneous lookup.
- */
- template <class SearchKey>
- bool contains(const SearchKey& key) const {
- return find(key) != end();
- }
-
- /**
- * Returns the first the element greater than or equal to 'key' if one exists, 'end()'
- * otherwise.
- *
- * Supports heterogeneous lookup.
- */
- template <class SearchKey>
- [[nodiscard]] iterator lower_bound(const SearchKey& key) const {
- return details::set::lower_bound<set>(_storage, key);
- }
-
- /**
- * Returns the first the element strictly greater than 'key' if one exists, 'end()' otherwise.
- *
- * Supports heterogeneous lookup.
- */
- template <class SearchKey>
- [[nodiscard]] iterator upper_bound(const SearchKey& key) const {
- return std::upper_bound(_storage.begin(), _storage.end(), key, comp{});
- }
-
-private:
- template <typename S>
- explicit set(S&& s) : _storage{std::forward<S>(s)} {}
-
- storage_type _storage;
-};
-
-} // namespace mongo::immutable
diff --git a/src/mongo/util/immutable/unordered_map.h b/src/mongo/util/immutable/unordered_map.h
deleted file mode 100644
index b024bd5b64a..00000000000
--- a/src/mongo/util/immutable/unordered_map.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * Copyright (C) 2023-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#pragma once
-
-#include <immer/map.hpp>
-#include <immer/map_transient.hpp>
-
-#include "mongo/stdx/trusted_hasher.h"
-#include "mongo/util/immutable/details/memory_policy.h"
-
-namespace mongo::immutable {
-
-/**
- * Immutable unordered hash table.
- *
- * Interfaces that "modify" the hash table are 'const' and returns a new version of the table with
- * the modifications applied and leaves the original version untouched.
- *
- * It is optimized for efficient copies and low memory usage when multiple versions of the table
- * exist simultaneously at the expense of regular lookups not being as efficient as the regular
- * stdx unordered containers. Suitable for use in code that uses the copy-on-write pattern.
- *
- * Thread-safety: All methods are const, it is safe to perform modifications that result in new
- * versions from multiple threads concurrently.
- *
- * Memory management: Internal memory management is done using reference counting, memory is free'd
- * as references to different versions of the table are released.
- *
- * Multiple modifications can be done efficiently using the 'transient()' interface.
- *
- * Documentation: 'immer/map.h' and https://sinusoid.es/immer/
- */
-template <class K,
- class V,
- class Hasher = DefaultHasher<K>,
- class Eq = absl::container_internal::hash_default_eq<K>>
-using unordered_map = immer::map<K, V, EnsureTrustedHasher<Hasher, K>, Eq, detail::MemoryPolicy>;
-} // namespace mongo::immutable
diff --git a/src/mongo/util/immutable/unordered_set.h b/src/mongo/util/immutable/unordered_set.h
deleted file mode 100644
index c5133218007..00000000000
--- a/src/mongo/util/immutable/unordered_set.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * Copyright (C) 2023-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#pragma once
-
-#include <immer/set.hpp>
-#include <immer/set_transient.hpp>
-
-#include "mongo/stdx/trusted_hasher.h"
-#include "mongo/util/immutable/details/memory_policy.h"
-
-namespace mongo::immutable {
-
-/**
- * Immutable unordered hash set.
- *
- * Interfaces that "modify" the hash set are 'const' and returns a new version of the set with
- * the modifications applied and leaves the original version untouched.
- *
- * It is optimized for efficient copies and low memory usage when multiple versions of the set
- * exist simultaneously at the expense of regular lookups not being as efficient as the regular
- * stdx unordered containers. Suitable for use in code that uses the copy-on-write
- * pattern.
- *
- * Thread-safety: All methods are const, it is safe to perform modifications that result in new
- * versions from multiple threads concurrently.
- *
- * Memory management: Internal memory management is done using reference counting, memory is free'd
- * as references to different versions of the set are released.
- *
- * Multiple modifications can be done efficiently using the 'transient()' interface.
- *
- * Documentation: 'immer/set.h' and https://sinusoid.es/immer/
- */
-template <class T,
- class Hasher = DefaultHasher<T>,
- class Eq = absl::container_internal::hash_default_eq<T>>
-using unordered_set = immer::set<T, EnsureTrustedHasher<Hasher, T>, Eq, detail::MemoryPolicy>;
-} // namespace mongo::immutable
diff --git a/src/mongo/util/immutable/vector.h b/src/mongo/util/immutable/vector.h
deleted file mode 100644
index b9cc205575d..00000000000
--- a/src/mongo/util/immutable/vector.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * Copyright (C) 2023-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#pragma once
-
-#include <immer/vector.hpp>
-#include <immer/vector_transient.hpp>
-
-#include "mongo/util/immutable/details/memory_policy.h"
-
-namespace mongo::immutable {
-
-/**
- * Immutable vector.
- *
- * Interfaces that "modify" the vector are 'const' and return a new version of the vector with
- * the modifications applied and leave the original version untouched.
- *
- * It is optimized for efficient copies and low memory usage when multiple versions of the vector
- * exist simultaneously at the expense of regular lookups not being as efficient as the regular
- * std::vector implementation. Suitable for use in code that uses the copy-on-write pattern.
- *
- * Thread-safety: All methods are const, it is safe to perform modifications that result in new
- * versions from multiple threads concurrently.
- *
- * Memory management: Internal memory management is done using reference counting, memory is free'd
- * as references to different versions of the vector are released.
- *
- * Multiple modifications can be done efficiently using the 'transient()' interface.
- *
- * Documentation: 'immer/vector.h' and https://sinusoid.es/immer/
- */
-template <class T>
-using vector = immer::vector<T, detail::MemoryPolicy>;
-} // namespace mongo::immutable
diff --git a/src/mongo/util/interruptible.h b/src/mongo/util/interruptible.h
index 1e9cc070c64..f7a02e971f9 100644
--- a/src/mongo/util/interruptible.h
+++ b/src/mongo/util/interruptible.h
@@ -107,6 +107,28 @@ protected:
* Returns the equivalent of Date_t::now() + waitFor for the InterruptibleBase's clock
*/
virtual Date_t getExpirationDateForWaitForValue(Milliseconds waitFor) = 0;
+
+ struct IgnoreInterruptsState {
+ bool ignoreInterrupts;
+ DeadlineState deadline;
+ };
+
+ /**
+ * Pushes an ignore interruption critical section into the InterruptibleBase.
+ * Until an associated popIgnoreInterrupts() is invoked, the InterruptibleBase should ignore
+ * interruptions related to explicit interruption or previously set deadlines.
+ *
+ * Note that new deadlines can be set after this is called, which will again introduce the
+ * possibility of interruption.
+ *
+ * Returns state needed to pop interruption.
+ */
+ virtual IgnoreInterruptsState pushIgnoreInterrupts() = 0;
+
+ /**
+ * Pops the ignored interruption critical section introduced by push.
+ */
+ virtual void popIgnoreInterrupts(IgnoreInterruptsState iis) = 0;
};
/**
@@ -172,6 +194,44 @@ private:
return DeadlineGuard(*this, deadline, error);
}
+ /**
+ * An interruption guard provides a region where interruption is ignored.
+ *
+ * Note that this causes the deadline to be reset to Date_t::max(), but that it can also be
+ * subsequently reduced in size after the fact.
+ */
+ class IgnoreInterruptionsGuard {
+ public:
+ IgnoreInterruptionsGuard(const IgnoreInterruptionsGuard&) = delete;
+ IgnoreInterruptionsGuard& operator=(const IgnoreInterruptionsGuard&) = delete;
+
+ IgnoreInterruptionsGuard(IgnoreInterruptionsGuard&& other)
+ : _interruptible(other._interruptible), _oldState(other._oldState) {
+ other._interruptible = nullptr;
+ }
+
+ IgnoreInterruptionsGuard& operator=(IgnoreInterruptionsGuard&&) = delete;
+
+ ~IgnoreInterruptionsGuard() {
+ if (_interruptible) {
+ _interruptible->popIgnoreInterrupts(_oldState);
+ }
+ }
+
+ private:
+ friend Interruptible;
+
+ explicit IgnoreInterruptionsGuard(Interruptible& interruptible)
+ : _interruptible(&interruptible), _oldState(_interruptible->pushIgnoreInterrupts()) {}
+
+ Interruptible* _interruptible;
+ IgnoreInterruptsState _oldState;
+ };
+
+ IgnoreInterruptionsGuard makeIgnoreInterruptionsGuard() {
+ return IgnoreInterruptionsGuard(*this);
+ }
+
public:
class WaitListener;
@@ -246,6 +306,24 @@ public:
}
/**
+ * Invokes the passed callback with an interruption guard active. Additionally handles the
+ * dance of try/catching the invocation and checking checkForInterrupt with the guard inactive
+ * (to allow a higher level timeout to override a lower level one, or for top level interruption
+ * to propagate)
+ */
+ template <typename Callback>
+ decltype(auto) runWithoutInterruptionExceptAtGlobalShutdown(Callback&& cb) {
+ try {
+ const auto guard = makeIgnoreInterruptionsGuard();
+ return std::forward<Callback>(cb)();
+ } catch (const ExceptionForCat<ErrorCategory::ExceededTimeLimitError>&) {
+ // May throw replacement exception
+ checkForInterrupt();
+ throw;
+ }
+ }
+
+ /**
* Raises a AssertionException if this operation is in a killed state.
*/
void checkForInterrupt() {
@@ -494,15 +572,7 @@ class Interruptible::NotInterruptible final : public Interruptible {
return stdx::cv_status::no_timeout;
}
- try {
- // If the system clock's time_point's compiler-dependent resolution is higher than
- // Date_t's milliseconds, it's possible for the conversion from Date_t to time_point
- // to overflow and trigger an exception. We catch that here to maintain the noexcept
- // contract.
- return cv.wait_until(m, deadline.toSystemTimePoint());
- } catch (const ExceptionFor<ErrorCodes::DurationOverflow>& ex) {
- return ex.toStatus();
- }
+ return cv.wait_until(m, deadline.toSystemTimePoint());
}
Date_t getDeadline() const override {
@@ -513,6 +583,19 @@ class Interruptible::NotInterruptible final : public Interruptible {
return Status::OK();
}
+ // It's invalid to call the deadline or ignore interruption guards on a possibly noop
+ // Interruptible.
+ //
+ // The noop Interruptible should only be invoked as a default arg at the bottom of the call
+ // stack (with types that won't modify it's invocation)
+ IgnoreInterruptsState pushIgnoreInterrupts() override {
+ MONGO_UNREACHABLE;
+ }
+
+ void popIgnoreInterrupts(IgnoreInterruptsState) override {
+ MONGO_UNREACHABLE;
+ }
+
DeadlineState pushArtificialDeadline(Date_t deadline, ErrorCodes::Error error) override {
MONGO_UNREACHABLE;
}
diff --git a/src/mongo/util/interruptible_test.cpp b/src/mongo/util/interruptible_test.cpp
deleted file mode 100644
index 04f3dea56c7..00000000000
--- a/src/mongo/util/interruptible_test.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * Copyright (C) 2022-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/platform/mutex.h"
-#include "mongo/stdx/condition_variable.h"
-#include "mongo/unittest/assert_that.h"
-#include "mongo/unittest/unittest.h"
-#include "mongo/util/interruptible.h"
-#include "mongo/util/time_support.h"
-
-#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kTest
-
-namespace mongo {
-namespace {
-
-class InterruptibleTest : public unittest::Test {};
-
-TEST_F(InterruptibleTest, NotInterruptibleWaitForConditionFailsWithOverflowError) {
- auto notInterruptible = Interruptible::notInterruptible();
-
- auto mutex = MONGO_MAKE_LATCH();
- stdx::condition_variable cv;
- stdx::unique_lock<Latch> lk(mutex);
-
- auto overflowDeadline = Date_t::max() - Milliseconds(1);
-
- ASSERT_THROWS_CODE(notInterruptible->waitForConditionOrInterruptUntil(
- cv, lk, overflowDeadline, [] { return false; }),
- DBException,
- ErrorCodes::DurationOverflow);
-}
-
-} // namespace
-
-} // namespace mongo
diff --git a/src/mongo/util/murmur3.h b/src/mongo/util/murmur3.h
deleted file mode 100644
index 9793cf3ae66..00000000000
--- a/src/mongo/util/murmur3.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/**
- * Copyright (C) 2023-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#pragma once
-
-#include <third_party/murmurhash3/MurmurHash3.h>
-
-#include "mongo/base/data_range.h"
-#include "mongo/base/data_type_endian.h"
-#include "mongo/base/data_view.h"
-#include "mongo/base/string_data.h"
-
-namespace mongo {
-
-/**
- * Wraps the third-party hash function MurmurHash3. Callers should generally prefer this wrapper (or
- * one of the other overloads below) rather than calling into the third-party functions directly.
- * This interface is intended to be easier to consume safely.
- */
-template <int SizeOfOutput>
-inline size_t murmur3(StringData str, size_t seed);
-
-/**
- * Template specialization for hashing a 'StringData' to a 32-bit hash code.
- */
-template <>
-inline size_t murmur3<4>(StringData str, size_t seed) {
- char hash[4];
- MurmurHash3_x86_32(str.rawData(), str.size(), seed, &hash);
- return ConstDataView(hash).read<LittleEndian<std::uint32_t>>();
-}
-
-/**
- * Template specialization for hashing a 'StringData' to a 64-bit hash code. Returns the first 8
- * bytes of the 128-bit version of MurmurHash, interpreting these 8 bytes as having a little-endian
- * byte order.
- */
-template <>
-inline size_t murmur3<8>(StringData str, size_t seed) {
- char hash[16];
- MurmurHash3_x64_128(str.rawData(), str.size(), seed, hash);
- return static_cast<size_t>(ConstDataView(hash).read<LittleEndian<std::uint64_t>>());
-}
-
-/**
- * Overload for callers which use a byte-array representation for the data and thus cannot easily
- * represent the input as a 'StringData'.
- */
-template <int SizeOfOutput>
-inline size_t murmur3(ConstDataRange data, size_t seed);
-
-/**
- * Template specialization for hashing a 'ConstDataRange' to a 32-bit hash code.
- */
-template <>
-inline size_t murmur3<4>(ConstDataRange data, size_t seed) {
- char hash[4];
- MurmurHash3_x86_32(data.data(), data.length(), seed, &hash);
- return ConstDataView(hash).read<LittleEndian<std::uint32_t>>();
-}
-
-/**
- * Template specialization for hashing a 'ConstDataRange' to a 64-bit hash code. Returns the first 8
- * bytes of the 128-bit version of MurmurHash, interpreting these 8 bytes as having a little-endian
- * byte order.
- */
-template <>
-inline size_t murmur3<8>(ConstDataRange data, size_t seed) {
- char hash[16];
- MurmurHash3_x64_128(data.data(), data.length(), seed, hash);
- return static_cast<size_t>(ConstDataView(hash).read<LittleEndian<std::uint64_t>>());
-}
-
-/**
- * Writes the full output of the 128-bit version of MurmurHash to the given 'output' array.
- */
-inline void murmur3(StringData str, size_t seed, std::array<char, 16>& output) {
- MurmurHash3_x64_128(str.rawData(), str.size(), seed, output.data());
-}
-
-/**
- * 128-bit overload where the input is given as a 'ConstDataRange'.
- */
-inline void murmur3(ConstDataRange data, size_t seed, std::array<char, 16>& output) {
- MurmurHash3_x64_128(data.data(), data.length(), seed, output.data());
-}
-
-} // namespace mongo
diff --git a/src/mongo/util/net/SConscript b/src/mongo/util/net/SConscript
index 272cd9740a1..9aec6ba3150 100644
--- a/src/mongo/util/net/SConscript
+++ b/src/mongo/util/net/SConscript
@@ -33,9 +33,11 @@ env.Library(
source=[
"ssl_options.cpp",
],
+ LIBDEPS=[
+ '$BUILD_DIR/mongo/base',
+ ],
LIBDEPS_PRIVATE=[
'$BUILD_DIR/mongo/db/server_options_core',
- '$BUILD_DIR/mongo/idl/server_parameter',
'$BUILD_DIR/mongo/util/options_parser/options_parser',
]
)
@@ -62,13 +64,13 @@ env.Library(
'ssl_options_server.idl',
],
LIBDEPS=[
+ '$BUILD_DIR/mongo/base',
'ssl_options',
],
LIBDEPS_PRIVATE=[
'$BUILD_DIR/mongo/db/auth/auth_options',
'$BUILD_DIR/mongo/db/auth/cluster_auth_mode',
'$BUILD_DIR/mongo/db/server_options_core',
- '$BUILD_DIR/mongo/idl/server_parameter',
'$BUILD_DIR/mongo/util/options_parser/options_parser',
]
)
@@ -207,8 +209,6 @@ else:
env.Library(
target='http_client_impl',
source=[
- 'http_client_options.idl',
- 'http_client_options.cpp',
'http_client_winhttp.cpp' if env.TargetOSIs('windows') else 'http_client_curl.cpp',
],
LIBDEPS=[
diff --git a/src/mongo/util/net/hostandport.cpp b/src/mongo/util/net/hostandport.cpp
index f351b51b9b9..eb22852a926 100644
--- a/src/mongo/util/net/hostandport.cpp
+++ b/src/mongo/util/net/hostandport.cpp
@@ -52,13 +52,6 @@ StatusWith<HostAndPort> HostAndPort::parse(StringData text) {
return StatusWith<HostAndPort>(result);
}
-Status validateHostAndPort(const std::string& hostAndPortStr) {
- if (hostAndPortStr.empty()) {
- return Status::OK();
- }
- return HostAndPort::parse(hostAndPortStr).getStatus();
-}
-
HostAndPort::HostAndPort() : _port(-1) {}
HostAndPort::HostAndPort(StringData text) {
diff --git a/src/mongo/util/net/hostandport.h b/src/mongo/util/net/hostandport.h
index 3c05c5d0c53..369437fb621 100644
--- a/src/mongo/util/net/hostandport.h
+++ b/src/mongo/util/net/hostandport.h
@@ -45,12 +45,6 @@ class StatusWith;
class StringData;
/**
- * Validate that a string is either empty or is parseable to a HostAndPort. This is intended for use
- * as an IDL validator callback.
- */
-Status validateHostAndPort(const std::string& hostAndPortStr);
-
-/**
* Name of a process on the network.
*
* Composed of some name component, followed optionally by a colon and a numeric port. The name
diff --git a/src/mongo/util/net/hostname_canonicalization.cpp b/src/mongo/util/net/hostname_canonicalization.cpp
index dd31e4b7015..f11ee345480 100644
--- a/src/mongo/util/net/hostname_canonicalization.cpp
+++ b/src/mongo/util/net/hostname_canonicalization.cpp
@@ -111,6 +111,7 @@ StatusWith<std::vector<std::string>> getHostFQDNs(std::string hostName,
std::vector<std::string> getNameInfoErrors;
for (shim_addrinfo* p = info; p; p = p->ai_next) {
+ std::stringstream getNameInfoError;
shim_char host[NI_MAXHOST] = {};
if ((err = shim_getnameinfo(
p->ai_addr, p->ai_addrlen, host, sizeof(host), nullptr, 0, NI_NAMEREQD)) == 0) {
@@ -129,7 +130,6 @@ StatusWith<std::vector<std::string>> getHostFQDNs(std::string hostName,
sin_addr = reinterpret_cast<void*>(&addr_in6->sin6_addr);
}
- std::stringstream getNameInfoError;
if (sin_addr) {
invariant(inet_ntop(p->ai_family, sin_addr, ip_str, sizeof(ip_str)) != nullptr);
getNameInfoError << ip_str;
@@ -138,8 +138,8 @@ StatusWith<std::vector<std::string>> getHostFQDNs(std::string hostName,
}
getNameInfoError << ": \"" << getAddrInfoStrError(err);
- getNameInfoErrors.push_back(getNameInfoError.str());
}
+ getNameInfoErrors.push_back(getNameInfoError.str());
}
if (!getNameInfoErrors.empty()) {
@@ -150,8 +150,6 @@ StatusWith<std::vector<std::string>> getHostFQDNs(std::string hostName,
"errors"_attr = getNameInfoErrors);
}
- LOGV2_DEBUG(7317600, 4, "Name info: {results}", "Name info", "results"_attr = results);
-
// Deduplicate the results list
std::sort(results.begin(), results.end());
results.erase(std::unique(results.begin(), results.end()), results.end());
diff --git a/src/mongo/util/net/http_client_curl.cpp b/src/mongo/util/net/http_client_curl.cpp
index 9db3c06f73f..fa1834c8bde 100644
--- a/src/mongo/util/net/http_client_curl.cpp
+++ b/src/mongo/util/net/http_client_curl.cpp
@@ -27,8 +27,6 @@
* it in the license file.
*/
-#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kNetwork
-
#include "mongo/platform/basic.h"
#include <cstddef>
@@ -49,24 +47,22 @@
#include "mongo/db/commands/server_status.h"
#include "mongo/executor/connection_pool.h"
#include "mongo/executor/connection_pool_stats.h"
-#include "mongo/logv2/log.h"
#include "mongo/platform/mutex.h"
#include "mongo/stdx/unordered_map.h"
#include "mongo/transport/transport_layer.h"
#include "mongo/util/alarm.h"
#include "mongo/util/alarm_runner_background_thread.h"
#include "mongo/util/assert_util.h"
-#include "mongo/util/bufreader.h"
#include "mongo/util/concurrency/thread_pool.h"
#include "mongo/util/functional.h"
#include "mongo/util/net/hostandport.h"
#include "mongo/util/net/http_client.h"
-#include "mongo/util/net/http_client_options.h"
#include "mongo/util/processinfo.h"
#include "mongo/util/strong_weak_finish_line.h"
#include "mongo/util/system_clock_source.h"
#include "mongo/util/timer.h"
+
namespace mongo {
namespace {
@@ -163,40 +159,20 @@ size_t WriteMemoryCallback(void* ptr, size_t size, size_t nmemb, void* data) {
*/
size_t ReadMemoryCallback(char* buffer, size_t size, size_t nitems, void* instream) {
- auto* bufReader = reinterpret_cast<BufReader*>(instream);
+ auto* cdrc = reinterpret_cast<ConstDataRangeCursor*>(instream);
size_t ret = 0;
- if (bufReader->remaining() > 0) {
- size_t readSize =
- std::min(size * nitems, static_cast<unsigned long>(bufReader->remaining()));
- auto buf = bufReader->readBytes(readSize);
- memcpy(buffer, buf.rawData(), readSize);
+ if (cdrc->length() > 0) {
+ size_t readSize = std::min(size * nitems, cdrc->length());
+ memcpy(buffer, cdrc->data(), readSize);
+ invariant(cdrc->advanceNoThrow(readSize).isOK());
ret = readSize;
}
return ret;
}
-/**
- * Seek into for data to the remote side
- */
-size_t SeekMemoryCallback(void* clientp, curl_off_t offset, int origin) {
-
- // Curl will call this in readrewind but only to reset the stream to the beginning
- // In other protocols (like FTP, SSH) or HTTP resumption they may ask for partial buffers which
- // we do not support.
- if (offset != 0 || origin != SEEK_SET) {
- return CURL_SEEKFUNC_CANTSEEK;
- }
-
- auto* bufReader = reinterpret_cast<BufReader*>(clientp);
-
- bufReader->rewindToStart();
-
- return CURL_SEEKFUNC_OK;
-}
-
struct CurlEasyCleanup {
void operator()(CURL* handle) {
if (handle) {
@@ -220,47 +196,6 @@ long longSeconds(Seconds tm) {
return static_cast<long>(durationCount<Seconds>(tm));
}
-
-StringData enumToString(curl_infotype type) {
- switch (type) {
- case CURLINFO_TEXT:
- return "TEXT"_sd;
- case CURLINFO_HEADER_IN:
- return "HEADER_IN"_sd;
- case CURLINFO_HEADER_OUT:
- return "HEADER_OUT"_sd;
- case CURLINFO_DATA_IN:
- return "DATA_IN"_sd;
- case CURLINFO_DATA_OUT:
- return "DATA_OUT"_sd;
- case CURLINFO_SSL_DATA_IN:
- return "SSL_DATA_IN"_sd;
- case CURLINFO_SSL_DATA_OUT:
- return "SSL_DATA_OUT"_sd;
- default:
- return "unknown"_sd;
- }
-}
-
-int curlDebugCallback(CURL* handle, curl_infotype type, char* data, size_t size, void* clientp) {
- switch (type) {
- case CURLINFO_TEXT:
- case CURLINFO_HEADER_IN:
- case CURLINFO_HEADER_OUT:
- case CURLINFO_DATA_IN:
- case CURLINFO_DATA_OUT:
- LOGV2_DEBUG(7661901,
- 1,
- "Curl",
- "type"_attr = enumToString(type),
- "message"_attr = StringData(data, size));
- [[fallthrough]];
-
- default:
- return 0;
- }
-}
-
CurlEasyHandle createCurlEasyHandle(Protocols protocol) {
CurlEasyHandle handle(curl_easy_init());
uassert(ErrorCodes::InternalError, "Curl initialization failed", handle);
@@ -291,10 +226,9 @@ CurlEasyHandle createCurlEasyHandle(Protocols protocol) {
}
// TODO: CURLOPT_EXPECT_100_TIMEOUT_MS?
- if (httpClientOptions.verboseLogging.loadRelaxed()) {
- curl_easy_setopt(handle.get(), CURLOPT_VERBOSE, 1);
- curl_easy_setopt(handle.get(), CURLOPT_DEBUGFUNCTION, curlDebugCallback);
- }
+ // TODO: consider making this configurable, defaults to stderr
+ // curl_easy_setopt(handle.get(), CURLOPT_VERBOSE, 1);
+ // curl_easy_setopt(_handle.get(), CURLOPT_DEBUGFUNCTION , ???);
return handle;
}
@@ -696,7 +630,7 @@ private:
curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT, longSeconds(_connectTimeout));
- BufReader bufReader(cdr.data(), cdr.length());
+ ConstDataRangeCursor cdrc(cdr);
switch (method) {
case HttpMethod::kGET:
uassert(ErrorCodes::BadValue,
@@ -711,22 +645,16 @@ private:
curl_easy_setopt(handle, CURLOPT_POST, 1);
curl_easy_setopt(handle, CURLOPT_READFUNCTION, ReadMemoryCallback);
- curl_easy_setopt(handle, CURLOPT_READDATA, &bufReader);
- curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, (long)bufReader.remaining());
-
- curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, SeekMemoryCallback);
- curl_easy_setopt(handle, CURLOPT_SEEKDATA, &bufReader);
+ curl_easy_setopt(handle, CURLOPT_READDATA, &cdrc);
+ curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, (long)cdrc.length());
break;
case HttpMethod::kPUT:
curl_easy_setopt(handle, CURLOPT_POST, 0);
curl_easy_setopt(handle, CURLOPT_PUT, 1);
curl_easy_setopt(handle, CURLOPT_READFUNCTION, ReadMemoryCallback);
- curl_easy_setopt(handle, CURLOPT_READDATA, &bufReader);
- curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (long)bufReader.remaining());
-
- curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, SeekMemoryCallback);
- curl_easy_setopt(handle, CURLOPT_SEEKDATA, &bufReader);
+ curl_easy_setopt(handle, CURLOPT_READDATA, &cdrc);
+ curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (long)cdrc.length());
break;
default:
MONGO_UNREACHABLE;
diff --git a/src/mongo/util/net/http_client_options.cpp b/src/mongo/util/net/http_client_options.cpp
deleted file mode 100644
index 7e96be222ac..00000000000
--- a/src/mongo/util/net/http_client_options.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * Copyright (C) 2023-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/util/net/http_client_options.h"
-
-namespace mongo {
-
-HttpClientOptions httpClientOptions;
-
-}
diff --git a/src/mongo/util/net/http_client_options.h b/src/mongo/util/net/http_client_options.h
deleted file mode 100644
index ebf4611677a..00000000000
--- a/src/mongo/util/net/http_client_options.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * Copyright (C) 2023-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#pragma once
-
-#include "mongo/platform/atomic_word.h"
-
-namespace mongo {
-struct HttpClientOptions {
- /**
- * Boolean flag that indicates whether verbose logging for http clients should be enabled.
- *
- * Note: only affects new handles. This means that if connection pooling is in use, this will
- * not take affect on existing connections.
- */
- AtomicWord<bool> verboseLogging;
-};
-
-extern HttpClientOptions httpClientOptions;
-} // namespace mongo
diff --git a/src/mongo/util/net/http_client_options.idl b/src/mongo/util/net/http_client_options.idl
deleted file mode 100644
index b53b5e5541b..00000000000
--- a/src/mongo/util/net/http_client_options.idl
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (C) 2023-present MongoDB, Inc.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the Server Side Public License, version 1,
-# as published by MongoDB, Inc.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# Server Side Public License for more details.
-#
-# You should have received a copy of the Server Side Public License
-# along with this program. If not, see
-# <http://www.mongodb.com/licensing/server-side-public-license>.
-#
-# As a special exception, the copyright holders give permission to link the
-# code of portions of this program with the OpenSSL library under certain
-# conditions as described in each individual source file and distribute
-# linked combinations including the program with the OpenSSL library. You
-# must comply with the Server Side Public License in all respects for
-# all of the code used other than as permitted herein. If you modify file(s)
-# with this exception, you may extend this exception to your version of the
-# file(s), but you are not obligated to do so. If you do not wish to do so,
-# delete this exception statement from your version. If you delete this
-# exception statement from all source files in the program, then also delete
-# it in the license file.
-
-global:
- cpp_namespace: "mongo"
- cpp_includes:
- - "mongo/util/net/http_client_options.h"
-
-server_parameters:
- httpVerboseLogging:
- description: Boolean flag that indicates whether verbose logging for http clients should be enabled.
- set_at: [ startup, runtime ]
- default: false
- cpp_varname: "httpClientOptions.verboseLogging"
diff --git a/src/mongo/util/net/ocsp/ocsp_manager.cpp b/src/mongo/util/net/ocsp/ocsp_manager.cpp
index b8d0b2683dd..1524d1da560 100644
--- a/src/mongo/util/net/ocsp/ocsp_manager.cpp
+++ b/src/mongo/util/net/ocsp/ocsp_manager.cpp
@@ -68,6 +68,7 @@ void OCSPManager::start(ServiceContext* service) {
void OCSPManager::shutdown(ServiceContext* service) {
get(service)->_pool->shutdown();
+ getOCSPManager(service).reset();
}
OCSPManager::OCSPManager() {
diff --git a/src/mongo/util/net/openssl_init.cpp b/src/mongo/util/net/openssl_init.cpp
index 5ef31f1212b..89e5a1c4498 100644
--- a/src/mongo/util/net/openssl_init.cpp
+++ b/src/mongo/util/net/openssl_init.cpp
@@ -46,12 +46,6 @@
#include <stack>
#include <vector>
-#if OPENSSL_VERSION_NUMBER > 0x30000000L
-#include <openssl/provider.h>
-#endif
-
-#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kNetwork
-
namespace mongo {
namespace {
@@ -150,54 +144,21 @@ private:
}
};
-#if OPENSSL_VERSION_NUMBER > 0x30000000L
-#define _SUPPORT_FIPS 1
-
-OSSL_PROVIDER* fipsProvider;
-OSSL_PROVIDER* baseProvider;
-
-void initFIPS() {
- // OpenSSL 3 has a different FIPS design then previous OpenSSL. To load FIPS, we use the FIPS
- // algorithm provider which we load into the "default" library context.
- fipsProvider = OSSL_PROVIDER_load(NULL, "fips");
- if (fipsProvider == NULL) {
- LOGV2_FATAL_NOTRACE(
- 7585801,
- "Failed to load OpenSSL 3 FIPS provider. OpenSSL was not compiled with FIPS support.",
- "error"_attr = SSLManagerInterface::getSSLErrorMessage(ERR_get_error()));
- }
-
- // Base provide has non-cryptographic algorihms (like encoding/decoding keys)
- baseProvider = OSSL_PROVIDER_load(NULL, "base");
- if (baseProvider == NULL) {
- LOGV2_FATAL_NOTRACE(7585802,
- "Failed to load OpenSSL 3 Base provider",
- "error"_attr =
- SSLManagerInterface::getSSLErrorMessage(ERR_get_error()));
- }
-}
-#elif defined(MONGO_CONFIG_HAVE_FIPS_MODE_SET)
-
-#define _SUPPORT_FIPS 1
-
-void initFIPS() {
+void setupFIPS() {
+// Turn on FIPS mode if requested, OPENSSL_FIPS must be defined by the OpenSSL headers
+#if defined(MONGO_CONFIG_HAVE_FIPS_MODE_SET)
int status = FIPS_mode_set(1);
if (!status) {
- LOGV2_FATAL_NOTRACE(23173,
- "Can't activate FIPS mode",
- "error"_attr =
- SSLManagerInterface::getSSLErrorMessage(ERR_get_error()));
+ LOGV2_FATAL(23173,
+ "can't activate FIPS mode: {error}",
+ "Can't activate FIPS mode",
+ "error"_attr = SSLManagerInterface::getSSLErrorMessage(ERR_get_error()));
+ fassertFailedNoTrace(16703);
}
-}
-#endif
-
-void setupFIPS() {
-// Turn on FIPS mode if requested, OPENSSL_FIPS must be defined by the OpenSSL headers
-#if defined(_SUPPORT_FIPS)
- initFIPS();
LOGV2(23172, "FIPS 140-2 mode activated");
#else
- LOGV2_FATAL_NOTRACE(23174, "this version of mongodb was not compiled with FIPS support");
+ LOGV2_FATAL(23174, "this version of mongodb was not compiled with FIPS support");
+ fassertFailedNoTrace(17089);
#endif
}
diff --git a/src/mongo/util/net/ssl_manager.cpp b/src/mongo/util/net/ssl_manager.cpp
index c784c3792d6..2f4a6fb713f 100644
--- a/src/mongo/util/net/ssl_manager.cpp
+++ b/src/mongo/util/net/ssl_manager.cpp
@@ -38,7 +38,6 @@
#include <string>
#include <vector>
-#include "mongo/base/data_view.h"
#include "mongo/base/init.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/client/internal_auth.h"
@@ -780,6 +779,7 @@ bool SSLConfiguration::isClusterMember(StringData subjectName) const {
void SSLConfiguration::getServerStatusBSON(BSONObjBuilder* security) const {
security->append("SSLServerSubjectName", _serverSubjectName.toString());
+ security->appendBool("SSLServerHasCertificateAuthority", hasCA);
security->appendDate("SSLServerCertificateExpirationDate", serverCertificateExpirationDate);
}
@@ -1048,7 +1048,7 @@ StatusWith<DERToken> DERToken::parse(ConstDataRange cdr, size_t* outLength) {
derLength = ConstDataView(lengthBuffer.data()).read<BigEndian<uint64_t>>();
} else {
// Length is <= 127 bytes, i.e. short form of length
- derLength = ConstDataView(&initialLengthByte).read<uint8_t>();
+ derLength = initialLengthByte;
}
// This is the total length of the TLV and all data
diff --git a/src/mongo/util/net/ssl_manager_apple.cpp b/src/mongo/util/net/ssl_manager_apple.cpp
index f3e0f4d6652..ec77dc17722 100644
--- a/src/mongo/util/net/ssl_manager_apple.cpp
+++ b/src/mongo/util/net/ssl_manager_apple.cpp
@@ -1369,15 +1369,20 @@ SSLManagerApple::SSLManagerApple(const SSLParams& params, bool isServer)
}
}
- // If the user has specified --setParameter tlsUseSystemCA=true, then no params.sslCAFile nor
- // params.sslClusterCAFile will be defined, and the SSL Manager will fall back to the System CA.
if (!params.sslCAFile.empty()) {
auto ca = uassertStatusOK(loadPEM(params.sslCAFile, "", kLoadPEMStripKeys));
_clientCA = std::move(ca);
+ _sslConfiguration.hasCA = _clientCA && ::CFArrayGetCount(_clientCA.get());
+ }
+
+ if (!params.sslCertificateSelector.empty() || !params.sslClusterCertificateSelector.empty()) {
+ // By using the system keychain, we acknowledge it exists.
+ _sslConfiguration.hasCA = true;
}
if (!_clientCA) {
- // No explicit CA was specified, use the Keychain CA explicitly
+ // No explicit CA was specified, use the Keychain CA explicitly on client connects,
+ // even though we're going to pretend it doesn't exist on server.
::CFArrayRef certs = nullptr;
uassertOSStatusOK(SecTrustCopyAnchorCertificates(&certs));
_clientCA.reset(certs);
@@ -1552,6 +1557,17 @@ Future<SSLPeerInfo> SSLManagerApple::parseAndValidatePeerCertificate(
recordTLSVersion(tlsVersionStatus.getValue(), hostForLogging);
+ /* While we always have a system CA via the Keychain,
+ * we'll pretend not to in terms of validation if the server
+ * was started using a PEM file (legacy mode).
+ *
+ * When a certificate selector is used, we'll override hasCA to true
+ * so that the validation path runs anyway.
+ */
+ if (!_sslConfiguration.hasCA && isSSLServer) {
+ return Future<SSLPeerInfo>::makeReady(SSLPeerInfo(sniName));
+ }
+
const auto badCert = [&](StringData msg, bool warn = false) -> Future<SSLPeerInfo> {
if (warn) {
LOGV2_WARNING(23209,
@@ -1576,7 +1592,7 @@ Future<SSLPeerInfo> SSLManagerApple::parseAndValidatePeerCertificate(
return SSLPeerInfo(sniName);
} else {
if (status == ::errSecSuccess) {
- return badCert(str::stream() << "No SSL certificate provided by peer: "
+ return badCert(str::stream() << "no SSL certificate provided by peer: "
<< stringFromOSStatus(status),
_weakValidation);
} else {
@@ -1654,14 +1670,11 @@ Future<SSLPeerInfo> SSLManagerApple::parseAndValidatePeerCertificate(
return swPeerSubjectName.getStatus();
}
const auto peerSubjectName = std::move(swPeerSubjectName.getValue());
- // The cipher will be presented as a number.
- ::SSLCipherSuite cipher;
- uassertOSStatusOK(::SSLGetNegotiatedCipher(ssl, &cipher));
-
- LOGV2_INFO(6723803,
- "Accepted TLS connection from peer",
- "peerSubjectName"_attr = peerSubjectName,
- "cipher"_attr = cipher);
+ LOGV2_DEBUG(23207,
+ 2,
+ "Accepted TLS connection from peer: {peerSubjectName}",
+ "Accepted TLS connection from peer",
+ "peerSubjectName"_attr = peerSubjectName);
// Server side.
if (remoteHost.empty()) {
@@ -1874,26 +1887,8 @@ MONGO_INITIALIZER_WITH_PREREQUISITES(SSLManager, ("EndStartupOptionHandling"))
kMongoDBRolesOID = ::CFStringCreateWithCString(
nullptr, mongodbRolesOID.identifier.c_str(), ::kCFStringEncodingUTF8);
- // TODO SERVER-67419 This retry logic is a workaround; reconsider this approach after
- // investigation.
- constexpr int kMaxRetries = 10;
if (!isSSLServer || (sslGlobalParams.sslMode.load() != SSLParams::SSLMode_disabled)) {
- for (int i = 0; i < kMaxRetries; i++) {
- try {
- theSSLManagerCoordinator = new SSLManagerCoordinator();
- return;
- } catch (const ExceptionFor<ErrorCodes::InvalidSSLConfiguration>& e) {
- bool isRetriableError = nullptr != strstr(e.what(), "No keychain is available.");
- if (!isRetriableError || i == kMaxRetries - 1) {
- // Rethrow if a different error or we fail on final iteration
- throw;
- }
- LOGV2_INFO(6741800,
- "Caught exception during apple SSLManagerCoordinator creation, retrying",
- "try"_attr = i,
- "error"_attr = e.what());
- }
- }
+ theSSLManagerCoordinator = new SSLManagerCoordinator();
}
}
diff --git a/src/mongo/util/net/ssl_manager_openssl.cpp b/src/mongo/util/net/ssl_manager_openssl.cpp
index 48461e4120d..f2ddee148c7 100644
--- a/src/mongo/util/net/ssl_manager_openssl.cpp
+++ b/src/mongo/util/net/ssl_manager_openssl.cpp
@@ -123,7 +123,12 @@ struct X509StackDeleter {
}
}
};
-using UniqueStackOfX509 = std::unique_ptr<STACK_OF(X509), X509StackDeleter>;
+
+// If we have an X509 Stack that is owned by an internal SSL Object, we need to use this
+// deleter.
+struct X509StackDeleterNoOp {
+ void operator()(STACK_OF(X509) * chain) {}
+};
// Modulus for Diffie-Hellman parameter 'ffdhe3072' defined in RFC 7919
constexpr std::array<std::uint8_t, 384> ffdhe3072_p = {
@@ -319,6 +324,25 @@ X509* X509_OBJECT_get0_X509(const X509_OBJECT* a) {
return a->data.x509;
}
+using UniqueVerifiedChainPolyfill = std::unique_ptr<STACK_OF(X509), X509StackDeleter>;
+
+STACK_OF(X509) * SSL_get0_verified_chain(SSL* s) {
+ auto* store = SSL_CTX_get_cert_store(SSL_get_SSL_CTX(s));
+ UniqueX509 peer(SSL_get_peer_certificate(s));
+ auto* peerChain = SSL_get_peer_cert_chain(s);
+
+ UniqueX509StoreCtx ctx(X509_STORE_CTX_new());
+ if (!X509_STORE_CTX_init(ctx.get(), store, peer.get(), peerChain)) {
+ return nullptr;
+ }
+
+ if (X509_verify_cert(ctx.get()) <= 0) {
+ return nullptr;
+ }
+
+ return X509_STORE_CTX_get1_chain(ctx.get());
+}
+
const OCSP_CERTID* OCSP_SINGLERESP_get0_id(const OCSP_SINGLERESP* single) {
return single->certId;
}
@@ -351,23 +375,14 @@ static ASN1OID tlsFeatureOID("1.3.6.1.5.5.7.1.24", "tlsfeature", "TLS Feature");
static int const NID_tlsfeature = OBJ_create(tlsFeatureOID.identifier.c_str(),
tlsFeatureOID.shortDescription.c_str(),
tlsFeatureOID.longDescription.c_str());
-#endif
-
-UniqueStackOfX509 SSLgetVerifiedChain(SSL* s) {
- auto* store = SSL_CTX_get_cert_store(SSL_get_SSL_CTX(s));
- auto* peerChain = SSL_get_peer_cert_chain(s);
- UniqueX509 peer(SSL_get_peer_certificate(s));
- UniqueX509StoreCtx ctx(X509_STORE_CTX_new());
- if (!X509_STORE_CTX_init(ctx.get(), store, peer.get(), peerChain)) {
- return nullptr;
- }
+#else
+using UniqueVerifiedChainPolyfill = std::unique_ptr<STACK_OF(X509), X509StackDeleterNoOp>;
- if (X509_verify_cert(ctx.get()) <= 0) {
- return nullptr;
- }
+#endif
- return UniqueStackOfX509(X509_STORE_CTX_get1_chain(ctx.get()));
+UniqueVerifiedChainPolyfill SSLgetVerifiedChain(SSL* s) {
+ return UniqueVerifiedChainPolyfill(SSL_get0_verified_chain(s));
}
SSLX509Name convertX509ToSSLX509Name(X509_NAME* x509Name) {
@@ -641,7 +656,7 @@ std::vector<std::vector<unsigned char>> convertStackOfX509ToDERVec(STACK_OF(X509
}
struct OCSPCacheKey {
- OCSPCacheKey(UniqueX509 cert, SSL_CTX* context, UniqueStackOfX509 intermediateCerts)
+ OCSPCacheKey(UniqueX509 cert, SSL_CTX* context, UniqueVerifiedChainPolyfill intermediateCerts)
: peerCert(std::move(cert)),
context(context),
intermediateCerts(std::move(intermediateCerts)),
@@ -2539,8 +2554,6 @@ Status SSLManagerOpenSSL::initSSLContext(SSL_CTX* context,
}
}
- // If the user has specified --setParameter tlsUseSystemCA=true, then no params.sslCAFile nor
- // params.sslClusterCAFile will be defined, and the SSL Manager will fall back to the System CA.
std::string cafile = params.sslCAFile;
if (direction == ConnectionDirection::kIncoming && !params.sslClusterCAFile.empty()) {
cafile = params.sslClusterCAFile;
@@ -2955,6 +2968,7 @@ Status SSLManagerOpenSSL::_setupCA(SSL_CTX* context, const std::string& caFile)
// Set SSL to require peer (client) certificate verification
// if a certificate is presented
SSL_CTX_set_verify(context, SSL_VERIFY_PEER, &SSLManagerOpenSSL::verify_cb);
+ _sslConfiguration.hasCA = true;
return Status::OK();
}
@@ -2979,7 +2993,7 @@ Status SSLManagerOpenSSL::_setupSystemCA(SSL_CTX* context) {
<< "(default certificate file: " << X509_get_default_cert_file() << ", "
<< "default certificate path: " << X509_get_default_cert_dir() << ")"};
}
- SSL_CTX_set_verify(context, SSL_VERIFY_PEER, &SSLManagerOpenSSL::verify_cb);
+
return Status::OK();
}
@@ -3219,6 +3233,9 @@ Future<SSLPeerInfo> SSLManagerOpenSSL::parseAndValidatePeerCertificate(
recordTLSVersion(tlsVersionStatus.getValue(), hostForLogging);
+ if (!_sslConfiguration.hasCA && isSSLServer)
+ return SSLPeerInfo(sni);
+
UniqueX509 peerCert(SSL_get_peer_certificate(conn));
if (nullptr == peerCert) { // no certificate presented by peer
@@ -3271,11 +3288,11 @@ Future<SSLPeerInfo> SSLManagerOpenSSL::parseAndValidatePeerCertificate(
// TODO: check optional cipher restriction, using cert.
auto peerSubject = getCertificateSubjectX509Name(peerCert.get());
- const auto cipher = SSL_get_current_cipher(conn);
- LOGV2_INFO(6723801,
- "Accepted TLS connection from peer",
- "peerSubject"_attr = peerSubject,
- "cipher"_attr = SSL_CIPHER_get_name(cipher));
+ LOGV2_DEBUG(23229,
+ 2,
+ "Accepted TLS connection from peer: {peerSubject}",
+ "Accepted TLS connection from peer",
+ "peerSubject"_attr = peerSubject);
StatusWith<stdx::unordered_set<RoleName>> swPeerCertificateRoles =
_parsePeerRoles(peerCert.get());
diff --git a/src/mongo/util/net/ssl_manager_test.cpp b/src/mongo/util/net/ssl_manager_test.cpp
index 913c8ba983b..1dd6585f7c1 100644
--- a/src/mongo/util/net/ssl_manager_test.cpp
+++ b/src/mongo/util/net/ssl_manager_test.cpp
@@ -780,5 +780,6 @@ TEST(SSLManager, InitContextNoSanWarning) {
ASSERT_FALSE(isSanWarningWritten(getCapturedTextFormatLogMessages()));
}
+
} // namespace
} // namespace mongo
diff --git a/src/mongo/util/net/ssl_manager_windows.cpp b/src/mongo/util/net/ssl_manager_windows.cpp
index 7169e1eef1f..adf86ae847f 100644
--- a/src/mongo/util/net/ssl_manager_windows.cpp
+++ b/src/mongo/util/net/ssl_manager_windows.cpp
@@ -1284,9 +1284,11 @@ Status SSLManagerWindows::_loadCertificates(const SSLParams& params) {
_clientCertificates[0] = std::get<0>(_clusterPEMCertificate).get();
}
- // If the user has specified --setParameter tlsUseSystemCA=true, then no params.sslCAFile nor
- // params.sslClusterCAFile will be defined, and the SSL Manager will fall back to the System CA.
if (!params.sslCAFile.empty()) {
+ // SChannel always has a CA even when the user does not specify one
+ // The openssl implementations uses this to decide if it wants to do certificate validation
+ // on the server side.
+ _sslConfiguration.hasCA = true;
auto swChain = readCertChains(params.sslCAFile, params.sslCRLFile);
if (!swChain.isOK()) {
@@ -1349,8 +1351,10 @@ Status SSLManagerWindows::_loadCertificates(const SSLParams& params) {
if (!params.sslCAFile.empty()) {
LOGV2_WARNING(23271,
"Mixing certs from the system certificate store and PEM files. This may "
- "produce unexpected results.");
+ "produced unexpected results.");
}
+
+ _sslConfiguration.hasCA = true;
}
if (_sslCertificate) {
@@ -2003,6 +2007,9 @@ Future<SSLPeerInfo> SSLManagerWindows::parseAndValidatePeerCertificate(
recordTLSVersion(tlsVersionStatus.getValue(), hostForLogging);
+ if (!_sslConfiguration.hasCA && isSSLServer)
+ return Future<SSLPeerInfo>::makeReady(SSLPeerInfo(sni));
+
SECURITY_STATUS ss = QueryContextAttributes(ssl, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &cert);
if (ss == SEC_E_NO_CREDENTIALS) { // no certificate presented by peer
@@ -2062,19 +2069,10 @@ Future<SSLPeerInfo> SSLManagerWindows::parseAndValidatePeerCertificate(
return Future<SSLPeerInfo>::makeReady(SSLPeerInfo(sni));
}
- SecPkgContext_CipherInfo cipherInfo;
- SECURITY_STATUS ssCipher = QueryContextAttributes(ssl, SECPKG_ATTR_CIPHER_INFO, &cipherInfo);
- if (ssCipher != SEC_E_OK) {
- return Status(ErrorCodes::SSLHandshakeFailed,
- str::stream()
- << "QueryContextAttributes for connection info failed with" << ssCipher);
- }
- const auto cipher = std::wstring(cipherInfo.szCipherSuite);
-
- LOGV2_INFO(6723802,
- "Accepted TLS connection from peer",
- "peerSubjectName"_attr = peerSubjectName,
- "cipher"_attr = toUtf8String(cipher));
+ LOGV2_DEBUG(23270,
+ 2,
+ "Accepted TLS connection from peer: {peerSubjectName}",
+ "peerSubjectName"_attr = peerSubjectName);
// If this is a server and client and server certificate are the same, log a warning.
if (remoteHost.empty() && _sslConfiguration.serverSubjectName() == peerSubjectName) {
diff --git a/src/mongo/util/net/ssl_options.h b/src/mongo/util/net/ssl_options.h
index 13f7303704e..e58bedcd076 100644
--- a/src/mongo/util/net/ssl_options.h
+++ b/src/mongo/util/net/ssl_options.h
@@ -93,7 +93,6 @@ struct SSLParams {
bool sslFIPSMode = false; // --sslFIPSMode
bool sslAllowInvalidCertificates = false; // --sslAllowInvalidCertificates
bool sslAllowInvalidHostnames = false; // --sslAllowInvalidHostnames
- bool sslUseSystemCA = false; // --setParameter tlsUseSystemCA
bool disableNonSSLConnectionLogging =
false; // --setParameter disableNonSSLConnectionLogging=true
bool disableNonSSLConnectionLoggingSet = false;
diff --git a/src/mongo/util/net/ssl_options_server.cpp b/src/mongo/util/net/ssl_options_server.cpp
index eb67d49e2c3..4612993df07 100644
--- a/src/mongo/util/net/ssl_options_server.cpp
+++ b/src/mongo/util/net/ssl_options_server.cpp
@@ -29,7 +29,6 @@
#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kControl
-#include "mongo/base/error_codes.h"
#include "mongo/platform/basic.h"
#include "mongo/util/net/ssl_options.h"
@@ -193,27 +192,14 @@ MONGO_STARTUP_OPTIONS_POST(SSLServerOptions)(InitializerContext*) {
const auto clusterAuthMode = serverGlobalParams.startupClusterAuthMode;
if (sslGlobalParams.sslMode.load() != SSLParams::SSLMode_disabled) {
- uassert(ErrorCodes::InvalidOptions,
- "Specifying a tlsClusterCAFile requires a tlsCAFile also be specified. See "
- "https://dochub.mongodb.org/core/mongod"
- "#std-option-mongod.--tlsClusterCAFile for details.",
- sslGlobalParams.sslClusterCAFile.empty() || !sslGlobalParams.sslCAFile.empty());
- uassert(ErrorCodes::InvalidOptions,
- "The use of both a CA File and the System Certificate store is not supported.",
- !sslGlobalParams.sslUseSystemCA || sslGlobalParams.sslCAFile.empty());
- uassert(ErrorCodes::InvalidOptions,
- "The use of TLS without specifying a chain of trust is no longer supported. See "
- "https://jira.mongodb.org/browse/SERVER-72839 for details.",
- sslGlobalParams.sslUseSystemCA || !sslGlobalParams.sslCAFile.empty());
- if (!sslGlobalParams.sslCRLFile.empty() && sslGlobalParams.sslCAFile.empty()) {
- uasserted(ErrorCodes::BadValue,
- "Specifying a tlsCRLFile requires a tlsCAFile also be specified.");
- }
bool usingCertifiateSelectors = params.count("net.tls.certificateSelector");
if (sslGlobalParams.sslPEMKeyFile.size() == 0 && !usingCertifiateSelectors) {
uasserted(ErrorCodes::BadValue,
"need tlsCertificateKeyFile or certificateSelector when TLS is enabled");
}
+ if (!sslGlobalParams.sslCRLFile.empty() && sslGlobalParams.sslCAFile.empty()) {
+ uasserted(ErrorCodes::BadValue, "need tlsCAFile with tlsCRLFile");
+ }
std::string sslCANotFoundError(
"No TLS certificate validation can be performed since"
diff --git a/src/mongo/util/net/ssl_options_server.idl b/src/mongo/util/net/ssl_options_server.idl
index d69af991a11..58b05893a36 100644
--- a/src/mongo/util/net/ssl_options_server.idl
+++ b/src/mongo/util/net/ssl_options_server.idl
@@ -40,13 +40,6 @@ global:
imports:
- "mongo/idl/basic_types.idl"
-server_parameters:
- tlsUseSystemCA:
- description: "Use System CA for certificate verification"
- set_at: startup
- cpp_varname: "sslGlobalParams.sslUseSystemCA"
- default: false
-
configs:
"net.tls.tlsOnNormalPorts":
description: "Use TLS on configured ports"
diff --git a/src/mongo/util/net/ssl_parameters.idl b/src/mongo/util/net/ssl_parameters.idl
index 9b3222159bb..c0cc5ca2c63 100644
--- a/src/mongo/util/net/ssl_parameters.idl
+++ b/src/mongo/util/net/ssl_parameters.idl
@@ -87,7 +87,7 @@ server_parameters:
when fetching OCSP Responses for peer certificate
set_at: startup
cpp_vartype: int
- default: 4
+ default: 5
cpp_varname: "gTLSOCSPVerifyTimeoutSecs"
validator:
gte: 1
diff --git a/src/mongo/util/net/ssl_types.h b/src/mongo/util/net/ssl_types.h
index e129b549056..6f859ee01aa 100644
--- a/src/mongo/util/net/ssl_types.h
+++ b/src/mongo/util/net/ssl_types.h
@@ -122,6 +122,7 @@ public:
SSLX509Name clientSubjectName;
Date_t serverCertificateExpirationDate;
+ bool hasCA = false;
private:
SSLX509Name _serverSubjectName;
diff --git a/src/mongo/util/pin_code_segments.cpp b/src/mongo/util/pin_code_segments.cpp
deleted file mode 100644
index 8d685d86d1d..00000000000
--- a/src/mongo/util/pin_code_segments.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- * Copyright (C) 2023-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kControl
-
-#include <elf.h>
-#include <link.h>
-#include <sys/mman.h>
-#include <vector>
-
-#include "mongo/db/initialize_server_global_state.h"
-#include "mongo/db/initialize_server_global_state_gen.h"
-#include "mongo/logv2/log.h"
-#include "mongo/util/hex.h"
-#include "mongo/util/pin_code_segments_params_gen.h"
-
-namespace mongo {
-namespace {
-
-using ElfWEhdr = ElfW(Ehdr);
-using ElfWHalf = ElfW(Half);
-using ElfWPhdr = ElfW(Phdr);
-
-struct CodeSegment {
- void* addr;
- size_t memSize;
-};
-
-int appendCodeSegments(dl_phdr_info* info, size_t size, void* data) {
- auto segments = reinterpret_cast<std::vector<CodeSegment>*>(data);
- for (ElfWHalf i = 0; i < info->dlpi_phnum; ++i) {
- const ElfWPhdr& phdr(info->dlpi_phdr[i]);
- if (phdr.p_type != PT_LOAD) {
- // Code segments must be of LOAD type.
- continue;
- }
- const auto f = phdr.p_flags;
- if (!(f & PF_R)) {
- // Code segments have read permissions so exclude segments that do not.
- continue;
- }
- if (!(f & PF_X)) {
- // Code segments have execute permissions so exclude segments that do not.
- continue;
- }
- if (f & PF_W) {
- // Code segments don't have write permissions so exclude segments that do.
- continue;
- }
- segments->push_back(
- {reinterpret_cast<void*>(info->dlpi_addr + phdr.p_vaddr), phdr.p_memsz});
- }
- return 0;
-}
-
-std::vector<CodeSegment> getCodeSegments() {
- std::vector<CodeSegment> segments;
- dl_iterate_phdr(appendCodeSegments, &segments);
- return segments;
-}
-
-MONGO_INITIALIZER(PinCodeSegments)(InitializerContext*) {
- if (!gLockCodeSegmentsInMemory) {
- return;
- }
- LOGV2(7394303, "Pinning code segments");
- size_t lockedMemSize = 0;
- auto codeSegments = getCodeSegments();
- for (auto&& segment : codeSegments) {
- if (mlock(segment.addr, segment.memSize) != 0) {
- auto ec = lastSystemError();
- LOGV2_FATAL(
- 7394301,
- "Failed to lock code segment, ensure system ulimits are properly configured",
- "error"_attr = errorMessage(ec),
- "address"_attr = unsignedHex(reinterpret_cast<uintptr_t>(segment.addr)),
- "memSize"_attr = segment.memSize,
- "memLockedSoFar"_attr = lockedMemSize);
- }
- lockedMemSize += segment.memSize;
- }
- LOGV2(7394302,
- "Successfully locked code segments into memory",
- "numSegments"_attr = codeSegments.size(),
- "totalLockedMemSize"_attr = lockedMemSize);
-}
-
-} // namespace
-} // namespace mongo
diff --git a/src/mongo/util/pin_code_segments_params.idl b/src/mongo/util/pin_code_segments_params.idl
deleted file mode 100644
index 2686a22a60a..00000000000
--- a/src/mongo/util/pin_code_segments_params.idl
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright (C) 2023-present MongoDB, Inc.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the Server Side Public License, version 1,
-# as published by MongoDB, Inc.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# Server Side Public License for more details.
-#
-# You should have received a copy of the Server Side Public License
-# along with this program. If not, see
-# <http://www.mongodb.com/licensing/server-side-public-license>.
-#
-# As a special exception, the copyright holders give permission to link the
-# code of portions of this program with the OpenSSL library under certain
-# conditions as described in each individual source file and distribute
-# linked combinations including the program with the OpenSSL library. You
-# must comply with the Server Side Public License in all respects for
-# all of the code used other than as permitted herein. If you modify file(s)
-# with this exception, you may extend this exception to your version of the
-# file(s), but you are not obligated to do so. If you do not wish to do so,
-# delete this exception statement from your version. If you delete this
-# exception statement from all source files in the program, then also delete
-# it in the license file.
-#
-
-global:
- cpp_namespace: "mongo"
-
-server_parameters:
- lockCodeSegmentsInMemory:
- description: >-
- When enabled, the server will attempt to call mlock() on all memory ranges corresponding
- to the process code segments (including shared libraries) on startup. If any call to mlock()
- fails, the server will terminate. When disabled, the server will not attempt to pin any code
- segments in memory. This feature is only available on Linux.
- set_at: startup
- cpp_vartype: bool
- cpp_varname: gLockCodeSegmentsInMemory
- default: false
diff --git a/src/mongo/util/processinfo.h b/src/mongo/util/processinfo.h
index 361276d5bf3..942ffc54136 100644
--- a/src/mongo/util/processinfo.h
+++ b/src/mongo/util/processinfo.h
@@ -113,13 +113,6 @@ public:
}
/**
- * Get the number of CPU sockets
- */
- static unsigned getNumCpuSockets() {
- return sysInfo().numCpuSockets;
- }
-
- /**
* Get the number of cores available. Make a best effort to get the cores for this process.
* If that information is not available, get the total number of CPUs.
*/
@@ -149,16 +142,6 @@ public:
}
/**
- * Get the number of NUMA nodes if NUMA is enabled, or 1 otherwise.
- */
- static unsigned long getNumNumaNodes() {
- if (sysInfo().hasNuma) {
- return sysInfo().numNumaNodes;
- }
- return 1;
- }
-
- /**
* Determine if we need to workaround slow msync performance on Illumos/Solaris
*/
static bool preferMsyncOverFSync() {
@@ -197,11 +180,9 @@ private:
unsigned long long memLimit;
unsigned numCores;
unsigned numPhysicalCores;
- unsigned numCpuSockets;
unsigned long long pageSize;
std::string cpuArch;
bool hasNuma;
- unsigned numNumaNodes;
BSONObj _extraStats;
// On non-Solaris (ie, Linux, Darwin, *BSD) kernels, prefer msync.
@@ -217,10 +198,8 @@ private:
memLimit(0),
numCores(0),
numPhysicalCores(0),
- numCpuSockets(0),
pageSize(0),
hasNuma(false),
- numNumaNodes(0),
preferMsyncOverFSync(true) {
// populate SystemInfo during construction
collectSystemInfo();
@@ -251,6 +230,8 @@ private:
ProcessId _pid;
+ static bool checkNumaEnabled();
+
static const SystemInfo& sysInfo() {
static ProcessInfo::SystemInfo systemInfo;
return systemInfo;
diff --git a/src/mongo/util/processinfo_freebsd.cpp b/src/mongo/util/processinfo_freebsd.cpp
index fbd49a6d4b9..d13b0295e92 100644
--- a/src/mongo/util/processinfo_freebsd.cpp
+++ b/src/mongo/util/processinfo_freebsd.cpp
@@ -92,6 +92,10 @@ int getSysctlByNameWithDefault<std::string>(const char* sysctlName,
return 0;
}
+bool ProcessInfo::checkNumaEnabled() {
+ return false;
+}
+
int ProcessInfo::getVirtualMemorySize() {
kvm_t* kd = NULL;
int cnt = 0;
@@ -160,7 +164,7 @@ void ProcessInfo::SystemInfo::collectSystemInfo() {
pageSize = static_cast<unsigned long long>(sysconf(_SC_PAGESIZE));
- hasNuma = false;
+ hasNuma = checkNumaEnabled();
}
void ProcessInfo::getExtraInfo(BSONObjBuilder& info) {}
diff --git a/src/mongo/util/processinfo_linux.cpp b/src/mongo/util/processinfo_linux.cpp
index 7793011e901..37e3d2ea3fc 100644
--- a/src/mongo/util/processinfo_linux.cpp
+++ b/src/mongo/util/processinfo_linux.cpp
@@ -407,30 +407,9 @@ public:
}
/**
- * count the number of processor packages
- */
- static int getNumCpuSockets() {
- std::set<std::string> socketIds;
-
- CpuInfoParser cpuInfoParser{
- {
- {"physical id", [&](const std::string& value) { socketIds.insert(value); }},
- },
- []() {}};
- cpuInfoParser.run();
-
- // On ARM64, the "physical id" field is unpopulated, causing there to be 0 sockets found. In
- // this case, we default to 1.
- return std::max(socketIds.size(), 1ul);
- }
-
- /**
* Get some details about the CPU
*/
- static void getCpuInfo(int& procCount,
- std::string& modelString,
- std::string& freq,
- std::string& features) {
+ static void getCpuInfo(int& procCount, std::string& freq, std::string& features) {
procCount = 0;
@@ -442,7 +421,6 @@ public:
{"features", [&](const std::string& value) { features = value; }},
#else
{"processor", [&](const std::string& value) { procCount++; }},
- {"model name", [&](const std::string& value) { modelString = value; }},
{"cpu MHz", [&](const std::string& value) { freq = value; }},
{"flags", [&](const std::string& value) { features = value; }},
#endif
@@ -668,49 +646,6 @@ void ProcessInfo::getExtraInfo(BSONObjBuilder& info) {
appendNumber("voluntary_context_switches", ru.ru_nvcsw);
appendNumber("involuntary_context_switches", ru.ru_nivcsw);
-
- LinuxProc p(_pid);
-
- // Append the number of thread in use
- appendNumber("threads", p._nlwp);
-}
-
-/**
- * If the process is running with (cc)NUMA enabled, return the number of NUMA nodes. Else, return 0.
- */
-unsigned long countNumaNodes() {
- bool hasMultipleNodes = false;
- bool hasNumaMaps = false;
-
- try {
- hasMultipleNodes = boost::filesystem::exists("/sys/devices/system/node/node1");
- hasNumaMaps = boost::filesystem::exists("/proc/self/numa_maps");
-
- if (hasMultipleNodes && hasNumaMaps) {
- // proc is populated with numa entries
-
- // read the second column of first line to determine numa state
- // ('default' = enabled, 'interleave' = disabled). Logic from version.cpp's warnings.
- std::string line =
- LinuxSysHelper::readLineFromFile("/proc/self/numa_maps").append(" \0");
- size_t pos = line.find(' ');
- if (pos != std::string::npos &&
- line.substr(pos + 1, 10).find("interleave") == std::string::npos) {
- // interleave not found, count NUMA nodes by finding the highest numbered node file
- unsigned long i = 2;
- while (boost::filesystem::exists(
- std::string(str::stream() << "/sys/devices/system/node/node" << i++)))
- ;
- return i - 1;
- }
- }
- } catch (boost::filesystem::filesystem_error& e) {
- LOGV2(23340,
- "WARNING: Cannot detect if NUMA interleaving is enabled. Failed to probe",
- "path"_attr = e.path1().string(),
- "reason"_attr = e.code().message());
- }
- return 0;
}
/**
@@ -719,15 +654,13 @@ unsigned long countNumaNodes() {
void ProcessInfo::SystemInfo::collectSystemInfo() {
utsname unameData;
std::string distroName, distroVersion;
- std::string cpuString, cpuFreq, cpuFeatures;
+ std::string cpuFreq, cpuFeatures;
int cpuCount;
int physicalCores;
- int cpuSockets;
std::string verSig = LinuxSysHelper::readLineFromFile("/proc/version_signature");
- LinuxSysHelper::getCpuInfo(cpuCount, cpuString, cpuFreq, cpuFeatures);
+ LinuxSysHelper::getCpuInfo(cpuCount, cpuFreq, cpuFeatures);
LinuxSysHelper::getNumPhysicalCores(physicalCores);
- cpuSockets = LinuxSysHelper::getNumCpuSockets();
LinuxSysHelper::getLinuxDistro(distroName, distroVersion);
if (uname(&unameData) == -1) {
@@ -744,12 +677,9 @@ void ProcessInfo::SystemInfo::collectSystemInfo() {
memLimit = LinuxSysHelper::getMemorySizeLimit();
addrSize = sizeof(void*) * CHAR_BIT;
numCores = cpuCount;
- numPhysicalCores = physicalCores;
- numCpuSockets = cpuSockets;
pageSize = static_cast<unsigned long long>(sysconf(_SC_PAGESIZE));
cpuArch = unameData.machine;
- numNumaNodes = countNumaNodes();
- hasNuma = numNumaNodes;
+ hasNuma = checkNumaEnabled();
BSONObjBuilder bExtra;
bExtra.append("versionString", LinuxSysHelper::readLineFromFile("/proc/version"));
@@ -769,16 +699,49 @@ void ProcessInfo::SystemInfo::collectSystemInfo() {
bExtra.append("versionSignature", verSig);
bExtra.append("kernelVersion", unameData.release);
- bExtra.append("cpuString", cpuString);
bExtra.append("cpuFrequencyMHz", cpuFreq);
bExtra.append("cpuFeatures", cpuFeatures);
bExtra.append("pageSize", static_cast<long long>(pageSize));
bExtra.append("numPages", static_cast<int>(sysconf(_SC_PHYS_PAGES)));
bExtra.append("maxOpenFiles", static_cast<int>(sysconf(_SC_OPEN_MAX)));
+ bExtra.append("physicalCores", physicalCores);
appendMountInfo(bExtra);
_extraStats = bExtra.obj();
}
+/**
+ * Determine if the process is running with (cc)NUMA
+ */
+bool ProcessInfo::checkNumaEnabled() {
+ bool hasMultipleNodes = false;
+ bool hasNumaMaps = false;
+
+ try {
+ hasMultipleNodes = boost::filesystem::exists("/sys/devices/system/node/node1");
+ hasNumaMaps = boost::filesystem::exists("/proc/self/numa_maps");
+ } catch (boost::filesystem::filesystem_error& e) {
+ LOGV2(23340,
+ "WARNING: Cannot detect if NUMA interleaving is enabled. Failed to probe",
+ "path"_attr = e.path1().string(),
+ "reason"_attr = e.code().message());
+ return false;
+ }
+
+ if (hasMultipleNodes && hasNumaMaps) {
+ // proc is populated with numa entries
+
+ // read the second column of first line to determine numa state
+ // ('default' = enabled, 'interleave' = disabled). Logic from version.cpp's warnings.
+ std::string line = LinuxSysHelper::readLineFromFile("/proc/self/numa_maps").append(" \0");
+ size_t pos = line.find(' ');
+ if (pos != std::string::npos &&
+ line.substr(pos + 1, 10).find("interleave") == std::string::npos)
+ // interleave not found;
+ return true;
+ }
+ return false;
+}
+
} // namespace mongo
diff --git a/src/mongo/util/processinfo_openbsd.cpp b/src/mongo/util/processinfo_openbsd.cpp
index 071b26b8521..40ad2fc1a70 100644
--- a/src/mongo/util/processinfo_openbsd.cpp
+++ b/src/mongo/util/processinfo_openbsd.cpp
@@ -96,6 +96,10 @@ int getSysctlByIDWithDefault<std::string>(const int* sysctlID,
return 0;
}
+bool ProcessInfo::checkNumaEnabled() {
+ return false;
+}
+
int ProcessInfo::getVirtualMemorySize() {
kvm_t* kd = NULL;
int cnt = 0;
@@ -177,7 +181,7 @@ void ProcessInfo::SystemInfo::collectSystemInfo() {
pageSize = static_cast<unsigned long long>(sysconf(_SC_PAGESIZE));
- hasNuma = false;
+ hasNuma = checkNumaEnabled();
}
void ProcessInfo::getExtraInfo(BSONObjBuilder& info) {}
diff --git a/src/mongo/util/processinfo_osx.cpp b/src/mongo/util/processinfo_osx.cpp
index 0cd0cbb4cf6..cb42b570513 100644
--- a/src/mongo/util/processinfo_osx.cpp
+++ b/src/mongo/util/processinfo_osx.cpp
@@ -182,11 +182,9 @@ void ProcessInfo::SystemInfo::collectSystemInfo() {
memSize = getSysctlByName<NumberVal>("hw.memsize");
memLimit = memSize;
numCores = getSysctlByName<NumberVal>("hw.ncpu"); // includes hyperthreading cores
- numPhysicalCores = getSysctlByName<NumberVal>("machdep.cpu.core_count");
- numCpuSockets = getSysctlByName<NumberVal>("hw.packages");
pageSize = static_cast<unsigned long long>(sysconf(_SC_PAGESIZE));
cpuArch = getSysctlByName<std::string>("hw.machine");
- hasNuma = false;
+ hasNuma = checkNumaEnabled();
BSONObjBuilder bExtra;
bExtra.append("versionString", getSysctlByName<std::string>("kern.version"));
@@ -196,6 +194,8 @@ void ProcessInfo::SystemInfo::collectSystemInfo() {
"nfsAsync",
static_cast<int>(getSysctlByName<NumberVal>("vfs.generic.nfs.client.allow_async")));
bExtra.append("model", getSysctlByName<std::string>("hw.model"));
+ bExtra.append("physicalCores",
+ static_cast<int>(getSysctlByName<NumberVal>("machdep.cpu.core_count")));
bExtra.append(
"cpuFrequencyMHz",
static_cast<int>((getSysctlByName<NumberVal>("hw.cpufrequency") / (1000 * 1000))));
@@ -206,4 +206,8 @@ void ProcessInfo::SystemInfo::collectSystemInfo() {
_extraStats = bExtra.obj();
}
+bool ProcessInfo::checkNumaEnabled() {
+ return false;
+}
+
} // namespace mongo
diff --git a/src/mongo/util/processinfo_solaris.cpp b/src/mongo/util/processinfo_solaris.cpp
index 10f97f9406d..95598132526 100644
--- a/src/mongo/util/processinfo_solaris.cpp
+++ b/src/mongo/util/processinfo_solaris.cpp
@@ -129,33 +129,6 @@ void ProcessInfo::getExtraInfo(BSONObjBuilder& info) {
info.appendNumber("page_faults", static_cast<long long>(p.prusage.pr_majf));
}
-bool checkNumaEnabled() {
- lgrp_cookie_t cookie = lgrp_init(LGRP_VIEW_OS);
-
- if (cookie == LGRP_COOKIE_NONE) {
- auto ec = lastSystemError();
- LOGV2_WARNING(23362,
- "lgrp_init failed: {errnoWithDescription}",
- "errnoWithDescription"_attr = errorMessage(ec));
- return false;
- }
-
- ON_BLOCK_EXIT([&] { lgrp_fini(cookie); });
-
- int groups = lgrp_nlgrps(cookie);
-
- if (groups == -1) {
- auto ec = lastSystemError();
- LOGV2_WARNING(23363,
- "lgrp_nlgrps failed: {errnoWithDescription}",
- "errnoWithDescription"_attr = errorMessage(ec));
- return false;
- }
-
- // NUMA machines have more then 1 locality group
- return groups > 1;
-}
-
/**
* Save a BSON obj representing the host system's details
*/
@@ -225,4 +198,29 @@ void ProcessInfo::SystemInfo::collectSystemInfo() {
_extraStats = bExtra.obj();
}
+bool ProcessInfo::checkNumaEnabled() {
+ lgrp_cookie_t cookie = lgrp_init(LGRP_VIEW_OS);
+
+ if (cookie == LGRP_COOKIE_NONE) {
+ LOGV2_WARNING(23362,
+ "lgrp_init failed: {errnoWithDescription}",
+ "errnoWithDescription"_attr = errnoWithDescription());
+ return false;
+ }
+
+ ON_BLOCK_EXIT([&] { lgrp_fini(cookie); });
+
+ int groups = lgrp_nlgrps(cookie);
+
+ if (groups == -1) {
+ LOGV2_WARNING(23363,
+ "lgrp_nlgrps failed: {errnoWithDescription}",
+ "errnoWithDescription"_attr = errnoWithDescription());
+ return false;
+ }
+
+ // NUMA machines have more then 1 locality group
+ return groups > 1;
+}
+
} // namespace mongo
diff --git a/src/mongo/util/processinfo_test.cpp b/src/mongo/util/processinfo_test.cpp
index ab35a6b7374..050835f97ac 100644
--- a/src/mongo/util/processinfo_test.cpp
+++ b/src/mongo/util/processinfo_test.cpp
@@ -33,30 +33,13 @@
#include <iostream>
#include <vector>
-#include "mongo/bson/bsonobj.h"
-#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/processinfo.h"
using boost::optional;
+using mongo::ProcessInfo;
-namespace mongo {
-
-namespace {
-using StringMap = std::map<std::string, uint64_t>;
-
-StringMap toStringMap(BSONObj& obj) {
- StringMap map;
-
- for (const auto& e : obj) {
- map[e.fieldName()] = e.numberLong();
- }
-
- return map;
-}
-
-#define ASSERT_KEY(_key) ASSERT_TRUE(stringMap.find(_key) != stringMap.end());
-
+namespace mongo_test {
TEST(ProcessInfo, SysInfoIsInitialized) {
ProcessInfo processInfo;
if (processInfo.supported()) {
@@ -64,20 +47,6 @@ TEST(ProcessInfo, SysInfoIsInitialized) {
}
}
-TEST(FTDCProcSysInfo, TestSysInfo) {
- auto sysInfo = ProcessInfo();
- BSONObjBuilder builder;
- sysInfo.appendSystemDetails(builder);
-
- BSONObj obj = builder.obj();
- auto stringMap = toStringMap(obj);
- ASSERT_KEY("extra");
-
- BSONObj extra = obj.getObjectField("extra");
- stringMap = toStringMap(extra);
- ASSERT_KEY("cpuString");
-}
-
TEST(ProcessInfo, GetNumAvailableCores) {
#if defined(__APPLE__) || defined(__linux__) || (defined(__sun) && defined(__SVR4)) || \
defined(_WIN32)
@@ -90,5 +59,4 @@ TEST(ProcessInfo, GetNumAvailableCores) {
TEST(ProcessInfo, GetNumCoresReturnsNonZeroNumberOfProcessors) {
ASSERT_GREATER_THAN(ProcessInfo::getNumCores(), 0u);
}
-} // namespace
-} // namespace mongo
+} // namespace mongo_test
diff --git a/src/mongo/util/processinfo_unknown.cpp b/src/mongo/util/processinfo_unknown.cpp
index c57516eaeac..1011c4dbfef 100644
--- a/src/mongo/util/processinfo_unknown.cpp
+++ b/src/mongo/util/processinfo_unknown.cpp
@@ -51,6 +51,10 @@ int ProcessInfo::getResidentSize() {
return -1;
}
+bool ProcessInfo::checkNumaEnabled() {
+ return false;
+}
+
void ProcessInfo::SystemInfo::collectSystemInfo() {}
void ProcessInfo::getExtraInfo(BSONObjBuilder& info) {}
diff --git a/src/mongo/util/processinfo_windows.cpp b/src/mongo/util/processinfo_windows.cpp
index 5f3c0514949..5cf7edbdee2 100644
--- a/src/mongo/util/processinfo_windows.cpp
+++ b/src/mongo/util/processinfo_windows.cpp
@@ -39,7 +39,6 @@
#include "mongo/logv2/log.h"
#include "mongo/util/processinfo.h"
-#include "mongo/util/text.h"
namespace mongo {
@@ -92,27 +91,13 @@ LpiRecords getLogicalProcessorInformationRecords() {
return lpiRecords;
}
-struct ParsedProcessorInfo {
- int physicalCoreCount;
- int numaNodeCount;
- int processorPackageCount;
-};
-
-ParsedProcessorInfo getProcessorInfo() {
- ParsedProcessorInfo ppi{0, 0, 0};
+int getPhysicalCores() {
+ int processorCoreCount = 0;
for (auto&& lpi : getLogicalProcessorInformationRecords()) {
- switch (lpi.Relationship) {
- case RelationProcessorCore:
- ppi.physicalCoreCount++;
- break;
- case RelationNumaNode:
- ppi.numaNodeCount++;
- break;
- case RelationProcessorPackage:
- ppi.processorPackageCount++;
- }
+ if (lpi.Relationship == RelationProcessorCore)
+ processorCoreCount++;
}
- return ppi;
+ return processorCoreCount;
}
} // namespace
@@ -249,43 +234,6 @@ bool getFileVersion(const char* filePath, DWORD& fileVersionMS, DWORD& fileVersi
return true;
}
-std::string getCpuString() {
- // get descriptive CPU string from registry
- HKEY hKey;
- LPCWSTR cpuKey = L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
- LPCWSTR valueName = L"ProcessorNameString";
- std::string cpuString;
-
- // Open the CPU key in the Windows Registry
- if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, cpuKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
- ScopeGuard guard([hKey] { RegCloseKey(hKey); });
- WCHAR cpuModel[128];
- DWORD bufferSize = sizeof(cpuModel);
-
- // Retrieve the value of ProcessorNameString
- if (RegQueryValueEx(hKey,
- valueName,
- nullptr,
- nullptr,
- reinterpret_cast<LPBYTE>(cpuModel),
- &bufferSize) == ERROR_SUCCESS) {
- cpuString = toUtf8String(cpuModel);
- } else {
- auto ec = lastSystemError();
- LOGV2_WARNING(7663101,
- "Failed to retrieve CPU model name from the registry",
- "error"_attr = errorMessage(ec));
- }
-
- // Close the registry key
- } else {
- auto ec = lastSystemError();
- LOGV2_WARNING(
- 7663102, "Failed to open CPU key in the registry", "error"_attr = errorMessage(ec));
- }
- return cpuString;
-}
-
void ProcessInfo::SystemInfo::collectSystemInfo() {
BSONObjBuilder bExtra;
std::stringstream verstr;
@@ -297,19 +245,10 @@ void ProcessInfo::SystemInfo::collectSystemInfo() {
GetNativeSystemInfo(&ntsysinfo);
addrSize = (ntsysinfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ? 64 : 32);
numCores = ntsysinfo.dwNumberOfProcessors;
- auto ppi = getProcessorInfo();
- numPhysicalCores = ppi.physicalCoreCount;
- numCpuSockets = ppi.processorPackageCount;
- hasNuma = ppi.numaNodeCount > 1;
- numNumaNodes = ppi.numaNodeCount;
+ numPhysicalCores = getPhysicalCores();
pageSize = static_cast<unsigned long long>(ntsysinfo.dwPageSize);
bExtra.append("pageSize", static_cast<long long>(pageSize));
-
- std::string cpuString = getCpuString();
- if (cpuString != nullptr) {
- bExtra.append("cpuString", cpuString);
- }
-
+ bExtra.append("physicalCores", static_cast<int>(numPhysicalCores));
// get memory info
mse.dwLength = sizeof(mse);
@@ -413,7 +352,21 @@ void ProcessInfo::SystemInfo::collectSystemInfo() {
osType = "Windows";
osVersion = verstr.str();
+ hasNuma = checkNumaEnabled();
_extraStats = bExtra.obj();
}
+
+bool ProcessInfo::checkNumaEnabled() {
+ DWORD numaNodeCount = 0;
+ for (auto&& lpi : getLogicalProcessorInformationRecords()) {
+ if (lpi.Relationship == RelationNumaNode)
+ // Non-NUMA systems report a single record of this type.
+ ++numaNodeCount;
+ }
+
+ // For non-NUMA machines, the count is 1
+ return numaNodeCount > 1;
+}
+
} // namespace mongo
diff --git a/src/mongo/util/procparser.cpp b/src/mongo/util/procparser.cpp
index a9067b27899..599608c9cc8 100644
--- a/src/mongo/util/procparser.cpp
+++ b/src/mongo/util/procparser.cpp
@@ -857,110 +857,5 @@ Status parseProcVMStatFile(StringData filename,
return parseProcVMStat(keys, swString.getValue(), builder);
}
-// Here is an example of the type of string it supports:
-//
-// > cat /proc/pressure/<cpu|io|memory>
-// some avg10=0.00 avg60=0.00 avg300=0.14 total=1434509127
-// full avg10=0.00 avg60=0.00 avg300=0.14 total=1035574668
-//
-// Note: /proc/pressure/cpu only has 'some' entry
-//
-Status parseProcPressure(StringData data, BSONObjBuilder* builder) {
- using string_split_iterator = boost::split_iterator<StringData::const_iterator>;
-
- // Split the file by lines.
- // token_compress_on means the iterator skips over consecutive '\n'.
- for (string_split_iterator lineIt = string_split_iterator(
- data.begin(),
- data.end(),
- boost::token_finder([](char c) { return c == '\n'; }, boost::token_compress_on));
- lineIt != string_split_iterator();
- ++lineIt) {
-
- StringData line((*lineIt).begin(), (*lineIt).end());
-
- // Split the line by spaces and equal signs since these are the delimiters for pressure
- // files. token_compress_on means the iterator skips over consecutive ' '. This is needed
- // for every line.
- string_split_iterator partIt =
- string_split_iterator(line.begin(),
- line.end(),
- boost::token_finder([](char c) { return c == ' ' || c == '='; },
- boost::token_compress_on));
-
- // Skip processing this line if we do not have a key.
- if (partIt == string_split_iterator()) {
- continue;
- }
-
- StringData time((*partIt).begin(), (*partIt).end());
-
- ++partIt;
-
- // Skip processing this line if we only have a key, and no arguments.
- if (partIt == string_split_iterator()) {
- continue;
- }
-
- // Share of time is either 'some' or 'full'.
- if (time != kPressureSomeTime && time != kPressureFullTime) {
- return Status(ErrorCodes::FailedToParse, "Couldn't find the share of time");
- }
-
- // Lookup for 'total' token in the parts.
- auto totalIt = std::find_if(partIt, string_split_iterator(), [](const auto& vec) {
- return StringData(vec.begin(), vec.end()) == "total"_sd;
- });
-
- // If 'total' token is not found on the row return an error.
- if (totalIt == string_split_iterator()) {
- return Status(ErrorCodes::NoSuchKey, "Failed to find 'total' token");
- }
-
- StringData totalToken((*totalIt).begin(), (*totalIt).end());
-
- ++totalIt;
-
- if (totalIt == string_split_iterator()) {
- return Status(ErrorCodes::FailedToParse, "No value found for 'total' token");
- }
-
- StringData stringValue((*totalIt).begin(), (*totalIt).end());
-
- double value;
-
- if (!NumberParser{}(stringValue, &value).isOK()) {
- return Status(ErrorCodes::FailedToParse,
- str::stream() << "Couldn't parse '" << stringValue << "' to number");
- }
-
- *builder << time << BSON("totalMicros" << value);
- }
-
- return Status::OK();
-}
-
-// Example of BSONObjBuilder created:
-// cpu : {
-// some : {
-// totalMicros : ...
-// },
-// fulll: {
-// totalMicros : ...
-// }
-// }
-Status parseProcPressureFile(StringData key, StringData filename, BSONObjBuilder* builder) {
- auto swString = readFileAsString(filename);
- if (!swString.isOK()) {
- return swString.getStatus();
- }
-
- BSONObjBuilder sub(builder->subobjStart(key));
- Status status = parseProcPressure(swString.getValue(), &sub);
- sub.doneFast();
-
- return status;
-}
-
} // namespace procparser
} // namespace mongo
diff --git a/src/mongo/util/procparser.h b/src/mongo/util/procparser.h
index 2e6eca39211..55f6c3c296f 100644
--- a/src/mongo/util/procparser.h
+++ b/src/mongo/util/procparser.h
@@ -159,23 +159,5 @@ Status parseProcVMStatFile(StringData filename,
BSONObjBuilder* builder);
-static const StringData kPressureSomeTime = "some"_sd;
-static const StringData kPressureFullTime = "full"_sd;
-
-/**
- * Read a string matching /proc/pressure/<cpu|io|memory> format and write the specified keys in
- * builder.
- *
- * keys - list of keys to check for in the data and output its value.
- * data - string to parsee
- * builder - BSON output
- */
-Status parseProcPressure(StringData data, BSONObjBuilder* builder);
-
-/**
- * Read from file, and write the specified keys in builder.
- */
-Status parseProcPressureFile(StringData key, StringData filename, BSONObjBuilder* builder);
-
} // namespace procparser
} // namespace mongo
diff --git a/src/mongo/util/procparser_test.cpp b/src/mongo/util/procparser_test.cpp
index 259243264ef..04edee43380 100644
--- a/src/mongo/util/procparser_test.cpp
+++ b/src/mongo/util/procparser_test.cpp
@@ -34,14 +34,12 @@
#include "mongo/util/procparser.h"
#include <boost/filesystem.hpp>
-#include <fcntl.h>
#include <map>
#include "mongo/bson/bsonobj.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/logv2/log.h"
#include "mongo/unittest/unittest.h"
-#include "mongo/util/processinfo.h"
namespace mongo {
@@ -82,25 +80,6 @@ StringMap toNestedStringMap(BSONObj& obj) {
return map;
}
-bool isPSISupported(StringData filename) {
- int fd = open(filename.toString().c_str(), 0);
- if (fd == -1) {
- return false;
- }
- ScopeGuard scopedGuard([fd] { close(fd); });
-
- std::array<char, 1> buf;
-
- while (read(fd, buf.data(), buf.size()) == -1) {
- auto ec = lastPosixError();
- if (ec == posixError(EOPNOTSUPP)) {
- return false;
- }
- ASSERT_EQ(ec, posixError(EINTR));
- }
- return true;
-}
-
#define ASSERT_KEY(_key) ASSERT_TRUE(stringMap.find(_key) != stringMap.end());
#define ASSERT_NO_KEY(_key) ASSERT_TRUE(stringMap.find(_key) == stringMap.end());
#define ASSERT_KEY_AND_VALUE(_key, _value) ASSERT_EQUALS(stringMap.at(_key), _value);
@@ -134,11 +113,6 @@ bool isPSISupported(StringData filename) {
ASSERT_OK(procparser::parseProcVMStat(_keys, _x, &builder)); \
auto obj = builder.obj(); \
auto stringMap = toStringMap(obj);
-#define ASSERT_PARSE_PRESSURE(_x) \
- BSONObjBuilder builder; \
- ASSERT_OK(procparser::parseProcPressure(_x, &builder)); \
- auto obj = builder.obj(); \
- auto stringMap = toStringMap(obj);
TEST(FTDCProcStat, TestStat) {
@@ -904,112 +878,5 @@ TEST(FTDCProcVMStat, TestLocalNonExistentVMStat) {
ASSERT_NOT_OK(procparser::parseProcVMStatFile("/proc/does_not_exist", keys, &builder));
}
-TEST(FTDCProcPressure, TestSuccess) {
- // Normal cases
- {
- ASSERT_PARSE_PRESSURE(
- "some avg10=0.10 avg60=6.50 avg300=1.00 total=14\nfull avg10=2.30 "
- "avg60=0.00 avg300=0.14 total=10");
- ASSERT(obj["some"]["totalMicros"].Double() == 14);
-
- ASSERT(obj["full"]["totalMicros"].Double() == 10);
- }
- {
- ASSERT_PARSE_PRESSURE("some avg10=0.10 avg60=6.50 avg300=1.00 total=14");
- ASSERT(obj["some"]["totalMicros"].Double() == 14);
-
- ASSERT(!obj["full"]);
- }
- {
- ASSERT_PARSE_PRESSURE(
- "some avg10=0.10 avg60=6.50 avg300=1.00 total=14\nfull avg10=2.30 "
- "avg60=0.00 avg300=0.14 total=10");
- ASSERT(obj["some"]["totalMicros"].Double() == 14);
-
- ASSERT(obj["full"]["totalMicros"].Double() == 10);
- }
-}
-
-TEST(FTDCProcPressure, TestFailure) {
- // Failure cases
- {
- BSONObjBuilder builder;
- ASSERT_NOT_OK(procparser::parseProcPressureFile("", "", &builder));
- }
-
- {
- BSONObjBuilder builder;
- ASSERT_NOT_OK(
- procparser::parseProcPressureFile("cpu", "/proc/non-existent-file", &builder));
- }
-
- // 'total' is not found in the data given.
- {
- BSONObjBuilder builder;
- ASSERT_NOT_OK(
- procparser::parseProcPressure("some avg10=0.10 avg60=6.50 avg300=1.00", &builder));
- }
-
- // 'total' is not found in one of the rows.
- {
- BSONObjBuilder builder;
- ASSERT_NOT_OK(
- procparser::parseProcPressure("some avg10=0.10 avg60=6.50 avg300=1.00\nfull avg10=2.30 "
- "avg60=0.00 avg300=0.14 total=10",
- &builder));
- }
-
- // 'total' is not given a valid number value.
- {
- BSONObjBuilder builder;
- ASSERT_NOT_OK(procparser::parseProcPressure(
- "some avg10=0.10 avg60=6.50 avg300=1.00 total=invalid", &builder));
- }
-}
-
-TEST(FTDCProcPressure, TestLocalPressureInfo) {
- if (isPSISupported("/proc/pressure/cpu")) {
- BSONObjBuilder builder;
-
- ASSERT_OK(procparser::parseProcPressureFile("cpu", "/proc/pressure/cpu", &builder));
-
- BSONObj obj = builder.obj();
- ASSERT(obj.hasField("cpu"));
- ASSERT(obj["cpu"]["some"]);
- ASSERT(obj["cpu"]["some"]["totalMicros"]);
-
- // After linux kernel 5.13, /proc/pressure/cpu includes 'full' filled with 0.
- ASSERT(!obj["cpu"]["full"] || obj["cpu"]["full"]["totalMicros"].Double() == 0);
- }
-
- if (isPSISupported("/proc/pressure/memory")) {
- BSONObjBuilder builder;
-
- ASSERT_OK(procparser::parseProcPressureFile("memory", "/proc/pressure/memory", &builder));
-
- BSONObj obj = builder.obj();
- ASSERT(obj.hasField("memory"));
- ASSERT(obj["memory"]["some"]);
- ASSERT(obj["memory"]["some"]["totalMicros"]);
-
- ASSERT(obj["memory"]["full"]);
- ASSERT(obj["memory"]["full"]["totalMicros"]);
- }
-
- if (isPSISupported("/proc/pressure/io")) {
- BSONObjBuilder builder;
-
- ASSERT_OK(procparser::parseProcPressureFile("io", "/proc/pressure/io", &builder));
-
- BSONObj obj = builder.obj();
- ASSERT(obj.hasField("io"));
- ASSERT(obj["io"]["some"]);
- ASSERT(obj["io"]["some"]["totalMicros"]);
-
- ASSERT(obj["io"]["full"]);
- ASSERT(obj["io"]["full"]["totalMicros"]);
- }
-}
-
} // namespace
} // namespace mongo
diff --git a/src/mongo/util/scoped_unlock.h b/src/mongo/util/scoped_unlock.h
deleted file mode 100644
index af9b04b9e0b..00000000000
--- a/src/mongo/util/scoped_unlock.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * Copyright (C) 2022-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#pragma once
-
-#include "mongo/stdx/mutex.h"
-#include "mongo/util/assert_util.h"
-
-
-namespace mongo {
-/**
- * RAII object that unlocks a unique_lock type on construction, and relocks it on destruction. The
- * unique_lock must be locked when it is given to ScopedUnlock.
- */
-template <typename T>
-class ScopedUnlock {
-public:
- /**
- * Construct a new Scoped Unlock object.
- * Unique_locks passed into this constructor must be locked, or an invariant failure will be
- * thrown.
- */
- explicit ScopedUnlock(stdx::unique_lock<T>& lock) : _lock(lock) {
- invariant(_lock.owns_lock(), "Locks in ScopedUnlock must be locked on initialization.");
- _lock.unlock();
- }
-
- ~ScopedUnlock() {
- if (!_dismissed) {
- _lock.lock();
- }
- }
-
- ScopedUnlock(const ScopedUnlock&) = delete;
- ScopedUnlock(ScopedUnlock&&) = delete;
- ScopedUnlock& operator=(const ScopedUnlock&) = delete;
- ScopedUnlock& operator=(ScopedUnlock&&) = delete;
-
- /** A dismissed ScopedUnlock does not lock on destruction. */
- void dismiss() noexcept {
- _dismissed = true;
- }
-
-private:
- stdx::unique_lock<T>& _lock;
- bool _dismissed = false;
-};
-
-} // namespace mongo
diff --git a/src/mongo/util/scoped_unlock_test.cpp b/src/mongo/util/scoped_unlock_test.cpp
deleted file mode 100644
index 20090e0961c..00000000000
--- a/src/mongo/util/scoped_unlock_test.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/**
- * Copyright (C) 2022-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/util/scoped_unlock.h"
-
-#include "mongo/platform/mutex.h"
-#include "mongo/unittest/death_test.h"
-#include "mongo/unittest/unittest.h"
-
-namespace mongo {
-namespace {
-TEST(ScopedUnlockTest, Relocked) {
- Mutex mutex;
- stdx::unique_lock lk(mutex);
-
- { ScopedUnlock scopedUnlock(lk); }
-
- ASSERT(lk.owns_lock()) << "ScopedUnlock should relock on destruction";
-}
-
-TEST(ScopedUnlockTest, Unlocked) {
- Mutex mutex;
- stdx::unique_lock<Mutex> lk(mutex);
-
- ScopedUnlock scopedUnlock(lk);
-
- ASSERT_FALSE(lk.owns_lock()) << "ScopedUnlock should unlock on construction";
-}
-
-TEST(ScopedUnlockTest, Dismissed) {
- Mutex mutex;
- stdx::unique_lock<Mutex> lk(mutex);
-
- {
- ScopedUnlock scopedUnlock(lk);
- scopedUnlock.dismiss();
- }
-
- ASSERT_FALSE(lk.owns_lock()) << "ScopedUnlock should not relock on destruction if dismissed";
-}
-
-DEATH_TEST(ScopedUnlockTest,
- InitUnlocked,
- "Locks in ScopedUnlock must be locked on initialization.") {
- Mutex mutex;
- stdx::unique_lock<Mutex> lk(mutex);
- lk.unlock();
-
- ScopedUnlock scopedUnlock(lk);
-}
-} // namespace
-} // namespace mongo
diff --git a/src/mongo/util/shared_buffer_fragment.h b/src/mongo/util/shared_buffer_fragment.h
index d94acd096aa..05f453c9f60 100644
--- a/src/mongo/util/shared_buffer_fragment.h
+++ b/src/mongo/util/shared_buffer_fragment.h
@@ -32,7 +32,6 @@
#include "mongo/util/shared_buffer.h"
#include <functional>
-#include <vector>
namespace mongo {
@@ -44,7 +43,7 @@ class SharedBufferFragment {
public:
SharedBufferFragment() : _offset(0), _size(0) {}
explicit SharedBufferFragment(SharedBuffer buffer, size_t size)
- : SharedBufferFragment(std::move(buffer), 0, size) {}
+ : _buffer(std::move(buffer)), _offset(0), _size(size) {}
explicit SharedBufferFragment(SharedBuffer buffer, ptrdiff_t offset, size_t size)
: _buffer(std::move(buffer)), _offset(offset), _size(size) {}
@@ -87,14 +86,10 @@ private:
size_t _size;
};
+
/**
* Builder of SharedBufferFragment where multiple fragments are using different parts of the same
- * underlying buffer or multiple buffers. Can only build one fragment at a time.
- *
- * Warning: This builder will hold references to all allocated buffers and will not release them
- * until freeUnused() is called. Memory is not reused. This means that failing to call this function
- * will result in an unbounded amount of memory usage for the lifetime of the builder. Even after
- * this builder is destructed, SharedBufferFragments can prevent memory from being freed.
+ * underlying buffer. Can only build one fragment at a time
*/
class SharedBufferFragmentBuilder {
public:
@@ -104,9 +99,6 @@ public:
size_t blockSize, GrowStrategy growStrategy = DoubleGrowStrategy(kDefaultMaxBlockSize))
: _offset(0), _blockSize(blockSize), _growStrategy(growStrategy) {}
- SharedBufferFragmentBuilder(SharedBufferFragmentBuilder&& other) = default;
- SharedBufferFragmentBuilder& operator=(SharedBufferFragmentBuilder&& other) = default;
-
struct ConstantGrowStrategy {
size_t operator()(size_t current) const {
return current;
@@ -127,20 +119,13 @@ public:
// May only be called if we are not currently building a fragment
SharedBufferFragmentBuilder& start(size_t initialSize) {
invariant(!_inUse);
- if (!_buffer.isShared()) {
- // Since there are no fragments sharing with this buffer, we can reset the offset to 0
- // to reuse unused space.
- _offset = 0;
- }
-
if (_buffer.capacity() < (_offset + initialSize)) {
- // If the capacity is 0, this is our initial allocation and we should not use the grow
- // strategy.
+ // If capacity is 0, then this is our initial allocation and we should not use the grow
+ // strategy
if (_buffer.capacity() > 0)
_blockSize = _growStrategy(_blockSize);
-
size_t allocSize = std::max(_blockSize, initialSize);
- _buffer = _alloc(std::move(_buffer), allocSize);
+ _buffer = SharedBuffer::allocate(allocSize);
_offset = 0;
}
_inUse = true;
@@ -153,18 +138,18 @@ public:
invariant(_inUse);
auto currentCapacity = capacity();
if (currentCapacity < size) {
- // If the capacity is 0, this is our initial allocation and we should not use the grow
- // strategy.
- if (currentCapacity > 0) {
- _blockSize = _growStrategy(_blockSize);
- }
+ _blockSize = _growStrategy(_blockSize);
size_t allocSize = std::max(_blockSize, size);
- if (_buffer) {
- _buffer = _realloc(std::move(_buffer), _offset, currentCapacity, allocSize);
- } else {
- _buffer = _alloc(std::move(_buffer), allocSize);
- }
+ // If nothing else is using the internal buffer it would be safe to use realloc. But as
+ // this potentially is a large buffer realloc would need copy all of it as it doesn't
+ // know how much is actually used. So we create a new buffer in all cases and reset the
+ // offset to 0. We only need to copy the memory of the fragment we are currently
+ // building.
+ auto newBuffer = SharedBuffer::allocate(allocSize);
+ if (_buffer)
+ memcpy(newBuffer.get(), _buffer.get() + _offset, currentCapacity);
+ _buffer = std::move(newBuffer);
_offset = 0;
}
}
@@ -206,38 +191,13 @@ public:
return _inUse;
}
- // Returns the memory used by all allocated buffers that are being tracked.
- size_t memUsage() {
- return _memUsage;
- }
-
- // Frees all unreferenced buffers except for the most recently allocated one. The caller must
- // ensure that no references to any shared buffers remain to maintain useful memory usage
- // information.
- void freeUnused();
-
private:
- SharedBuffer _alloc(SharedBuffer&& existing, size_t allocSize) {
- return _realloc(std::move(existing), 0, 0, allocSize);
- }
-
- SharedBuffer _realloc(SharedBuffer&& existing,
- size_t offset,
- size_t existingSize,
- size_t newSize);
-
- // The current working buffer of this builder.
SharedBuffer _buffer;
ptrdiff_t _offset;
size_t _blockSize;
GrowStrategy _growStrategy;
bool _inUse{false};
-
- // This is a list of old buffers that may still be in use by other fragments. Counts towards
- // total memory usage and buffers must be freed by calling using freeUnused() when buffers are
- // no longer needed.
- std::vector<SharedBuffer> _activeBuffers;
- size_t _memUsage = 0;
};
+
} // namespace mongo
diff --git a/src/mongo/util/shared_buffer_fragment_builder.cpp b/src/mongo/util/shared_buffer_fragment_builder.cpp
deleted file mode 100644
index 06dbda2352b..00000000000
--- a/src/mongo/util/shared_buffer_fragment_builder.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- * Copyright (C) 2024-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/util/shared_buffer_fragment.h"
-
-namespace mongo {
-
-void SharedBufferFragmentBuilder::freeUnused() {
- if (_activeBuffers.empty()) {
- return;
- }
-
- // Normally all buffers are expected to no longer be shared and can be freed immediately,
- // however, the last buffer may still be shared with the owning SharedBufferFragmentBuilder.
- auto it = std::remove_if(
- _activeBuffers.begin(), _activeBuffers.end(), [](auto&& buf) { return !buf.isShared(); });
- _activeBuffers.erase(it, _activeBuffers.end());
-
- // Recalculate memory used by active buffers.
- size_t remaining = _buffer.capacity();
- for (auto&& buf : _activeBuffers) {
- remaining += buf.capacity();
- }
- _memUsage = remaining;
-}
-
-SharedBuffer SharedBufferFragmentBuilder::_realloc(SharedBuffer&& existing,
- size_t offset,
- size_t existingSize,
- size_t newSize) {
- // If nothing else is using the internal buffer it would be safe to use realloc. But as
- // this potentially is a large buffer realloc would need copy all of it as it doesn't
- // know how much is actually used. So we create a new buffer in all cases
- auto newBuffer = SharedBuffer::allocate(newSize);
- _memUsage += newBuffer.capacity();
-
- // When existingSize is 0 we may be in an initial alloc().
- if (existing && existingSize) {
- memcpy(newBuffer.get(), existing.get() + offset, existingSize);
- }
-
- // If this buffer is actively used somewhere, we'll need to keep a reference to it for
- // tracking memory usage since there may be other fragments that are also holding onto a
- // reference. Otherwise, we let it get freed. Callers will have to take care to clean up
- // these shared references regularly using freeUnused().
- if (existing.isShared()) {
- _activeBuffers.push_back(std::move(existing));
- } else {
- _memUsage -= existing.capacity();
- }
- return newBuffer;
-}
-} // namespace mongo
diff --git a/src/mongo/util/shared_buffer_test.cpp b/src/mongo/util/shared_buffer_test.cpp
deleted file mode 100644
index 7d0eefac43d..00000000000
--- a/src/mongo/util/shared_buffer_test.cpp
+++ /dev/null
@@ -1,426 +0,0 @@
-/**
- * Copyright (C) 2019-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/base/string_data.h"
-#include "mongo/util/shared_buffer.h"
-#include "mongo/util/shared_buffer_fragment.h"
-
-#include "mongo/unittest/unittest.h"
-
-namespace mongo {
-namespace {
-
-using SharedBufferTest = unittest::Test;
-
-TEST_F(SharedBufferTest, ReallocOrCopyNull) {
- SharedBuffer buf;
- ASSERT_EQ(buf.capacity(), 0u);
- ASSERT(!buf);
- ASSERT(!buf.isShared());
- buf.reallocOrCopy(10);
- ASSERT(buf);
- ASSERT(!buf.isShared());
- ASSERT_EQ(buf.capacity(), 10u);
-}
-
-TEST_F(SharedBufferTest, ReallocOrCopyNullShared) {
- // null SharedBuffers are never considered "shared", even when copied.
- SharedBuffer buf;
- const SharedBuffer sharer = buf;
- ASSERT_EQ(buf.capacity(), 0u);
- ASSERT(!buf);
- ASSERT(!buf.isShared());
- buf.reallocOrCopy(10);
- ASSERT(buf);
- ASSERT(!buf.isShared());
- ASSERT_EQ(buf.capacity(), 10u);
- ASSERT_EQ(sharer.capacity(), 0u);
-}
-
-SharedBuffer makeBuffer() {
- SharedBuffer buf = SharedBuffer::allocate(4);
- memcpy(buf.get(), "foo", 4);
- return buf;
-}
-
-TEST_F(SharedBufferTest, ReallocOrCopyGrow) {
- SharedBuffer buf = makeBuffer();
- ASSERT_EQ(buf.capacity(), 4u);
- ASSERT(buf);
- ASSERT(!buf.isShared());
- buf.reallocOrCopy(10);
- ASSERT(buf);
- ASSERT(!buf.isShared());
- ASSERT_EQ(buf.capacity(), 10u);
- ASSERT_EQ("foo"_sd, buf.get());
-}
-
-TEST_F(SharedBufferTest, ReallocOrCopyGrowShared) {
- SharedBuffer buf = makeBuffer();
- const SharedBuffer sharer = buf;
- ASSERT_EQ(buf.capacity(), 4u);
- ASSERT(buf);
- ASSERT(buf.isShared());
- buf.reallocOrCopy(10);
- ASSERT(buf);
- ASSERT(!buf.isShared());
- ASSERT_EQ(buf.capacity(), 10u);
- ASSERT_EQ(sharer.capacity(), 4u);
- ASSERT_EQ("foo"_sd, buf.get());
- ASSERT_EQ("foo"_sd, sharer.get());
- ASSERT_NE(buf.get(), sharer.get());
-}
-
-TEST_F(SharedBufferTest, ReallocOrCopyShrink) {
- SharedBuffer buf = makeBuffer();
- ASSERT_EQ(buf.capacity(), 4u);
- ASSERT(buf);
- ASSERT(!buf.isShared());
- // The buffer is already at least 1 byte.
- buf.reallocOrCopy(1);
- ASSERT(buf);
- // We copy it anyway.
- ASSERT(!buf.isShared());
- ASSERT_EQ(buf.capacity(), 1u);
- ASSERT_EQ('f', buf.get()[0]);
-}
-
-TEST_F(SharedBufferTest, ReallocOrCopyShrinkShared) {
- SharedBuffer buf = makeBuffer();
- const SharedBuffer sharer = buf;
- ASSERT_EQ(buf.capacity(), 4u);
- ASSERT(buf);
- ASSERT(buf.isShared());
- // The buffer is already at least 1 byte.
- buf.reallocOrCopy(1);
- ASSERT(buf);
- // We copy it anyway.
- ASSERT(!buf.isShared());
- ASSERT_EQ(buf.capacity(), 1u);
- ASSERT_EQ(sharer.capacity(), 4u);
- ASSERT_EQ('f', buf.get()[0]);
- ASSERT_EQ("foo"_sd, sharer.get());
- ASSERT_NE(buf.get(), sharer.get());
-}
-
-TEST_F(SharedBufferTest, SharedBufferFragmentBuilder) {
- constexpr size_t kBlockSize = 16;
- SharedBufferFragmentBuilder builder(kBlockSize);
-
- auto verifyFragment = [](const SharedBufferFragment& fragment, uint8_t expected) {
- for (size_t i = 0; i < fragment.size(); ++i)
- ASSERT(memcmp(fragment.get() + i, &expected, 1) == 0);
- };
-
- builder.start(kBlockSize / 2);
- ASSERT_EQ(builder.capacity(), kBlockSize);
- uint8_t one = 1;
- memset(builder.get(), one, kBlockSize / 2);
- auto fragment1 = builder.finish(kBlockSize / 2);
- ASSERT_EQ(fragment1.size(), kBlockSize / 2);
- verifyFragment(fragment1, one);
-
- builder.start(kBlockSize / 2);
- ASSERT_EQ(builder.capacity(), kBlockSize / 2);
- // We can use less than we ask for
- uint8_t two = 2;
- memset(builder.get(), two, kBlockSize / 4);
- auto fragment2 = builder.finish(kBlockSize / 4);
- ASSERT_EQ(fragment2.size(), kBlockSize / 4);
- // Buffers should not overlap and be next to each other
- ASSERT_EQ(fragment1.get() + fragment1.size(), fragment2.get());
- verifyFragment(fragment2, two);
-
- // Verify that anything written is transfered when we grow
- builder.start(builder.capacity());
- ASSERT_EQ(builder.capacity(), kBlockSize / 4);
- uint8_t three = 3;
- size_t written = kBlockSize / 4;
- // Write current capacity
- memset(builder.get(), three, written);
- builder.grow(kBlockSize);
- // Write the rest
- memset(builder.get() + written, three, builder.capacity() - written);
- auto fragment3 = builder.finish(kBlockSize);
- for (size_t i = 0; i < (kBlockSize / 4); ++i)
- ASSERT(memcmp(fragment3.get() + i, &three, 1) == 0);
- ASSERT_EQ(builder.capacity(), kBlockSize);
- verifyFragment(fragment3, three);
-
- builder.start(builder.capacity());
- auto ptr = builder.get();
- builder.discard();
- builder.start(builder.capacity());
- ASSERT_EQ(builder.get(), ptr);
-
- // No buffers should have been overwritten by others
- verifyFragment(fragment1, one);
- verifyFragment(fragment2, two);
- verifyFragment(fragment3, three);
-}
-
-TEST_F(SharedBufferTest, ManualFreeSharedBufferFragmentMemUsage1) {
- constexpr size_t kBlockSize = 16;
- SharedBufferFragmentBuilder builder(kBlockSize,
- SharedBufferFragmentBuilder::DoubleGrowStrategy(
- SharedBufferFragmentBuilder::kDefaultMaxBlockSize));
-
- {
- builder.start(kBlockSize / 2);
- ASSERT_EQ(kBlockSize, builder.capacity());
- auto fragment1 = builder.finish(kBlockSize / 2);
- ASSERT_EQ(kBlockSize / 2, fragment1.size());
- ASSERT_EQ(kBlockSize, builder.memUsage());
-
- builder.start(kBlockSize / 2);
- ASSERT_EQ(kBlockSize / 2, builder.capacity());
- auto fragment2 = builder.finish(kBlockSize / 2);
- ASSERT_EQ(kBlockSize / 2, fragment2.size());
- ASSERT_EQ(kBlockSize, builder.memUsage());
- }
-
- ASSERT_EQ(kBlockSize, builder.memUsage());
- builder.freeUnused();
- // At a mimimum we will have a buffer of at least one block size in use.
- ASSERT_EQ(kBlockSize, builder.memUsage());
-}
-
-TEST_F(SharedBufferTest, ManualFreeSharedBufferFragmentMemUsage2) {
- constexpr size_t kBlockSize = 16;
- SharedBufferFragmentBuilder builder(kBlockSize,
- SharedBufferFragmentBuilder::DoubleGrowStrategy(
- SharedBufferFragmentBuilder::kDefaultMaxBlockSize));
-
- // This test allocates fragments on a block boundaries.
- {
- builder.start(kBlockSize);
- ASSERT_EQ(kBlockSize, builder.capacity());
- auto fragment1 = builder.finish(kBlockSize);
- ASSERT_EQ(kBlockSize, fragment1.size());
- ASSERT_EQ(kBlockSize, builder.memUsage());
-
- builder.start(kBlockSize);
- // Second buffer is allocated with twice the capacity.
- ASSERT_EQ(2 * kBlockSize, builder.capacity());
- auto fragment2 = builder.finish(kBlockSize);
- ASSERT_EQ(kBlockSize, fragment2.size());
- ASSERT_EQ(3 * kBlockSize, builder.memUsage());
- }
-
- ASSERT_EQ(3 * kBlockSize, builder.memUsage());
- builder.freeUnused();
- // Remaining buffer is the last allocated block of 2x block size;
- ASSERT_EQ(2 * kBlockSize, builder.memUsage());
-}
-
-TEST_F(SharedBufferTest, ManualFreeSharedBufferFragmentMemUsage3) {
- constexpr size_t kBlockSize = 16;
- SharedBufferFragmentBuilder builder(kBlockSize,
- SharedBufferFragmentBuilder::DoubleGrowStrategy(
- SharedBufferFragmentBuilder::kDefaultMaxBlockSize));
-
- size_t expectedMem = 0;
- ASSERT_EQ(expectedMem, builder.memUsage());
-
- // This test allocates a fragment that isn't on a block boundary.
- {
- builder.start(1);
- ASSERT_EQ(kBlockSize, builder.capacity());
- auto fragment1 = builder.finish(1);
- ASSERT_EQ(1, fragment1.size());
- ASSERT_EQ(kBlockSize, builder.memUsage());
-
- builder.start(kBlockSize);
- // We will realloc with double the size.
- ASSERT_EQ(2 * kBlockSize, builder.capacity());
- auto fragment2 = builder.finish(kBlockSize);
- ASSERT_EQ(kBlockSize, fragment2.size());
- // We have one buffer of size kBlockSize and another of 2x.
- ASSERT_EQ(3 * kBlockSize, builder.memUsage());
- }
-
- ASSERT_EQ(3 * kBlockSize, builder.memUsage());
- builder.freeUnused();
- // The last buffer we allocated was 2x the block size, so this is what remains.
- ASSERT_EQ(2 * kBlockSize, builder.memUsage());
-}
-
-TEST_F(SharedBufferTest, ManualFreeSharedBufferFragmentMemUsageGrow) {
- constexpr size_t kBlockSize = 16;
- SharedBufferFragmentBuilder builder(kBlockSize,
- SharedBufferFragmentBuilder::DoubleGrowStrategy(
- SharedBufferFragmentBuilder::kDefaultMaxBlockSize));
- {
- builder.start(1);
- ASSERT_EQ(kBlockSize, builder.capacity());
- auto fragment1 = builder.finish(1);
- ASSERT_EQ(1, fragment1.size());
- ASSERT_EQ(kBlockSize, builder.memUsage());
-
- builder.start(1);
- ASSERT_EQ(kBlockSize - 1, builder.capacity());
- // We will realloc a buffer of 2x the block size.
- builder.grow(kBlockSize);
- auto fragment2 = builder.finish(kBlockSize);
- ASSERT_EQ(kBlockSize, fragment2.size());
- ASSERT_EQ(3 * kBlockSize, builder.memUsage());
- }
-
- ASSERT_EQ(3 * kBlockSize, builder.memUsage());
- builder.freeUnused();
- // The last buffer we allocated was 2x the block size, so this is what remains.
- ASSERT_EQ(2 * kBlockSize, builder.memUsage());
-}
-
-TEST_F(SharedBufferTest, ManualFreeSharedBufferFragmentMemReUse) {
- constexpr size_t kBlockSize = 16;
- SharedBufferFragmentBuilder builder(kBlockSize,
- SharedBufferFragmentBuilder::DoubleGrowStrategy(
- SharedBufferFragmentBuilder::kDefaultMaxBlockSize));
-
- {
- builder.start(kBlockSize);
- ASSERT_EQ(kBlockSize, builder.capacity());
- auto fragment1 = builder.finish(kBlockSize);
- ASSERT_EQ(kBlockSize, fragment1.size());
- ASSERT_EQ(kBlockSize, builder.memUsage());
- }
-
- {
- // Expect that there is no increase in memory usage when we allocate a new buffer of the
- // same size.
- builder.start(kBlockSize);
- ASSERT_EQ(kBlockSize, builder.capacity());
- auto fragment2 = builder.finish(kBlockSize);
- ASSERT_EQ(kBlockSize, fragment2.size());
- ASSERT_EQ(kBlockSize, builder.memUsage());
- }
-
- {
- // Expect that the memory usage only doubles and no more when we allocate a new buffer
- // greater than the current capacity.
- builder.start(2 * kBlockSize);
- ASSERT_EQ(2 * kBlockSize, builder.capacity());
- auto fragment = builder.finish(2 * kBlockSize);
- ASSERT_EQ(2 * kBlockSize, builder.memUsage());
- }
-}
-
-TEST_F(SharedBufferTest, ManualFreeSharedBufferFragmentLotsOfGrows) {
- constexpr size_t kBlockSize = 16;
- SharedBufferFragmentBuilder builder(kBlockSize,
- SharedBufferFragmentBuilder::DoubleGrowStrategy(
- SharedBufferFragmentBuilder::kDefaultMaxBlockSize));
-
- // Ensure that when we grow and we are the exclusive user of the builder, we don't need to
- // allocate new buffers.
- {
- builder.start(kBlockSize);
- ASSERT_EQ(kBlockSize, builder.capacity());
- ASSERT_EQ(kBlockSize, builder.memUsage());
-
- builder.grow(2 * kBlockSize);
- ASSERT_EQ(2 * kBlockSize, builder.capacity());
- ASSERT_EQ(2 * kBlockSize, builder.memUsage());
-
- builder.grow(3 * kBlockSize);
- // We double the buffer size internally every time we realloc.
- ASSERT_EQ(4 * kBlockSize, builder.capacity());
- ASSERT_EQ(4 * kBlockSize, builder.memUsage());
-
- builder.grow(4 * kBlockSize);
- ASSERT_EQ(4 * kBlockSize, builder.capacity());
- ASSERT_EQ(4 * kBlockSize, builder.memUsage());
-
- builder.grow(5 * kBlockSize);
- ASSERT_EQ(8 * kBlockSize, builder.capacity());
- ASSERT_EQ(8 * kBlockSize, builder.memUsage());
-
- auto fragment1 = builder.finish(5 * kBlockSize);
- ASSERT_EQ(fragment1.size(), 5 * kBlockSize);
-
- // If we start a new fragment, we expect that it's capacity is what remains in the buffer.
- builder.start(kBlockSize);
- ASSERT_EQ(3 * kBlockSize, builder.capacity());
- ASSERT_EQ(8 * kBlockSize, builder.memUsage());
-
- auto fragment2 = builder.finish(kBlockSize);
- ASSERT_EQ(kBlockSize, fragment2.size());
- }
-
- // This has no effect on memory usage since the last allocated buffer is 8x the block size. We
- // can't reclaim the unused space right now, but the next fragment we build should be able to.
- builder.freeUnused();
- ASSERT_EQ(2 * kBlockSize, builder.capacity());
- ASSERT_EQ(8 * kBlockSize, builder.memUsage());
-
- {
- builder.start(kBlockSize);
- // Since there are no active fragments, we expect to have the full capacity of the
- // underlying buffer.
- ASSERT_EQ(8 * kBlockSize, builder.capacity());
- ASSERT_EQ(8 * kBlockSize, builder.memUsage());
-
- builder.grow(8 * kBlockSize);
- ASSERT_EQ(8 * kBlockSize, builder.capacity());
- ASSERT_EQ(8 * kBlockSize, builder.memUsage());
-
- auto fragment1 = builder.finish(8 * kBlockSize);
- ASSERT_EQ(fragment1.size(), 8 * kBlockSize);
- }
-
- builder.freeUnused();
- ASSERT_EQ(8 * kBlockSize, builder.memUsage());
-}
-
-TEST_F(SharedBufferTest, ManyUnusedBuffers) {
- constexpr size_t kBlockSize = 16;
- SharedBufferFragmentBuilder builder(
- kBlockSize, SharedBufferFragmentBuilder::DoubleGrowStrategy(kBlockSize));
-
- {
- // Create many fragments, stop using them them all at once, and expect freeUnused to reclaim
- // all but one.
- std::vector<SharedBufferFragment> fragments;
- for (int i = 0; i < 128; i++) {
- builder.start(kBlockSize);
- ASSERT_EQ(kBlockSize, builder.capacity());
- fragments.emplace_back(builder.finish(kBlockSize));
- ASSERT_EQ(kBlockSize * (i + 1), builder.memUsage());
- }
- }
-
- builder.freeUnused();
- ASSERT_EQ(kBlockSize, builder.memUsage());
-}
-
-} // namespace
-} // namespace mongo
diff --git a/src/mongo/util/signal_handlers_synchronous.cpp b/src/mongo/util/signal_handlers_synchronous.cpp
index 59e2e2ef923..8f052ea134b 100644
--- a/src/mongo/util/signal_handlers_synchronous.cpp
+++ b/src/mongo/util/signal_handlers_synchronous.cpp
@@ -65,8 +65,6 @@ namespace mongo {
namespace {
-using namespace fmt::literals;
-
#if defined(_WIN32)
const char* strsignal(int signalNum) {
// should only see SIGABRT on windows
@@ -193,38 +191,22 @@ private:
stdx::mutex MallocFreeOStreamGuard::_streamMutex; // NOLINT
thread_local int MallocFreeOStreamGuard::terminateDepth = 0;
-void logNoRecursion(StringData message) {
- // If we were within a log call when we hit a signal, don't call back into the logging
- // subsystem.
- if (logv2::loggingInProgress()) {
- logv2::signalSafeWriteToStderr(message);
- } else {
- LOGV2_FATAL_CONTINUE(6384300, "Writing fatal message", "message"_attr = message);
- }
-}
-
// must hold MallocFreeOStreamGuard to call
void writeMallocFreeStreamToLog() {
- mallocFreeOStream << "\n";
- logNoRecursion(mallocFreeOStream.str());
+ LOGV2_FATAL_OPTIONS(
+ 4757800,
+ logv2::LogOptions(logv2::FatalMode::kContinue, logv2::LogTruncation::Disabled),
+ "{message}",
+ "Writing fatal message",
+ "message"_attr = mallocFreeOStream.str());
mallocFreeOStream.rewind();
}
// must hold MallocFreeOStreamGuard to call
-void printStackTraceNoRecursion() {
- if (logv2::loggingInProgress()) {
- printStackTrace(mallocFreeOStream);
- writeMallocFreeStreamToLog();
- } else {
- printStackTrace();
- }
-}
-
-// must hold MallocFreeOStreamGuard to call
void printSignalAndBacktrace(int signalNum) {
- mallocFreeOStream << "Got signal: " << signalNum << " (" << strsignal(signalNum) << ").";
+ mallocFreeOStream << "Got signal: " << signalNum << " (" << strsignal(signalNum) << ").\n";
writeMallocFreeStreamToLog();
- printStackTraceNoRecursion();
+ printStackTrace();
}
// this will be called in certain c++ error cases, for example if there are two active
@@ -240,7 +222,7 @@ void myTerminate() {
mallocFreeOStream << " No exception is active";
}
writeMallocFreeStreamToLog();
- printStackTraceNoRecursion();
+ printStackTrace();
breakpoint();
endProcessWithSignal(SIGABRT);
}
@@ -259,16 +241,22 @@ void myInvalidParameterHandler(const wchar_t* expression,
const wchar_t* file,
unsigned int line,
uintptr_t pReserved) {
-
- logNoRecursion(
- "Invalid parameter detected in function {} in {} at line {} with expression '{}'\n"_format(
- toUtf8String(function), toUtf8String(file), line, toUtf8String(expression)));
+ LOGV2_FATAL_CONTINUE(
+ 23815,
+ "Invalid parameter detected in function {function} in {file} at line {line} "
+ "with expression '{expression}'",
+ "Invalid parameter detected",
+ "function"_attr = toUtf8String(function),
+ "file"_attr = toUtf8String(file),
+ "line"_attr = line,
+ "expression"_attr = toUtf8String(expression));
abruptQuit(SIGABRT);
}
void myPureCallHandler() {
- logNoRecursion("Pure call handler invoked. Immediate exit due to invalid pure call\n");
+ LOGV2_FATAL_CONTINUE(23818,
+ "Pure call handler invoked. Immediate exit due to invalid pure call");
abruptQuit(SIGABRT);
}
@@ -357,9 +345,9 @@ void setupSynchronousSignalHandlers() {
void reportOutOfMemoryErrorAndExit() {
MallocFreeOStreamGuard lk{};
- mallocFreeOStream << "out of memory.";
+ mallocFreeOStream << "out of memory.\n";
writeMallocFreeStreamToLog();
- printStackTraceNoRecursion();
+ printStackTrace();
quickExit(EXIT_ABRUPT);
}
diff --git a/src/mongo/util/stacktrace.cpp b/src/mongo/util/stacktrace.cpp
index a1c3c69a39a..b9b15b7410a 100644
--- a/src/mongo/util/stacktrace.cpp
+++ b/src/mongo/util/stacktrace.cpp
@@ -117,21 +117,3 @@ void logBacktraceObject(const BSONObj& bt, StackTraceSink* sink, bool withHumanR
}
} // namespace mongo::stack_trace_detail
-
-void mongo::StackTrace::log(bool withHumanReadable) const {
- if (hasError()) {
- LOGV2_ERROR(31430, "Error collecting stack trace", "error"_attr = _error);
- }
-
- StackTraceSink* logv2Sink = nullptr;
- stack_trace_detail::logBacktraceObject(_stacktrace, logv2Sink, withHumanReadable);
-}
-
-void mongo::StackTrace::sink(StackTraceSink* sink, bool withHumanReadable) const {
- using namespace fmt::literals;
- if (hasError()) {
- *sink << fmt::format(FMT_STRING("Error collecting stack trace: {}"), _error);
- }
-
- stack_trace_detail::logBacktraceObject(_stacktrace, sink, withHumanReadable);
-}
diff --git a/src/mongo/util/stacktrace.h b/src/mongo/util/stacktrace.h
index fdfce44af08..e36a0aea03b 100644
--- a/src/mongo/util/stacktrace.h
+++ b/src/mongo/util/stacktrace.h
@@ -39,8 +39,6 @@
#include "mongo/base/string_data.h"
#include "mongo/bson/bsonobj.h"
#include "mongo/config.h"
-#include "mongo/util/future.h"
-#include "mongo/util/synchronized_value.h"
/**
* All-thread backtrace is only implemented on Linux. Even on Linux, it's only AS-safe
@@ -93,46 +91,6 @@ private:
std::string& _s;
};
-/**
- * A `StackTrace` object also encapsulates any errors encountered while attaining stacktrace
- * information. Oddly, a `StackTrace` object can be in an error state (`hasError` returns true) and
- * have non-empty stacktrace information via `getBSONRepresentation`. It is legal to call
- * `getBSONRepresentation` even when in an error state.
- *
- * Likewise, it is always safe to call `log` or `sink`, regardless of error state. Those output
- * methods will write out any errors along with any available stacktrace information.
- *
- * Disabling log truncation is strongly recommended when logging a BSONObj returned from
- * `getBSONRepresentation` by hand.
- */
-class StackTrace {
-public:
- explicit StackTrace(BSONObj stacktrace) : _stacktrace(stacktrace) {}
-
- StackTrace(BSONObj stacktrace, std::string error)
- : _stacktrace(stacktrace), _error(std::move(error)) {}
-
- void log(bool withHumanReadable = true) const;
-
- void sink(StackTraceSink* sink, bool withHumanReadable = true) const;
-
- BSONObj getBSONRepresentation() const {
- return _stacktrace;
- }
-
- bool hasError() const {
- return !_error.empty();
- }
-
- std::string getError() const {
- return _error;
- }
-
-private:
- BSONObj _stacktrace;
- std::string _error;
-};
-
namespace stack_trace_detail {
/**
* A utility for uint64_t <=> uppercase hex string conversions. It
@@ -185,64 +143,6 @@ private:
void logBacktraceObject(const BSONObj& bt, StackTraceSink* sink, bool withHumanReadable);
-/**
- * Multiple waiters can register their interest in the next stack trace,
- * by calling `waiter()` and obtaining a waiter object, which will block
- * in its destructor until the next stack trace completes.
- * On the "producer side", each stack trace collection calls `notifier()`
- * to become that next stack trace. It will signal the end of the collection
- * by destroying the notifier object returned by that `notifier` call.
- */
-class PrintAllStacksSession {
-public:
- /** Notifies observers on its destruction. */
- class Notifier {
- public:
- explicit Notifier(std::unique_ptr<SharedPromise<void>> prom) : _prom{std::move(prom)} {}
- ~Notifier() {
- if (_prom)
- _prom->emplaceValue();
- }
-
- Notifier(Notifier&&) = default;
- Notifier& operator=(Notifier&&) = default;
-
- private:
- std::unique_ptr<SharedPromise<void>> _prom;
- };
-
- /** Blocks in its destructor waiting for a session to complete. */
- class Waiter {
- public:
- explicit Waiter(SharedSemiFuture<void> fut) : _fut{std::move(fut)} {}
- ~Waiter() {
- if (_fut.valid())
- _fut.get();
- }
-
- Waiter(Waiter&&) = default;
- Waiter& operator=(Waiter&&) = default;
-
- private:
- SharedSemiFuture<void> _fut;
- };
-
- Notifier notifier() {
- // Consume and retain the current SharedPromise from _promise.
- return Notifier{std::exchange(**_promise, {})};
- }
-
- Waiter waiter() {
- auto updateGuard = _promise.synchronize();
- if (!*updateGuard)
- *updateGuard = std::make_unique<SharedPromise<void>>();
- return Waiter{(*updateGuard)->getFuture()};
- }
-
-private:
- synchronized_value<std::unique_ptr<SharedPromise<void>>> _promise;
-};
-
} // namespace stack_trace_detail
#ifndef _WIN32
@@ -379,7 +279,6 @@ size_t rawBacktrace(void** addrs, size_t capacity);
void printStackTrace(StackTraceSink& sink);
void printStackTrace(std::ostream& os);
void printStackTrace();
-StackTrace getStackTrace();
#if defined(MONGO_STACKTRACE_CAN_DUMP_ALL_THREADS)
@@ -404,9 +303,6 @@ void markAsStackTraceProcessingThread();
void printAllThreadStacks();
void printAllThreadStacks(StackTraceSink& sink);
-/** Calls `printAllThreadStacks` and blocks until it is completed. */
-void printAllThreadStacksBlocking();
-
#endif // defined(MONGO_STACKTRACE_CAN_DUMP_ALL_THREADS)
} // namespace mongo
diff --git a/src/mongo/util/stacktrace_posix.cpp b/src/mongo/util/stacktrace_posix.cpp
index 2bbfd8867bd..2bf807ed4ae 100644
--- a/src/mongo/util/stacktrace_posix.cpp
+++ b/src/mongo/util/stacktrace_posix.cpp
@@ -97,6 +97,9 @@ struct Options {
// Add the processInfo block
bool withProcessInfo = true;
+ // Add "human readable" breakdown when dumping stack. 1 line per frame.
+ bool withHumanReadable = true;
+
// only include the somap entries relevant to the backtrace
bool trimSoMap = true;
@@ -421,7 +424,7 @@ private:
* analysis tool. For example, on Linux it contains a subobject named "somap", describing
* the objects referenced in the "b" fields of the "backtrace" list.
*/
-StackTrace getStackTraceImpl(const Options& options) {
+void printStackTraceImpl(const Options& options, StackTraceSink* sink = nullptr) {
using namespace fmt::literals;
std::string err;
BSONObjBuilder bob;
@@ -438,9 +441,16 @@ StackTrace getStackTraceImpl(const Options& options) {
appendStackTraceObject(&bob, iteration, options);
#endif
- return StackTrace(bob.obj(), err);
+ if (!err.empty()) {
+ if (sink) {
+ *sink << fmt::format(FMT_STRING("Error collecting stack trace: {}"), err);
+ }
+ LOGV2_ERROR(31430, "Error collecting stack trace", "error"_attr = err);
+ }
+ stack_trace_detail::logBacktraceObject(bob.done(), sink, options.withHumanReadable);
}
+
} // namespace
} // namespace stack_trace_detail
@@ -464,17 +474,10 @@ const StackTraceAddressMetadata& StackTraceAddressMetadataGenerator::load(void*
return _meta;
}
-StackTrace getStackTrace() {
- stack_trace_detail::Options options{};
- options.rawAddress = true;
- return getStackTraceImpl(options);
-}
-
void printStackTrace(StackTraceSink& sink) {
stack_trace_detail::Options options{};
options.rawAddress = true;
- const bool withHumanReadable = true;
- getStackTraceImpl(options).sink(&sink, withHumanReadable);
+ stack_trace_detail::printStackTraceImpl(options, &sink);
}
void printStackTrace(std::ostream& os) {
@@ -485,8 +488,7 @@ void printStackTrace(std::ostream& os) {
void printStackTrace() {
stack_trace_detail::Options options{};
options.rawAddress = true;
- const bool withHumanReadable = true;
- getStackTraceImpl(options).log(withHumanReadable);
+ stack_trace_detail::printStackTraceImpl(options, nullptr);
}
} // namespace mongo
diff --git a/src/mongo/util/stacktrace_test.cpp b/src/mongo/util/stacktrace_test.cpp
index 6c6d2f36366..2a9ad180c28 100644
--- a/src/mongo/util/stacktrace_test.cpp
+++ b/src/mongo/util/stacktrace_test.cpp
@@ -645,64 +645,6 @@ TEST_F(PrintAllThreadStacksTest, Go_200_Threads) {
doPrintAllThreadStacks(200);
}
-TEST_F(PrintAllThreadStacksTest, SessionBasic) {
- stack_trace_detail::PrintAllStacksSession session;
-
- auto waiter = boost::make_optional(session.waiter());
- stdx::thread producer{[&] { auto notifier = session.notifier(); }};
- waiter = {};
- producer.join();
-}
-
-TEST_F(PrintAllThreadStacksTest, SessionProducerConsumers) {
- synchronized_value<std::string> values;
- stack_trace_detail::PrintAllStacksSession session;
-
- struct PromiseFuture {
- SharedPromise<void> promise;
- SharedSemiFuture<void> future;
- };
-
- std::vector<PromiseFuture> promisesFutures(2);
-
- for (auto& pf : promisesFutures) {
- pf.future = pf.promise.getFuture();
- }
-
- std::vector<stdx::thread> consumers;
- for (auto& pf : promisesFutures) {
- consumers.emplace_back([&, p = &pf.promise] {
- auto waiter = boost::make_optional(session.waiter());
- p->emplaceValue();
- waiter = {};
- values->push_back('3');
- });
- }
-
- // This does not enforce ordering between push_back('3') above and the push_back calls below if
- // PrintAllStacksSession::waiter/notifier don't.
- for (auto& pf : promisesFutures) {
- pf.future.wait();
- }
-
- stdx::thread producer{[&] {
- values->push_back('1');
- auto notifier = boost::make_optional(session.notifier());
- values->push_back('2');
- notifier = {};
- values->push_back('3');
- }};
-
- for (auto& consumer : consumers) {
- consumer.join();
- }
-
- producer.join();
-
- auto guard = values.synchronize();
- ASSERT(*guard == "12333") << *guard;
-}
-
#endif // defined(MONGO_STACKTRACE_CAN_DUMP_ALL_THREADS)
#if defined(MONGO_CONFIG_USE_LIBUNWIND) || defined(MONGO_CONFIG_HAVE_EXECINFO_BACKTRACE)
diff --git a/src/mongo/util/stacktrace_threads.cpp b/src/mongo/util/stacktrace_threads.cpp
index 51581c4dc9b..d7157d0e212 100644
--- a/src/mongo/util/stacktrace_threads.cpp
+++ b/src/mongo/util/stacktrace_threads.cpp
@@ -324,7 +324,6 @@ class State {
public:
void printStacks(StackTraceSink& sink);
void printStacks();
- void printAllThreadStacksBlocking();
/**
* We need signals for two purpposes in the stack tracing system.
@@ -383,7 +382,6 @@ private:
int _signal = 0;
std::atomic<int> _processingTid = -1; // NOLINT
std::atomic<StackCollectionOperation*> _stackCollection = nullptr; // NOLINT
- PrintAllStacksSession _printAllStacksSession;
MONGO_STATIC_ASSERT(decltype(_processingTid)::is_always_lock_free);
MONGO_STATIC_ASSERT(decltype(_stackCollection)::is_always_lock_free);
@@ -525,16 +523,10 @@ void State::printStacks() {
LOGV2(31426, "===== multithread stacktrace session end =====");
}
};
-
LogEmitter emitter;
- auto notifier = _printAllStacksSession.notifier();
printToEmitter(emitter);
}
-void State::printAllThreadStacksBlocking() {
- auto waiter = _printAllStacksSession.waiter();
- kill(getpid(), _signal); // The SignalHandler thread calls printAllThreadStacks.
-}
void State::printToEmitter(AbstractEmitter& emitter) {
std::vector<ThreadBacktrace> messageStorage;
@@ -674,7 +666,6 @@ void initialize(int signal) {
}
} // namespace
-
} // namespace stack_trace_detail
@@ -686,10 +677,6 @@ void printAllThreadStacks() {
stack_trace_detail::stateSingleton->printStacks();
}
-void printAllThreadStacksBlocking() {
- stack_trace_detail::stateSingleton->printAllThreadStacksBlocking();
-}
-
void setupStackTraceSignalAction(int signal) {
stack_trace_detail::initialize(signal);
}
diff --git a/src/mongo/util/stacktrace_windows.cpp b/src/mongo/util/stacktrace_windows.cpp
index f34d616aa6a..99ceea9e937 100644
--- a/src/mongo/util/stacktrace_windows.cpp
+++ b/src/mongo/util/stacktrace_windows.cpp
@@ -66,6 +66,11 @@ namespace {
const size_t kPathBufferSize = 1024;
+struct Options {
+ bool withHumanReadable = false;
+ bool rawAddress = false;
+};
+
// On Windows the symbol handler must be initialized at process startup and cleaned up at shutdown.
// This class wraps up that logic and gives access to the process handle associated with the
// symbol handler. Because access to the symbol handler API is not thread-safe, it also provides
@@ -207,11 +212,14 @@ struct TraceItem {
std::pair<std::string, size_t> symbol;
};
-void appendTrace(BSONObjBuilder* bob, const std::vector<TraceItem>& traceList) {
+void appendTrace(BSONObjBuilder* bob,
+ const std::vector<TraceItem>& traceList,
+ const Options& options) {
auto bt = BSONArrayBuilder(bob->subarrayStart("backtrace"));
for (const auto& item : traceList) {
auto o = BSONObjBuilder(bt.subobjStart());
- o.append("a", stack_trace_detail::Hex(item.address));
+ if (options.rawAddress)
+ o.append("a", stack_trace_detail::Hex(item.address));
if (!item.module.empty())
o.append("module", item.module);
if (!item.source.first.empty()) {
@@ -286,51 +294,59 @@ std::vector<TraceItem> makeTraceList(CONTEXT& context) {
return traceList;
}
-StackTrace getStackTraceImpl(CONTEXT& context) {
- std::vector<TraceItem> traceList = makeTraceList(context);
- if (traceList.empty()) {
- return StackTrace(BSONObj(), "");
- }
-
+void printTraceList(const std::vector<TraceItem>& traceList,
+ StackTraceSink* sink,
+ const Options& options) {
+ using namespace fmt::literals;
+ if (traceList.empty())
+ return;
BSONObjBuilder bob;
- appendTrace(&bob, traceList);
- return StackTrace(bob.obj(), "");
+ appendTrace(&bob, traceList, options);
+ stack_trace_detail::logBacktraceObject(bob.done(), sink, options.withHumanReadable);
}
-} // namespace
-StackTrace getStackTrace() {
+/** `sink` can be nullptr to emit structured logs instead of writing to a sink. */
+void printWindowsStackTraceImpl(CONTEXT& context, StackTraceSink* sink) {
+ Options options{};
+ options.withHumanReadable = true;
+ options.rawAddress = true;
+ printTraceList(makeTraceList(context), sink, options);
+}
+
+void printWindowsStackTraceImpl(StackTraceSink* sink) {
CONTEXT context;
memset(&context, 0, sizeof(context));
context.ContextFlags = CONTEXT_CONTROL;
RtlCaptureContext(&context);
-
- return getStackTraceImpl(context);
+ printWindowsStackTraceImpl(context, sink);
}
+} // namespace
+
void printWindowsStackTrace(CONTEXT& context, StackTraceSink& sink) {
- getStackTraceImpl(context).sink(&sink);
+ printWindowsStackTraceImpl(context, &sink);
}
void printWindowsStackTrace(CONTEXT& context, std::ostream& os) {
OstreamStackTraceSink sink{os};
- printWindowsStackTrace(context, sink);
+ printWindowsStackTraceImpl(context, &sink);
}
void printWindowsStackTrace(CONTEXT& context) {
- getStackTraceImpl(context).log();
+ printWindowsStackTraceImpl(context, nullptr);
}
void printStackTrace(StackTraceSink& sink) {
- getStackTrace().sink(&sink);
+ printWindowsStackTraceImpl(&sink);
}
void printStackTrace(std::ostream& os) {
OstreamStackTraceSink sink{os};
- printStackTrace(sink);
+ printWindowsStackTraceImpl(&sink);
}
void printStackTrace() {
- getStackTrace().log();
+ printWindowsStackTraceImpl(nullptr);
}
} // namespace mongo
diff --git a/src/mongo/util/str_escape.cpp b/src/mongo/util/str_escape.cpp
index c42a916bab0..d191fb92252 100644
--- a/src/mongo/util/str_escape.cpp
+++ b/src/mongo/util/str_escape.cpp
@@ -37,82 +37,45 @@ namespace mongo::str {
namespace {
constexpr char kHexChar[] = "0123456789abcdef";
-// Appends the bytes in the range [begin, end) to the output buffer,
-// which can either be a fmt::memory_buffer, or a std::string.
-template <typename Buffer, typename Iterator>
-void appendBuffer(Buffer& buffer, Iterator begin, Iterator end) {
- buffer.append(begin, end);
-}
-
// 'singleHandler' Function to write a valid single byte UTF-8 sequence with desired escaping.
// 'invalidByteHandler' Function to write a byte of invalid UTF-8 encoding
// 'twoEscaper' Function to write a valid two byte UTF-8 sequence with desired escaping, for C1
// control codes.
-// 'maxLength' Max length to write into output buffer; A value of std::string::npos means unbounded.
-// An escape sequence will not be written if appending the entire sequence will exceed this limit.
-// 'wouldWrite' Output to contain the total bytes that would have been written to the buffer if no
-// size limit is in place.
-//
// All these functions take a function object as their first parameter to perform the
// writing of any escaped data. This function expects the number of handled bytes as its first
// parameter and the corresponding escaped string as the second. They are templates to they can be
// inlined.
-template <typename Buffer,
- typename SingleByteHandler,
- typename InvalidByteHandler,
- typename TwoByteEscaper>
-void escape(Buffer& buffer,
+template <typename SingleByteHandler, typename InvalidByteHandler, typename TwoByteEscaper>
+void escape(fmt::memory_buffer& buffer,
StringData str,
SingleByteHandler singleHandler,
InvalidByteHandler invalidByteHandler,
- TwoByteEscaper twoEscaper,
- size_t maxLength,
- size_t* wouldWrite) {
- // The range [inFirst, it) contains input that does not need to be escaped and that has not been
+ TwoByteEscaper twoEscaper) {
+ // The range [begin, it) contains input that does not need to be escaped and that has not been
// written to output yet.
- // The range [it, inLast) contains remaining input to scan. 'inFirst' is pointing to the
- // beginning of the input that has not yet been written to 'escaped'. 'it' is pointing to the
- // beginning of the unicode code point we're currently processing in the while-loop below.
- // 'inLast' is the end of the input sequence.
- auto inFirst = str.begin();
- auto inLast = str.end();
- auto it = inFirst;
- size_t cap = maxLength;
- size_t total = 0;
+ // The range [it end) contains remaining input to scan 'begin' is pointing to the beginning of
+ // the input that has not yet been written to 'escaped'.
+ // 'it' is pointing to the beginning of the unicode code point we're currently processing in the
+ // while-loop below. 'end' is the end of the input sequence.
+ auto begin = str.begin();
+ auto it = str.begin();
+ auto end = str.end();
// Writes an escaped sequence to output after flushing pending input that does not need to be
// escaped. 'it' is assumed to be at the beginning of the input sequence represented by the
// escaped data.
// 'numHandled' the number of bytes of unescaped data being written escaped in 'escapeSequence'
auto flushAndWrite = [&](size_t numHandled, StringData escapeSequence) {
- // Appends the range [wFirst, wLast) to the output if the result is within the max length.
- // 'canTruncate' controls the behavior if appending the entire range would exceed the limit.
- // If true, this appends input up to the length limit. Otherwise, none is appended.
- auto boundedWrite = [&](auto wFirst, auto wLast, bool canTruncate) {
- size_t len = std::distance(wFirst, wLast);
- total += len;
- if (maxLength != std::string::npos) {
- if (len > cap) {
- if (!canTruncate) {
- cap = 0;
- }
- len = cap;
- }
- cap -= len;
- }
- appendBuffer(buffer, wFirst, wFirst + len);
- };
-
// Flush range of unmodified input
- boundedWrite(inFirst, it, true);
- inFirst = it + numHandled;
+ buffer.append(begin, it);
+ begin = it + numHandled;
// Write escaped data
- boundedWrite(escapeSequence.begin(), escapeSequence.end(), false);
+ buffer.append(escapeSequence.rawData(), escapeSequence.rawData() + escapeSequence.size());
};
auto isValidCodePoint = [&](auto pos, int len) {
- return std::distance(pos, inLast) >= len &&
+ return std::distance(pos, end) >= len &&
std::all_of(pos + 1, pos + len, [](uint8_t c) { return (c >> 6) == 0b10; });
};
@@ -135,7 +98,7 @@ void escape(Buffer& buffer,
auto writeInvalid = [&](uint8_t c) { invalidByteHandler(flushAndWrite, c); };
- while (it != inLast) {
+ while (it != end) {
uint8_t c = *it;
bool bit7 = (c >> 7) & 1;
if (MONGO_likely(!bit7)) {
@@ -193,15 +156,10 @@ void escape(Buffer& buffer,
}
}
// Write last block
- flushAndWrite(0, {});
- if (wouldWrite) {
- *wouldWrite = total;
- }
+ buffer.append(begin, it);
}
} // namespace
-
-template <typename Buffer>
-void escapeForTextCommon(Buffer& buffer, StringData str, size_t maxLength, size_t* wouldWrite) {
+void escapeForText(fmt::memory_buffer& buffer, StringData str) {
auto singleByteHandler = [](const auto& writer, uint8_t unescaped) {
switch (unescaped) {
case '\0':
@@ -329,26 +287,16 @@ void escapeForTextCommon(Buffer& buffer, StringData str, size_t maxLength, size_
str,
std::move(singleByteHandler),
std::move(invalidByteHandler),
- std::move(twoByteEscaper),
- maxLength,
- wouldWrite);
+ std::move(twoByteEscaper));
}
-void escapeForText(fmt::memory_buffer& buffer,
- StringData str,
- size_t maxLength,
- size_t* wouldWrite) {
- escapeForTextCommon(buffer, str, maxLength, wouldWrite);
+std::string escapeForText(StringData str) {
+ fmt::memory_buffer buffer;
+ escapeForText(buffer, str);
+ return fmt::to_string(buffer);
}
-std::string escapeForText(StringData str, size_t maxLength, size_t* wouldWrite) {
- std::string buffer;
- escapeForTextCommon(buffer, str, maxLength, wouldWrite);
- return buffer;
-}
-
-template <typename Buffer>
-void escapeForJSONCommon(Buffer& buffer, StringData str, size_t maxLength, size_t* wouldWrite) {
+void escapeForJSON(fmt::memory_buffer& buffer, StringData str) {
auto singleByteHandler = [](const auto& writer, uint8_t unescaped) {
switch (unescaped) {
case '\0':
@@ -479,21 +427,11 @@ void escapeForJSONCommon(Buffer& buffer, StringData str, size_t maxLength, size_
str,
std::move(singleByteHandler),
std::move(invalidByteHandler),
- std::move(twoByteEscaper),
- maxLength,
- wouldWrite);
+ std::move(twoByteEscaper));
}
-
-void escapeForJSON(fmt::memory_buffer& buffer,
- StringData str,
- size_t maxLength,
- size_t* wouldWrite) {
- escapeForJSONCommon(buffer, str, maxLength, wouldWrite);
-}
-
-std::string escapeForJSON(StringData str, size_t maxLength, size_t* wouldWrite) {
- std::string buffer;
- escapeForJSONCommon(buffer, str, maxLength, wouldWrite);
- return buffer;
+std::string escapeForJSON(StringData str) {
+ fmt::memory_buffer buffer;
+ escapeForJSON(buffer, str);
+ return fmt::to_string(buffer);
}
} // namespace mongo::str
diff --git a/src/mongo/util/str_escape.h b/src/mongo/util/str_escape.h
index 2d82e5697cd..47fe3d30060 100644
--- a/src/mongo/util/str_escape.h
+++ b/src/mongo/util/str_escape.h
@@ -35,75 +35,9 @@
#include <string>
namespace mongo::str {
+void escapeForText(fmt::memory_buffer& buffer, StringData str);
+std::string escapeForText(StringData str);
-/**
- * Escapes the special characters in 'str' for use as printable text.
- *
- * The backslash (`\`) character is escaped with another backslash, yielding the
- * 2-character sequence {`\`, `\`}.
- *
- * The single-byte control characters (octets 0x00-0x1f, 0x7f) are generally escaped
- * using the format "\xHH", where the 2 `H` characters are replaced by the 2 hex digits
- * of the octet. For instance, the octet 0x7f would yield the sequence: {`\`, `x`, `7`, `f`}.
- * Exemptions to this rule are the following octets, which are escaped using C-style escape
- * sequences:
- * 0x00 -> {`\`, `0`}
- * 0x07 -> {`\`, `a`}
- * 0x08 -> {`\`, `b`}
- * 0x09 -> {`\`, `t`}
- * 0x0a -> {`\`, `n`}
- * 0x0b -> {`\`, `v`}
- * 0x0c -> {`\`, `f`}
- * 0x0d -> {`\`, `r`}
- * 0x1b -> {`\`, `e`}
- *
- * The two-byte UTF-8 sequences between 0xC280 (U+0080) and 0xC29F (U+009F), inclusive, are
- * also escaped as they are considered control characters. The escape sequence for these has
- * the format: "\xC2\xHH", where the 2 `H` characters are replaced by the 2 hex digits of the
- * second octet.
- *
- * Invalid bytes found are replaced with the escape sequence following the format: "\xHH",
- * similar to how single-byte control characters are escaped.
- *
- * This writes the escaped output to 'buffer', and stops writing when either the output
- * length reaches the 'maxLength', or if appending the next escape sequence will cause the
- * output to exceed 'maxLength'. A 'maxLength' value of std::string::npos means unbounded.
- *
- * The 'wouldWrite' output is updated to contain the total bytes that would have been written
- * if there was no length limit.
- */
-void escapeForText(fmt::memory_buffer& buffer,
- StringData str,
- size_t maxLength = std::string::npos,
- size_t* wouldWrite = nullptr);
-std::string escapeForText(StringData str,
- size_t maxLength = std::string::npos,
- size_t* wouldWrite = nullptr);
-
-/**
- * Escapes the special characters in 'str' for use in JSON.
- *
- * This differs from escapeForText in that the double-quote character (`"`) is escaped
- * with a backslash, yielding the 2-character sequence {`\`, `"`}.
- *
- * The general format of the escape sequences for single-byte control characters becomes
- * "\u00HH", where the 2 `H` characters are replaced by the 2 hex digits of the octet.
- * For example, the octet 0x7f would yield the sequence: {`\`, `u`, `0`, `0`, `7`, `f`}.
- * The list of octets escaped using C-style escape sequences is also shortened to:
- * 0x08 -> {`\`, `b`}
- * 0x09 -> {`\`, `t`}
- * 0x0a -> {`\`, `n`}
- * 0x0c -> {`\`, `f`}
- * 0x0d -> {`\`, `r`}
- * For two-byte control characters, the format of the escape sequence becomes "\uc2HH",
- * where the 2 `H` characters are replaced by the 2 hex digits of the second octet.
- * Invalid bytes found are replaced with the sequence: "\ufffd".
- */
-void escapeForJSON(fmt::memory_buffer& buffer,
- StringData str,
- size_t maxLength = std::string::npos,
- size_t* wouldWrite = nullptr);
-std::string escapeForJSON(StringData str,
- size_t maxLength = std::string::npos,
- size_t* wouldWrite = nullptr);
+void escapeForJSON(fmt::memory_buffer& buffer, StringData str);
+std::string escapeForJSON(StringData str);
} // namespace mongo::str
diff --git a/src/mongo/util/string_map.h b/src/mongo/util/string_map.h
index ee3bf8da366..2285a3d630b 100644
--- a/src/mongo/util/string_map.h
+++ b/src/mongo/util/string_map.h
@@ -33,7 +33,6 @@
#include <absl/container/flat_hash_set.h>
#include "mongo/base/string_data.h"
-#include "mongo/stdx/trusted_hasher.h"
#include "mongo/util/assert_util.h"
namespace mongo {
@@ -119,11 +118,4 @@ using StringDataMap = absl::flat_hash_map<StringData, V, StringMapHasher, String
using StringDataSet = absl::flat_hash_set<StringData, StringMapHasher, StringMapEq>;
-// StringMapHasher is a trusted hasher, no need to wrap in a secondary layer of hashing when used in
-// stdx unordered containers.
-template <>
-struct IsTrustedHasher<StringMapHasher, std::string> : std::true_type {};
-template <>
-struct IsTrustedHasher<StringMapHasher, StringData> : std::true_type {};
-
} // namespace mongo
diff --git a/src/mongo/util/tick_source_bm.cpp b/src/mongo/util/tick_source_bm.cpp
deleted file mode 100644
index ef6381b753d..00000000000
--- a/src/mongo/util/tick_source_bm.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- * Copyright (C) 2022-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/util/duration.h"
-#include "mongo/util/system_tick_source.h"
-#include "mongo/util/tick_source.h"
-#include <benchmark/benchmark.h>
-
-
-namespace mongo {
-
-static void BM_getTicks(benchmark::State& state) {
- auto tickSource = SystemTickSource::get();
- for (auto _ : state) {
- benchmark::DoNotOptimize(tickSource->getTicks());
- }
- state.SetItemsProcessed(state.iterations());
-}
-
-BENCHMARK(BM_getTicks);
-
-} // namespace mongo
diff --git a/src/mongo/util/uuid.h b/src/mongo/util/uuid.h
index ce10692d1de..078f658d07c 100644
--- a/src/mongo/util/uuid.h
+++ b/src/mongo/util/uuid.h
@@ -160,11 +160,6 @@ public:
return _uuid >= rhs._uuid;
}
- template <typename H>
- friend H AbslHashValue(H h, const UUID& uuid) {
- return H::combine(std::move(h), uuid._uuid);
- }
-
/**
* Returns true only if the UUID is the RFC 4122 variant, v4 (random).
*/
diff --git a/src/mongo/util/version/releases.h.tpl b/src/mongo/util/version/releases.h.tpl
index ddab494047d..701266bc71f 100644
--- a/src/mongo/util/version/releases.h.tpl
+++ b/src/mongo/util/version/releases.h.tpl
@@ -242,24 +242,6 @@ constexpr StringData toString(FeatureCompatibilityVersion v) {
return findExtended(v).second;
}
-inline int majorVersion(FeatureCompatibilityVersion v) {
- auto str = toString(v);
- auto pos = str.rfind('.');
- if (pos == std::string::npos) {
- return -1;
- }
- return stoi(str.substr(pos-1, 1).toString());
-}
-
-inline int minorVersion(FeatureCompatibilityVersion v) {
- auto str = toString(v);
- auto pos = str.rfind('.');
- if (pos == std::string::npos) {
- return -1;
- }
- return stoi(str.substr(pos+1, 1).toString());
-}
-
/**
* Pointers to nodes of the extended table that represent numbered software versions.
* Other FCV enum members, such as those representing transitions, are excluded.
diff --git a/src/mongo/util/version/releases.yml b/src/mongo/util/version/releases.yml
index 3babf32c3f7..ca829d039c9 100644
--- a/src/mongo/util/version/releases.yml
+++ b/src/mongo/util/version/releases.yml
@@ -24,27 +24,6 @@ longTermSupportReleases:
- "4.4"
- "5.0"
-# List of stable MongoDB versions since 2.0 that have been EOL'd.
-# Entries to this section will also stop them from running as "old" versions in
-# multiversion testing across all branches. This is because there is no need to
-# run multiversion testing against EOL versions.
-eolVersions:
- - "2.0"
- - "2.2"
- - "2.4"
- - "2.6"
- - "3.0" # 2018-02
- - "3.2" # 2018-09
- - "3.4" # 2020-01
- - "3.6" # 2021-04
- - "4.0" # 2022-04
-# - "4.2" # 2023-04
-# - "4.4" # 2024-02
-# - "5.0" # 2024-10
- - "5.1" # 2022-01
- - "5.2" # 2022-04
- - "5.3" # 2022-07
-
# Optional.
# Using this special override extends FCV constant generation down to the previous value of
# lastLTS. This is intended to ease the transition to a new lastLTS by extending the lifetime