diff options
author | Billy Donahue <billy.donahue@mongodb.com> | 2020-04-08 11:48:42 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-10-23 14:58:22 +0000 |
commit | 84b65f6e4cd9a143b2aaf9ae4062500d6bf59c98 (patch) | |
tree | d519ddd57efc3a42c8ff3cb7eacef1285444e22f | |
parent | 1d59792abdd3242fdcf28bea23d743a999824066 (diff) | |
download | mongo-84b65f6e4cd9a143b2aaf9ae4062500d6bf59c98.tar.gz |
SERVER-47518 StaticImmortal<T>
-rw-r--r-- | src/mongo/util/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/util/static_immortal.h | 126 | ||||
-rw-r--r-- | src/mongo/util/static_immortal_test.cpp | 109 |
3 files changed, 236 insertions, 0 deletions
diff --git a/src/mongo/util/SConscript b/src/mongo/util/SConscript index 8b2e6aee637..8e7f037cf0e 100644 --- a/src/mongo/util/SConscript +++ b/src/mongo/util/SConscript @@ -557,6 +557,7 @@ icuEnv.CppUnitTest( 'future_test_shared_future.cpp', 'hierarchical_acquisition_test.cpp', 'icu_test.cpp', + 'static_immortal_test.cpp', 'invalidating_lru_cache_test.cpp', 'itoa_test.cpp', 'latch_analyzer_test.cpp' if get_option('use-diagnostic-latches') == 'on' else [], diff --git a/src/mongo/util/static_immortal.h b/src/mongo/util/static_immortal.h new file mode 100644 index 00000000000..146e506dc00 --- /dev/null +++ b/src/mongo/util/static_immortal.h @@ -0,0 +1,126 @@ +/** + * Copyright (C) 2020-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 <type_traits> +#include <utility> + +namespace mongo { + +/** + * A helper class for defining reliable static duration variables that are safe at + * shutdown. It's also useful as a greppable identifier to emphasize, track, and + * whitelist such objects across the codebase. This is a way to make an object that + * never dies and isn't allocated. T need not be destructible, as it is never destroyed. + * + * Notice that in every example, there's a `static` storage class specifier on the + * defined variable. This is very important. + * + * The `StaticImmortal<T>` contains raw memory suitable for storing a `T`, and has a trivial + * destructor. No action is taken at shutdown time, so the on-board `T` remains undestroyed. + * + * Initialization syntax examples: + * static StaticImmortal<Map> m{}; + * static auto m = StaticImmortal<Map>(); + * + * // Explicitly specified `T`. Curly braces cannot be collapsed here. + * static StaticImmortal<Map> m{{{"hello", 123}, {"bye", 456}}}; + * static StaticImmortal<Map> m = {{{"hello", 123}, {"bye", 456}}}; + * + * // `T` can also be deduced from the initializing value: + * static StaticImmortal m = Map{{"hello", 123}, {"bye", 456}}; + * static StaticImmortal m = [] { return Map{{"hello", 123}, {"bye", 456}}; }(); + * + * // As with `optional<T>`, dereference operators `->` and `*` yield a reference to `T`. + * auto iter = m->find("hello"); + * functionTakingAMapReference(*m); + */ +template <typename T> +class StaticImmortal { +public: + using value_type = T; + + constexpr StaticImmortal() { + _construct(); + } + + constexpr StaticImmortal(const value_type& obj) { + _construct(obj); + } + + constexpr StaticImmortal(value_type&& obj) { + _construct(std::move(obj)); + } + + template <typename... A> + explicit constexpr StaticImmortal(A&&... args) { + _construct(std::forward<A>(args)...); + } + + StaticImmortal(const StaticImmortal&) = delete; + StaticImmortal(StaticImmortal&&) = delete; + + constexpr value_type& value() noexcept { + return *reinterpret_cast<value_type*>(&_storage); + } + constexpr const value_type& value() const noexcept { + return *reinterpret_cast<const value_type*>(&_storage); + } + + /** + * @{ + * Deref operators like optional. + */ + constexpr value_type& operator*() noexcept { + return value(); + } + constexpr const value_type& operator*() const noexcept { + return value(); + } + constexpr value_type* operator->() noexcept { + return &value(); + } + constexpr const value_type* operator->() const noexcept { + return &value(); + } + /** @} */ + +private: + using Storage = std::aligned_storage_t<sizeof(value_type), alignof(value_type)>; + + template <typename... A> + constexpr void _construct(A&&... args) { + new (&_storage) value_type(std::forward<A>(args)...); + } + + Storage _storage; +}; + +} // namespace mongo diff --git a/src/mongo/util/static_immortal_test.cpp b/src/mongo/util/static_immortal_test.cpp new file mode 100644 index 00000000000..90ed283de23 --- /dev/null +++ b/src/mongo/util/static_immortal_test.cpp @@ -0,0 +1,109 @@ +/** + * Copyright (C) 2020-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/static_immortal.h" + +#include <map> +#include <string> +#include <type_traits> +#include <utility> + +#include "mongo/unittest/unittest.h" + +namespace mongo { +namespace { + +using Map = std::map<std::string, int>; + +int ctors; + +struct Indestructible { + explicit Indestructible() { + ++ctors; + } + + ~Indestructible() = delete; // Compile failure if StaticImmortal tries to kill me + + void use() {} + void useConst() const {} +}; + +TEST(StaticImmortalTest, BasicConstructorAndCast) { + ctors = 0; + { + auto&& x = *StaticImmortal<Indestructible>(); + x.use(); + } + ASSERT_EQ(ctors, 1); +} + +TEST(StaticImmortalTest, PointerSyntax) { + auto&& x = StaticImmortal<Indestructible>(); + x->useConst(); + (*x).useConst(); + x->use(); + (*x).use(); + + const auto& cx = x; + cx->useConst(); + (*cx).useConst(); +} + +TEST(StaticImmortalTest, StaticDurationIdiom) { + static auto&& x = StaticImmortal<Indestructible>(); + static_assert(std::is_same_v<decltype(x), StaticImmortal<Indestructible>&&>); +} + +TEST(StaticImmortalTest, DeducedValueTypeCopyInit) { + static const StaticImmortal m = Map{{"hello", 123}, {"bye", 456}}; + ASSERT_EQ(m->find("bye")->second, 456); +} + +TEST(StaticImmortalTest, DeducedValueTypeExpression) { + static const StaticImmortal m = [] { return Map{{"hello", 123}, {"bye", 456}}; }(); + ASSERT_EQ(m->find("bye")->second, 456); +} + +TEST(StaticImmortalTest, BraceInit) { + static const StaticImmortal<Map> m{{{"hello", 123}, {"bye", 456}}}; + ASSERT_EQ(m->find("bye")->second, 456); +} + +TEST(StaticImmortalTest, ListInit) { + static const StaticImmortal<Map> m = {{{"hello", 123}, {"bye", 456}}}; + ASSERT_EQ(m->find("bye")->second, 456); +} + +TEST(StaticImmortalTest, EmptyBrace) { + static const StaticImmortal<Map> empty{}; + ASSERT_TRUE(empty->empty()); +} + +} // namespace +} // namespace mongo |