2 * Copyright 2016 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 /* override-include-guard */
19 #error "This should only be included by hazptr.h"
22 #include <folly/experimental/hazptr/debug.h>
24 #include <unordered_set>
31 constexpr hazptr_domain::hazptr_domain(memory_resource* mr) noexcept
35 void hazptr_domain::flush(const hazptr_obj_reclaim<T>* reclaim) {
36 DEBUG_PRINT(this << " " << reclaim);
37 flush(reinterpret_cast<const hazptr_obj_reclaim<void>*>(reclaim));
41 inline void hazptr_domain::objRetire(hazptr_obj_base<T>* p) {
42 DEBUG_PRINT(this << " " << p);
43 objRetire(reinterpret_cast<hazptr_obj_base<void>*>(p));
46 /** hazptr_obj_base */
49 inline void hazptr_obj_base<T>::retire(
50 hazptr_domain* domain,
51 const hazptr_obj_reclaim<T>* reclaim,
52 const storage_policy /* policy */) {
53 DEBUG_PRINT(this << " " << reclaim << " " << &domain);
55 domain->objRetire<T>(this);
58 /* Definition of default_hazptr_obj_reclaim */
61 inline hazptr_obj_reclaim<T>* default_hazptr_obj_reclaim() {
62 static hazptr_obj_reclaim<T> fn = [](T* p) {
63 DEBUG_PRINT("default_hazptr_obj_reclaim " << p << " " << sizeof(T));
73 friend class hazptr_domain;
74 template <typename> friend class hazptr_owner;
76 std::atomic<const void*> hazptr_ = {nullptr};
77 hazptr_rec* next_ = {nullptr};
78 std::atomic<bool> active_ = {false};
80 void set(const void* p) noexcept;
81 const void* get() const noexcept;
82 void clear() noexcept;
83 void release() noexcept;
89 inline hazptr_owner<T>::hazptr_owner(
90 hazptr_domain* domain,
91 const cache_policy /* policy */) {
93 hazptr_ = domain_->hazptrAcquire();
94 DEBUG_PRINT(this << " " << domain_ << " " << hazptr_);
95 if (hazptr_ == nullptr) { std::bad_alloc e; throw e; }
99 hazptr_owner<T>::~hazptr_owner() noexcept {
101 domain_->hazptrRelease(hazptr_);
104 template <typename T>
105 inline bool hazptr_owner<T>::protect(const T* ptr, const std::atomic<T*>& src)
107 DEBUG_PRINT(this << " " << ptr << " " << &src);
110 return (src.load() == ptr);
113 template <typename T>
114 inline void hazptr_owner<T>::set(const T* ptr) const noexcept {
115 DEBUG_PRINT(this << " " << ptr);
119 template <typename T>
120 inline void hazptr_owner<T>::clear() const noexcept {
125 template <typename T>
126 inline void hazptr_owner<T>::swap(hazptr_owner<T>& rhs) noexcept {
128 this << " " << this->hazptr_ << " " << this->domain_ << " -- "
129 << &rhs << " " << rhs.hazptr_ << " " << rhs.domain_);
130 std::swap(this->domain_, rhs.domain_);
131 std::swap(this->hazptr_, rhs.hazptr_);
134 template <typename T>
135 inline void swap(hazptr_owner<T>& lhs, hazptr_owner<T>& rhs) noexcept {
139 ////////////////////////////////////////////////////////////////////////////////
140 // Non-template part of implementation
141 ////////////////////////////////////////////////////////////////////////////////
143 // - Thread caching of hazptr_rec-s
144 // - Private storage of retired objects
145 // - Optimized memory order
147 /** Definition of default_hazptr_domain() */
148 inline hazptr_domain* default_hazptr_domain() {
149 static hazptr_domain d;
155 inline void hazptr_rec::set(const void* p) noexcept {
156 DEBUG_PRINT(this << " " << p);
160 inline const void* hazptr_rec::get() const noexcept {
161 DEBUG_PRINT(this << " " << hazptr_.load());
162 return hazptr_.load();
165 inline void hazptr_rec::clear() noexcept {
168 hazptr_.store(nullptr);
171 inline void hazptr_rec::release() noexcept {
175 active_.store(false);
180 inline hazptr_domain::~hazptr_domain() {
182 { /* free all hazptr_rec-s */
184 for (auto p = hazptrs_.load(); p; p = next) {
186 mr_->deallocate(static_cast<void*>(p), sizeof(hazptr_rec));
189 { /* reclaim all remaining retired objects */
191 for (auto p = retired_.load(); p; p = next) {
198 inline void hazptr_domain::flush() {
200 auto rcount = rcount_.exchange(0);
201 auto p = retired_.exchange(nullptr);
203 for (; p; p = next) {
208 rcount_.fetch_add(rcount);
211 inline hazptr_rec* hazptr_domain::hazptrAcquire() {
214 for (p = hazptrs_.load(); p; p = next) {
216 bool active = p->active_.load();
218 if (p->active_.compare_exchange_weak(active, true)) {
219 DEBUG_PRINT(this << " " << p);
224 p = static_cast<hazptr_rec*>(mr_->allocate(sizeof(hazptr_rec)));
228 p->active_.store(true);
230 p->next_ = hazptrs_.load();
231 if (hazptrs_.compare_exchange_weak(p->next_, p)) {
235 auto hcount = hcount_.fetch_add(1);
236 DEBUG_PRINT(this << " " << p << " " << sizeof(hazptr_rec) << " " << hcount);
240 inline void hazptr_domain::hazptrRelease(hazptr_rec* p) const noexcept {
241 DEBUG_PRINT(this << " " << p);
246 hazptr_domain::pushRetired(hazptr_obj* head, hazptr_obj* tail, int count) {
247 tail->next_ = retired_.load();
248 // ORDER: store-store order
249 while (!retired_.compare_exchange_weak(tail->next_, head)) {}
250 return rcount_.fetch_add(count);
253 inline void hazptr_domain::objRetire(hazptr_obj* p) {
254 auto rcount = pushRetired(p, p, 1) + 1;
255 if (rcount >= kScanThreshold * hcount_.load()) {
260 inline void hazptr_domain::bulkReclaim() {
262 auto h = hazptrs_.load();
263 auto hcount = hcount_.load();
264 auto rcount = rcount_.load();
266 if (rcount < kScanThreshold * hcount) {
269 if (rcount_.compare_exchange_weak(rcount, 0)) {
273 /* ORDER: store-load order between removing each object and scanning
274 * the hazard pointers -- can be combined in one fence */
275 std::unordered_set<const void*> hs;
276 for (; h; h = h->next_) {
277 hs.insert(h->hazptr_.load());
280 hazptr_obj* retired = nullptr;
281 hazptr_obj* tail = nullptr;
282 auto p = retired_.exchange(nullptr);
284 for (; p; p = next) {
286 if (hs.count(p) == 0) {
291 if (tail == nullptr) {
298 pushRetired(retired, tail, rcount);
302 inline void hazptr_domain::flush(const hazptr_obj_reclaim<void>* reclaim) {
303 DEBUG_PRINT(this << " " << reclaim);
304 auto rcount = rcount_.exchange(0);
305 auto p = retired_.exchange(nullptr);
306 hazptr_obj* retired = nullptr;
307 hazptr_obj* tail = nullptr;
309 for (; p; p = next) {
311 if (p->reclaim_ == reclaim) {
316 if (tail == nullptr) {
323 pushRetired(retired, tail, rcount);
329 inline void hazptr_user::flush() {
333 /** hazptr_remover */
335 inline void hazptr_remover::flush() {
340 } // namespace hazptr