From: Maged Michael Date: Sun, 1 Oct 2017 01:11:41 +0000 (-0700) Subject: Optimize local and bulk management of hazptr_holder-s X-Git-Tag: v2017.10.02.00~1 X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=commitdiff_plain;h=bf4d53709b769785cb1f8afeca5547dd18439acf Optimize local and bulk management of hazptr_holder-s Summary: Changes: - Added hazptr_local for optimized management of local hazptr_holder-s. - Added hazptr_array for optimized management of hazptr_holder-s - Added benchmarks for hazptr_local and hazptr_array - Added tests for hazptr_local and hazptr_array - Changed SWMRList example to use hazptr_local<2> instead of two hazptr_holder-s. - Updated benchmark performance results. Reviewed By: davidtgoldblatt Differential Revision: D5833721 fbshipit-source-id: 154811f67c38abac7342cecb71f829778ccf76b2 --- diff --git a/folly/experimental/hazptr/bench/HazptrBench.h b/folly/experimental/hazptr/bench/HazptrBench.h index 22709e77..1b62bc50 100644 --- a/folly/experimental/hazptr/bench/HazptrBench.h +++ b/folly/experimental/hazptr/bench/HazptrBench.h @@ -95,8 +95,9 @@ inline uint64_t bench(std::string name, int ops, const RepFunc& repFn) { return res; } +const int ops = 1000000; + inline uint64_t listBench(std::string name, int nthreads, int size) { - int ops = 100000; auto repFn = [&] { SWMRListSet s; auto init = [&] { @@ -116,7 +117,6 @@ inline uint64_t listBench(std::string name, int nthreads, int size) { } inline uint64_t holderBench(std::string name, int nthreads) { - int ops = 100000; auto repFn = [&] { auto init = [] {}; auto fn = [&](int tid) { @@ -130,11 +130,40 @@ inline uint64_t holderBench(std::string name, int nthreads) { return bench(name, ops, repFn); } +template +inline uint64_t arrayBench(std::string name, int nthreads) { + auto repFn = [&] { + auto init = [] {}; + auto fn = [&](int tid) { + for (int j = tid; j < 10 * ops; j += nthreads) { + hazptr_array a; + } + }; + auto endFn = [] {}; + return run_once(nthreads, init, fn, endFn); + }; + return bench(name, ops, repFn); +} + +template +inline uint64_t localBench(std::string name, int nthreads) { + auto repFn = [&] { + auto init = [] {}; + auto fn = [&](int tid) { + for (int j = tid; j < 10 * ops; j += nthreads) { + hazptr_local<10> a; + } + }; + auto endFn = [] {}; + return run_once(nthreads, init, fn, endFn); + }; + return bench(name, ops, repFn); +} + inline uint64_t retireBench(std::string name, int nthreads) { struct Foo : hazptr_obj_base { int x; }; - int ops = 100000; auto repFn = [&] { auto init = [] {}; auto fn = [&](int tid) { @@ -155,10 +184,26 @@ const int sizes[] = {10, 100}; inline void benches(std::string name) { std::cout << "------------------------------------------- " << name << "\n"; for (int i : nthr) { - std::cout << i << " threads -- construct/destruct 10 hazptr_holder-s" + std::cout << i << " threads -- 10x construct/destruct hazptr_holder" << std::endl; holderBench(name + " ", i); holderBench(name + " - dup ", i); + std::cout << i << " threads -- 10x construct/destruct hazptr_array<10>" + << std::endl; + arrayBench<10>(name + " ", i); + arrayBench<10>(name + " - dup ", i); + std::cout << i << " threads -- 10x construct/destruct hazptr_array<3>" + << std::endl; + arrayBench<3>(name + " ", i); + arrayBench<3>(name + " - dup ", i); + std::cout << i << " threads -- 10x construct/destruct hazptr_local<10>" + << std::endl; + localBench<10>(name + " ", i); + localBench<10>(name + " - dup ", i); + std::cout << i << " threads -- 10x construct/destruct hazptr_local<1>" + << std::endl; + localBench<1>(name + " ", i); + localBench<1>(name + " - dup ", i); std::cout << i << " threads -- allocate/retire/reclaim object" << std::endl; retireBench(name + " ", i); retireBench(name + " - dup ", i); @@ -175,6 +220,56 @@ inline void benches(std::string name) { } // namespace folly /* +------------------------------------------- amb - tc +1 threads -- 10x construct/destruct hazptr_holder + amb - tc 49 ns 46 ns 44 ns + amb - tc - dup 47 ns 45 ns 44 ns +1 threads -- 10x construct/destruct hazptr_array<10> + amb - tc 132 ns 122 ns 117 ns + amb - tc - dup 130 ns 122 ns 117 ns +1 threads -- 10x construct/destruct hazptr_array<3> + amb - tc 66 ns 64 ns 63 ns + amb - tc - dup 64 ns 64 ns 63 ns +1 threads -- 10x construct/destruct hazptr_local<10> + amb - tc 29 ns 27 ns 27 ns + amb - tc - dup 28 ns 27 ns 27 ns +1 threads -- 10x construct/destruct hazptr_local<1> + amb - tc 27 ns 27 ns 27 ns + amb - tc - dup 28 ns 28 ns 27 ns +1 threads -- allocate/retire/reclaim object + amb - tc 65 ns 62 ns 60 ns + amb - tc - dup 65 ns 60 ns 59 ns +1 threads -- 10-item list + amb - tc 21 ns 21 ns 20 ns + amb - tc - dup 22 ns 21 ns 21 ns +1 threads -- 100-item list + amb - tc 229 ns 224 ns 220 ns + amb - tc - dup 223 ns 219 ns 216 ns +10 threads -- 10x construct/destruct hazptr_holder + amb - tc 9 ns 8 ns 7 ns + amb - tc - dup 9 ns 8 ns 8 ns +10 threads -- 10x construct/destruct hazptr_array<10> + amb - tc 27 ns 23 ns 15 ns + amb - tc - dup 26 ns 20 ns 13 ns +10 threads -- 10x construct/destruct hazptr_array<3> + amb - tc 11 ns 11 ns 7 ns + amb - tc - dup 11 ns 9 ns 7 ns +10 threads -- 10x construct/destruct hazptr_local<10> + amb - tc 5 ns 3 ns 3 ns + amb - tc - dup 3 ns 3 ns 3 ns +10 threads -- 10x construct/destruct hazptr_local<1> + amb - tc 3 ns 3 ns 3 ns + amb - tc - dup 5 ns 4 ns 3 ns +10 threads -- allocate/retire/reclaim object + amb - tc 17 ns 15 ns 14 ns + amb - tc - dup 17 ns 15 ns 14 ns +10 threads -- 10-item list + amb - tc 4 ns 4 ns 2 ns + amb - tc - dup 4 ns 4 ns 3 ns +10 threads -- 100-item list + amb - tc 33 ns 31 ns 24 ns + amb - tc - dup 33 ns 32 ns 30 ns +---------------------------------------------------------- ------------------------------------------- no amb - no tc 1 threads -- construct/destruct 10 hazptr_holder-s no amb - no tc 2518 ns 2461 ns 2431 ns @@ -252,57 +347,5 @@ no amb - tc - dup 24 ns 23 ns 21 ns 10 threads -- 100-item list no amb - tc 215 ns 208 ns 188 ns no amb - tc - dup 215 ns 209 ns 197 ns ----------------------------------------------------------- -------------------------------------------- amb - tc -1 threads -- construct/destruct 10 hazptr_holder-s - amb - tc 56 ns 54 ns 54 ns - amb - tc - dup 55 ns 54 ns 53 ns -1 threads -- allocate/retire/reclaim object - amb - tc 62 ns 61 ns 61 ns - amb - tc - dup 62 ns 61 ns 61 ns -1 threads -- 10-item list - amb - tc 36 ns 35 ns 33 ns - amb - tc - dup 37 ns 35 ns 34 ns -1 threads -- 100-item list - amb - tc 262 ns 247 ns 230 ns - amb - tc - dup 249 ns 238 ns 230 ns -10 threads -- construct/destruct 10 hazptr_holder-s - amb - tc 14 ns 12 ns 11 ns - amb - tc - dup 12 ns 11 ns 11 ns -10 threads -- allocate/retire/reclaim object - amb - tc 18 ns 17 ns 15 ns - amb - tc - dup 18 ns 17 ns 15 ns -10 threads -- 10-item list - amb - tc 9 ns 8 ns 8 ns - amb - tc - dup 8 ns 8 ns 7 ns -10 threads -- 100-item list - amb - tc 52 ns 42 ns 28 ns - amb - tc - dup 44 ns 37 ns 28 ns ----------------------------------------------------------- -------------------------------------------- one domain -1 threads -- construct/destruct 10 hazptr_holder-s - one domain 57 ns 56 ns 55 ns - one domain - dup 56 ns 54 ns 53 ns -1 threads -- allocate/retire/reclaim object - one domain 87 ns 71 ns 64 ns - one domain - dup 69 ns 68 ns 68 ns -1 threads -- 10-item list - one domain 32 ns 30 ns 29 ns - one domain - dup 31 ns 30 ns 29 ns -1 threads -- 100-item list - one domain 269 ns 238 ns 226 ns - one domain - dup 237 ns 232 ns 227 ns -10 threads -- construct/destruct 10 hazptr_holder-s - one domain 16 ns 12 ns 10 ns - one domain - dup 11 ns 10 ns 10 ns -10 threads -- allocate/retire/reclaim object - one domain 19 ns 17 ns 16 ns - one domain - dup 19 ns 17 ns 15 ns -10 threads -- 10-item list - one domain 6 ns 5 ns 5 ns - one domain - dup 6 ns 5 ns 5 ns -10 threads -- 100-item list - one domain 40 ns 39 ns 35 ns - one domain - dup 40 ns 39 ns 35 ns ---------------------------------------------------------- */ diff --git a/folly/experimental/hazptr/example/SWMRList.h b/folly/experimental/hazptr/example/SWMRList.h index 83755a63..20aa0e3b 100644 --- a/folly/experimental/hazptr/example/SWMRList.h +++ b/folly/experimental/hazptr/example/SWMRList.h @@ -99,15 +99,16 @@ class SWMRListSet { /* Used by readers */ bool contains(const T& val) const { - /* Acquire two hazard pointers for hand-over-hand traversal. */ - hazptr_holder hptr_prev; - hazptr_holder hptr_curr; + /* Two hazard pointers for hand-over-hand traversal. */ + hazptr_local<2> hptr; + hazptr_holder* hptr_prev = &hptr[0]; + hazptr_holder* hptr_curr = &hptr[1]; while (true) { auto prev = &head_; auto curr = prev->load(std::memory_order_acquire); while (true) { if (!curr) { return false; } - if (!hptr_curr.try_protect(curr, *prev)) + if (!hptr_curr->try_protect(curr, *prev)) break; auto next = curr->next_.load(std::memory_order_acquire); if (prev->load(std::memory_order_acquire) != curr) @@ -119,20 +120,9 @@ class SWMRListSet { } prev = &(curr->next_); curr = next; - /* Swap does not change the values of the owned hazard - * pointers themselves. After the swap, The hazard pointer - * owned by hptr_prev continues to protect the node that - * contains the pointer *prev. The hazard pointer owned by - * hptr_curr will continue to protect the node that contains - * the old *prev (unless the old prev was &head), which no - * longer needs protection, so hptr_curr's hazard pointer is - * now free to protect *curr in the next iteration (if curr != - * null). - */ - swap(hptr_curr, hptr_prev); + std::swap(hptr_curr, hptr_prev); } } - /* The hazard pointers are released automatically. */ } }; diff --git a/folly/experimental/hazptr/hazptr-impl.h b/folly/experimental/hazptr/hazptr-impl.h index 12c4fd39..043d04a1 100644 --- a/folly/experimental/hazptr/hazptr-impl.h +++ b/folly/experimental/hazptr/hazptr-impl.h @@ -117,17 +117,23 @@ static_assert( struct hazptr_tc { hazptr_tc_entry entry_[HAZPTR_TC_SIZE]; - int count_; + size_t count_; +#ifndef NDEBUG + bool local_; +#endif public: + hazptr_tc_entry& operator[](size_t i); hazptr_rec* get(); bool put(hazptr_rec* hprec); + size_t count(); }; static_assert( std::is_trivial::value, "hazptr_tc must be trivial to avoid a branch to check initialization"); +hazptr_tc* hazptr_tc_tls(); void hazptr_tc_init(); void hazptr_tc_shutdown(); hazptr_rec* hazptr_tc_try_get(); @@ -243,7 +249,7 @@ FOLLY_ALWAYS_INLINE hazptr_holder::hazptr_holder(hazptr_domain& domain) { if (hazptr_ == nullptr) { std::bad_alloc e; throw e; } } -FOLLY_ALWAYS_INLINE hazptr_holder::hazptr_holder(std::nullptr_t) { +FOLLY_ALWAYS_INLINE hazptr_holder::hazptr_holder(std::nullptr_t) noexcept { domain_ = nullptr; hazptr_ = nullptr; DEBUG_PRINT(this << " " << domain_ << " " << hazptr_); @@ -350,6 +356,183 @@ FOLLY_ALWAYS_INLINE void swap(hazptr_holder& lhs, hazptr_holder& rhs) noexcept { lhs.swap(rhs); } +/** + * hazptr_array + */ + +template +FOLLY_ALWAYS_INLINE hazptr_array::hazptr_array() { + auto h = reinterpret_cast(&raw_); + if (HAZPTR_TC) { + auto ptc = hazptr_tc_tls(); + if (LIKELY(ptc != nullptr)) { + auto& tc = *ptc; + auto count = tc.count(); + if (M <= count) { + size_t offset = count - M; + for (size_t i = 0; i < M; ++i) { + auto hprec = tc[offset + i].hprec_; + DCHECK(hprec != nullptr); + DEBUG_PRINT(i << " " << &h[i]); + new (&h[i]) hazptr_holder(nullptr); + h[i].hazptr_ = hprec; + DEBUG_PRINT( + i << " " << &h[i] << " " << h[i].domain_ << " " << h[i].hazptr_); + } + tc.count_ = offset; + return; + } + } + } + // slow path + for (size_t i = 0; i < M; ++i) { + new (&h[i]) hazptr_holder; + DEBUG_PRINT( + i << " " << &h[i] << " " << h[i].domain_ << " " << h[i].hazptr_); + } +} + +template +FOLLY_ALWAYS_INLINE hazptr_array::hazptr_array( + hazptr_array&& other) noexcept { + DEBUG_PRINT(this << " " << M << " " << &other); + auto h = reinterpret_cast(&raw_); + for (size_t i = 0; i < M; ++i) { + new (&h[i]) hazptr_holder(std::move(other.h_[i])); + DEBUG_PRINT(i << " " << &h[i] << " " << &other.h_[i]); + } + empty_ = other.empty_; + other.empty_ = true; +} + +template +FOLLY_ALWAYS_INLINE hazptr_array::hazptr_array(std::nullptr_t) noexcept { + DEBUG_PRINT(this << " " << M); + auto h = reinterpret_cast(&raw_); + for (size_t i = 0; i < M; ++i) { + new (&h[i]) hazptr_holder(nullptr); + DEBUG_PRINT(i << " " << &h[i]); + } + empty_ = true; +} + +template +FOLLY_ALWAYS_INLINE hazptr_array::~hazptr_array() { + if (empty_) { + return; + } + auto h = reinterpret_cast(&raw_); + if (HAZPTR_TC) { + auto ptc = hazptr_tc_tls(); + if (LIKELY(ptc != nullptr)) { + auto& tc = *ptc; + auto count = tc.count(); + if (count + M <= HAZPTR_TC_SIZE) { + for (size_t i = 0; i < M; ++i) { + tc[count + i].hprec_ = h[i].hazptr_; + DEBUG_PRINT(i << " " << &h[i]); + new (&h[i]) hazptr_holder(nullptr); + DEBUG_PRINT( + i << " " << &h[i] << " " << h[i].domain_ << " " << h[i].hazptr_); + } + tc.count_ = count + M; + return; + } + } + } + // slow path + for (size_t i = 0; i < M; ++i) { + h[i].~hazptr_holder(); + } +} + +template +FOLLY_ALWAYS_INLINE hazptr_array& hazptr_array::operator=( + hazptr_array&& other) noexcept { + DEBUG_PRINT(this << " " << M << " " << &other); + auto h = reinterpret_cast(&raw_); + for (size_t i = 0; i < M; ++i) { + h[i] = std::move(other[i]); + DEBUG_PRINT(i << " " << &h[i] << " " << &other[i]); + } + return *this; +} + +template +FOLLY_ALWAYS_INLINE hazptr_holder& hazptr_array::operator[]( + size_t i) noexcept { + auto h = reinterpret_cast(&raw_); + DCHECK(i < M); + return h[i]; +} + +/** + * hazptr_local + */ + +template +FOLLY_ALWAYS_INLINE hazptr_local::hazptr_local() { + auto h = reinterpret_cast(&raw_); + if (HAZPTR_TC) { + auto ptc = hazptr_tc_tls(); + if (LIKELY(ptc != nullptr)) { + auto& tc = *ptc; + auto count = tc.count(); + if (M <= count) { +#ifndef NDEBUG + DCHECK(!tc.local_); + tc.local_ = true; +#endif + // Fast path + for (size_t i = 0; i < M; ++i) { + auto hprec = tc[i].hprec_; + DCHECK(hprec != nullptr); + DEBUG_PRINT(i << " " << &h[i]); + new (&h[i]) hazptr_holder(nullptr); + h[i].hazptr_ = hprec; + DEBUG_PRINT( + i << " " << &h[i] << " " << h[i].domain_ << " " << h[i].hazptr_); + } + return; + } + } + } + // Slow path + need_destruct_ = true; + for (size_t i = 0; i < M; ++i) { + new (&h[i]) hazptr_holder; + DEBUG_PRINT( + i << " " << &h[i] << " " << h[i].domain_ << " " << h[i].hazptr_); + } +} + +template +FOLLY_ALWAYS_INLINE hazptr_local::~hazptr_local() { + if (LIKELY(!need_destruct_)) { +#ifndef NDEBUG + auto ptc = hazptr_tc_tls(); + DCHECK(ptc != nullptr); + auto& tc = *ptc; + DCHECK(tc.local_); + tc.local_ = false; +#endif + return; + } + // Slow path + auto h = reinterpret_cast(&raw_); + for (size_t i = 0; i < M; ++i) { + h[i].~hazptr_holder(); + } +} + +template +FOLLY_ALWAYS_INLINE hazptr_holder& hazptr_local::operator[]( + size_t i) noexcept { + auto h = reinterpret_cast(&raw_); + DCHECK(i < M); + return h[i]; +} + //////////////////////////////////////////////////////////////////////////////// // [TODO]: // - Control of reclamation (when and by whom) @@ -633,6 +816,11 @@ inline void hazptr_tc_entry::evict() { /** hazptr_tc */ +FOLLY_ALWAYS_INLINE hazptr_tc_entry& hazptr_tc::operator[](size_t i) { + DCHECK(i <= HAZPTR_TC_SIZE); + return entry_[i]; +} + FOLLY_ALWAYS_INLINE hazptr_rec* hazptr_tc::get() { if (LIKELY(count_ != 0)) { auto hprec = entry_[--count_].get(); @@ -652,48 +840,64 @@ FOLLY_ALWAYS_INLINE bool hazptr_tc::put(hazptr_rec* hprec) { return false; } +FOLLY_ALWAYS_INLINE size_t hazptr_tc::count() { + return count_; +} + /** hazptr_tc free functions */ -FOLLY_ALWAYS_INLINE hazptr_rec* hazptr_tc_try_get() { - DEBUG_PRINT(TLS_UNINITIALIZED << TLS_ALIVE << TLS_DESTROYED); +FOLLY_ALWAYS_INLINE hazptr_tc* hazptr_tc_tls() { DEBUG_PRINT(tls_state_); if (LIKELY(tls_state_ == TLS_ALIVE)) { DEBUG_PRINT(tls_state_); - return tls_tc_data_.get(); + return &tls_tc_data_; } else if (tls_state_ == TLS_UNINITIALIZED) { tls_life_odr_use(); - return tls_tc_data_.get(); + return &tls_tc_data_; } return nullptr; } -FOLLY_ALWAYS_INLINE bool hazptr_tc_try_put(hazptr_rec* hprec) { - DEBUG_PRINT(tls_state_); - if (LIKELY(tls_state_ == TLS_ALIVE)) { - DEBUG_PRINT(tls_state_); - return tls_tc_data_.put(hprec); - } - return false; -} - inline void hazptr_tc_init() { DEBUG_PRINT(""); auto& tc = tls_tc_data_; DEBUG_PRINT(&tc); tc.count_ = 0; - for (int i = 0; i < HAZPTR_TC_SIZE; ++i) { - tc.entry_[i].hprec_ = nullptr; - } +#ifndef NDEBUG + tc.local_ = false; +#endif } inline void hazptr_tc_shutdown() { auto& tc = tls_tc_data_; DEBUG_PRINT(&tc); - for (int i = 0; i < tc.count_; ++i) { + for (size_t i = 0; i < tc.count_; ++i) { tc.entry_[i].evict(); } } +FOLLY_ALWAYS_INLINE hazptr_rec* hazptr_tc_try_get() { + DEBUG_PRINT(TLS_UNINITIALIZED << TLS_ALIVE << TLS_DESTROYED); + DEBUG_PRINT(tls_state_); + if (LIKELY(tls_state_ == TLS_ALIVE)) { + DEBUG_PRINT(tls_state_); + return tls_tc_data_.get(); + } else if (tls_state_ == TLS_UNINITIALIZED) { + tls_life_odr_use(); + return tls_tc_data_.get(); + } + return nullptr; +} + +FOLLY_ALWAYS_INLINE bool hazptr_tc_try_put(hazptr_rec* hprec) { + DEBUG_PRINT(tls_state_); + if (LIKELY(tls_state_ == TLS_ALIVE)) { + DEBUG_PRINT(tls_state_); + return tls_tc_data_.put(hprec); + } + return false; +} + /** * hazptr_priv */ diff --git a/folly/experimental/hazptr/hazptr.h b/folly/experimental/hazptr/hazptr.h index 456fa6c4..f1776e4b 100644 --- a/folly/experimental/hazptr/hazptr.h +++ b/folly/experimental/hazptr/hazptr.h @@ -34,6 +34,15 @@ class hazptr_obj; template class hazptr_obj_base; +/** hazptr_local: Optimized template for bulk construction and destruction of + * hazard pointers */ +template +class hazptr_array; + +/** hazptr_local: Optimized template for locally-used hazard pointers */ +template +class hazptr_local; + /** hazptr_domain: Class of hazard pointer domains. Each domain manages a set * of hazard pointers and a set of retired objects. */ class hazptr_domain { @@ -100,12 +109,17 @@ class hazptr_obj_base : public hazptr_obj { /** hazptr_holder: Class for automatic acquisition and release of * hazard pointers, and interface for hazard pointer operations. */ class hazptr_holder { + template + friend class hazptr_array; + template + friend class hazptr_local; + public: /* Constructor automatically acquires a hazard pointer. */ explicit hazptr_holder(hazptr_domain& domain = default_hazptr_domain()); /* Construct an empty hazptr_holder. */ // Note: This diverges from the proposal in P0233R4 - explicit hazptr_holder(std::nullptr_t); + explicit hazptr_holder(std::nullptr_t) noexcept; /* Destructor automatically clears and releases the owned hazard pointer. */ ~hazptr_holder(); @@ -154,6 +168,69 @@ class hazptr_holder { void swap(hazptr_holder&, hazptr_holder&) noexcept; +using aligned_hazptr_holder = typename std:: + aligned_storage::type; + +/** + * hazptr_array: Optimized for bulk construction and destruction of + * hazptr_holder-s. + * + * WARNING: Do not move from or to individual hazptr_holder-s. + * Only move the whole hazptr_array. + */ +template +class hazptr_array { + static_assert(M > 0, "M must be a positive integer."); + + public: + hazptr_array(); + explicit hazptr_array(std::nullptr_t) noexcept; + + hazptr_array(const hazptr_array&) = delete; + hazptr_array& operator=(const hazptr_array&) = delete; + hazptr_array(hazptr_array&& other) noexcept; + hazptr_array& operator=(hazptr_array&& other) noexcept; + + ~hazptr_array(); + + hazptr_holder& operator[](size_t i) noexcept; + + private: + aligned_hazptr_holder raw_[M]; + bool empty_{false}; +}; + +/** + * hazptr_local: Optimized for construction and destruction of + * one or more hazptr_holder-s with local scope. + * + * WARNING 1: Do not move from or to individual hazptr_holder-s. + * + * WARNING 2: There can only be one hazptr_local active for the same + * thread at any time. This is not tracked and checked by the + * implementation because it would negate the performance gains of + * this class. + */ +template +class hazptr_local { + static_assert(M > 0, "M must be a positive integer."); + + public: + hazptr_local(); + hazptr_local(const hazptr_local&) = delete; + hazptr_local& operator=(const hazptr_local&) = delete; + hazptr_local(hazptr_local&&) = delete; + hazptr_local& operator=(hazptr_local&&) = delete; + + ~hazptr_local(); + + hazptr_holder& operator[](size_t i) noexcept; + + private: + aligned_hazptr_holder raw_[M]; + bool need_destruct_{false}; +}; + } // namespace hazptr } // namespace folly diff --git a/folly/experimental/hazptr/test/HazptrTest.cpp b/folly/experimental/hazptr/test/HazptrTest.cpp index 04cadd37..8468ad57 100644 --- a/folly/experimental/hazptr/test/HazptrTest.cpp +++ b/folly/experimental/hazptr/test/HazptrTest.cpp @@ -348,3 +348,49 @@ TEST_F(HazptrTest, Move) { hptr2.reset(); } } + +TEST_F(HazptrTest, Array) { + struct Foo : hazptr_obj_base { + int a; + }; + for (int i = 0; i < 100; ++i) { + Foo* x = new Foo; + x->a = i; + hazptr_array<10> hptr; + // Protect object + hptr[9].reset(x); + // Empty array + hazptr_array<10> h; + // Move assignment + h = std::move(hptr); + // Retire object + x->retire(); + // Unprotect object - hptr2 is nonempty + h[9].reset(); + } + { + // Abnormal case + hazptr_array h; + } +} + +TEST_F(HazptrTest, Local) { + struct Foo : hazptr_obj_base { + int a; + }; + for (int i = 0; i < 100; ++i) { + Foo* x = new Foo; + x->a = i; + hazptr_local<10> hptr; + // Protect object + hptr[9].reset(x); + // Retire object + x->retire(); + // Unprotect object - hptr2 is nonempty + hptr[9].reset(); + } + { + // Abnormal case + hazptr_local h; + } +}