Commit 7bf18354 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by facebook-github-bot-0

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
parent 4d4ca9ec
/*
* 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 <utility>
#include <glog/logging.h>
#include <folly/Likely.h>
#include <folly/Portability.h>
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<map<string, int>> data{
* map<string, int>{{"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<string, int>{{"key1", 17}, {"key2", 19}, {"key3", 23}};
* callSomethingTakingAMapByRef(*data);
* }
*/
template <typename T>
class Indestructible final {
public:
template <typename... Args>
explicit constexpr Indestructible(Args&&... args) noexcept(
std::is_nothrow_constructible<T, decltype(args)...>::value)
: storage_(std::forward<Args>(args)...), inited_(true) {}
~Indestructible() = default;
Indestructible(Indestructible const&) = delete;
Indestructible& operator=(Indestructible const&) = delete;
Indestructible(Indestructible&& other) noexcept(
std::is_nothrow_move_constructible<T>::value)
: storage_(std::move(other.storage_.value)) {
other.inited_ = false;
}
Indestructible& operator=(Indestructible&& other) noexcept(
std::is_nothrow_move_assignable<T>::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 <typename... Args>
explicit constexpr Storage(Args&&... args)
: value(std::forward<Args>(args)...) {}
~Storage() {}
};
Storage storage_;
bool inited_{false};
};
}
......@@ -195,6 +195,7 @@ nobase_follyinclude_HEADERS = \
IPAddressV4.h \
IPAddressV6.h \
IPAddressException.h \
Indestructible.h \
IndexedMemPool.h \
init/Init.h \
IntrusiveList.h \
......
/*
* 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 <folly/Indestructible.h>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <gtest/gtest.h>
#include <folly/Memory.h>
using namespace std;
using namespace folly;
namespace {
struct Magic {
function<void()> dtor_;
function<void()> move_;
Magic(function<void()> ctor, function<void()> dtor, function<void()> 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<map<string, int>> data{
map<string, int>{{"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<Indestructible<Magic>>(
[&] {
++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<Magic> sing( // move assignment
[&] {
++state;
value = 7;
},
[&] { state = -1; },
[&] { ++moves; });
EXPECT_EQ(1, state);
EXPECT_EQ(7, value);
EXPECT_EQ(0, moves);
Indestructible<Magic> move_ctor(std::move(sing)); // move constructor
EXPECT_EQ(1, state);
EXPECT_EQ(1, moves);
Indestructible<Magic> move_assign = std::move(move_ctor); // move assignment
EXPECT_EQ(1, state);
EXPECT_EQ(2, moves);
}
......@@ -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 \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment