2 * Copyright 2017 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
34 /** hazptr_obj_base */
36 template <typename T, typename D>
37 inline void hazptr_obj_base<T, D>::retire(hazptr_domain& domain, D deleter) {
38 DEBUG_PRINT(this << " " << &domain);
39 deleter_ = std::move(deleter);
40 reclaim_ = [](hazptr_obj* p) {
41 auto hobp = static_cast<hazptr_obj_base*>(p);
42 auto obj = static_cast<T*>(hobp);
45 domain.objRetire(this);
51 friend class hazptr_domain;
52 template <typename> friend class hazptr_owner;
54 std::atomic<const void*> hazptr_ = {nullptr};
55 hazptr_rec* next_ = {nullptr};
56 std::atomic<bool> active_ = {false};
58 void set(const void* p) noexcept;
59 const void* get() const noexcept;
60 void clear() noexcept;
61 void release() noexcept;
67 inline hazptr_owner<T>::hazptr_owner(hazptr_domain& domain) {
69 hazptr_ = domain_->hazptrAcquire();
70 DEBUG_PRINT(this << " " << domain_ << " " << hazptr_);
71 if (hazptr_ == nullptr) { std::bad_alloc e; throw e; }
75 hazptr_owner<T>::~hazptr_owner() {
77 domain_->hazptrRelease(hazptr_);
82 inline bool hazptr_owner<T>::try_protect(T*& ptr, const A& src) noexcept {
84 std::is_same<decltype(std::declval<A>().load()), T*>::value,
85 "Return type of A::load() must be T*");
86 DEBUG_PRINT(this << " " << ptr << " " << &src);
99 inline T* hazptr_owner<T>::get_protected(const A& src) noexcept {
101 std::is_same<decltype(std::declval<A>().load()), T*>::value,
102 "Return type of A::load() must be T*");
104 while (!try_protect(p, src)) {}
105 DEBUG_PRINT(this << " " << p << " " << &src);
109 template <typename T>
110 inline void hazptr_owner<T>::set(const T* ptr) noexcept {
111 auto p = static_cast<hazptr_obj*>(const_cast<T*>(ptr));
112 DEBUG_PRINT(this << " " << ptr << " p:" << p);
116 template <typename T>
117 inline void hazptr_owner<T>::clear() noexcept {
122 template <typename T>
123 inline void hazptr_owner<T>::swap(hazptr_owner<T>& rhs) noexcept {
125 this << " " << this->hazptr_ << " " << this->domain_ << " -- "
126 << &rhs << " " << rhs.hazptr_ << " " << rhs.domain_);
127 std::swap(this->domain_, rhs.domain_);
128 std::swap(this->hazptr_, rhs.hazptr_);
131 template <typename T>
132 inline void swap(hazptr_owner<T>& lhs, hazptr_owner<T>& rhs) noexcept {
136 ////////////////////////////////////////////////////////////////////////////////
137 // Non-template part of implementation
138 ////////////////////////////////////////////////////////////////////////////////
140 // - Thread caching of hazptr_rec-s
141 // - Private storage of retired objects
142 // - Control of reclamation (when and by whom)
143 // - Optimized memory order
145 /** Definition of default_hazptr_domain() */
146 inline hazptr_domain& default_hazptr_domain() {
147 static hazptr_domain d;
154 inline void hazptr_rec::set(const void* p) noexcept {
155 DEBUG_PRINT(this << " " << p);
159 inline const void* hazptr_rec::get() const noexcept {
160 DEBUG_PRINT(this << " " << hazptr_.load());
161 return hazptr_.load();
164 inline void hazptr_rec::clear() noexcept {
166 hazptr_.store(nullptr);
169 inline void hazptr_rec::release() noexcept {
172 active_.store(false);
177 inline const void* hazptr_obj::getObjPtr() const {
184 inline hazptr_domain::~hazptr_domain() {
186 { /* reclaim all remaining retired objects */
188 auto retired = retired_.exchange(nullptr);
190 for (auto p = retired; p; p = next) {
194 retired = retired_.exchange(nullptr);
197 { /* free all hazptr_rec-s */
199 for (auto p = hazptrs_.load(); p; p = next) {
201 mr_->deallocate(static_cast<void*>(p), sizeof(hazptr_rec));
206 inline void hazptr_domain::try_reclaim() {
212 inline hazptr_rec* hazptr_domain::hazptrAcquire() {
215 for (p = hazptrs_.load(); p; p = next) {
217 bool active = p->active_.load();
219 if (p->active_.compare_exchange_weak(active, true)) {
220 DEBUG_PRINT(this << " " << p);
225 p = static_cast<hazptr_rec*>(mr_->allocate(sizeof(hazptr_rec)));
229 p->active_.store(true);
231 p->next_ = hazptrs_.load();
232 if (hazptrs_.compare_exchange_weak(p->next_, p)) {
236 auto hcount = hcount_.fetch_add(1);
237 DEBUG_PRINT(this << " " << p << " " << sizeof(hazptr_rec) << " " << hcount);
241 inline void hazptr_domain::hazptrRelease(hazptr_rec* p) noexcept {
242 DEBUG_PRINT(this << " " << p);
247 hazptr_domain::pushRetired(hazptr_obj* head, hazptr_obj* tail, int count) {
248 tail->next_ = retired_.load();
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::tryBulkReclaim() {
263 auto hcount = hcount_.load();
264 auto rcount = rcount_.load();
265 if (rcount < kScanThreshold * hcount) {
268 if (rcount_.compare_exchange_weak(rcount, 0)) {
275 inline void hazptr_domain::bulkReclaim() {
277 auto p = retired_.exchange(nullptr);
278 auto h = hazptrs_.load();
279 std::unordered_set<const void*> hs;
280 for (; h; h = h->next_) {
281 hs.insert(h->hazptr_.load());
284 hazptr_obj* retired = nullptr;
285 hazptr_obj* tail = nullptr;
287 for (; p; p = next) {
289 if (hs.count(p->getObjPtr()) == 0) {
290 DEBUG_PRINT(this << " " << p << " " << p->reclaim_);
295 if (tail == nullptr) {
302 pushRetired(retired, tail, rcount);
307 } // namespace hazptr