summaryrefslogtreecommitdiff
path: root/src/mongo/stdx
diff options
context:
space:
mode:
authorADAM David Alan Martin <adam.martin@10gen.com>2019-08-22 23:30:37 -0400
committerADAM David Alan Martin <adam.martin@10gen.com>2019-08-23 09:46:34 -0400
commit088f6faa006b8414fe5b7197a2ae18c3cc0efdc9 (patch)
tree9c01b18ce2371b056f655217c20da990e080e4a5 /src/mongo/stdx
parent2fd30c80a014077844e6a4ddb926e3ebef4868a0 (diff)
downloadmongo-088f6faa006b8414fe5b7197a2ae18c3cc0efdc9.tar.gz
SERVER-25240 Make `stdx::set_terminate` which works on windows.
The `stdx::set_terminate` primitive, on windows, wraps the per-thread terminate handler and emulates a single global terminate handler.
Diffstat (limited to 'src/mongo/stdx')
-rw-r--r--src/mongo/stdx/SConscript63
-rw-r--r--src/mongo/stdx/exception.h70
-rw-r--r--src/mongo/stdx/set_terminate_dispatch_test.cpp59
-rw-r--r--src/mongo/stdx/set_terminate_from_main_die_in_thread_test.cpp63
-rw-r--r--src/mongo/stdx/set_terminate_from_thread_die_in_main_test.cpp63
-rw-r--r--src/mongo/stdx/set_terminate_from_thread_die_in_thread_test.cpp67
-rw-r--r--src/mongo/stdx/set_terminate_internals.cpp95
-rw-r--r--src/mongo/stdx/thread.h55
8 files changed, 514 insertions, 21 deletions
diff --git a/src/mongo/stdx/SConscript b/src/mongo/stdx/SConscript
index f954fdf8398..bd253c826c7 100644
--- a/src/mongo/stdx/SConscript
+++ b/src/mongo/stdx/SConscript
@@ -13,6 +13,17 @@ env.Benchmark(
],
)
+env.Library(
+ target='stdx',
+ source=[
+ 'set_terminate_internals.cpp',
+ ],
+ LIBDEPS=[
+ # Ideally, there should be no linking dependencies upon any other libraries, for `libstdx`.
+ # This library is a shim filling in for deficiencies in various standard library
+ # implementations. There should never be any link-time dependencies into mongo internals.
+ ],
+)
env.CppUnitTest(
target='stdx_test',
@@ -23,3 +34,55 @@ env.CppUnitTest(
'$BUILD_DIR/third_party/shim_abseil',
],
)
+
+# The tests for `stdx::set_terminate` need to run outside of the mongo unittest harneses.
+# The tests require altering the global `set_terminate` handler, which our unittest framework
+# doesn't expect to have happen. Further, the tests have to return successfully from a
+# terminate condition which interacts poorly with the unittest framework.
+#
+# A set of dedicated binaries to each test case is actually the simplest way to accomplish
+# robust testing of this mechanism.
+
+# Needs to be a different test -- It has to have direct control over the `main()` entry point.
+env.RegisterUnitTest(env.Program(
+ target='set_terminate_dispatch_test',
+ source=[
+ 'set_terminate_dispatch_test.cpp',
+ ],
+ LIBDEPS=[
+ 'stdx',
+ ]
+)[0])
+
+# Needs to be a different test -- It has to have direct control over the `main()` entry point.
+env.RegisterUnitTest(env.Program(
+ target='set_terminate_from_main_die_in_thread_test',
+ source=[
+ 'set_terminate_from_main_die_in_thread_test.cpp',
+ ],
+ LIBDEPS=[
+ 'stdx',
+ ]
+)[0])
+
+# Needs to be a different test -- It has to have direct control over the `main()` entry point.
+env.RegisterUnitTest(env.Program(
+ target='set_terminate_from_thread_die_in_main_test',
+ source=[
+ 'set_terminate_from_thread_die_in_main_test.cpp',
+ ],
+ LIBDEPS=[
+ 'stdx',
+ ]
+)[0])
+
+# Needs to be a different test -- It has to have direct control over the `main()` entry point.
+env.RegisterUnitTest(env.Program(
+ target='set_terminate_from_thread_die_in_thread_test',
+ source=[
+ 'set_terminate_from_thread_die_in_thread_test.cpp',
+ ],
+ LIBDEPS=[
+ 'stdx',
+ ]
+)[0])
diff --git a/src/mongo/stdx/exception.h b/src/mongo/stdx/exception.h
new file mode 100644
index 00000000000..9755cf131b6
--- /dev/null
+++ b/src/mongo/stdx/exception.h
@@ -0,0 +1,70 @@
+/**
+ * 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.
+ */
+
+#pragma once
+
+#include <atomic>
+#include <exception>
+#include <utility>
+
+// This file provides a wrapper over the function registered by `std::set_terminate`. This
+// facilitates making `stdx::set_terminate` work correctly on windows. In windows,
+// `std::set_terminate` works on a per-thread basis. Our `stdx::thread` header registers our
+// handler using the `stdx::terminate_detail::TerminateHandlerInterface::dispatch` as an entry point
+// for `std::set_terminate` when a thread starts on windows. `stdx::set_terminate` sets the handler
+// globally for all threads. Our wrapper, which is registered with each thread, calls the global
+// handler.
+//
+// NOTE: Our wrapper is not initialization order safe. It is not safe to set the terminate handler
+// until main has started.
+
+namespace mongo::stdx {
+
+// In order to grant `mongo::stdx::thread` access to the dispatch method, we need to know this
+// class's name. A forward-decl header would be overkill for this singular special case.
+class thread;
+
+// This must be the same as the definition in standard. Do not alter this alias.
+using ::std::terminate_handler;
+
+#if defined(_WIN32)
+class TerminateHandlerDetailsInterface {
+ friend ::mongo::stdx::thread;
+ static void dispatch() noexcept;
+};
+
+terminate_handler set_terminate(const terminate_handler handler) noexcept;
+
+terminate_handler get_terminate() noexcept;
+
+#else
+using ::std::get_terminate; // NOLINT
+using ::std::set_terminate; // NOLINT
+#endif
+} // namespace mongo::stdx
diff --git a/src/mongo/stdx/set_terminate_dispatch_test.cpp b/src/mongo/stdx/set_terminate_dispatch_test.cpp
new file mode 100644
index 00000000000..b796fc257a3
--- /dev/null
+++ b/src/mongo/stdx/set_terminate_dispatch_test.cpp
@@ -0,0 +1,59 @@
+/**
+ * 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/stdx/exception.h"
+
+#include <stdlib.h>
+
+#include <iostream>
+
+#include "mongo/stdx/thread.h"
+
+namespace {
+
+namespace stdx = ::mongo::stdx;
+
+void writeFeedbackAndCleanlyExit() {
+ std::cout << "Entered terminate handler." << std::endl;
+ exit(EXIT_SUCCESS);
+}
+
+void testTerminateDispatch() {
+ std::cout << "Setting terminate handler" << std::endl;
+ stdx::set_terminate(writeFeedbackAndCleanlyExit);
+ std::cout << "Calling terminate." << std::endl;
+ std::terminate();
+ exit(EXIT_FAILURE);
+}
+} // namespace
+
+int main() {
+ testTerminateDispatch();
+ return EXIT_FAILURE;
+}
diff --git a/src/mongo/stdx/set_terminate_from_main_die_in_thread_test.cpp b/src/mongo/stdx/set_terminate_from_main_die_in_thread_test.cpp
new file mode 100644
index 00000000000..f887039e95c
--- /dev/null
+++ b/src/mongo/stdx/set_terminate_from_main_die_in_thread_test.cpp
@@ -0,0 +1,63 @@
+/**
+ * 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/stdx/exception.h"
+
+#include <stdlib.h>
+
+#include <iostream>
+
+#include "mongo/stdx/thread.h"
+
+namespace {
+
+namespace stdx = ::mongo::stdx;
+
+void writeFeedbackAndCleanlyExit() {
+ std::cout << "Entered terminate handler." << std::endl;
+ exit(EXIT_SUCCESS);
+}
+
+void testTerminateDispatch() {
+ std::cout << "Setting terminate handler" << std::endl;
+ stdx::set_terminate(writeFeedbackAndCleanlyExit);
+ std::cout << "Starting background thread (which will terminate)." << std::endl;
+ stdx::thread{[] {
+ std::cout << "Calling terminate from background thread." << std::endl;
+ std::terminate();
+ }}
+ .join();
+ exit(EXIT_FAILURE);
+}
+} // namespace
+
+int main() {
+ testTerminateDispatch();
+ return EXIT_FAILURE;
+}
diff --git a/src/mongo/stdx/set_terminate_from_thread_die_in_main_test.cpp b/src/mongo/stdx/set_terminate_from_thread_die_in_main_test.cpp
new file mode 100644
index 00000000000..54a043073ce
--- /dev/null
+++ b/src/mongo/stdx/set_terminate_from_thread_die_in_main_test.cpp
@@ -0,0 +1,63 @@
+/**
+ * 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/stdx/exception.h"
+
+#include <stdlib.h>
+
+#include <iostream>
+
+#include "mongo/stdx/thread.h"
+
+namespace {
+
+namespace stdx = ::mongo::stdx;
+
+void writeFeedbackAndCleanlyExit() {
+ std::cout << "Entered terminate handler." << std::endl;
+ exit(EXIT_SUCCESS);
+}
+
+void testTerminateDispatch() {
+ std::cout << "Starting background thread (which will call `set_terminate`)." << std::endl;
+ stdx::thread{[] {
+ std::cout << "Setting terminate handler" << std::endl;
+ stdx::set_terminate(writeFeedbackAndCleanlyExit);
+ }}
+ .join();
+ std::cout << "Calling terminate." << std::endl;
+ std::terminate();
+ exit(EXIT_FAILURE);
+}
+} // namespace
+
+int main() {
+ testTerminateDispatch();
+ return EXIT_FAILURE;
+}
diff --git a/src/mongo/stdx/set_terminate_from_thread_die_in_thread_test.cpp b/src/mongo/stdx/set_terminate_from_thread_die_in_thread_test.cpp
new file mode 100644
index 00000000000..119d5b44c5d
--- /dev/null
+++ b/src/mongo/stdx/set_terminate_from_thread_die_in_thread_test.cpp
@@ -0,0 +1,67 @@
+/**
+ * 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/stdx/exception.h"
+
+#include <stdlib.h>
+
+#include <iostream>
+
+#include "mongo/stdx/thread.h"
+
+namespace {
+
+namespace stdx = ::mongo::stdx;
+
+void writeFeedbackAndCleanlyExit() {
+ std::cout << "Entered terminate handler." << std::endl;
+ exit(EXIT_SUCCESS);
+}
+
+void testTerminateDispatch() {
+ std::cout << "Starting background thread (which will call `set_terminate`)." << std::endl;
+ stdx::thread{[] {
+ std::cout << "Setting terminate handler" << std::endl;
+ stdx::set_terminate(writeFeedbackAndCleanlyExit);
+ }}
+ .join();
+ std::cout << "Starting background thread (which will terminate)." << std::endl;
+ stdx::thread{[] {
+ std::cout << "Calling terminate from background thread." << std::endl;
+ std::terminate();
+ }}
+ .join();
+ exit(EXIT_FAILURE);
+}
+} // namespace
+
+int main() {
+ testTerminateDispatch();
+ return EXIT_FAILURE;
+}
diff --git a/src/mongo/stdx/set_terminate_internals.cpp b/src/mongo/stdx/set_terminate_internals.cpp
new file mode 100644
index 00000000000..68e709388b2
--- /dev/null
+++ b/src/mongo/stdx/set_terminate_internals.cpp
@@ -0,0 +1,95 @@
+/**
+ * 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/stdx/exception.h"
+
+#include <atomic>
+#include <utility>
+
+#if defined(_WIN32)
+
+namespace mongo {
+namespace stdx {
+// `dispatch_impl` is circularly dependent with the initialization of `terminationHandler`, but
+// should not have linkage. To facilitate matching the definition to the declaration, we make this
+// function `static`, rather than placing it in the anonymous namespace.
+[[noreturn]] static void dispatch_impl() noexcept;
+
+namespace {
+void uninitializedTerminateHandler() {}
+
+::std::atomic<terminate_handler> terminationHandler(&uninitializedTerminateHandler); // NOLINT
+
+void registerTerminationHook() noexcept {
+ const auto oldHandler =
+ terminationHandler.exchange(::std::set_terminate(&dispatch_impl)); // NOLINT
+ if (oldHandler != &uninitializedTerminateHandler)
+ std::abort(); // Someone set the handler, somehow before we got to initialize ourselves.
+}
+
+
+[[maybe_unused]] const int initializeTerminationHandler = []() noexcept {
+ registerTerminationHook();
+ return 0;
+}
+();
+
+} // namespace
+} // namespace stdx
+
+stdx::terminate_handler stdx::set_terminate(const stdx::terminate_handler handler) noexcept {
+ const auto oldHandler = terminationHandler.exchange(handler);
+ if (oldHandler == &uninitializedTerminateHandler)
+ std::abort(); // Do not let people set terminate before the initializer has run.
+ return oldHandler;
+}
+
+stdx::terminate_handler stdx::get_terminate() noexcept {
+ const auto currentHandler = terminationHandler.load();
+ if (currentHandler == &uninitializedTerminateHandler)
+ std::abort(); // Do not let people see the terminate handler before the initializer has
+ // run.
+ return currentHandler;
+}
+
+void stdx::dispatch_impl() noexcept {
+ if (const ::std::terminate_handler handler = terminationHandler.load())
+ handler();
+
+ // The standard says that returning from your handler is undefined. We may as well make the
+ // wrapper have stronger guarantees.
+ std::abort();
+}
+
+void stdx::TerminateHandlerDetailsInterface::dispatch() noexcept {
+ return stdx::dispatch_impl();
+}
+} // namespace mongo
+#endif
diff --git a/src/mongo/stdx/thread.h b/src/mongo/stdx/thread.h
index 2968e9dcae2..43a7cf52879 100644
--- a/src/mongo/stdx/thread.h
+++ b/src/mongo/stdx/thread.h
@@ -35,6 +35,8 @@
#include <thread>
#include <type_traits>
+#include "mongo/stdx/exception.h"
+
namespace mongo {
namespace stdx {
@@ -60,37 +62,44 @@ public:
using ::std::thread::id; // NOLINT
using ::std::thread::native_handle_type; // NOLINT
- thread() noexcept : ::std::thread::thread() {} // NOLINT
+ thread() noexcept = default;
+ ~thread() noexcept = default;
thread(const thread&) = delete;
-
- thread(thread&& other) noexcept
- : ::std::thread::thread(static_cast<::std::thread&&>(std::move(other))) {} // NOLINT
+ thread(thread&& other) noexcept = default;
+ thread& operator=(const thread&) = delete;
+ thread& operator=(thread&& other) noexcept = default;
/**
* As of C++14, the Function overload for std::thread requires that this constructor only
* participate in overload resolution if std::decay_t<Function> is not the same type as thread.
* That prevents this overload from intercepting calls that might generate implicit conversions
* before binding to other constructors (specifically move/copy constructors).
+ *
+ * NOTE: The `Function f` parameter must be taken by value, not reference or forwarding
+ * reference, as it is used on the far side of the thread launch, and this ctor has to properly
+ * transfer ownership to the far side's thread.
*/
- template <
- class Function,
- class... Args,
- typename std::enable_if<!std::is_same<thread, typename std::decay<Function>::type>::value,
- int>::type = 0>
- explicit thread(Function&& f, Args&&... args) try:
- ::std::thread::thread(std::forward<Function>(f), std::forward<Args>(args)...) {} // NOLINT
- catch (...) {
- std::terminate();
+ template <class Function,
+ class... Args,
+ std::enable_if_t<!std::is_same_v<thread, std::decay_t<Function>>, int> = 0>
+ explicit thread(Function f, Args&&... args) noexcept
+ : ::std::thread::thread( // NOLINT
+ [
+ f = std::move(f),
+ pack = std::make_tuple(std::forward<Args>(args)...)
+ ]() mutable noexcept {
+#if defined(_WIN32)
+ // On Win32 we have to set the terminate handler per thread.
+ // We set it to our universal terminate handler, which people can register via the
+ // `stdx::set_terminate` hook.
+ ::std::set_terminate( // NOLINT
+ ::mongo::stdx::TerminateHandlerDetailsInterface::dispatch);
+#endif
+ return std::apply(std::move(f), std::move(pack));
+ }) {
}
- thread& operator=(const thread&) = delete;
-
- thread& operator=(thread&& other) noexcept {
- return static_cast<thread&>(
- ::std::thread::operator=(static_cast<::std::thread&&>(std::move(other)))); // NOLINT
- };
-
using ::std::thread::get_id; // NOLINT
using ::std::thread::hardware_concurrency; // NOLINT
using ::std::thread::joinable; // NOLINT
@@ -100,10 +109,11 @@ public:
using ::std::thread::join; // NOLINT
void swap(thread& other) noexcept {
- ::std::thread::swap(static_cast<::std::thread&>(other)); // NOLINT
+ this->::std::thread::swap(other); // NOLINT
}
};
+
inline void swap(thread& lhs, thread& rhs) noexcept {
lhs.swap(rhs);
}
@@ -142,4 +152,7 @@ void sleep_until(const std::chrono::time_point<Clock, Duration>& sleep_time) {
} // namespace this_thread
} // namespace stdx
+
+static_assert(std::is_move_constructible_v<stdx::thread>);
+static_assert(std::is_move_assignable_v<stdx::thread>);
} // namespace mongo