Fix destruction order
authorMaged Michael <magedmichael@fb.com>
Wed, 16 Aug 2017 01:01:19 +0000 (18:01 -0700)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Wed, 16 Aug 2017 01:11:34 +0000 (18:11 -0700)
Summary:
- Added `hazptr.cpp` and `memory_resource.cpp`. Moved singleton code to source.
  - Changed the singleton for the default `hazptr_domain` to a global.
  - Changed hazptr_stats singleton to a global.
  - Moved the thread caching calls from the hazptr_domain functions to the constructor/destructor of hazptr_holder.
  - Changed the TLS singletons to TLS globals.
  - Changed some inlining directives.
  - Leak the hazptr_rec-s in the default domain

Reviewed By: davidtgoldblatt, djwatson

Differential Revision: D5553753

fbshipit-source-id: da69eecec55c0f78fb8ef5591f9aeffee99ff3fa

folly/experimental/hazptr/bench/HazptrBench.h
folly/experimental/hazptr/example/SWMRList.h
folly/experimental/hazptr/hazptr-impl.h
folly/experimental/hazptr/hazptr.cpp [new file with mode: 0644]
folly/experimental/hazptr/hazptr.h
folly/experimental/hazptr/memory_resource.cpp [new file with mode: 0644]
folly/experimental/hazptr/memory_resource.h
folly/experimental/hazptr/test/HazptrTest.cpp

index 4aca6c53b1a76a7f41ef156dbc8599045e82256a..22709e77ebe08c09e2dc20d6378605a38ac64826 100644 (file)
@@ -98,7 +98,6 @@ inline uint64_t bench(std::string name, int ops, const RepFunc& repFn) {
 inline uint64_t listBench(std::string name, int nthreads, int size) {
   int ops = 100000;
   auto repFn = [&] {
-    hazptr_holder dummy[100];
     SWMRListSet<uint64_t> s;
     auto init = [&] {
       for (int i = 0; i < size; ++i) {
@@ -119,7 +118,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 = [&] {
-    hazptr_holder dummy[100];
     auto init = [] {};
     auto fn = [&](int tid) {
       for (int j = tid; j < ops; j += nthreads) {
@@ -138,7 +136,6 @@ inline uint64_t retireBench(std::string name, int nthreads) {
   };
   int ops = 100000;
   auto repFn = [&] {
-    hazptr_holder dummy[100];
     auto init = [] {};
     auto fn = [&](int tid) {
       for (int j = tid; j < ops; j += nthreads) {
index 6df6a3c362f186530d22f3d0b759319e5664e7cb..83755a63520fad0b62770ef43cab01a73a60302b 100644 (file)
@@ -53,7 +53,6 @@ class SWMRListSet {
   };
 
   std::atomic<Node*> head_ = {nullptr};
-  hazptr_domain& domain_;
 
   /* Used by the single writer */
   void locate_lower_bound(const T& v, std::atomic<Node*>*& prev) const {
@@ -67,9 +66,6 @@ class SWMRListSet {
   }
 
  public:
-  explicit SWMRListSet(hazptr_domain& domain = default_hazptr_domain())
-      : domain_(domain) {}
-
   ~SWMRListSet() {
     Node* next;
     for (auto p = head_.load(); p; p = next) {
@@ -97,14 +93,15 @@ class SWMRListSet {
     prev->store(curr_next, std::memory_order_release);
     // ...and only then null out the removed node.
     curr->next_.store(nullptr, std::memory_order_release);
-    curr->retire(domain_);
+    curr->retire();
     return true;
   }
+
   /* Used by readers */
   bool contains(const T& val) const {
     /* Acquire two hazard pointers for hand-over-hand traversal. */
-    hazptr_holder hptr_prev(domain_);
-    hazptr_holder hptr_curr(domain_);
+    hazptr_holder hptr_prev;
+    hazptr_holder hptr_curr;
     while (true) {
       auto prev = &head_;
       auto curr = prev->load(std::memory_order_acquire);
index 4dd0de889c3113cce692721edb8fdd7b5f2b1beb..12c4fd39ffa4586e0b75da864d1751abff8ca77e 100644 (file)
@@ -69,16 +69,15 @@ namespace folly {
 namespace hazptr {
 
 /**
- * helper classes and functions
+ *  Helper classes and functions
  */
 
 /** hazptr_stats */
 
 class hazptr_stats;
-hazptr_stats& hazptr_stats();
 
 #if HAZPTR_STATS
-#define INC_HAZPTR_STATS(x) hazptr_stats().x()
+#define INC_HAZPTR_STATS(x) hazptr_stats_.x()
 #else
 #define INC_HAZPTR_STATS(x)
 #endif
@@ -91,65 +90,97 @@ class hazptr_mb {
   static void heavy();
 };
 
-/** hazptr_tc
+/**
+ *  TLS structures
+ */
+
+/** TLS life state */
+
+enum hazptr_tls_state { TLS_ALIVE, TLS_UNINITIALIZED, TLS_DESTROYED };
+
+/** hazptr_tc structures
  *  Thread caching of hazptr_rec-s that belong to the default domain.
  */
 
-class hazptr_tc_entry {
-  friend class hazptr_tc;
-  hazptr_rec* hprec_{nullptr};
+struct hazptr_tc_entry {
+  hazptr_rec* hprec_;
 
- public:
   void fill(hazptr_rec* hprec);
   hazptr_rec* get();
   void evict();
 };
 
-class hazptr_tc {
-  hazptr_tc_entry tc_[HAZPTR_TC_SIZE];
-  int count_{0};
+static_assert(
+    std::is_trivial<hazptr_tc_entry>::value,
+    "hazptr_tc_entry must be trivial"
+    " to avoid a branch to check initialization");
+
+struct hazptr_tc {
+  hazptr_tc_entry entry_[HAZPTR_TC_SIZE];
+  int count_;
 
  public:
-  hazptr_tc();
-  ~hazptr_tc();
   hazptr_rec* get();
   bool put(hazptr_rec* hprec);
 };
 
-hazptr_tc& hazptr_tc();
+static_assert(
+    std::is_trivial<hazptr_tc>::value,
+    "hazptr_tc must be trivial to avoid a branch to check initialization");
+
+void hazptr_tc_init();
+void hazptr_tc_shutdown();
+hazptr_rec* hazptr_tc_try_get();
+bool hazptr_tc_try_put(hazptr_rec* hprec);
 
-/** hazptr_priv
+/** hazptr_priv structures
  *  Thread private lists of retired objects that belong to the default domain.
  */
 
-class hazptr_priv {
-  hazptr_domain* domain_{&default_hazptr_domain()};
-  hazptr_obj* head_{nullptr};
-  hazptr_obj* tail_{nullptr};
-  int rcount_{0};
-  bool active_{true};
+struct hazptr_priv {
+  hazptr_obj* head_;
+  hazptr_obj* tail_;
+  int rcount_;
+  bool active_;
 
- public:
-  hazptr_priv();
-  ~hazptr_priv();
   void push(hazptr_obj* obj);
-
- private:
   void pushAllToDomain();
 };
 
-hazptr_priv& hazptr_priv();
+static_assert(
+    std::is_trivial<hazptr_priv>::value,
+    "hazptr_priv must be trivial to avoid a branch to check initialization");
+
+void hazptr_priv_init();
+void hazptr_priv_shutdown();
+bool hazptr_priv_try_retire(hazptr_obj* obj);
+
+/** hazptr_tls_life */
+
+struct hazptr_tls_life {
+  hazptr_tls_life();
+  ~hazptr_tls_life();
+};
+
+void tls_life_odr_use();
+
+/** tls globals */
+
+extern thread_local hazptr_tls_state tls_state_;
+extern thread_local hazptr_tc tls_tc_data_;
+extern thread_local hazptr_priv tls_priv_data_;
+extern thread_local hazptr_tls_life tls_life_; // last
 
 /**
- * public functions
+ *  hazptr_domain
  */
 
-/** hazptr_domain */
-
-constexpr hazptr_domain::hazptr_domain(memory_resource* mr) noexcept
+inline constexpr hazptr_domain::hazptr_domain(memory_resource* mr) noexcept
     : mr_(mr) {}
 
-/** hazptr_obj_base */
+/**
+ *  hazptr_obj_base
+ */
 
 template <typename T, typename D>
 inline void hazptr_obj_base<T, D>::retire(hazptr_domain& domain, D deleter) {
@@ -162,18 +193,21 @@ inline void hazptr_obj_base<T, D>::retire(hazptr_domain& domain, D deleter) {
   };
   if (HAZPTR_PRIV &&
       (HAZPTR_ONE_DOMAIN || (&domain == &default_hazptr_domain()))) {
-    hazptr_priv().push(this);
-  } else {
-    domain.objRetire(this);
+    if (hazptr_priv_try_retire(this)) {
+      return;
+    }
   }
+  domain.objRetire(this);
 }
 
-/** hazptr_rec */
+/**
+ *  hazptr_rec
+ */
 
 class hazptr_rec {
   friend class hazptr_domain;
   friend class hazptr_holder;
-  friend class hazptr_tc_entry;
+  friend struct hazptr_tc_entry;
 
   FOLLY_ALIGN_TO_AVOID_FALSE_SHARING
   std::atomic<const void*> hazptr_{nullptr};
@@ -188,16 +222,28 @@ class hazptr_rec {
   void release() noexcept;
 };
 
-/** hazptr_holder */
+/**
+ *  hazptr_holder
+ */
 
 FOLLY_ALWAYS_INLINE hazptr_holder::hazptr_holder(hazptr_domain& domain) {
   domain_ = &domain;
+  if (LIKELY(
+          HAZPTR_TC &&
+          (HAZPTR_ONE_DOMAIN || &domain == &default_hazptr_domain()))) {
+    auto hprec = hazptr_tc_try_get();
+    if (LIKELY(hprec != nullptr)) {
+      hazptr_ = hprec;
+      DEBUG_PRINT(this << " " << domain_ << " " << hazptr_);
+      return;
+    }
+  }
   hazptr_ = domain_->hazptrAcquire();
   DEBUG_PRINT(this << " " << domain_ << " " << hazptr_);
   if (hazptr_ == nullptr) { std::bad_alloc e; throw e; }
 }
 
-inline hazptr_holder::hazptr_holder(std::nullptr_t) {
+FOLLY_ALWAYS_INLINE hazptr_holder::hazptr_holder(std::nullptr_t) {
   domain_ = nullptr;
   hazptr_ = nullptr;
   DEBUG_PRINT(this << " " << domain_ << " " << hazptr_);
@@ -205,22 +251,30 @@ inline hazptr_holder::hazptr_holder(std::nullptr_t) {
 
 FOLLY_ALWAYS_INLINE hazptr_holder::~hazptr_holder() {
   DEBUG_PRINT(this);
-  if (domain_) {
+  if (LIKELY(hazptr_ != nullptr)) {
     hazptr_->clear();
+    if (LIKELY(
+            HAZPTR_TC &&
+            (HAZPTR_ONE_DOMAIN || domain_ == &default_hazptr_domain()))) {
+      if (LIKELY(hazptr_tc_try_put(hazptr_))) {
+        return;
+      }
+    }
     domain_->hazptrRelease(hazptr_);
   }
 }
 
-inline hazptr_holder::hazptr_holder(hazptr_holder&& rhs) noexcept {
+FOLLY_ALWAYS_INLINE hazptr_holder::hazptr_holder(hazptr_holder&& rhs) noexcept {
   domain_ = rhs.domain_;
   hazptr_ = rhs.hazptr_;
   rhs.domain_ = nullptr;
   rhs.hazptr_ = nullptr;
 }
 
-inline hazptr_holder& hazptr_holder::operator=(hazptr_holder&& rhs) noexcept {
+FOLLY_ALWAYS_INLINE
+hazptr_holder& hazptr_holder::operator=(hazptr_holder&& rhs) noexcept {
   /* Self-move is a no-op.  */
-  if (this != &rhs) {
+  if (LIKELY(this != &rhs)) {
     this->~hazptr_holder();
     new (this) hazptr_holder(std::move(rhs));
   }
@@ -243,7 +297,7 @@ FOLLY_ALWAYS_INLINE bool hazptr_holder::try_protect(
   reset(f(ptr));
   /*** Full fence ***/ hazptr_mb::light();
   T* p = src.load(std::memory_order_acquire);
-  if (p != ptr) {
+  if (UNLIKELY(p != ptr)) {
     ptr = p;
     reset();
     return false;
@@ -303,15 +357,14 @@ FOLLY_ALWAYS_INLINE void swap(hazptr_holder& lhs, hazptr_holder& rhs) noexcept {
 
 /** Definition of default_hazptr_domain() */
 
-inline hazptr_domain& default_hazptr_domain() {
-  static hazptr_domain d;
-  DEBUG_PRINT(&d);
-  return d;
+FOLLY_ALWAYS_INLINE hazptr_domain& default_hazptr_domain() {
+  DEBUG_PRINT(&default_domain_);
+  return default_domain_;
 }
 
 /** hazptr_rec */
 
-inline void hazptr_rec::set(const void* p) noexcept {
+FOLLY_ALWAYS_INLINE void hazptr_rec::set(const void* p) noexcept {
   DEBUG_PRINT(this << " " << p);
   hazptr_.store(p, std::memory_order_release);
 }
@@ -322,7 +375,7 @@ inline const void* hazptr_rec::get() const noexcept {
   return p;
 }
 
-inline void hazptr_rec::clear() noexcept {
+FOLLY_ALWAYS_INLINE void hazptr_rec::clear() noexcept {
   DEBUG_PRINT(this);
   hazptr_.store(nullptr, std::memory_order_release);
 }
@@ -369,7 +422,11 @@ inline hazptr_domain::~hazptr_domain() {
       retired = retired_.exchange(nullptr);
     }
   }
-  { /* free all hazptr_rec-s */
+  /* Leak the data for the default domain to avoid destruction order
+   * issues with thread caches.
+   */
+  if (this != &default_hazptr_domain()) {
+    /* free all hazptr_rec-s */
     hazptr_rec* next;
     for (auto p = hazptrs_.load(std::memory_order_acquire); p; p = next) {
       next = p->next_;
@@ -379,13 +436,7 @@ inline hazptr_domain::~hazptr_domain() {
   }
 }
 
-FOLLY_ALWAYS_INLINE hazptr_rec* hazptr_domain::hazptrAcquire() {
-  if (HAZPTR_TC && (HAZPTR_ONE_DOMAIN || this == &default_hazptr_domain())) {
-    auto hprec = hazptr_tc().get();
-    if (hprec) {
-      return hprec;
-    }
-  }
+inline hazptr_rec* hazptr_domain::hazptrAcquire() {
   hazptr_rec* p;
   hazptr_rec* next;
   for (p = hazptrs_.load(std::memory_order_acquire); p; p = next) {
@@ -395,6 +446,7 @@ FOLLY_ALWAYS_INLINE hazptr_rec* hazptr_domain::hazptrAcquire() {
     }
   }
   p = static_cast<hazptr_rec*>(mr_->allocate(sizeof(hazptr_rec)));
+  DEBUG_PRINT(this << " " << p << " " << sizeof(hazptr_rec));
   if (p == nullptr) {
     return nullptr;
   }
@@ -408,11 +460,7 @@ FOLLY_ALWAYS_INLINE hazptr_rec* hazptr_domain::hazptrAcquire() {
   return p;
 }
 
-FOLLY_ALWAYS_INLINE void hazptr_domain::hazptrRelease(hazptr_rec* p) noexcept {
-  if (HAZPTR_TC && (HAZPTR_ONE_DOMAIN || this == &default_hazptr_domain()) &&
-      hazptr_tc().put(p)) {
-    return;
-  }
+inline void hazptr_domain::hazptrRelease(hazptr_rec* p) noexcept {
   DEBUG_PRINT(this << " " << p);
   p->release();
 }
@@ -506,6 +554,8 @@ class hazptr_stats {
   std::atomic<uint64_t> seq_cst_{0};
 };
 
+extern hazptr_stats hazptr_stats_;
+
 inline hazptr_stats::~hazptr_stats() {
   DEBUG_PRINT(this << " light " << light_.load());
   DEBUG_PRINT(this << " heavy " << heavy_.load());
@@ -530,15 +580,9 @@ inline void hazptr_stats::seq_cst() {
   }
 }
 
-inline class hazptr_stats& hazptr_stats() {
-  static class hazptr_stats stats_;
-  DEBUG_PRINT(&stats_);
-  return stats_;
-}
-
 /** hazptr_mb */
 
-inline void hazptr_mb::light() {
+FOLLY_ALWAYS_INLINE void hazptr_mb::light() {
   DEBUG_PRINT("");
   if (HAZPTR_AMB) {
     folly::asymmetricLightBarrier();
@@ -560,41 +604,38 @@ inline void hazptr_mb::heavy() {
   }
 }
 
-/** hazptr_tc - functions */
+/**
+ *  TLS structures
+ */
+
+/**
+ *  hazptr_tc structures
+ */
+
+/** hazptr_tc_entry */
 
-inline void hazptr_tc_entry::fill(hazptr_rec* hprec) {
+FOLLY_ALWAYS_INLINE void hazptr_tc_entry::fill(hazptr_rec* hprec) {
   hprec_ = hprec;
   DEBUG_PRINT(this << " " << hprec);
 }
 
-inline hazptr_rec* hazptr_tc_entry::get() {
+FOLLY_ALWAYS_INLINE hazptr_rec* hazptr_tc_entry::get() {
   auto hprec = hprec_;
-  hprec_ = nullptr;
   DEBUG_PRINT(this << " " << hprec);
   return hprec;
 }
 
 inline void hazptr_tc_entry::evict() {
   auto hprec = hprec_;
-  hprec_ = nullptr;
   hprec->release();
   DEBUG_PRINT(this << " " << hprec);
 }
 
-inline hazptr_tc::hazptr_tc() {
-  DEBUG_PRINT(this);
-}
-
-inline hazptr_tc::~hazptr_tc() {
-  DEBUG_PRINT(this);
-  for (int i = 0; i < count_; ++i) {
-    tc_[i].evict();
-  }
-}
+/** hazptr_tc */
 
-inline hazptr_rec* hazptr_tc::get() {
-  if (count_ > 0) {
-    auto hprec = tc_[--count_].get();
+FOLLY_ALWAYS_INLINE hazptr_rec* hazptr_tc::get() {
+  if (LIKELY(count_ != 0)) {
+    auto hprec = entry_[--count_].get();
     DEBUG_PRINT(this << " " << hprec);
     return hprec;
   }
@@ -602,66 +643,147 @@ inline hazptr_rec* hazptr_tc::get() {
   return nullptr;
 }
 
-inline bool hazptr_tc::put(hazptr_rec* hprec) {
-  if (count_ < HAZPTR_TC_SIZE) {
-    tc_[count_++].fill(hprec);
+FOLLY_ALWAYS_INLINE bool hazptr_tc::put(hazptr_rec* hprec) {
+  if (LIKELY(count_ < HAZPTR_TC_SIZE)) {
+    entry_[count_++].fill(hprec);
     DEBUG_PRINT(this << " " << count_ - 1);
     return true;
   }
   return false;
 }
 
-FOLLY_ALWAYS_INLINE class hazptr_tc& hazptr_tc() {
-  static thread_local class hazptr_tc tc;
-  DEBUG_PRINT(&tc);
-  return tc;
+/** hazptr_tc free functions */
+
+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;
 }
 
-/** hazptr_priv - functions */
+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 hazptr_priv::hazptr_priv() {
-  DEBUG_PRINT(this);
+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;
+  }
 }
 
-inline hazptr_priv::~hazptr_priv() {
-  DEBUG_PRINT(this);
-  DCHECK(active_);
-  active_ = false;
-  if (tail_) {
-    pushAllToDomain();
+inline void hazptr_tc_shutdown() {
+  auto& tc = tls_tc_data_;
+  DEBUG_PRINT(&tc);
+  for (int i = 0; i < tc.count_; ++i) {
+    tc.entry_[i].evict();
   }
 }
 
+/**
+ *  hazptr_priv
+ */
+
 inline void hazptr_priv::push(hazptr_obj* obj) {
+  auto& domain = default_hazptr_domain();
   obj->next_ = nullptr;
   if (tail_) {
     tail_->next_ = obj;
   } else {
     if (!active_) {
-      default_hazptr_domain().objRetire(obj);
+      domain.objRetire(obj);
       return;
     }
     head_ = obj;
   }
   tail_ = obj;
   ++rcount_;
-  if (domain_->reachedThreshold(rcount_)) {
+  if (domain.reachedThreshold(rcount_)) {
     pushAllToDomain();
   }
 }
 
 inline void hazptr_priv::pushAllToDomain() {
-  domain_->pushRetired(head_, tail_, rcount_);
+  auto& domain = default_hazptr_domain();
+  domain.pushRetired(head_, tail_, rcount_);
   head_ = nullptr;
   tail_ = nullptr;
   rcount_ = 0;
-  domain_->tryBulkReclaim();
+  domain.tryBulkReclaim();
+}
+
+inline void hazptr_priv_init() {
+  auto& priv = tls_priv_data_;
+  DEBUG_PRINT(&priv);
+  priv.head_ = nullptr;
+  priv.tail_ = nullptr;
+  priv.rcount_ = 0;
+  priv.active_ = true;
 }
 
-inline class hazptr_priv& hazptr_priv() {
-  static thread_local class hazptr_priv priv;
+inline void hazptr_priv_shutdown() {
+  auto& priv = tls_priv_data_;
   DEBUG_PRINT(&priv);
-  return priv;
+  DCHECK(priv.active_);
+  priv.active_ = false;
+  if (priv.tail_) {
+    priv.pushAllToDomain();
+  }
+}
+
+inline bool hazptr_priv_try_retire(hazptr_obj* obj) {
+  DEBUG_PRINT(tls_state_);
+  if (tls_state_ == TLS_ALIVE) {
+    DEBUG_PRINT(tls_state_);
+    tls_priv_data_.push(obj);
+    return true;
+  } else if (tls_state_ == TLS_UNINITIALIZED) {
+    DEBUG_PRINT(tls_state_);
+    tls_life_odr_use();
+    tls_priv_data_.push(obj);
+    return true;
+  }
+  return false;
+}
+
+/** hazptr_tls_life */
+
+inline void tls_life_odr_use() {
+  DEBUG_PRINT(tls_state_);
+  CHECK(tls_state_ == TLS_UNINITIALIZED);
+  auto volatile tlsOdrUse = &tls_life_;
+  CHECK(tlsOdrUse != nullptr);
+  DEBUG_PRINT(tlsOdrUse);
+}
+
+inline hazptr_tls_life::hazptr_tls_life() {
+  DEBUG_PRINT(this);
+  CHECK(tls_state_ == TLS_UNINITIALIZED);
+  hazptr_tc_init();
+  hazptr_priv_init();
+  tls_state_ = TLS_ALIVE;
+}
+
+inline hazptr_tls_life::~hazptr_tls_life() {
+  DEBUG_PRINT(this);
+  CHECK(tls_state_ == TLS_ALIVE);
+  hazptr_tc_shutdown();
+  hazptr_priv_shutdown();
+  tls_state_ = TLS_DESTROYED;
 }
 
 } // namespace folly
diff --git a/folly/experimental/hazptr/hazptr.cpp b/folly/experimental/hazptr/hazptr.cpp
new file mode 100644 (file)
index 0000000..60870a8
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hazptr.h"
+
+namespace folly {
+namespace hazptr {
+
+FOLLY_STATIC_CTOR_PRIORITY_MAX hazptr_domain default_domain_;
+
+hazptr_stats hazptr_stats_;
+
+thread_local hazptr_tls_state tls_state_ = TLS_UNINITIALIZED;
+thread_local hazptr_tc tls_tc_data_;
+thread_local hazptr_priv tls_priv_data_;
+thread_local hazptr_tls_life tls_life_; // last
+
+} // namespace hazptr
+} // namespace folly
index 301fdfab173d002fed36e94216fa4903fa9bae42..456fa6c4b7816cd56c94f78b9ba9546e489430fb 100644 (file)
@@ -51,7 +51,7 @@ class hazptr_domain {
   friend class hazptr_holder;
   template <typename, typename>
   friend class hazptr_obj_base;
-  friend class hazptr_priv;
+  friend struct hazptr_priv;
 
   memory_resource* mr_;
   std::atomic<hazptr_rec*> hazptrs_ = {nullptr};
@@ -71,12 +71,14 @@ class hazptr_domain {
 /** Get the default hazptr_domain */
 hazptr_domain& default_hazptr_domain();
 
+extern hazptr_domain default_domain_;
+
 /** Definition of hazptr_obj */
 class hazptr_obj {
   friend class hazptr_domain;
   template <typename, typename>
   friend class hazptr_obj_base;
-  friend class hazptr_priv;
+  friend struct hazptr_priv;
 
   void (*reclaim_)(hazptr_obj*);
   hazptr_obj* next_;
diff --git a/folly/experimental/hazptr/memory_resource.cpp b/folly/experimental/hazptr/memory_resource.cpp
new file mode 100644 (file)
index 0000000..4c8da83
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "memory_resource.h"
+
+namespace folly {
+namespace hazptr {
+
+memory_resource** default_mr_ptr() {
+  /* library-local */ static memory_resource* default_mr =
+      new_delete_resource();
+  DEBUG_PRINT(&default_mr << " " << default_mr);
+  return &default_mr;
+}
+
+memory_resource* get_default_resource() {
+  DEBUG_PRINT("");
+  return *default_mr_ptr();
+}
+
+void set_default_resource(memory_resource* mr) {
+  DEBUG_PRINT("");
+  *default_mr_ptr() = mr;
+}
+
+memory_resource* new_delete_resource() {
+  class new_delete : public memory_resource {
+   public:
+    void* allocate(
+        const size_t bytes,
+        const size_t alignment = folly::max_align_v) override {
+      (void)alignment;
+      void* p = static_cast<void*>(new char[bytes]);
+      DEBUG_PRINT(this << " " << p << " " << bytes);
+      return p;
+    }
+    void deallocate(
+        void* p,
+        const size_t bytes,
+        const size_t alignment = folly::max_align_v) override {
+      (void)alignment;
+      (void)bytes;
+      DEBUG_PRINT(p << " " << bytes);
+      delete[] static_cast<char*>(p);
+    }
+  };
+  static new_delete mr;
+  return &mr;
+}
+
+} // namespace hazptr
+} // namespace folly
index d1869dfa3c42ca8d44b8c71fbad1e04dcc94714a..9123b8cef79809382e9d1055951323435ae4e986 100644 (file)
@@ -22,6 +22,7 @@
 /// std::pmr::memory_resource (C++17) as needed for developing a
 /// hazptr prototype.
 ////////////////////////////////////////////////////////////////////////////////
+
 #include <folly/Portability.h>
 #include <memory>
 
@@ -44,51 +45,5 @@ memory_resource* get_default_resource();
 void set_default_resource(memory_resource*);
 memory_resource* new_delete_resource();
 
-////////////////////////////////////////////////////////////////////////////////
-/// Implementation
-////////////////////////////////////////////////////////////////////////////////
-
-inline memory_resource** default_mr_ptr() {
-  /* library-local */ static memory_resource* default_mr =
-      new_delete_resource();
-  DEBUG_PRINT(&default_mr << " " << default_mr);
-  return &default_mr;
-}
-
-inline memory_resource* get_default_resource() {
-  DEBUG_PRINT("");
-  return *default_mr_ptr();
-}
-
-inline void set_default_resource(memory_resource* mr) {
-  DEBUG_PRINT("");
-  *default_mr_ptr() = mr;
-}
-
-inline memory_resource* new_delete_resource() {
-  class new_delete : public memory_resource {
-   public:
-    void* allocate(
-        const size_t bytes,
-        const size_t alignment = folly::max_align_v) override {
-      (void)alignment;
-      void* p = static_cast<void*>(new char[bytes]);
-      DEBUG_PRINT(this << " " << p << " " << bytes);
-      return p;
-    }
-    void deallocate(
-        void* p,
-        const size_t bytes,
-        const size_t alignment = folly::max_align_v) override {
-      (void)alignment;
-      (void)bytes;
-      DEBUG_PRINT(p << " " << bytes);
-      delete[] static_cast<char*>(p);
-    }
-  };
-  static new_delete mr;
-  return &mr;
-}
-
 } // namespace folly
 } // namespace hazptr
index 301aaa805d53cf53f75ac4497d674f6af0452560..04cadd37ed38cdd99753ec3e854c69dea62ff656 100644 (file)
@@ -198,12 +198,11 @@ TEST_F(HazptrTest, LIFO) {
 
 TEST_F(HazptrTest, SWMRLIST) {
   using T = uint64_t;
-  hazptr_domain custom_domain;
 
   CHECK_GT(FLAGS_num_threads, 0);
   for (int i = 0; i < FLAGS_num_reps; ++i) {
     DEBUG_PRINT("========== start of rep scope");
-    SWMRListSet<T> s(custom_domain);
+    SWMRListSet<T> s;
     std::vector<std::thread> threads(FLAGS_num_threads);
     for (int tid = 0; tid < FLAGS_num_threads; ++tid) {
       threads[tid] = std::thread([&s, tid]() {