cachelib/allocator/CacheItem-inl.h (391 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. */ // CacheItem implementations. namespace facebook { namespace cachelib { template <typename CacheTrait> uint32_t CacheItem<CacheTrait>::getRequiredSize(Key key, uint32_t size) noexcept { const uint64_t requiredSize = static_cast<uint64_t>(size) + key.size() + sizeof(Item); XDCHECK_LE(requiredSize, static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())); if (requiredSize > static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())) { return 0; } return static_cast<uint32_t>(requiredSize); } template <typename CacheTrait> uint64_t CacheItem<CacheTrait>::getRefcountMax() noexcept { return RefcountWithFlags::kAccessRefMask; } template <typename CacheTrait> CacheItem<CacheTrait>::CacheItem(Key key, uint32_t size, uint32_t creationTime, uint32_t expiryTime) : creationTime_(creationTime), expiryTime_(expiryTime), alloc_(key, size) {} template <typename CacheTrait> CacheItem<CacheTrait>::CacheItem(Key key, uint32_t size, uint32_t creationTime) : CacheItem(key, size, creationTime, 0 /* expiryTime_ */) {} template <typename CacheTrait> const typename CacheItem<CacheTrait>::Key CacheItem<CacheTrait>::getKey() const noexcept { return alloc_.getKey(); } template <typename CacheTrait> const void* CacheItem<CacheTrait>::getMemory() const noexcept { return getMemoryInternal(); } template <typename CacheTrait> void* CacheItem<CacheTrait>::getMemory() noexcept { return getMemoryInternal(); } template <typename CacheTrait> void* CacheItem<CacheTrait>::getMemoryInternal() const noexcept { if (isChainedItem()) { return asChainedItem().getMemory(); } else { return alloc_.getMemory(); } } template <typename CacheTrait> uint32_t CacheItem<CacheTrait>::getOffsetForMemory() const noexcept { return reinterpret_cast<uintptr_t>(getMemory()) - reinterpret_cast<uintptr_t>(this); } template <typename CacheTrait> uint32_t CacheItem<CacheTrait>::getSize() const noexcept { if (isChainedItem()) { return asChainedItem().getSize(); } else { return alloc_.getSize(); } } template <typename CacheTrait> uint32_t CacheItem<CacheTrait>::getExpiryTime() const noexcept { return expiryTime_; } template <typename CacheTrait> bool CacheItem<CacheTrait>::isExpired() const noexcept { thread_local uint32_t staleTime = 0; if (expiryTime_ == 0) { return false; } if (expiryTime_ < staleTime) { return true; } uint32_t currentTime = static_cast<uint32_t>(util::getCurrentTimeSec()); if (currentTime != staleTime) { staleTime = currentTime; } return expiryTime_ < currentTime; } template <typename CacheTrait> bool CacheItem<CacheTrait>::isExpired(uint32_t currentTimeSec) const noexcept { return (expiryTime_ > 0 && expiryTime_ < currentTimeSec); } template <typename CacheTrait> uint32_t CacheItem<CacheTrait>::getCreationTime() const noexcept { return creationTime_; } template <typename CacheTrait> std::chrono::seconds CacheItem<CacheTrait>::getConfiguredTTL() const noexcept { return std::chrono::seconds(expiryTime_ > 0 ? expiryTime_ - creationTime_ : 0); } template <typename CacheTrait> uint32_t CacheItem<CacheTrait>::getLastAccessTime() const noexcept { return mmHook_.getUpdateTime(); } template <typename CacheTrait> std::string CacheItem<CacheTrait>::toString() const { if (isChainedItem()) { return asChainedItem().toString(); } else { return folly::sformat( "item: " "memory={}:raw-ref={}:size={}:key={}:hex-key={}:" "isInMMContainer={}:isAccessible={}:isMoving={}:references={}:ctime={}:" "expTime={}:updateTime={}:isNvmClean={}:isNvmEvicted={}:hasChainedItem=" "{}", this, getRefCountAndFlagsRaw(), getSize(), folly::humanify(getKey().str()), folly::hexlify(getKey()), isInMMContainer(), isAccessible(), isMoving(), getRefCount(), getCreationTime(), getExpiryTime(), getLastAccessTime(), isNvmClean(), isNvmEvicted(), hasChainedItem()); } } template <typename CacheTrait> void CacheItem<CacheTrait>::changeKey(Key key) { if (!isChainedItem()) { throw std::invalid_argument("Item is not chained type"); } alloc_.changeKey(key); XDCHECK_EQ(key, getKey()); } template <typename CacheTrait> RefcountWithFlags::Value CacheItem<CacheTrait>::getRefCount() const noexcept { return ref_.getAccessRef(); } template <typename CacheTrait> RefcountWithFlags::Value CacheItem<CacheTrait>::getRefCountAndFlagsRaw() const noexcept { return ref_.getRaw(); } template <typename CacheTrait> bool CacheItem<CacheTrait>::isDrained() const noexcept { return ref_.isDrained(); } template <typename CacheTrait> bool CacheItem<CacheTrait>::isExclusive() const noexcept { return ref_.isExclusive(); } template <typename CacheTrait> void CacheItem<CacheTrait>::markAccessible() noexcept { ref_.markAccessible(); } template <typename CacheTrait> void CacheItem<CacheTrait>::unmarkAccessible() noexcept { ref_.unmarkAccessible(); } template <typename CacheTrait> void CacheItem<CacheTrait>::markInMMContainer() noexcept { ref_.markInMMContainer(); } template <typename CacheTrait> void CacheItem<CacheTrait>::unmarkInMMContainer() noexcept { ref_.unmarkInMMContainer(); } template <typename CacheTrait> bool CacheItem<CacheTrait>::isAccessible() const noexcept { return ref_.isAccessible(); } template <typename CacheTrait> bool CacheItem<CacheTrait>::isInMMContainer() const noexcept { return ref_.isInMMContainer(); } template <typename CacheTrait> bool CacheItem<CacheTrait>::markMoving() noexcept { return ref_.markMoving(); } template <typename CacheTrait> void CacheItem<CacheTrait>::unmarkMoving() noexcept { ref_.unmarkMoving(); } template <typename CacheTrait> bool CacheItem<CacheTrait>::isMoving() const noexcept { return ref_.isMoving(); } template <typename CacheTrait> bool CacheItem<CacheTrait>::isOnlyMoving() const noexcept { return ref_.isOnlyMoving(); } template <typename CacheTrait> void CacheItem<CacheTrait>::markNvmClean() noexcept { ref_.markNvmClean(); } template <typename CacheTrait> void CacheItem<CacheTrait>::unmarkNvmClean() noexcept { ref_.unmarkNvmClean(); } template <typename CacheTrait> bool CacheItem<CacheTrait>::isNvmClean() const noexcept { return ref_.isNvmClean(); } template <typename CacheTrait> void CacheItem<CacheTrait>::markNvmEvicted() noexcept { ref_.markNvmEvicted(); } template <typename CacheTrait> void CacheItem<CacheTrait>::unmarkNvmEvicted() noexcept { ref_.unmarkNvmEvicted(); } template <typename CacheTrait> bool CacheItem<CacheTrait>::isNvmEvicted() const noexcept { return ref_.isNvmEvicted(); } template <typename CacheTrait> void CacheItem<CacheTrait>::markIsChainedItem() noexcept { XDCHECK(!hasChainedItem()); ref_.markIsChainedItem(); } template <typename CacheTrait> void CacheItem<CacheTrait>::unmarkIsChainedItem() noexcept { XDCHECK(!hasChainedItem()); ref_.unmarkIsChainedItem(); } template <typename CacheTrait> void CacheItem<CacheTrait>::markHasChainedItem() noexcept { XDCHECK(!isChainedItem()); ref_.markHasChainedItem(); } template <typename CacheTrait> void CacheItem<CacheTrait>::unmarkHasChainedItem() noexcept { XDCHECK(!isChainedItem()); ref_.unmarkHasChainedItem(); } template <typename CacheTrait> typename CacheItem<CacheTrait>::ChainedItem& CacheItem<CacheTrait>::asChainedItem() noexcept { return *static_cast<ChainedItem*>(this); } template <typename CacheTrait> const typename CacheItem<CacheTrait>::ChainedItem& CacheItem<CacheTrait>::asChainedItem() const noexcept { return *static_cast<const ChainedItem*>(this); } template <typename CacheTrait> bool CacheItem<CacheTrait>::isChainedItem() const noexcept { return ref_.isChainedItem(); } template <typename CacheTrait> bool CacheItem<CacheTrait>::hasChainedItem() const noexcept { return ref_.hasChainedItem(); } template <typename CacheTrait> template <typename RefcountWithFlags::Flags flagBit> void CacheItem<CacheTrait>::setFlag() noexcept { ref_.template setFlag<flagBit>(); } template <typename CacheTrait> template <typename RefcountWithFlags::Flags flagBit> void CacheItem<CacheTrait>::unSetFlag() noexcept { ref_.template unSetFlag<flagBit>(); } template <typename CacheTrait> template <typename RefcountWithFlags::Flags flagBit> bool CacheItem<CacheTrait>::isFlagSet() const noexcept { return ref_.template isFlagSet<flagBit>(); } template <typename CacheTrait> bool CacheItem<CacheTrait>::updateExpiryTime(uint32_t expiryTimeSecs) noexcept { // check for moving to make sure we are not updating the expiry time while at // the same time re-allocating the item with the old state of the expiry time // in moveRegularItem(). See D6852328 if (isMoving() || !isInMMContainer() || isChainedItem()) { return false; } // attempt to atomically update the value of expiryTime while (true) { uint32_t currExpTime = expiryTime_; if (__sync_bool_compare_and_swap(&expiryTime_, currExpTime, expiryTimeSecs)) { return true; } } } template <typename CacheTrait> bool CacheItem<CacheTrait>::extendTTL(std::chrono::seconds ttl) noexcept { return updateExpiryTime(util::getCurrentTimeSec() + ttl.count()); } // Chained items are chained in a single linked list. The payload of each // chained item is chained to the next item. template <typename CacheTrait> class CACHELIB_PACKED_ATTR ChainedItemPayload { public: using ChainedItem = CacheChainedItem<CacheTrait>; using Item = CacheItem<CacheTrait>; using PtrCompressor = typename ChainedItem::PtrCompressor; // Pointer to the next chained allocation. initialize to nullptr. SListHook<Item> hook_{}; // Payload mutable unsigned char data_[0]; // Usable memory for this allocation. The caller is free to do whatever he // wants with it and needs to ensure concurrency for access into this // piece of memory. void* getMemory() const noexcept { return &data_; } ChainedItem* getNext(const PtrCompressor& compressor) const noexcept { return static_cast<ChainedItem*>(hook_.getNext(compressor)); } void setNext(const ChainedItem* next, const PtrCompressor& compressor) noexcept { hook_.setNext(static_cast<const Item*>(next), compressor); XDCHECK_EQ(reinterpret_cast<uintptr_t>(getNext(compressor)), reinterpret_cast<uintptr_t>(next)); } }; template <typename CacheTrait> uint32_t CacheChainedItem<CacheTrait>::getRequiredSize(uint32_t size) noexcept { const uint64_t requiredSize = static_cast<uint64_t>(size) + static_cast<uint64_t>(kKeySize) + sizeof(Item) + sizeof(Payload); XDCHECK_LE(requiredSize, static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())); if (requiredSize > static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())) { return 0; } return static_cast<uint32_t>(requiredSize); } template <typename CacheTrait> CacheChainedItem<CacheTrait>::CacheChainedItem(CompressedPtr ptr, uint32_t size, uint32_t creationTime) : Item(Key{reinterpret_cast<const char*>(&ptr), kKeySize}, size + sizeof(Payload), creationTime) { this->markIsChainedItem(); // Explicitly call ChainedItemPayload's ctor to initialize it properly, since // ChainedItemPayload is not a member of CacheChainedItem. new (reinterpret_cast<void*>(&getPayload())) Payload(); } template <typename CacheTrait> void CacheChainedItem<CacheTrait>::changeKey(CompressedPtr newKey) { if (this->isAccessible()) { throw std::invalid_argument(folly::sformat( "chained item {} is still in access container while modifying the key ", toString())); } Item::changeKey(Key{reinterpret_cast<const char*>(&newKey), kKeySize}); } template <typename CacheTrait> typename CacheChainedItem<CacheTrait>::Item& CacheChainedItem<CacheTrait>::getParentItem( const PtrCompressor& compressor) const noexcept { const auto compressedPtr = *reinterpret_cast<const CompressedPtr*>(this->getKey().begin()); return *compressor.unCompress(compressedPtr); } template <typename CacheTrait> void* CacheChainedItem<CacheTrait>::getMemory() const noexcept { return getPayload().getMemory(); } template <typename CacheTrait> uint32_t CacheChainedItem<CacheTrait>::getSize() const noexcept { // Chained Item has its own embedded payload in its KAllocation, so we // need to deduct its size here to give the user the accurate usable size return this->alloc_.getSize() - sizeof(Payload); } template <typename CacheTrait> std::string CacheChainedItem<CacheTrait>::toString() const { const auto cPtr = *reinterpret_cast<const CompressedPtr*>(Item::getKey().data()); return folly::sformat( "chained item: " "memory={}:raw-ref={}:size={}:parent-compressed-ptr={}:" "isInMMContainer={}:isAccessible={}:isMoving={}:references={}:ctime={}:" "expTime={}:updateTime={}", this, Item::getRefCountAndFlagsRaw(), Item::getSize(), cPtr.getRaw(), Item::isInMMContainer(), Item::isAccessible(), Item::isMoving(), Item::getRefCount(), Item::getCreationTime(), Item::getExpiryTime(), Item::getLastAccessTime()); } template <typename CacheTrait> void CacheChainedItem<CacheTrait>::appendChain( ChainedItem& newChain, const PtrCompressor& compressor) { if (getNext(compressor)) { throw std::invalid_argument( folly::sformat("Item: {} is not the last item in a chain. Next: {}", toString(), getNext(compressor)->toString())); } setNext(&newChain, compressor); } template <typename CacheTrait> typename CacheChainedItem<CacheTrait>::ChainedItem* CacheChainedItem<CacheTrait>::getNext( const PtrCompressor& compressor) const noexcept { return getPayload().getNext(compressor); } template <typename CacheTrait> void CacheChainedItem<CacheTrait>::setNext( const ChainedItem* next, const PtrCompressor& compressor) noexcept { getPayload().setNext(next, compressor); XDCHECK_EQ(reinterpret_cast<uintptr_t>(getNext(compressor)), reinterpret_cast<uintptr_t>(next)); } template <typename CacheTrait> typename CacheChainedItem<CacheTrait>::Payload& CacheChainedItem<CacheTrait>::getPayload() { return *reinterpret_cast<Payload*>(this->alloc_.getMemory()); } template <typename CacheTrait> const typename CacheChainedItem<CacheTrait>::Payload& CacheChainedItem<CacheTrait>::getPayload() const { return *reinterpret_cast<const Payload*>(this->alloc_.getMemory()); } } // namespace cachelib } // namespace facebook