Transfer ownership from a unique_ptr to a ThreadLocalPtr
authorChad Parry <cparry@fb.com>
Thu, 12 May 2016 21:47:19 +0000 (14:47 -0700)
committerFacebook Github Bot 8 <facebook-github-bot-8-bot@fb.com>
Thu, 12 May 2016 21:53:33 +0000 (14:53 -0700)
Summary:
This `ThreadLocalPtr::reset` overload will accept a `unique_ptr`. It's actually not totally exception safe, simply because `ElementWrapper::set` is not exception safe. The best I can say is that my additional code is exactly as safe as the underlying implemenation.

liketolivedangerously

Reviewed By: ericniebler

Differential Revision: D3271563

fbshipit-source-id: 774bcf31924b1ed4e29a6cb1c0a36ad710ab6034

folly/ThreadLocal.h
folly/test/ThreadLocalTest.cpp

index d1bc430c92e820c0e6262b155655c3fa2f3c3627..b47742072a65a9c837872edf602b8fe4989b9137 100644 (file)
@@ -193,6 +193,34 @@ class ThreadLocalPtr {
     return get() != nullptr;
   }
 
+  /**
+   * reset() that transfers ownership from a smart pointer
+   */
+  template <
+      typename SourceT,
+      typename Deleter,
+      typename = typename std::enable_if<
+          std::is_convertible<SourceT*, T*>::value>::type>
+  void reset(std::unique_ptr<SourceT, Deleter> source) {
+    auto deleter = [delegate = source.get_deleter()](
+        T * ptr, TLPDestructionMode) {
+      delegate(ptr);
+    };
+    reset(source.release(), deleter);
+  }
+
+  /**
+   * reset() that transfers ownership from a smart pointer with the default
+   * deleter
+   */
+  template <
+      typename SourceT,
+      typename = typename std::enable_if<
+          std::is_convertible<SourceT*, T*>::value>::type>
+  void reset(std::unique_ptr<SourceT> source) {
+    reset(source.release());
+  }
+
   /**
    * reset() with a custom deleter:
    * deleter(T* ptr, TLPDestructionMode mode)
index 6c2544a9325f05818eded3ddc1511b8ca910f725..f9cd3f3f1404ba362fcdf9438dc76e2bd7d27fd4 100644 (file)
@@ -36,6 +36,7 @@
 #include <gtest/gtest.h>
 
 #include <folly/Baton.h>
+#include <folly/Memory.h>
 #include <folly/experimental/io/FsUtil.h>
 
 using namespace folly;
@@ -48,7 +49,7 @@ struct Widget {
   }
 
   static void customDeleter(Widget* w, TLPDestructionMode mode) {
-    totalVal_ += (mode == TLPDestructionMode::ALL_THREADS) * 1000;
+    totalVal_ += (mode == TLPDestructionMode::ALL_THREADS) ? 1000 : 1;
     delete w;
   }
 };
@@ -72,6 +73,37 @@ TEST(ThreadLocalPtr, CustomDeleter1) {
         w.reset(new Widget(), Widget::customDeleter);
         w.get()->val_ += 10;
       }).join();
+    EXPECT_EQ(11, Widget::totalVal_);
+  }
+  EXPECT_EQ(11, Widget::totalVal_);
+}
+
+TEST(ThreadLocalPtr, CustomDeleterOwnershipTransfer) {
+  Widget::totalVal_ = 0;
+  {
+    ThreadLocalPtr<Widget> w;
+    auto deleter = [](Widget* ptr) {
+      Widget::customDeleter(ptr, TLPDestructionMode::THIS_THREAD);
+    };
+    std::unique_ptr<Widget, typeof(deleter)> source(new Widget(), deleter);
+    std::thread([&w, &source]() {
+      w.reset(std::move(source));
+      w.get()->val_ += 10;
+    }).join();
+    EXPECT_EQ(11, Widget::totalVal_);
+  }
+  EXPECT_EQ(11, Widget::totalVal_);
+}
+
+TEST(ThreadLocalPtr, DefaultDeleterOwnershipTransfer) {
+  Widget::totalVal_ = 0;
+  {
+    ThreadLocalPtr<Widget> w;
+    auto source = folly::make_unique<Widget>();
+    std::thread([&w, &source]() {
+      w.reset(std::move(source));
+      w.get()->val_ += 10;
+    }).join();
     EXPECT_EQ(10, Widget::totalVal_);
   }
   EXPECT_EQ(10, Widget::totalVal_);