lru_list.h 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #ifndef __LRU_LIST_H
  15. #define __LRU_LIST_H
  16. #include <list>
  17. #include <string>
  18. #include <boost/noncopyable.hpp>
  19. #include <boost/shared_ptr.hpp>
  20. #include "locks.h"
  21. namespace isc {
  22. namespace nsas {
  23. /// \brief LRU List
  24. ///
  25. /// Provides the LRU list for the zone and nameserver objects. The list is
  26. /// created with a specific size. Entries are added to the back of the list
  27. /// and removed from the front. It is also possible to pull an element out
  28. /// of the middle of the list and add it to the end of the list, an action that
  29. /// should be done when the element is referenced.
  30. ///
  31. /// It is not intended that the class be copied, and the derivation from
  32. /// boost::noncopyable enforces this.
  33. template <typename T>
  34. class LruList : boost::noncopyable {
  35. public:
  36. typedef typename std::list<boost::shared_ptr<T> > lru_list;
  37. typedef typename lru_list::iterator iterator;
  38. /// \brief Dropped Operation
  39. ///
  40. /// When an object is dropped from the LRU list because it has not been
  41. /// accessed for some time, it is possible that the action should trigger
  42. /// some other functions. For this reason, it is possible to register
  43. /// a list-wide functor object to execute in this casee.
  44. ///
  45. /// Note that the function does not execute as the result of a call to
  46. /// remove() - that is an explicit call and it is assumed that the caller
  47. /// will handle any additional operations needed.
  48. class Dropped {
  49. public:
  50. /// \brief Constructor
  51. Dropped(){}
  52. /// \brief Virtual Destructor
  53. virtual ~Dropped(){}
  54. /// \brief Dropped Object Handler
  55. ///
  56. /// Function object called when the object drops off the end of the
  57. /// LRU list.
  58. ///
  59. /// \param drop Object being dropped.
  60. virtual void operator()(T* drop) const = 0;
  61. };
  62. /// \brief Constructor
  63. ///
  64. /// \param max_size Maximum size of the list before elements are dropped.
  65. /// \param dropped Pointer to a function object that will get called as
  66. /// elements are dropped. This object will be stored using a shared_ptr,
  67. /// so should be allocated with new().
  68. LruList(uint32_t max_size = 1000, Dropped* dropped = NULL) :
  69. max_size_(max_size), count_(0), dropped_(dropped)
  70. {}
  71. /// \brief Virtual Destructor
  72. virtual ~LruList()
  73. {}
  74. /// \brief Add Element
  75. ///
  76. /// Add a new element to the end of the list.
  77. ///
  78. /// \param element Reference to the element to add.
  79. ///
  80. /// \return Handle describing the element in the LRU list.
  81. virtual void add(boost::shared_ptr<T>& element);
  82. /// \brief Remove Element
  83. ///
  84. /// Removes an element from the list. If the element is not present (i.e.
  85. /// its internal list pointer is invalid), this is a no-op.
  86. ///
  87. /// \param element Reference to the element to remove.
  88. virtual void remove(boost::shared_ptr<T>& element);
  89. /// \brief Touch Element
  90. ///
  91. /// The name comes from the Unix "touch" command. All this does is to
  92. /// move the specified entry from the middle of the list to the end of
  93. /// the list.
  94. ///
  95. /// \param element Reference to the element to touch.
  96. virtual void touch(boost::shared_ptr<T>& element);
  97. /// \brief Drop All the Elements in the List .
  98. ///
  99. /// All the elements will be dropped from the list container, and their
  100. /// drop handler(if there is one) will be called, left the size of list
  101. /// to 0.
  102. virtual void clear();
  103. /// \brief Return Size of the List
  104. ///
  105. /// An independent count is kept of the list size, as list.size() may take
  106. /// some time if the list is big.
  107. ///
  108. /// \return Number of elements in the list
  109. virtual uint32_t size() const {
  110. // Don't bother to lock the mutex. If an update is in progress, we
  111. // receive either the value just before the update or just after it.
  112. // Either way, this call could have come just before or just after
  113. // that operation, so the value would have been just as uncertain.
  114. return count_;
  115. }
  116. /// \brief Return Maximum Size
  117. ///
  118. /// \return Maximum size of the list
  119. virtual uint32_t getMaxSize() const {
  120. return max_size_;
  121. }
  122. /// \brief Set Maximum Size
  123. ///
  124. /// \param new_size New maximum list size
  125. virtual void setMaxSize(uint32_t max_size) {
  126. max_size_ = max_size;
  127. }
  128. private:
  129. isc::locks::mutex mutex_; ///< List protection
  130. std::list<boost::shared_ptr<T> > lru_; ///< The LRU list itself
  131. uint32_t max_size_; ///< Max size of the list
  132. uint32_t count_; ///< Count of elements
  133. boost::shared_ptr<Dropped> dropped_; ///< Dropped object
  134. };
  135. // Add entry to the list
  136. template <typename T>
  137. void LruList<T>::add(boost::shared_ptr<T>& element) {
  138. // Protect list against concurrent access
  139. isc::locks::scoped_lock<isc::locks::mutex> lock(mutex_);
  140. // Add the entry and set its pointer field to point into the list.
  141. // insert() is used to get the pointer.
  142. element->setLruIterator(lru_.insert(lru_.end(), element));
  143. // ... and update the count while we have the mutex.
  144. ++count_;
  145. // If the count takes us above the maximum size of the list, remove elements
  146. // from the front. The current list size could be more than one above the
  147. // maximum size of the list if the maximum size was changed after
  148. // construction.
  149. while (count_ > max_size_) {
  150. if (!lru_.empty()) {
  151. // Run the drop handler (if there is one) on the
  152. // to-be-dropped object.
  153. if (dropped_) {
  154. (*dropped_)(lru_.begin()->get());
  155. }
  156. // ... and get rid of it from the list
  157. lru_.pop_front();
  158. --count_;
  159. }
  160. else {
  161. // TODO: Log this condition (count_ > 0 when list empty) -
  162. // it should not happen
  163. count_ = 0;
  164. break;
  165. }
  166. }
  167. }
  168. // Remove an element from the list
  169. template <typename T>
  170. void LruList<T>::remove(boost::shared_ptr<T>& element) {
  171. // An element can only be removed it its internal pointer is valid.
  172. // If it is, the pointer can be used to access the list because no matter
  173. // what other elements are added or removed, the pointer remains valid.
  174. //
  175. // If the pointer is not valid, this is a no-op.
  176. if (element->iteratorValid()) {
  177. // Is valid, so protect list against concurrent access
  178. isc::locks::scoped_lock<isc::locks::mutex> lock(mutex_);
  179. lru_.erase(element->getLruIterator()); // Remove element from list
  180. element->invalidateIterator(); // Invalidate pointer
  181. --count_; // One less element
  182. }
  183. }
  184. // Touch an element - remove it from the list and add to the end
  185. template <typename T>
  186. void LruList<T>::touch(boost::shared_ptr<T>& element) {
  187. // As before, if the pointer is not valid, this is a no-op.
  188. if (element->iteratorValid()) {
  189. // Protect list against concurrent access
  190. isc::locks::scoped_lock<isc::locks::mutex> lock(mutex_);
  191. // Move the element to the end of the list.
  192. lru_.splice(lru_.end(), lru_, element->getLruIterator());
  193. // Update the iterator in the element to point to it. We can
  194. // offset from end() as a list has a bidirectional iterator.
  195. iterator i = lru_.end();
  196. element->setLruIterator(--i);
  197. }
  198. }
  199. // Clear the list- left the size of list to 0
  200. template <typename T>
  201. void LruList<T>::clear() {
  202. // Protect list against concurrent access
  203. isc::locks::scoped_lock<isc::locks::mutex> lock(mutex_);
  204. // ... and update the count while we have the mutex.
  205. count_ = 0;
  206. typename std::list<boost::shared_ptr<T> >::iterator iter;
  207. if (dropped_) {
  208. for (iter = lru_.begin(); iter != lru_.end(); ++iter) {
  209. // Call the drop handler.
  210. (*dropped_)(iter->get());
  211. }
  212. }
  213. lru_.clear();
  214. }
  215. } // namespace nsas
  216. } // namespace isc
  217. #endif // __LRU_LIST_H