cachelib/allocator/datastruct/DList.h (137 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 <folly/logging/xlog.h> #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" #include "cachelib/allocator/serialize/gen-cpp2/objects_types.h" #pragma GCC diagnostic pop #include "cachelib/common/CompilerUtils.h" namespace facebook { namespace cachelib { // node information for the double linked list modelling the lru. It has the // previous, next information with the last time the item was updated in the // LRU. template <typename T> struct CACHELIB_PACKED_ATTR DListHook { using Time = uint32_t; using CompressedPtr = typename T::CompressedPtr; using PtrCompressor = typename T::PtrCompressor; void setNext(T* const n, const PtrCompressor& compressor) noexcept { next_ = compressor.compress(n); } void setNext(CompressedPtr next) noexcept { next_ = next; } void setPrev(T* const p, const PtrCompressor& compressor) noexcept { prev_ = compressor.compress(p); } void setPrev(CompressedPtr prev) noexcept { prev_ = prev; } CompressedPtr getNext() const noexcept { return CompressedPtr(next_); } T* getNext(const PtrCompressor& compressor) const noexcept { return compressor.unCompress(next_); } CompressedPtr getPrev() const noexcept { return CompressedPtr(prev_); } T* getPrev(const PtrCompressor& compressor) const noexcept { return compressor.unCompress(prev_); } // set and get the time when the node was updated in the lru. void setUpdateTime(Time time) noexcept { updateTime_ = time; } Time getUpdateTime() const noexcept { return updateTime_; } private: CompressedPtr next_{}; // next node in the linked list CompressedPtr prev_{}; // previous node in the linked list // timestamp when this was last updated to the head of the list Time updateTime_{0}; }; // uses a double linked list to implement an LRU. T must be have a public // member of type Hook and HookPtr must point to that. template <typename T, DListHook<T> T::*HookPtr> class DList { public: using CompressedPtr = typename T::CompressedPtr; using PtrCompressor = typename T::PtrCompressor; using DListObject = serialization::DListObject; DList() = default; DList(const DList&) = delete; DList& operator=(const DList&) = delete; explicit DList(PtrCompressor compressor) noexcept : compressor_(std::move(compressor)) {} // Restore DList from saved state. // // @param object Save DList object // @param compressor PtrCompressor object DList(const DListObject& object, PtrCompressor compressor) : compressor_(std::move(compressor)), head_(compressor_.unCompress(CompressedPtr{*object.compressedHead()})), tail_(compressor_.unCompress(CompressedPtr{*object.compressedTail()})), size_(*object.size()) {} /** * Exports the current state as a thrift object for later restoration. */ DListObject saveState() const { DListObject state; *state.compressedHead() = compressor_.compress(head_).saveState(); *state.compressedTail() = compressor_.compress(tail_).saveState(); *state.size() = size_; return state; } T* getNext(const T& node) const noexcept { return (node.*HookPtr).getNext(compressor_); } T* getPrev(const T& node) const noexcept { return (node.*HookPtr).getPrev(compressor_); } void setNext(T& node, T* next) noexcept { (node.*HookPtr).setNext(next, compressor_); } void setNextFrom(T& node, const T& other) noexcept { (node.*HookPtr).setNext((other.*HookPtr).getNext()); } void setPrev(T& node, T* prev) noexcept { (node.*HookPtr).setPrev(prev, compressor_); } void setPrevFrom(T& node, const T& other) noexcept { (node.*HookPtr).setPrev((other.*HookPtr).getPrev()); } // Links the passed node to the head of the double linked list // @param node node to be linked at the head void linkAtHead(T& node) noexcept; // Links the passed node to the tail of the double linked list // @param node node to be linked at the tail void linkAtTail(T& node) noexcept; // Add node before nextNode. // // @param nextNode node before which to insert // @param node node to insert // @note nextNode must be in the list and node must not be in the list void insertBefore(T& nextNode, T& node) noexcept; // removes the node completely from the linked list and cleans up the node // appropriately by setting its next and prev as nullptr. void remove(T& node) noexcept; // Unlinks the destination node and replaces it with the source node // // @param oldNode destination node // @param newNode source node void replace(T& oldNode, T& newNode) noexcept; // moves a node that belongs to the linked list to the head of the linked // list. void moveToHead(T& node) noexcept; T* getHead() const noexcept { return head_; } T* getTail() const noexcept { return tail_; } size_t size() const noexcept { return size_; } // Iterator interface for the double linked list. Supports both iterating // from the tail and head. class Iterator { public: enum class Direction { FROM_HEAD, FROM_TAIL }; Iterator(T* p, Direction d, const DList<T, HookPtr>& dlist) noexcept : curr_(p), dir_(d), dlist_(&dlist) {} virtual ~Iterator() = default; // copyable and movable Iterator(const Iterator&) = default; Iterator& operator=(const Iterator&) = default; Iterator(Iterator&&) noexcept = default; Iterator& operator=(Iterator&&) noexcept = default; // moves the iterator forward and backward. Calling ++ once the iterator // has reached the end is undefined. Iterator& operator++() noexcept; Iterator& operator--() noexcept; T* operator->() const noexcept { return curr_; } T& operator*() const noexcept { return *curr_; } bool operator==(const Iterator& other) const noexcept { return dlist_ == other.dlist_ && curr_ == other.curr_ && dir_ == other.dir_; } bool operator!=(const Iterator& other) const noexcept { return !(*this == other); } explicit operator bool() const noexcept { return curr_ != nullptr && dlist_ != nullptr; } T* get() const noexcept { return curr_; } // Invalidates this iterator void reset() noexcept { curr_ = nullptr; } // Reset the iterator back to the beginning void resetToBegin() noexcept { curr_ = dir_ == Direction::FROM_HEAD ? dlist_->head_ : dlist_->tail_; } protected: void goForward() noexcept; void goBackward() noexcept; // the current position of the iterator in the list T* curr_{nullptr}; // the direction we are iterating. Direction dir_{Direction::FROM_HEAD}; const DList<T, HookPtr>* dlist_{nullptr}; }; // provides an iterator starting from the head of the linked list. Iterator begin() const noexcept; // provides an iterator starting from the tail of the linked list. Iterator rbegin() const noexcept; // Iterator to compare against for the end. Iterator end() const noexcept; Iterator rend() const noexcept; private: // unlinks the node from the linked list. Does not correct the next and // previous. void unlink(const T& node) noexcept; const PtrCompressor compressor_{}; // head of the linked list T* head_{nullptr}; // tail of the linked list T* tail_{nullptr}; // size of the list size_t size_{0}; }; } // namespace cachelib } // namespace facebook #include "cachelib/allocator/datastruct/DList-inl.h"