Commit bf4d5370 authored by Maged Michael's avatar Maged Michael Committed by Facebook Github Bot

Optimize local and bulk management of hazptr_holder-s

Summary:
Changes:
- Added hazptr_local<M> for optimized management of local hazptr_holder-s.
- Added hazptr_array<M> 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
parent 49af015a
...@@ -95,8 +95,9 @@ inline uint64_t bench(std::string name, int ops, const RepFunc& repFn) { ...@@ -95,8 +95,9 @@ inline uint64_t bench(std::string name, int ops, const RepFunc& repFn) {
return res; return res;
} }
const int ops = 1000000;
inline uint64_t listBench(std::string name, int nthreads, int size) { inline uint64_t listBench(std::string name, int nthreads, int size) {
int ops = 100000;
auto repFn = [&] { auto repFn = [&] {
SWMRListSet<uint64_t> s; SWMRListSet<uint64_t> s;
auto init = [&] { auto init = [&] {
...@@ -116,7 +117,6 @@ inline uint64_t listBench(std::string name, int nthreads, int size) { ...@@ -116,7 +117,6 @@ inline uint64_t listBench(std::string name, int nthreads, int size) {
} }
inline uint64_t holderBench(std::string name, int nthreads) { inline uint64_t holderBench(std::string name, int nthreads) {
int ops = 100000;
auto repFn = [&] { auto repFn = [&] {
auto init = [] {}; auto init = [] {};
auto fn = [&](int tid) { auto fn = [&](int tid) {
...@@ -130,11 +130,40 @@ inline uint64_t holderBench(std::string name, int nthreads) { ...@@ -130,11 +130,40 @@ inline uint64_t holderBench(std::string name, int nthreads) {
return bench(name, ops, repFn); return bench(name, ops, repFn);
} }
template <size_t M>
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<M> a;
}
};
auto endFn = [] {};
return run_once(nthreads, init, fn, endFn);
};
return bench(name, ops, repFn);
}
template <size_t M>
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) { inline uint64_t retireBench(std::string name, int nthreads) {
struct Foo : hazptr_obj_base<Foo> { struct Foo : hazptr_obj_base<Foo> {
int x; int x;
}; };
int ops = 100000;
auto repFn = [&] { auto repFn = [&] {
auto init = [] {}; auto init = [] {};
auto fn = [&](int tid) { auto fn = [&](int tid) {
...@@ -155,10 +184,26 @@ const int sizes[] = {10, 100}; ...@@ -155,10 +184,26 @@ const int sizes[] = {10, 100};
inline void benches(std::string name) { inline void benches(std::string name) {
std::cout << "------------------------------------------- " << name << "\n"; std::cout << "------------------------------------------- " << name << "\n";
for (int i : nthr) { 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; << std::endl;
holderBench(name + " ", i); holderBench(name + " ", i);
holderBench(name + " - dup ", 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; std::cout << i << " threads -- allocate/retire/reclaim object" << std::endl;
retireBench(name + " ", i); retireBench(name + " ", i);
retireBench(name + " - dup ", i); retireBench(name + " - dup ", i);
...@@ -175,6 +220,56 @@ inline void benches(std::string name) { ...@@ -175,6 +220,56 @@ inline void benches(std::string name) {
} // namespace folly } // 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 ------------------------------------------- no amb - no tc
1 threads -- construct/destruct 10 hazptr_holder-s 1 threads -- construct/destruct 10 hazptr_holder-s
no amb - no tc 2518 ns 2461 ns 2431 ns no amb - no tc 2518 ns 2461 ns 2431 ns
...@@ -252,57 +347,5 @@ no amb - tc - dup 24 ns 23 ns 21 ns ...@@ -252,57 +347,5 @@ no amb - tc - dup 24 ns 23 ns 21 ns
10 threads -- 100-item list 10 threads -- 100-item list
no amb - tc 215 ns 208 ns 188 ns no amb - tc 215 ns 208 ns 188 ns
no amb - tc - dup 215 ns 209 ns 197 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
---------------------------------------------------------- ----------------------------------------------------------
*/ */
...@@ -99,15 +99,16 @@ class SWMRListSet { ...@@ -99,15 +99,16 @@ class SWMRListSet {
/* Used by readers */ /* Used by readers */
bool contains(const T& val) const { bool contains(const T& val) const {
/* Acquire two hazard pointers for hand-over-hand traversal. */ /* Two hazard pointers for hand-over-hand traversal. */
hazptr_holder hptr_prev; hazptr_local<2> hptr;
hazptr_holder hptr_curr; hazptr_holder* hptr_prev = &hptr[0];
hazptr_holder* hptr_curr = &hptr[1];
while (true) { while (true) {
auto prev = &head_; auto prev = &head_;
auto curr = prev->load(std::memory_order_acquire); auto curr = prev->load(std::memory_order_acquire);
while (true) { while (true) {
if (!curr) { return false; } if (!curr) { return false; }
if (!hptr_curr.try_protect(curr, *prev)) if (!hptr_curr->try_protect(curr, *prev))
break; break;
auto next = curr->next_.load(std::memory_order_acquire); auto next = curr->next_.load(std::memory_order_acquire);
if (prev->load(std::memory_order_acquire) != curr) if (prev->load(std::memory_order_acquire) != curr)
...@@ -119,20 +120,9 @@ class SWMRListSet { ...@@ -119,20 +120,9 @@ class SWMRListSet {
} }
prev = &(curr->next_); prev = &(curr->next_);
curr = next; curr = next;
/* Swap does not change the values of the owned hazard std::swap(hptr_curr, hptr_prev);
* 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);
} }
} }
/* The hazard pointers are released automatically. */
} }
}; };
......
...@@ -117,17 +117,23 @@ static_assert( ...@@ -117,17 +117,23 @@ static_assert(
struct hazptr_tc { struct hazptr_tc {
hazptr_tc_entry entry_[HAZPTR_TC_SIZE]; hazptr_tc_entry entry_[HAZPTR_TC_SIZE];
int count_; size_t count_;
#ifndef NDEBUG
bool local_;
#endif
public: public:
hazptr_tc_entry& operator[](size_t i);
hazptr_rec* get(); hazptr_rec* get();
bool put(hazptr_rec* hprec); bool put(hazptr_rec* hprec);
size_t count();
}; };
static_assert( static_assert(
std::is_trivial<hazptr_tc>::value, std::is_trivial<hazptr_tc>::value,
"hazptr_tc must be trivial to avoid a branch to check initialization"); "hazptr_tc must be trivial to avoid a branch to check initialization");
hazptr_tc* hazptr_tc_tls();
void hazptr_tc_init(); void hazptr_tc_init();
void hazptr_tc_shutdown(); void hazptr_tc_shutdown();
hazptr_rec* hazptr_tc_try_get(); hazptr_rec* hazptr_tc_try_get();
...@@ -243,7 +249,7 @@ FOLLY_ALWAYS_INLINE hazptr_holder::hazptr_holder(hazptr_domain& domain) { ...@@ -243,7 +249,7 @@ FOLLY_ALWAYS_INLINE hazptr_holder::hazptr_holder(hazptr_domain& domain) {
if (hazptr_ == nullptr) { std::bad_alloc e; throw e; } 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; domain_ = nullptr;
hazptr_ = nullptr; hazptr_ = nullptr;
DEBUG_PRINT(this << " " << domain_ << " " << hazptr_); DEBUG_PRINT(this << " " << domain_ << " " << hazptr_);
...@@ -350,6 +356,183 @@ FOLLY_ALWAYS_INLINE void swap(hazptr_holder& lhs, hazptr_holder& rhs) noexcept { ...@@ -350,6 +356,183 @@ FOLLY_ALWAYS_INLINE void swap(hazptr_holder& lhs, hazptr_holder& rhs) noexcept {
lhs.swap(rhs); lhs.swap(rhs);
} }
/**
* hazptr_array
*/
template <size_t M>
FOLLY_ALWAYS_INLINE hazptr_array<M>::hazptr_array() {
auto h = reinterpret_cast<hazptr_holder*>(&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 <size_t M>
FOLLY_ALWAYS_INLINE hazptr_array<M>::hazptr_array(
hazptr_array&& other) noexcept {
DEBUG_PRINT(this << " " << M << " " << &other);
auto h = reinterpret_cast<hazptr_holder*>(&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 <size_t M>
FOLLY_ALWAYS_INLINE hazptr_array<M>::hazptr_array(std::nullptr_t) noexcept {
DEBUG_PRINT(this << " " << M);
auto h = reinterpret_cast<hazptr_holder*>(&raw_);
for (size_t i = 0; i < M; ++i) {
new (&h[i]) hazptr_holder(nullptr);
DEBUG_PRINT(i << " " << &h[i]);
}
empty_ = true;
}
template <size_t M>
FOLLY_ALWAYS_INLINE hazptr_array<M>::~hazptr_array() {
if (empty_) {
return;
}
auto h = reinterpret_cast<hazptr_holder*>(&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 <size_t M>
FOLLY_ALWAYS_INLINE hazptr_array<M>& hazptr_array<M>::operator=(
hazptr_array&& other) noexcept {
DEBUG_PRINT(this << " " << M << " " << &other);
auto h = reinterpret_cast<hazptr_holder*>(&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 <size_t M>
FOLLY_ALWAYS_INLINE hazptr_holder& hazptr_array<M>::operator[](
size_t i) noexcept {
auto h = reinterpret_cast<hazptr_holder*>(&raw_);
DCHECK(i < M);
return h[i];
}
/**
* hazptr_local
*/
template <size_t M>
FOLLY_ALWAYS_INLINE hazptr_local<M>::hazptr_local() {
auto h = reinterpret_cast<hazptr_holder*>(&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 <size_t M>
FOLLY_ALWAYS_INLINE hazptr_local<M>::~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<hazptr_holder*>(&raw_);
for (size_t i = 0; i < M; ++i) {
h[i].~hazptr_holder();
}
}
template <size_t M>
FOLLY_ALWAYS_INLINE hazptr_holder& hazptr_local<M>::operator[](
size_t i) noexcept {
auto h = reinterpret_cast<hazptr_holder*>(&raw_);
DCHECK(i < M);
return h[i];
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// [TODO]: // [TODO]:
// - Control of reclamation (when and by whom) // - Control of reclamation (when and by whom)
...@@ -633,6 +816,11 @@ inline void hazptr_tc_entry::evict() { ...@@ -633,6 +816,11 @@ inline void hazptr_tc_entry::evict() {
/** hazptr_tc */ /** 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() { FOLLY_ALWAYS_INLINE hazptr_rec* hazptr_tc::get() {
if (LIKELY(count_ != 0)) { if (LIKELY(count_ != 0)) {
auto hprec = entry_[--count_].get(); auto hprec = entry_[--count_].get();
...@@ -652,48 +840,64 @@ FOLLY_ALWAYS_INLINE bool hazptr_tc::put(hazptr_rec* hprec) { ...@@ -652,48 +840,64 @@ FOLLY_ALWAYS_INLINE bool hazptr_tc::put(hazptr_rec* hprec) {
return false; return false;
} }
FOLLY_ALWAYS_INLINE size_t hazptr_tc::count() {
return count_;
}
/** hazptr_tc free functions */ /** hazptr_tc free functions */
FOLLY_ALWAYS_INLINE hazptr_rec* hazptr_tc_try_get() { FOLLY_ALWAYS_INLINE hazptr_tc* hazptr_tc_tls() {
DEBUG_PRINT(TLS_UNINITIALIZED << TLS_ALIVE << TLS_DESTROYED);
DEBUG_PRINT(tls_state_); DEBUG_PRINT(tls_state_);
if (LIKELY(tls_state_ == TLS_ALIVE)) { if (LIKELY(tls_state_ == TLS_ALIVE)) {
DEBUG_PRINT(tls_state_); DEBUG_PRINT(tls_state_);
return tls_tc_data_.get(); return &tls_tc_data_;
} else if (tls_state_ == TLS_UNINITIALIZED) { } else if (tls_state_ == TLS_UNINITIALIZED) {
tls_life_odr_use(); tls_life_odr_use();
return tls_tc_data_.get(); return &tls_tc_data_;
} }
return nullptr; 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() { inline void hazptr_tc_init() {
DEBUG_PRINT(""); DEBUG_PRINT("");
auto& tc = tls_tc_data_; auto& tc = tls_tc_data_;
DEBUG_PRINT(&tc); DEBUG_PRINT(&tc);
tc.count_ = 0; tc.count_ = 0;
for (int i = 0; i < HAZPTR_TC_SIZE; ++i) { #ifndef NDEBUG
tc.entry_[i].hprec_ = nullptr; tc.local_ = false;
} #endif
} }
inline void hazptr_tc_shutdown() { inline void hazptr_tc_shutdown() {
auto& tc = tls_tc_data_; auto& tc = tls_tc_data_;
DEBUG_PRINT(&tc); DEBUG_PRINT(&tc);
for (int i = 0; i < tc.count_; ++i) { for (size_t i = 0; i < tc.count_; ++i) {
tc.entry_[i].evict(); 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 * hazptr_priv
*/ */
......
...@@ -34,6 +34,15 @@ class hazptr_obj; ...@@ -34,6 +34,15 @@ class hazptr_obj;
template <typename T, typename Deleter> template <typename T, typename Deleter>
class hazptr_obj_base; class hazptr_obj_base;
/** hazptr_local: Optimized template for bulk construction and destruction of
* hazard pointers */
template <size_t M>
class hazptr_array;
/** hazptr_local: Optimized template for locally-used hazard pointers */
template <size_t M>
class hazptr_local;
/** hazptr_domain: Class of hazard pointer domains. Each domain manages a set /** hazptr_domain: Class of hazard pointer domains. Each domain manages a set
* of hazard pointers and a set of retired objects. */ * of hazard pointers and a set of retired objects. */
class hazptr_domain { class hazptr_domain {
...@@ -100,12 +109,17 @@ class hazptr_obj_base : public hazptr_obj { ...@@ -100,12 +109,17 @@ class hazptr_obj_base : public hazptr_obj {
/** hazptr_holder: Class for automatic acquisition and release of /** hazptr_holder: Class for automatic acquisition and release of
* hazard pointers, and interface for hazard pointer operations. */ * hazard pointers, and interface for hazard pointer operations. */
class hazptr_holder { class hazptr_holder {
template <size_t M>
friend class hazptr_array;
template <size_t M>
friend class hazptr_local;
public: public:
/* Constructor automatically acquires a hazard pointer. */ /* Constructor automatically acquires a hazard pointer. */
explicit hazptr_holder(hazptr_domain& domain = default_hazptr_domain()); explicit hazptr_holder(hazptr_domain& domain = default_hazptr_domain());
/* Construct an empty hazptr_holder. */ /* Construct an empty hazptr_holder. */
// Note: This diverges from the proposal in P0233R4 // 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. */ /* Destructor automatically clears and releases the owned hazard pointer. */
~hazptr_holder(); ~hazptr_holder();
...@@ -154,6 +168,69 @@ class hazptr_holder { ...@@ -154,6 +168,69 @@ class hazptr_holder {
void swap(hazptr_holder&, hazptr_holder&) noexcept; void swap(hazptr_holder&, hazptr_holder&) noexcept;
using aligned_hazptr_holder = typename std::
aligned_storage<sizeof(hazptr_holder), alignof(hazptr_holder)>::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 <size_t M = 1>
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 <size_t M = 1>
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 hazptr
} // namespace folly } // namespace folly
......
...@@ -348,3 +348,49 @@ TEST_F(HazptrTest, Move) { ...@@ -348,3 +348,49 @@ TEST_F(HazptrTest, Move) {
hptr2.reset(); hptr2.reset();
} }
} }
TEST_F(HazptrTest, Array) {
struct Foo : hazptr_obj_base<Foo> {
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<HAZPTR_TC_SIZE + 1> h;
}
}
TEST_F(HazptrTest, Local) {
struct Foo : hazptr_obj_base<Foo> {
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<HAZPTR_TC_SIZE + 1> h;
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment