summaryrefslogtreecommitdiff
path: root/src/mongo/stdx/thread.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/stdx/thread.h')
-rw-r--r--src/mongo/stdx/thread.h85
1 files changed, 85 insertions, 0 deletions
diff --git a/src/mongo/stdx/thread.h b/src/mongo/stdx/thread.h
index 43a7cf52879..fda0e5531e1 100644
--- a/src/mongo/stdx/thread.h
+++ b/src/mongo/stdx/thread.h
@@ -30,6 +30,9 @@
#pragma once
#include <chrono>
+#include <csignal>
+#include <cstddef>
+#include <cstdint>
#include <ctime>
#include <exception>
#include <thread>
@@ -37,8 +40,86 @@
#include "mongo/stdx/exception.h"
+#if defined(__linux__) || defined(__FreeBSD__)
+#define MONGO_HAS_SIGALTSTACK 1
+#else
+#define MONGO_HAS_SIGALTSTACK 0
+#endif
+
namespace mongo {
namespace stdx {
+namespace support {
+
+/**
+ * Manages an alternate stack for signal handlers.
+ * A dummy implementation is provided on platforms which do not support `sigaltstack`.
+ */
+class SigAltStackController {
+public:
+#if MONGO_HAS_SIGALTSTACK
+ /** Return an object that installs and uninstalls our `_stackStorage` as `sigaltstack`. */
+ auto makeInstallGuard() const {
+ struct Guard {
+ explicit Guard(const SigAltStackController& controller) : _controller(controller) {
+ _controller._install();
+ }
+
+ ~Guard() {
+ _controller._uninstall();
+ }
+
+ const SigAltStackController& _controller;
+ };
+ return Guard{*this};
+ }
+
+private:
+ void _install() const {
+ stack_t ss;
+ ss.ss_sp = _stackStorage.get();
+ ss.ss_flags = 0;
+ ss.ss_size = kStackSize;
+ if (sigaltstack(&ss, nullptr)) {
+ abort();
+ }
+ }
+
+ void _uninstall() const {
+ stack_t ss;
+ ss.ss_flags = SS_DISABLE;
+ if (sigaltstack(&ss, nullptr)) {
+ abort();
+ }
+ }
+
+ // Signal stack consumption was measured in mongo/util/stacktrace_test.
+ // 64 kiB is 4X our worst case, so that should be enough.
+ // . signal handler action
+ // . --use-libunwind : ----\ =============================
+ // . --dbg=on : -\ \ minimal | print | backtrace
+ // . = = ========|=========|==========
+ // . N N : 4,344 | 7,144 | 5,096
+ // . Y N : 4,424 | 7,528 | 5,160
+ // . N Y : 4,344 | 13,048 | 7,352
+ // . Y Y : 4,424 | 13,672 | 8,392
+ // ( https://jira.mongodb.org/secure/attachment/233569/233569_stacktrace-writeup.txt )
+ static constexpr std::size_t kMongoMinSignalStackSize = std::size_t{64} << 10;
+
+ static constexpr std::size_t kStackSize =
+ std::max(kMongoMinSignalStackSize, std::size_t{MINSIGSTKSZ});
+ std::unique_ptr<std::byte[]> _stackStorage = std::make_unique<std::byte[]>(kStackSize);
+
+#else // !MONGO_HAS_SIGALTSTACK
+ auto makeInstallGuard() const {
+ struct Guard {
+ ~Guard() {} // needed to suppress 'unused variable' warnings.
+ };
+ return Guard{};
+ }
+#endif // !MONGO_HAS_SIGALTSTACK
+};
+
+} // namespace support
/**
* We're wrapping std::thread here, rather than aliasing it, because we'd like
@@ -49,6 +130,8 @@ namespace stdx {
* of the system failed thread creation (as the exception itself is caught at
* the top of the stack).
*
+ * We also want to allocate and install a `sigaltstack` to diagnose stack overflows.
+ *
* We're putting this in stdx, rather than having it as some kind of
* mongo::Thread, because the signature and use of the type is otherwise
* completely identical. Rather than migrate all callers, it was deemed
@@ -86,6 +169,7 @@ public:
explicit thread(Function f, Args&&... args) noexcept
: ::std::thread::thread( // NOLINT
[
+ sigAltStackController = support::SigAltStackController(),
f = std::move(f),
pack = std::make_tuple(std::forward<Args>(args)...)
]() mutable noexcept {
@@ -96,6 +180,7 @@ public:
::std::set_terminate( // NOLINT
::mongo::stdx::TerminateHandlerDetailsInterface::dispatch);
#endif
+ auto sigAltStackGuard = sigAltStackController.makeInstallGuard();
return std::apply(std::move(f), std::move(pack));
}) {
}