fixing double destruction of CSL::data_type
authorXin Liu <xliu@fb.com>
Fri, 26 Oct 2012 18:44:41 +0000 (11:44 -0700)
committerJordan DeLong <jdelong@fb.com>
Mon, 29 Oct 2012 23:32:30 +0000 (16:32 -0700)
Summary:
the currently code calls both ~SkipListNode() and node->data_.~value_type()
causes double destructing the object.

Test Plan: adding dihde's testing code to a test case

Reviewed By: emailweixu@fb.com

FB internal diff: D612289

folly/ConcurrentSkipList-inl.h
folly/ConcurrentSkipList.h
folly/test/ConcurrentSkipListTest.cpp

index 78be7243186dc7d0147918c545a26c46178047ea..750949ad24a695f43a2c35f43b01b05c42085329 100644 (file)
@@ -42,40 +42,28 @@ class SkipListNode : boost::noncopyable {
  public:
   typedef T value_type;
 
-  static SkipListNode* create(int height,
-      const value_type& data, bool isHead = false) {
+  template<typename U,
+    typename=typename std::enable_if<std::is_convertible<U, T>::value>::type>
+  static SkipListNode* create(int height, U&& data, bool isHead = false) {
     DCHECK(height >= 1 && height < 64) << height;
 
-    size_t size = sizeof(SkipListNode) + height * sizeof(SkipListNode*);
+    size_t size = sizeof(SkipListNode) +
+      height * sizeof(std::atomic<SkipListNode*>);
     auto* node = static_cast<SkipListNode*>(malloc(size));
-    new (node) SkipListNode(height);
-
-    node->spinLock_.init();
-    node->setFlags(0);
-
-    if (isHead) {
-      node->setIsHeadNode();
-    } else {
-      new (&(node->data_)) value_type(data);
-    }
+    // do placement new
+    new (node) SkipListNode(height, std::forward<U>(data), isHead);
     return node;
   }
 
   static void destroy(SkipListNode* node) {
-    if (!node->isHeadNode()) {
-      node->data_.~value_type();
-    }
     node->~SkipListNode();
     free(node);
   }
 
-  // assuming lock acquired
-  SkipListNode* promoteFrom(const SkipListNode* node) {
+  // copy the head node to a new head node assuming lock acquired
+  SkipListNode* copyHead(SkipListNode* node) {
     DCHECK(node != nullptr && height_ > node->height_);
     setFlags(node->getFlags());
-    if (!isHeadNode()) {
-      new (&(data_)) value_type(node->data());
-    }
     for (int i = 0; i < node->height_; ++i) {
       setSkip(i, node->skip(i));
     }
@@ -125,14 +113,22 @@ class SkipListNode : boost::noncopyable {
   }
 
  private:
-  ~SkipListNode() {
+  // Note! this can only be called from create() as a placement new.
+  template<typename U>
+  SkipListNode(uint8_t height, U&& data, bool isHead) :
+      height_(height), data_(std::forward<U>(data)) {
+    spinLock_.init();
+    setFlags(0);
+    if (isHead) setIsHeadNode();
+    // need to explicitly init the dynamic atomic pointer array
     for (uint8_t i = 0; i < height_; ++i) {
-      skip_[i].~atomic();
+      new (&skip_[i]) std::atomic<SkipListNode*>(nullptr);
     }
   }
-  explicit SkipListNode(uint8_t height) : height_(height) {
+
+  ~SkipListNode() {
     for (uint8_t i = 0; i < height_; ++i) {
-      new (&skip_[i]) std::atomic<SkipListNode*>(nullptr);
+      skip_[i].~atomic();
     }
   }
 
index 3299d75c7ba1ba38c78fe1bf89019b01ef13113c..b3a6be251af01947ad57e1ef0bffd846ed5c05dd 100644 (file)
@@ -358,7 +358,8 @@ class ConcurrentSkipList {
   //     list with the same key.
   //   pair.second stores whether the data is added successfully:
   //     0 means not added, otherwise reutrns the new size.
-  std::pair<NodeType*, size_t> addOrGetData(const value_type &data) {
+  template<typename U>
+  std::pair<NodeType*, size_t> addOrGetData(U &&data) {
     NodeType *preds[MAX_HEIGHT], *succs[MAX_HEIGHT];
     NodeType *newNode;
     size_t newSize;
@@ -387,7 +388,7 @@ class ConcurrentSkipList {
       }
 
       // locks acquired and all valid, need to modify the links under the locks.
-      newNode = NodeType::create(nodeHeight, data);
+      newNode = NodeType::create(nodeHeight, std::forward<U>(data));
       for (int layer = 0; layer < nodeHeight; ++layer) {
         newNode->setSkip(layer, succs[layer]);
         preds[layer]->setSkip(layer, newNode);
@@ -553,7 +554,7 @@ class ConcurrentSkipList {
     { // need to guard the head node in case others are adding/removing
       // nodes linked to the head.
       ScopedLocker g = oldHead->acquireGuard();
-      newHead->promoteFrom(oldHead);
+      newHead->copyHead(oldHead);
       NodeType* expected = oldHead;
       if (!head_.compare_exchange_strong(expected, newHead,
           std::memory_order_release)) {
@@ -650,8 +651,10 @@ class ConcurrentSkipList<T, Comp, MAX_HEIGHT>::Accessor {
   const_iterator cbegin() const { return begin(); }
   const_iterator cend() const { return end(); }
 
-  std::pair<iterator, bool> insert(const key_type &data) {
-    auto ret = sl_->addOrGetData(data);
+  template<typename U,
+    typename=typename std::enable_if<std::is_convertible<U, T>::value>::type>
+  std::pair<iterator, bool> insert(U&& data) {
+    auto ret = sl_->addOrGetData(std::forward<U>(data));
     return std::make_pair(iterator(ret.first), ret.second);
   }
   size_t erase(const key_type &data) { return remove(data); }
index c7bb99772059e422d41eaac53aa76b46f5b03e35..bc9f2bd71e793c79ef64dce1e0dbd72db6c135ca 100644 (file)
@@ -16,6 +16,7 @@
 
 // @author: Xin Liu <xliux@fb.com>
 
+#include <memory>
 #include <set>
 #include <vector>
 #include <thread>
@@ -207,6 +208,55 @@ TEST(ConcurrentSkipList, SequentialAccess) {
 
 }
 
+static std::string makeRandomeString(int len) {
+  std::string s;
+  for (int j = 0; j < len; j++) {
+    s.push_back((rand() % 26) + 'A');
+  }
+  return s;
+}
+
+TEST(ConcurrentSkipList, TestStringType) {
+  typedef folly::ConcurrentSkipList<std::string> SkipListT;
+  boost::shared_ptr<SkipListT> skip = SkipListT::createInstance();
+  SkipListT::Accessor accessor(skip);
+  {
+    for (int i = 0; i < 100000; i++) {
+      std::string s = makeRandomeString(7);
+      accessor.insert(s);
+    }
+  }
+  EXPECT_TRUE(std::is_sorted(accessor.begin(), accessor.end()));
+}
+
+struct UniquePtrComp {
+  bool operator ()(
+      const std::unique_ptr<int> &x, const std::unique_ptr<int> &y) const {
+    if (!x) return false;
+    if (!y) return true;
+    return *x < *y;
+  }
+};
+
+TEST(ConcurrentSkipList, TestMovableData) {
+  typedef folly::ConcurrentSkipList<std::unique_ptr<int>, UniquePtrComp>
+    SkipListT;
+  auto sl = SkipListT::createInstance() ;
+  SkipListT::Accessor accessor(sl);
+
+  static const int N = 10;
+  for (int i = 0; i < N; ++i) {
+    accessor.insert(std::unique_ptr<int>(new int(i)));
+  }
+
+  for (int i = 0; i < N; ++i) {
+    EXPECT_TRUE(accessor.find(std::unique_ptr<int>(new int(i))) !=
+        accessor.end());
+  }
+  EXPECT_TRUE(accessor.find(std::unique_ptr<int>(new int(N))) ==
+      accessor.end());
+}
+
 void testConcurrentAdd(int numThreads) {
   auto skipList(SkipListType::create(kHeadHeight));