lrulist.h 3.4 KB

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