/* * Copyright 2016-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. * 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. */ #pragma once #define HAZPTR_H #include /* Stand-in for C++17 std::pmr::memory_resource */ #include namespace folly { namespace hazptr { /** hazptr_rec: Private class that contains hazard pointers. */ class hazptr_rec; /** hazptr_obj: Private class for objects protected by hazard pointers. */ class hazptr_obj; /** hazptr_obj_base: Base template for objects protected by hazard pointers. */ template class hazptr_obj_base; /** hazptr_obj_base_refcounted: * Base template for reference counted objects protected by hazard pointers. */ template class hazptr_obj_base_refcounted; /** hazptr_local: Optimized template for bulk construction and destruction of * hazard pointers */ template class hazptr_array; /** hazptr_local: Optimized template for locally-used hazard pointers */ template class hazptr_local; /** hazptr_priv: Per-thread list of retired objects pushed in bulk to domain */ class hazptr_priv; /** hazptr_domain: Class of hazard pointer domains. Each domain manages a set * of hazard pointers and a set of retired objects. */ class hazptr_domain { memory_resource* mr_; std::atomic hazptrs_ = {nullptr}; std::atomic retired_ = {nullptr}; /* Using signed int for rcount_ because it may transiently be * negative. Using signed int for all integer variables that may be * involved in calculations related to the value of rcount_. */ std::atomic hcount_ = {0}; std::atomic rcount_ = {0}; static constexpr uint64_t syncTimePeriod_{2000000000}; // in ns std::atomic syncTime_{0}; public: constexpr explicit hazptr_domain( memory_resource* = get_default_resource()) noexcept; ~hazptr_domain(); hazptr_domain(const hazptr_domain&) = delete; hazptr_domain(hazptr_domain&&) = delete; hazptr_domain& operator=(const hazptr_domain&) = delete; hazptr_domain& operator=(hazptr_domain&&) = delete; /** Free-function retire. May allocate memory */ template > void retire(T* obj, D reclaim = {}); void cleanup(); void tryTimedCleanup(); private: friend class hazptr_holder; template friend class hazptr_obj_base; template friend class hazptr_obj_base_refcounted; friend class hazptr_priv; void objRetire(hazptr_obj*); hazptr_rec* hazptrAcquire(); void hazptrRelease(hazptr_rec*) noexcept; int pushRetired(hazptr_obj* head, hazptr_obj* tail, int count); bool reachedThreshold(int rcount); void tryBulkReclaim(); void bulkReclaim(); }; /** Get the default hazptr_domain */ hazptr_domain& default_hazptr_domain(); extern hazptr_domain default_domain_; /** Free-function retire, that operates on the default domain */ template > void hazptr_retire(T* obj, D reclaim = {}); /** hazptr_cleanup * Reclaims all reclaimable objects retired to the domain before this call. */ void hazptr_cleanup(hazptr_domain& domain = default_hazptr_domain()); /** Definition of hazptr_obj */ class hazptr_obj { friend class hazptr_domain; template friend class hazptr_obj_base; template friend class hazptr_obj_base_refcounted; friend class hazptr_priv; void (*reclaim_)(hazptr_obj*); hazptr_obj* next_; public: // All constructors set next_ to this in order to catch misuse bugs like // double retire. hazptr_obj() noexcept : next_(this) {} hazptr_obj(const hazptr_obj&) noexcept : next_(this) {} hazptr_obj(hazptr_obj&&) noexcept : next_(this) {} hazptr_obj& operator=(const hazptr_obj&) { return *this; } hazptr_obj& operator=(hazptr_obj&&) { return *this; } private: void set_next(hazptr_obj* obj) { next_ = obj; } void retireCheck() { // Only for catching misuse bugs like double retire if (next_ != this) { retireCheckFail(); } } FOLLY_NOINLINE void retireCheckFail() { CHECK_EQ(next_, this); } const void* getObjPtr() const; }; /** Definition of hazptr_obj_base */ template > class hazptr_obj_base : public hazptr_obj { public: /* Retire a removed object and pass the responsibility for * reclaiming it to the hazptr library */ void retire(hazptr_domain& domain = default_hazptr_domain(), D reclaim = {}); private: D deleter_; }; /** Definition of hazptr_recounted_obj_base */ template > class hazptr_obj_base_refcounted : public hazptr_obj { public: /* Retire a removed object and pass the responsibility for * reclaiming it to the hazptr library */ void retire(hazptr_domain& domain = default_hazptr_domain(), D reclaim = {}); /* aquire_ref() increments the reference count * * acquire_ref_safe() is the same as acquire_ref() except that in * addition the caller guarantees that the call is made in a * thread-safe context, e.g., the object is not yet shared. This is * just an optimization to save an atomic operation. * * release_ref() decrements the reference count and returns true if * the object is safe to reclaim. */ void acquire_ref(); void acquire_ref_safe(); bool release_ref(); private: void preRetire(D deleter); std::atomic refcount_{0}; D deleter_; }; /** hazptr_holder: Class for automatic acquisition and release of * hazard pointers, and interface for hazard pointer operations. */ class hazptr_holder { template friend class hazptr_array; template friend class hazptr_local; public: /* Constructor automatically acquires a hazard pointer. */ explicit hazptr_holder(hazptr_domain& domain = default_hazptr_domain()); /* Construct an empty hazptr_holder. */ // Note: This diverges from the proposal in P0233R4 explicit hazptr_holder(std::nullptr_t) noexcept; /* Destructor automatically clears and releases the owned hazard pointer. */ ~hazptr_holder(); hazptr_holder(const hazptr_holder&) = delete; hazptr_holder& operator=(const hazptr_holder&) = delete; // Note: This diverges from the proposal in P0233R4 which disallows // move constructor and assignment operator. hazptr_holder(hazptr_holder&&) noexcept; hazptr_holder& operator=(hazptr_holder&&) noexcept; /** Hazard pointer operations */ /* Returns a protected pointer from the source */ template T* get_protected(const std::atomic& src) noexcept; /* Returns a protected pointer from the source, filtering the protected pointer through function Func. Useful for stealing bits of the pointer word */ template T* get_protected(const std::atomic& src, Func f) noexcept; /* Return true if successful in protecting ptr if src == ptr after * setting the hazard pointer. Otherwise sets ptr to src. */ template bool try_protect(T*& ptr, const std::atomic& src) noexcept; /* Return true if successful in protecting ptr if src == ptr after * setting the hazard pointer, filtering the pointer through Func. * Otherwise sets ptr to src. */ template bool try_protect(T*& ptr, const std::atomic& src, Func f) noexcept; /* Set the hazard pointer to ptr */ template void reset(const T* ptr) noexcept; /* Set the hazard pointer to nullptr */ void reset(std::nullptr_t = nullptr) noexcept; /* Swap ownership of hazard pointers between hazptr_holder-s. */ /* Note: The owned hazard pointers remain unmodified during the swap * and continue to protect the respective objects that they were * protecting before the swap, if any. */ void swap(hazptr_holder&) noexcept; private: hazptr_domain* domain_; hazptr_rec* hazptr_; }; void swap(hazptr_holder&, hazptr_holder&) noexcept; using aligned_hazptr_holder = typename std:: aligned_storage::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 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 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 slow_path_{false}; }; } // namespace hazptr } // namespace folly #include