cachelib/allocator/ChainedAllocs.h (57 lines of code) (raw):

/* * Copyright (c) Facebook, Inc. and its affiliates. * * 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 #include <stdexcept> namespace facebook { namespace cachelib { // exposes the parent and its chain of allocations through an iterator and // index. The chain is traversed in the LIFO order. The caller needs to ensure // that there are no concurrent addChainedItem or popChainedItem while this // happens. template <typename Cache, typename Handle, typename Iter> class CacheChainedAllocs { public: using Item = typename Cache::Item; using ChainedItem = typename Iter::Item; CacheChainedAllocs(CacheChainedAllocs&&) = default; CacheChainedAllocs& operator=(CacheChainedAllocs&&) = default; // return the parent of the chain. const Item& getParentItem() const noexcept { return *parent_; } // iterate and compute the length of the chain. This is O(N) computation. // // @return the length of the chain size_t computeChainLength() const { const auto chain = getChain(); return std::distance(chain.begin(), chain.end()); } // return the nTh in the chain from the beginning. n = 0 is the first in the // chain and last inserted. ChainedItem* getNthInChain(size_t n) { size_t i = 0; for (auto& c : getChain()) { if (i++ == n) { return &c; } } return nullptr; } folly::Range<Iter> getChain() const { return folly::Range<Iter>{Iter{&head_, compressor_}, Iter{}}; } private: friend Cache; using LockType = typename Cache::ChainedItemLock; using ReadLockHolder = typename LockType::ReadLockHolder; using PtrCompressor = typename Item::PtrCompressor; CacheChainedAllocs(const CacheChainedAllocs&) = delete; CacheChainedAllocs& operator=(const CacheChainedAllocs&) = delete; // only the cache can create this view of chained allocs // // @param l the lock to be held while iterating on the chain // @param parent handle to the parent // @param head beginning of the chain of the allocations // @param c pointer compressor to traverse the chain CacheChainedAllocs(ReadLockHolder l, Handle parent, Item& head, const PtrCompressor& c) : lock_(std::move(l)), parent_(std::move(parent)), head_(head), compressor_(c) { if (!parent_ || !parent_->hasChainedItem()) { throw std::invalid_argument("Parent does not have a chain"); } if (!head_.isChainedItem()) { throw std::invalid_argument("Head of chained allocation is invalid"); } } // lock protecting the traversal of the chain ReadLockHolder lock_; // handle to the parent item. holding this ensures that remaining of the // chain is not evicted. Handle parent_; // verify this would not cause issues with the moving slab release logic. // Evicting logic is fine since it looks for the parent's refcount Item& head_; // pointer compressor to traverse the chain. const PtrCompressor& compressor_; }; } // namespace cachelib } // namespace facebook