lrulist.h 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. // Copyright (c) 2013, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. //
  6. #pragma once
  7. #include <atomic>
  8. #include "util/mutexlock.h"
  9. namespace ROCKSDB_NAMESPACE {
  10. // LRU element definition
  11. //
  12. // Any object that needs to be part of the LRU algorithm should extend this
  13. // class
  14. template <class T>
  15. struct LRUElement {
  16. explicit LRUElement() : next_(nullptr), prev_(nullptr), refs_(0) {}
  17. virtual ~LRUElement() { assert(!refs_); }
  18. T* next_;
  19. T* prev_;
  20. std::atomic<size_t> refs_;
  21. };
  22. // LRU implementation
  23. //
  24. // In place LRU implementation. There is no copy or allocation involved when
  25. // inserting or removing an element. This makes the data structure slim
  26. template <class T>
  27. class LRUList {
  28. public:
  29. virtual ~LRUList() {
  30. MutexLock _(&lock_);
  31. assert(!head_);
  32. assert(!tail_);
  33. }
  34. // Push element into the LRU at the cold end
  35. inline void Push(T* const t) {
  36. assert(t);
  37. assert(!t->next_);
  38. assert(!t->prev_);
  39. MutexLock _(&lock_);
  40. assert((!head_ && !tail_) || (head_ && tail_));
  41. assert(!head_ || !head_->prev_);
  42. assert(!tail_ || !tail_->next_);
  43. t->next_ = head_;
  44. if (head_) {
  45. head_->prev_ = t;
  46. }
  47. head_ = t;
  48. if (!tail_) {
  49. tail_ = t;
  50. }
  51. }
  52. // Unlink the element from the LRU
  53. inline void Unlink(T* const t) {
  54. MutexLock _(&lock_);
  55. UnlinkImpl(t);
  56. }
  57. // Evict an element from the LRU
  58. inline T* Pop() {
  59. MutexLock _(&lock_);
  60. assert(tail_ && head_);
  61. assert(!tail_->next_);
  62. assert(!head_->prev_);
  63. T* t = head_;
  64. while (t && t->refs_) {
  65. t = t->next_;
  66. }
  67. if (!t) {
  68. // nothing can be evicted
  69. return nullptr;
  70. }
  71. assert(!t->refs_);
  72. // unlike the element
  73. UnlinkImpl(t);
  74. return t;
  75. }
  76. // Move the element from the front of the list to the back of the list
  77. inline void Touch(T* const t) {
  78. MutexLock _(&lock_);
  79. UnlinkImpl(t);
  80. PushBackImpl(t);
  81. }
  82. // Check if the LRU is empty
  83. inline bool IsEmpty() const {
  84. MutexLock _(&lock_);
  85. return !head_ && !tail_;
  86. }
  87. private:
  88. // Unlink an element from the LRU
  89. void UnlinkImpl(T* const t) {
  90. assert(t);
  91. lock_.AssertHeld();
  92. assert(head_ && tail_);
  93. assert(t->prev_ || head_ == t);
  94. assert(t->next_ || tail_ == t);
  95. if (t->prev_) {
  96. t->prev_->next_ = t->next_;
  97. }
  98. if (t->next_) {
  99. t->next_->prev_ = t->prev_;
  100. }
  101. if (tail_ == t) {
  102. tail_ = tail_->prev_;
  103. }
  104. if (head_ == t) {
  105. head_ = head_->next_;
  106. }
  107. t->next_ = t->prev_ = nullptr;
  108. }
  109. // Insert an element at the hot end
  110. inline void PushBack(T* const t) {
  111. MutexLock _(&lock_);
  112. PushBackImpl(t);
  113. }
  114. inline void PushBackImpl(T* const t) {
  115. assert(t);
  116. assert(!t->next_);
  117. assert(!t->prev_);
  118. lock_.AssertHeld();
  119. assert((!head_ && !tail_) || (head_ && tail_));
  120. assert(!head_ || !head_->prev_);
  121. assert(!tail_ || !tail_->next_);
  122. t->prev_ = tail_;
  123. if (tail_) {
  124. tail_->next_ = t;
  125. }
  126. tail_ = t;
  127. if (!head_) {
  128. head_ = tail_;
  129. }
  130. }
  131. mutable port::Mutex lock_; // synchronization primitive
  132. T* head_ = nullptr; // front (cold)
  133. T* tail_ = nullptr; // back (hot)
  134. };
  135. } // namespace ROCKSDB_NAMESPACE