Indestructible
authorYedidya Feldblum <yfeldblum@fb.com>
Fri, 19 Feb 2016 07:10:20 +0000 (23:10 -0800)
committerfacebook-github-bot-0 <folly-bot@fb.com>
Fri, 19 Feb 2016 08:20:25 +0000 (00:20 -0800)
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 [new file with mode: 0644]
folly/Makefile.am
folly/test/IndestructibleTest.cpp [new file with mode: 0644]
folly/test/Makefile.am

diff --git a/folly/Indestructible.h b/folly/Indestructible.h
new file mode 100644 (file)
index 0000000..4e01ab8
--- /dev/null
@@ -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 <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};
+};
+}
index ddef486a9a476f3ff9d6af2a6995e4eda02d6c43..c70b9d2a152a00e832dd8bcc102ced686ae3e31b 100644 (file)
@@ -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 (file)
index 0000000..6733138
--- /dev/null
@@ -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 <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);
+}
index deb2e69faffa432734c0d323aa28515a5df7a5bd..ba6b3a5e51ce6762769d73ef9d2e415da44267a9 100644 (file)
@@ -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 \