lru_list.h 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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. // $Id$
  15. #ifndef __LRU_LIST_H
  16. #define __LRU_LIST_H
  17. #include <list>
  18. #include <string>
  19. // Workaround for a problem with boost and sunstudio 5.10
  20. // There is a version check in there that appears wrong,
  21. // which makes including boost/thread.hpp fail
  22. // This will probably be fixed in a future version of boost,
  23. // in which case this part can be removed then
  24. #ifdef NEED_SUNPRO_WORKAROUND
  25. #if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
  26. #undef __SUNPRO_CC
  27. #define __SUNPRO_CC 0x5090
  28. #endif
  29. #endif // NEED_SUNPRO_WORKAROUND
  30. #include <boost/noncopyable.hpp>
  31. #include <boost/shared_ptr.hpp>
  32. #include <boost/thread.hpp>
  33. #include <boost/interprocess/sync/scoped_lock.hpp>
  34. namespace isc {
  35. namespace nsas {
  36. /// \brief LRU List
  37. ///
  38. /// Provides the LRU list for the zone and nameserver objects. The list is
  39. /// created with a specific size. Entries are added to the back of the list
  40. /// and removed from the front. It is also possible to pull an element out
  41. /// of the middle of the list and add it to the end of the list, an action that
  42. /// should be done when the element is referenced.
  43. ///
  44. /// It is not intended that the class be copied, and the derivation from
  45. /// boost::noncopyable enforces this.
  46. template <typename T>
  47. class LruList : boost::noncopyable {
  48. public:
  49. typedef typename std::list<boost::shared_ptr<T> > lru_list;
  50. typedef typename lru_list::iterator iterator;
  51. /// \brief Dropped Operation
  52. ///
  53. /// When an object is dropped from the LRU list because it has not been
  54. /// accessed for some time, it is possible that the action should trigger
  55. /// some other functions. For this reason, it is possible to register
  56. /// a list-wide functor object to execute in this casee.
  57. ///
  58. /// Note that the function does not execute as the result of a call to
  59. /// remove() - that is an explicit call and it is assumed that the caller
  60. /// will handle any additional operations needed.
  61. class Dropped {
  62. public:
  63. /// \brief Constructor
  64. Dropped(){}
  65. /// \brief Virtual Destructor
  66. virtual ~Dropped(){}
  67. /// \brief Dropped Object Handler
  68. ///
  69. /// Function object called when the object drops off the end of the
  70. /// LRU list.
  71. ///
  72. /// \param drop Object being dropped.
  73. virtual void operator()(T* drop) const = 0;
  74. };
  75. /// \brief Constructor
  76. ///
  77. /// \param max_size Maximum size of the list before elements are dropped.
  78. /// \param dropped Pointer to a function object that will get called as
  79. /// elements are dropped. This object will be stored using a shared_ptr,
  80. /// so should be allocated with new().
  81. LruList(uint32_t max_size = 1000, Dropped* dropped = NULL) :
  82. max_size_(max_size), count_(0), dropped_(dropped)
  83. {}
  84. /// \brief Virtual Destructor
  85. virtual ~LruList()
  86. {}
  87. /// \brief Add Element
  88. ///
  89. /// Add a new element to the end of the list.
  90. ///
  91. /// \param element Reference to the element to add.
  92. ///
  93. /// \return Handle describing the element in the LRU list.
  94. virtual void add(boost::shared_ptr<T>& element);
  95. /// \brief Remove Element
  96. ///
  97. /// Removes an element from the list. If the element is not present (i.e.
  98. /// its internal list pointer is invalid), this is a no-op.
  99. ///
  100. /// \param element Reference to the element to remove.
  101. virtual void remove(boost::shared_ptr<T>& element);
  102. /// \brief Touch Element
  103. ///
  104. /// The name comes from the Unix "touch" command. All this does is to
  105. /// move the specified entry from the middle of the list to the end of
  106. /// the list.
  107. ///
  108. /// \param element Reference to the element to touch.
  109. virtual void touch(boost::shared_ptr<T>& element);
  110. /// \brief Return Size of the List
  111. ///
  112. /// An independent count is kept of the list size, as list.size() may take
  113. /// some time if the list is big.
  114. ///
  115. /// \return Number of elements in the list
  116. virtual uint32_t size() const {
  117. // Don't bother to lock the mutex. If an update is in progress, we
  118. // receive either the value just before the update or just after it.
  119. // Either way, this call could have come just before or just after
  120. // that operation, so the value would have been just as uncertain.
  121. return count_;
  122. }
  123. /// \brief Return Maximum Size
  124. ///
  125. /// \return Maximum size of the list
  126. virtual uint32_t getMaxSize() const {
  127. return max_size_;
  128. }
  129. /// \brief Set Maximum Size
  130. ///
  131. /// \param new_size New maximum list size
  132. virtual void setMaxSize(uint32_t max_size) {
  133. max_size_ = max_size;
  134. }
  135. private:
  136. boost::mutex mutex_; ///< List protection
  137. std::list<boost::shared_ptr<T> > lru_; ///< The LRU list itself
  138. uint32_t max_size_; ///< Max size of the list
  139. uint32_t count_; ///< Count of elements
  140. boost::shared_ptr<Dropped> dropped_; ///< Dropped object
  141. };
  142. // Add entry to the list
  143. template <typename T>
  144. void LruList<T>::add(boost::shared_ptr<T>& element) {
  145. // Protect list against concurrent access
  146. boost::interprocess::scoped_lock<boost::mutex> lock(mutex_);
  147. // Add the entry and set its pointer field to point into the list.
  148. // insert() is used to get the pointer.
  149. element->setLruIterator(lru_.insert(lru_.end(), element));
  150. // ... and update the count while we have the mutex.
  151. ++count_;
  152. // If the count takes us above the maximum size of the list, remove elements
  153. // from the front. The current list size could be more than one above the
  154. // maximum size of the list if the maximum size was changed after
  155. // construction.
  156. while (count_ > max_size_) {
  157. if (!lru_.empty()) {
  158. // Run the drop handler (if there is one) on the
  159. // to-be-dropped object.
  160. if (dropped_) {
  161. (*dropped_)(lru_.begin()->get());
  162. }
  163. // ... and get rid of it from the list
  164. lru_.pop_front();
  165. --count_;
  166. }
  167. else {
  168. // TODO: Log this condition (count_ > 0 when list empty) -
  169. // it should not happen
  170. count_ = 0;
  171. break;
  172. }
  173. }
  174. }
  175. // Remove an element from the list
  176. template <typename T>
  177. void LruList<T>::remove(boost::shared_ptr<T>& element) {
  178. // An element can only be removed it its internal pointer is valid.
  179. // If it is, the pointer can be used to access the list because no matter
  180. // what other elements are added or removed, the pointer remains valid.
  181. //
  182. // If the pointer is not valid, this is a no-op.
  183. if (element->iteratorValid()) {
  184. // Is valid, so protect list against concurrent access
  185. boost::interprocess::scoped_lock<boost::mutex> lock(mutex_);
  186. lru_.erase(element->getLruIterator()); // Remove element from list
  187. element->invalidateIterator(); // Invalidate pointer
  188. --count_; // One less element
  189. }
  190. }
  191. // Touch an element - remove it from the list and add to the end
  192. template <typename T>
  193. void LruList<T>::touch(boost::shared_ptr<T>& element) {
  194. // As before, if the pointer is not valid, this is a no-op.
  195. if (element->iteratorValid()) {
  196. // Protect list against concurrent access
  197. boost::interprocess::scoped_lock<boost::mutex> lock(mutex_);
  198. // Move the element to the end of the list.
  199. lru_.splice(lru_.end(), lru_, element->getLruIterator());
  200. // Update the iterator in the element to point to it. We can
  201. // offset from end() as a list has a bidirectional iterator.
  202. iterator i = lru_.end();
  203. element->setLruIterator(--i);
  204. }
  205. }
  206. } // namespace nsas
  207. } // namespace isc
  208. #endif // __LRU_LIST_H