From d638ae7a87538e30014a37ba8941f0cec51198a0 Mon Sep 17 00:00:00 2001 From: Den Raskovalov Date: Fri, 5 Feb 2016 21:31:32 -0800 Subject: [PATCH] Call destructor for non-trivial destructors if arena allocator is used in ConcurrentSkipList. Reviewed By: philippv Differential Revision: D2900534 fb-gh-sync-id: c711a160d541d937ada24f2b524016dbceb40ee2 --- folly/ConcurrentSkipList-inl.h | 4 +- folly/ConcurrentSkipList.h | 27 +++++-- folly/test/ConcurrentSkipListTest.cpp | 108 ++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 8 deletions(-) diff --git a/folly/ConcurrentSkipList-inl.h b/folly/ConcurrentSkipList-inl.h index befa0c95..48e3f63f 100644 --- a/folly/ConcurrentSkipList-inl.h +++ b/folly/ConcurrentSkipList-inl.h @@ -73,7 +73,7 @@ class SkipListNode : private boost::noncopyable { template static constexpr bool destroyIsNoOp() { return IsArenaAllocator::value && - boost::has_trivial_destructor>::value; + boost::has_trivial_destructor::value; } // copy the head node to a new head node assuming lock acquired @@ -236,6 +236,8 @@ class NodeRecycler createInstance( - int height = 1, const NodeAlloc& alloc = NodeAlloc()) { + static std::shared_ptr createInstance(int height, + const NodeAlloc& alloc) { return std::make_shared(height, alloc); } + static std::shared_ptr createInstance(int height = 1) { + return std::make_shared(height); + } + //=================================================================== // Below are implementation details. // Please see ConcurrentSkipList::Accessor for stdlib-like APIs. diff --git a/folly/test/ConcurrentSkipListTest.cpp b/folly/test/ConcurrentSkipListTest.cpp index 2a26ae36..f99b00ec 100644 --- a/folly/test/ConcurrentSkipListTest.cpp +++ b/folly/test/ConcurrentSkipListTest.cpp @@ -16,6 +16,7 @@ // @author: Xin Liu +#include #include #include #include @@ -24,15 +25,48 @@ #include #include + +#include #include #include +#include #include + #include DEFINE_int32(num_threads, 12, "num concurrent threads to test"); namespace { +template +struct ParanoidArenaAlloc { + explicit ParanoidArenaAlloc(ParentAlloc* arena) : arena_(arena) {} + + void* allocate(size_t size) { + void* result = arena_->allocate(size); + allocated_.insert(result); + return result; + } + + void deallocate(void* ptr) { + EXPECT_EQ(1, allocated_.erase(ptr)); + arena_->deallocate(ptr); + } + + bool isEmpty() const { return allocated_.empty(); } + + ParentAlloc* arena_; + std::set allocated_; +}; +} + +namespace folly { +template <> +struct IsArenaAllocator> : std::true_type {}; +} + +namespace { + using namespace folly; using std::vector; @@ -384,6 +418,80 @@ TEST(ConcurrentSkipList, ConcurrentAccess) { testConcurrentAccess(1000000, 100000, kMaxValue); } +struct NonTrivialValue { + static std::atomic InstanceCounter; + static const int kBadPayLoad; + + NonTrivialValue() : payload_(kBadPayLoad) { ++InstanceCounter; } + + explicit NonTrivialValue(int payload) : payload_(payload) { + ++InstanceCounter; + } + + NonTrivialValue(const NonTrivialValue& rhs) : payload_(rhs.payload_) { + ++InstanceCounter; + } + + NonTrivialValue& operator=(const NonTrivialValue& rhs) { + payload_ = rhs.payload_; + return *this; + } + + ~NonTrivialValue() { --InstanceCounter; } + + bool operator<(const NonTrivialValue& rhs) const { + EXPECT_NE(kBadPayLoad, payload_); + EXPECT_NE(kBadPayLoad, rhs.payload_); + return payload_ < rhs.payload_; + } + + private: + int payload_; +}; + +std::atomic NonTrivialValue::InstanceCounter(0); +const int NonTrivialValue::kBadPayLoad = 0xDEADBEEF; + +template +void TestNonTrivialDeallocation(SkipListPtrType& list) { + { + auto accessor = typename SkipListPtrType::element_type::Accessor(list); + static const size_t N = 10000; + for (size_t i = 0; i < N; ++i) { + accessor.add(NonTrivialValue(i)); + } + list.reset(); + } + EXPECT_EQ(0, NonTrivialValue::InstanceCounter); +} + +template +void NonTrivialDeallocationWithParanoid() { + using Alloc = ParanoidArenaAlloc; + using SkipListType = + ConcurrentSkipList, Alloc>; + ParentAlloc parentAlloc; + Alloc paranoidAlloc(&parentAlloc); + auto list = SkipListType::createInstance(10, paranoidAlloc); + TestNonTrivialDeallocation(list); + EXPECT_TRUE(paranoidAlloc.isEmpty()); +} + +TEST(ConcurrentSkipList, NonTrivialDeallocationWithParanoidSysAlloc) { + NonTrivialDeallocationWithParanoid(); +} + +TEST(ConcurrentSkipList, NonTrivialDeallocationWithParanoidSysArena) { + NonTrivialDeallocationWithParanoid(); +} + +TEST(ConcurrentSkipList, NonTrivialDeallocationWithSysArena) { + using SkipListType = + ConcurrentSkipList, SysArena>; + auto list = SkipListType::createInstance(10); + TestNonTrivialDeallocation(list); +} + } // namespace int main(int argc, char* argv[]) { -- 2.34.1