From 7bf1835488a101938eb7ec336a3cbeb05384952b Mon Sep 17 00:00:00 2001 From: Yedidya Feldblum Date: Thu, 18 Feb 2016 23:10:20 -0800 Subject: [PATCH] Indestructible Summary:[Folly] `Indestructible`. For when you need a Meyers singleton that will never be destructed, even at program exit. Good for large data structures with many heap allocations, as long as they have no behavior. No point in ever destructing them. HT: Proxygen, and Orvid. Reviewed By: andriigrynenko Differential Revision: D2947959 fb-gh-sync-id: 7dd1f725edf137ba81fbf4032a0819fd7c627261 shipit-source-id: 7dd1f725edf137ba81fbf4032a0819fd7c627261 --- folly/Indestructible.h | 121 ++++++++++++++++++++++++++++++ folly/Makefile.am | 1 + folly/test/IndestructibleTest.cpp | 101 +++++++++++++++++++++++++ folly/test/Makefile.am | 4 + 4 files changed, 227 insertions(+) create mode 100644 folly/Indestructible.h create mode 100644 folly/test/IndestructibleTest.cpp diff --git a/folly/Indestructible.h b/folly/Indestructible.h new file mode 100644 index 00000000..4e01ab86 --- /dev/null +++ b/folly/Indestructible.h @@ -0,0 +1,121 @@ +/* + * Copyright 2016 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +namespace folly { + +/*** + * Indestructible + * + * When you need a Meyers singleton that will not get destructed, even at + * shutdown, and you also want the object stored inline. + * + * Use like: + * + * void doSomethingWithExpensiveData(); + * + * void doSomethingWithExpensiveData() { + * static const Indestructible> data{ + * map{{"key1", 17}, {"key2", 19}, {"key3", 23}}, + * }; + * callSomethingTakingAMapByRef(*data); + * } + * + * This should be used only for Meyers singletons, and, even then, only when + * the instance does not need to be destructed ever. + * + * This should not be used more generally, e.g., as member fields, etc. + * + * This is designed as an alternative, but with one fewer allocation at + * construction time and one fewer pointer dereference at access time, to the + * Meyers singleton pattern of: + * + * void doSomethingWithExpensiveData() { + * static const auto data = // never `delete`d + * new map{{"key1", 17}, {"key2", 19}, {"key3", 23}}; + * callSomethingTakingAMapByRef(*data); + * } + */ + +template +class Indestructible final { + + public: + template + explicit constexpr Indestructible(Args&&... args) noexcept( + std::is_nothrow_constructible::value) + : storage_(std::forward(args)...), inited_(true) {} + + ~Indestructible() = default; + + Indestructible(Indestructible const&) = delete; + Indestructible& operator=(Indestructible const&) = delete; + + Indestructible(Indestructible&& other) noexcept( + std::is_nothrow_move_constructible::value) + : storage_(std::move(other.storage_.value)) { + other.inited_ = false; + } + Indestructible& operator=(Indestructible&& other) noexcept( + std::is_nothrow_move_assignable::value) { + storage_.value = std::move(other.storage_.value); + other.inited_ = false; + } + + T* get() { + check(); + return &storage_.value; + } + T const* get() const { + check(); + return &storage_.value; + } + T& operator*() { return *get(); } + T const& operator*() const { return *get(); } + T* operator->() { return get(); } + T const* operator->() const { return get(); } + + private: + void check() const { + if (UNLIKELY(!inited_)) { + fail(); + } + } + + FOLLY_NORETURN FOLLY_NOINLINE static void fail() { + LOG(FATAL) << "Indestructible is not initialized"; + } + + union Storage { + T value; + + template + explicit constexpr Storage(Args&&... args) + : value(std::forward(args)...) {} + + ~Storage() {} + }; + + Storage storage_; + bool inited_{false}; +}; +} diff --git a/folly/Makefile.am b/folly/Makefile.am index ddef486a..c70b9d2a 100644 --- a/folly/Makefile.am +++ b/folly/Makefile.am @@ -195,6 +195,7 @@ nobase_follyinclude_HEADERS = \ IPAddressV4.h \ IPAddressV6.h \ IPAddressException.h \ + Indestructible.h \ IndexedMemPool.h \ init/Init.h \ IntrusiveList.h \ diff --git a/folly/test/IndestructibleTest.cpp b/folly/test/IndestructibleTest.cpp new file mode 100644 index 00000000..6733138d --- /dev/null +++ b/folly/test/IndestructibleTest.cpp @@ -0,0 +1,101 @@ +/* + * Copyright 2016 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace folly; + +namespace { + +struct Magic { + function dtor_; + function move_; + Magic(function ctor, function dtor, function move) + : dtor_(std::move(dtor)), move_(std::move(move)) { + ctor(); + } + Magic(Magic&& other) /* may throw */ { *this = std::move(other); } + Magic& operator=(Magic&& other) { + dtor_ = std::move(other.dtor_); + move_ = std::move(other.move_); + move_(); + return *this; + } + ~Magic() { dtor_(); } +}; + +class IndestructibleTest : public testing::Test {}; +} + +TEST_F(IndestructibleTest, access) { + const Indestructible> data{ + map{{"key1", 17}, {"key2", 19}, {"key3", 23}}}; + + auto& m = *data; + EXPECT_EQ(19, m.at("key2")); +} + +TEST_F(IndestructibleTest, no_destruction) { + int state = 0; + int value = 0; + + auto sing = make_unique>( + [&] { + ++state; + value = 7; + }, + [&] { state = -1; }, + [] {}); + EXPECT_EQ(1, state); + EXPECT_EQ(7, value); + + sing = nullptr; + EXPECT_EQ(1, state); +} + +TEST_F(IndestructibleTest, move) { + int state = 0; + int value = 0; + int moves = 0; + + Indestructible sing( // move assignment + [&] { + ++state; + value = 7; + }, + [&] { state = -1; }, + [&] { ++moves; }); + + EXPECT_EQ(1, state); + EXPECT_EQ(7, value); + EXPECT_EQ(0, moves); + + Indestructible move_ctor(std::move(sing)); // move constructor + EXPECT_EQ(1, state); + EXPECT_EQ(1, moves); + + Indestructible move_assign = std::move(move_ctor); // move assignment + EXPECT_EQ(1, state); + EXPECT_EQ(2, moves); +} diff --git a/folly/test/Makefile.am b/folly/test/Makefile.am index deb2e69f..ba6b3a5e 100644 --- a/folly/test/Makefile.am +++ b/folly/test/Makefile.am @@ -203,6 +203,10 @@ thread_name_test_SOURCES = ThreadNameTest.cpp thread_name_test_LDADD = libfollytestmain.la TESTS += thread_name_test +indestructible_test_SOURCES = IndestructibleTest.cpp +indestructible_test_LDADD = libfollytestmain.la +TESTS += indestructible_test + futures_test_SOURCES = \ ../futures/test/CollectTest.cpp \ -- 2.34.1