From: Maged Michael Date: Wed, 10 Jan 2018 16:40:46 +0000 (-0800) Subject: Add hazptr_obj_batch X-Git-Tag: v2018.01.15.00~22 X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=commitdiff_plain;h=028415ba5c1fee579d249a97dd49752fe3c7348b Add hazptr_obj_batch Summary: Provide capability for custom batching retirement of objects, for example object that belong to the same data structure, to avoid the risk fragmenting related objects across thread local lists of retired objects of many threads, which could lead to unnecessarily high memory usage. Reviewed By: djwatson Differential Revision: D6686603 fbshipit-source-id: fadcade73e71170ef1bcec221c4da6f4ddeecff5 --- diff --git a/folly/experimental/hazptr/hazptr-impl.h b/folly/experimental/hazptr/hazptr-impl.h index 5c343c5c..83f43b6f 100644 --- a/folly/experimental/hazptr/hazptr-impl.h +++ b/folly/experimental/hazptr/hazptr-impl.h @@ -216,14 +216,7 @@ inline void hazptr_obj_base_refcounted::retire( hazptr_domain& domain, D deleter) { DEBUG_PRINT(this << " " << &domain); - deleter_ = std::move(deleter); - reclaim_ = [](hazptr_obj* p) { - auto hrobp = static_cast(p); - if (hrobp->release_ref()) { - auto obj = static_cast(hrobp); - hrobp->deleter_(obj); - } - }; + preRetire(deleter); if (HAZPTR_PRIV && (HAZPTR_ONE_DOMAIN || (&domain == &default_hazptr_domain()))) { if (hazptr_priv_try_retire(this)) { @@ -264,6 +257,19 @@ inline bool hazptr_obj_base_refcounted::release_ref() { return oldval == 0; } +template +inline void hazptr_obj_base_refcounted::preRetire(D deleter) { + DCHECK(next_ == nullptr); + deleter_ = std::move(deleter); + reclaim_ = [](hazptr_obj* p) { + auto hrobp = static_cast(p); + if (hrobp->release_ref()) { + auto obj = static_cast(hrobp); + hrobp->deleter_(obj); + } + }; +} + /** * hazptr_rec */ @@ -680,6 +686,7 @@ inline hazptr_domain::~hazptr_domain() { while (retired) { for (auto p = retired; p; p = next) { next = p->next_; + DCHECK(p != next); DEBUG_PRINT(this << " " << p << " " << p->reclaim_); (*(p->reclaim_))(p); } @@ -787,6 +794,7 @@ inline void hazptr_domain::bulkReclaim() { hazptr_obj* next; for (; p; p = next) { next = p->next_; + DCHECK(p != next); if (hs.count(p->getObjPtr()) == 0) { DEBUG_PRINT(this << " " << p << " " << p->reclaim_); (*(p->reclaim_))(p); @@ -1071,5 +1079,73 @@ inline hazptr_tls_life::~hazptr_tls_life() { tls_state_ = TLS_DESTROYED; } +/** hazptr_obj_batch */ +/* Only for default domain. Supports only hazptr_obj_base_refcounted + * and a thread-safe access only, for now. */ + +class hazptr_obj_batch { + static constexpr size_t DefaultThreshold = 20; + hazptr_obj* head_{nullptr}; + hazptr_obj* tail_{nullptr}; + size_t rcount_{0}; + size_t threshold_{DefaultThreshold}; + + public: + hazptr_obj_batch() {} + hazptr_obj_batch(hazptr_obj* head, hazptr_obj* tail, size_t rcount) + : head_(head), tail_(tail), rcount_(rcount) {} + + ~hazptr_obj_batch() { + retire_all(); + } + + /* Prepare a hazptr_obj_base_refcounted for retirement but don't + push it the domain yet. Return true if the batch is ready. */ + template > + hazptr_obj_batch prep_retire_refcounted( + hazptr_obj_base_refcounted* obj, + D deleter = {}) { + obj->preRetire(deleter); + obj->next_ = head_; + head_ = obj; + if (tail_ == nullptr) { + tail_ = obj; + } + if (++rcount_ < threshold_) { + return hazptr_obj_batch(); + } else { + auto head = head_; + auto tail = tail_; + auto rcount = rcount_; + clear(); + return hazptr_obj_batch(head, tail, rcount); + } + } + + bool empty() { + return rcount_ == 0; + } + + void retire_all() { + if (!empty()) { + auto& domain = default_hazptr_domain(); + domain.pushRetired(head_, tail_, rcount_); + domain.tryBulkReclaim(); + clear(); + } + } + + void set_threshold(size_t thresh) { + threshold_ = thresh; + } + + private: + void clear() { + head_ = nullptr; + tail_ = nullptr; + rcount_ = 0; + } +}; + } // namespace hazptr } // namespace folly diff --git a/folly/experimental/hazptr/hazptr.h b/folly/experimental/hazptr/hazptr.h index 48e8392a..392761e2 100644 --- a/folly/experimental/hazptr/hazptr.h +++ b/folly/experimental/hazptr/hazptr.h @@ -1,5 +1,5 @@ /* - * Copyright 2017 Facebook, Inc. + * Copyright 2017-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -67,6 +67,7 @@ class hazptr_domain { void retire(T* obj, D reclaim = {}); private: + friend class hazptr_obj_batch; friend class hazptr_holder; template friend class hazptr_obj_base; @@ -100,6 +101,7 @@ void hazptr_retire(T* obj, D reclaim = {}); /** Definition of hazptr_obj */ class hazptr_obj { + friend class hazptr_obj_batch; friend class hazptr_domain; template friend class hazptr_obj_base; @@ -108,7 +110,7 @@ class hazptr_obj { friend struct hazptr_priv; void (*reclaim_)(hazptr_obj*); - hazptr_obj* next_; + hazptr_obj* next_{nullptr}; // nullptr for debugging const void* getObjPtr() const; }; @@ -128,6 +130,8 @@ class hazptr_obj_base : public hazptr_obj { /** Definition of hazptr_recounted_obj_base */ template > class hazptr_obj_base_refcounted : public hazptr_obj { + friend class hazptr_obj_batch; + public: /* Retire a removed object and pass the responsibility for * reclaiming it to the hazptr library */ @@ -148,6 +152,8 @@ class hazptr_obj_base_refcounted : public hazptr_obj { bool release_ref(); private: + void preRetire(D deleter); + std::atomic refcount_{0}; D deleter_; };