Adding a release function for ThreadLocalPtr.
authorAkshay Vaidya <akshayv@fb.com>
Fri, 9 May 2014 17:07:05 +0000 (10:07 -0700)
committerDave Watson <davejwatson@fb.com>
Tue, 20 May 2014 19:53:59 +0000 (12:53 -0700)
Summary:
ThreadLocalPtr manages the lifecycle of the object that is
stored with it. We have a use case where we sometimes want to transfer ownership
of the stored object to another thread by wrapping them with
unique_ptrs. Adding a release function, similar to to the
unique_ptr::release is the cleanest way for us to transfer ownership.

Test Plan:
I can do some on off testing using a command line tool, but I
was wondering about how to add some unit tests. Not sure when the folly
unit tests were.

Reviewed By: njormrod@fb.com

FB internal diff: D1321588

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

index bae58411713318a01a404206eb1478c40b550807..05d26bfbe1858d5e34c9a9cb6fd544b40dfc9eaa 100644 (file)
@@ -165,6 +165,13 @@ class ThreadLocalPtr {
     return *get();
   }
 
+  T* release() {
+    threadlocal_detail::ElementWrapper& w =
+      threadlocal_detail::StaticMeta<Tag>::get(id_);
+
+    return static_cast<T*>(w.release());
+  }
+
   void reset(T* newPtr = nullptr) {
     threadlocal_detail::ElementWrapper& w =
       threadlocal_detail::StaticMeta<Tag>::get(id_);
index 423b0f2ddcf386c469de200ce05a448bde53f1fc..b883861a6b4c6d773bc061d45d210f9f878fc8c0 100644 (file)
@@ -77,13 +77,19 @@ struct ElementWrapper {
     if (ptr != nullptr) {
       DCHECK(deleter != nullptr);
       deleter->dispose(ptr, mode);
-      if (ownsDeleter) {
-        delete deleter;
-      }
-      ptr = nullptr;
-      deleter = nullptr;
-      ownsDeleter = false;
+
+      cleanup();
+    }
+  }
+
+  void* release() {
+    auto retPtr = ptr;
+
+    if (ptr != nullptr) {
+      cleanup();
     }
+
+    return retPtr;
   }
 
   template <class Ptr>
@@ -114,6 +120,15 @@ struct ElementWrapper {
     }
   }
 
+  void cleanup() {
+    if (ownsDeleter) {
+      delete deleter;
+    }
+    ptr = nullptr;
+    deleter = nullptr;
+    ownsDeleter = false;
+  }
+
   void* ptr;
   DeleterBase* deleter;
   bool ownsDeleter;
index 4a25b0583c5b07a5fe32c05286ef09546011d33d..332fa80e89091d02466424f3ff657bf8ce83dcd0 100644 (file)
@@ -86,6 +86,21 @@ TEST(ThreadLocalPtr, resetNull) {
   EXPECT_FALSE(tl);
 }
 
+TEST(ThreadLocalPtr, TestRelease) {
+  Widget::totalVal_ = 0;
+  ThreadLocalPtr<Widget> w;
+  std::unique_ptr<Widget> wPtr;
+  std::thread([&w, &wPtr]() {
+      w.reset(new Widget());
+      w.get()->val_ += 10;
+
+      wPtr.reset(w.release());
+    }).join();
+  EXPECT_EQ(0, Widget::totalVal_);
+  wPtr.reset();
+  EXPECT_EQ(10, Widget::totalVal_);
+}
+
 // Test deleting the ThreadLocalPtr object
 TEST(ThreadLocalPtr, CustomDeleter2) {
   Widget::totalVal_ = 0;