123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
- //
- // Permission to use, copy, modify, and/or distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- // PERFORMANCE OF THIS SOFTWARE.
- // $Id$
- #ifndef __LRU_LIST_H
- #define __LRU_LIST_H
- #include <list>
- #include <string>
- // Workaround for a problem with boost and sunstudio 5.10
- // There is a version check in there that appears wrong,
- // which makes including boost/thread.hpp fail
- // This will probably be fixed in a future version of boost,
- // in which case this part can be removed then
- #ifdef NEED_SUNPRO_WORKAROUND
- #if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
- #undef __SUNPRO_CC
- #define __SUNPRO_CC 0x5090
- #endif
- #endif // NEED_SUNPRO_WORKAROUND
- #include <boost/noncopyable.hpp>
- #include <boost/shared_ptr.hpp>
- #include <boost/thread.hpp>
- #include <boost/interprocess/sync/scoped_lock.hpp>
- namespace isc {
- namespace nsas {
- /// \brief LRU List
- ///
- /// Provides the LRU list for the zone and nameserver objects. The list is
- /// created with a specific size. Entries are added to the back of the list
- /// and removed from the front. It is also possible to pull an element out
- /// of the middle of the list and add it to the end of the list, an action that
- /// should be done when the element is referenced.
- ///
- /// It is not intended that the class be copied, and the derivation from
- /// boost::noncopyable enforces this.
- template <typename T>
- class LruList : boost::noncopyable {
- public:
- typedef typename std::list<boost::shared_ptr<T> > lru_list;
- typedef typename lru_list::iterator iterator;
- /// \brief Dropped Operation
- ///
- /// When an object is dropped from the LRU list because it has not been
- /// accessed for some time, it is possible that the action should trigger
- /// some other functions. For this reason, it is possible to register
- /// a list-wide functor object to execute in this casee.
- ///
- /// Note that the function does not execute as the result of a call to
- /// remove() - that is an explicit call and it is assumed that the caller
- /// will handle any additional operations needed.
- class Dropped {
- public:
- /// \brief Constructor
- Dropped(){}
- /// \brief Virtual Destructor
- virtual ~Dropped(){}
- /// \brief Dropped Object Handler
- ///
- /// Function object called when the object drops off the end of the
- /// LRU list.
- ///
- /// \param drop Object being dropped.
- virtual void operator()(T* drop) const = 0;
- };
- /// \brief Constructor
- ///
- /// \param max_size Maximum size of the list before elements are dropped.
- /// \param dropped Pointer to a function object that will get called as
- /// elements are dropped. This object will be stored using a shared_ptr,
- /// so should be allocated with new().
- LruList(uint32_t max_size = 1000, Dropped* dropped = NULL) :
- max_size_(max_size), count_(0), dropped_(dropped)
- {}
- /// \brief Virtual Destructor
- virtual ~LruList()
- {}
- /// \brief Add Element
- ///
- /// Add a new element to the end of the list.
- ///
- /// \param element Reference to the element to add.
- ///
- /// \return Handle describing the element in the LRU list.
- virtual void add(boost::shared_ptr<T>& element);
- /// \brief Remove Element
- ///
- /// Removes an element from the list. If the element is not present (i.e.
- /// its internal list pointer is invalid), this is a no-op.
- ///
- /// \param element Reference to the element to remove.
- virtual void remove(boost::shared_ptr<T>& element);
- /// \brief Touch Element
- ///
- /// The name comes from the Unix "touch" command. All this does is to
- /// move the specified entry from the middle of the list to the end of
- /// the list.
- ///
- /// \param element Reference to the element to touch.
- virtual void touch(boost::shared_ptr<T>& element);
- /// \brief Return Size of the List
- ///
- /// An independent count is kept of the list size, as list.size() may take
- /// some time if the list is big.
- ///
- /// \return Number of elements in the list
- virtual uint32_t size() const {
- // Don't bother to lock the mutex. If an update is in progress, we
- // receive either the value just before the update or just after it.
- // Either way, this call could have come just before or just after
- // that operation, so the value would have been just as uncertain.
- return count_;
- }
- /// \brief Return Maximum Size
- ///
- /// \return Maximum size of the list
- virtual uint32_t getMaxSize() const {
- return max_size_;
- }
- /// \brief Set Maximum Size
- ///
- /// \param new_size New maximum list size
- virtual void setMaxSize(uint32_t max_size) {
- max_size_ = max_size;
- }
- private:
- boost::mutex mutex_; ///< List protection
- std::list<boost::shared_ptr<T> > lru_; ///< The LRU list itself
- uint32_t max_size_; ///< Max size of the list
- uint32_t count_; ///< Count of elements
- boost::shared_ptr<Dropped> dropped_; ///< Dropped object
- };
- // Add entry to the list
- template <typename T>
- void LruList<T>::add(boost::shared_ptr<T>& element) {
- // Protect list against concurrent access
- boost::interprocess::scoped_lock<boost::mutex> lock(mutex_);
- // Add the entry and set its pointer field to point into the list.
- // insert() is used to get the pointer.
- element->setLruIterator(lru_.insert(lru_.end(), element));
- // ... and update the count while we have the mutex.
- ++count_;
- // If the count takes us above the maximum size of the list, remove elements
- // from the front. The current list size could be more than one above the
- // maximum size of the list if the maximum size was changed after
- // construction.
- while (count_ > max_size_) {
- if (!lru_.empty()) {
- // Run the drop handler (if there is one) on the
- // to-be-dropped object.
- if (dropped_) {
- (*dropped_)(lru_.begin()->get());
- }
- // ... and get rid of it from the list
- lru_.pop_front();
- --count_;
- }
- else {
- // TODO: Log this condition (count_ > 0 when list empty) -
- // it should not happen
- count_ = 0;
- break;
- }
- }
- }
- // Remove an element from the list
- template <typename T>
- void LruList<T>::remove(boost::shared_ptr<T>& element) {
- // An element can only be removed it its internal pointer is valid.
- // If it is, the pointer can be used to access the list because no matter
- // what other elements are added or removed, the pointer remains valid.
- //
- // If the pointer is not valid, this is a no-op.
- if (element->iteratorValid()) {
- // Is valid, so protect list against concurrent access
- boost::interprocess::scoped_lock<boost::mutex> lock(mutex_);
- lru_.erase(element->getLruIterator()); // Remove element from list
- element->invalidateIterator(); // Invalidate pointer
- --count_; // One less element
- }
- }
- // Touch an element - remove it from the list and add to the end
- template <typename T>
- void LruList<T>::touch(boost::shared_ptr<T>& element) {
- // As before, if the pointer is not valid, this is a no-op.
- if (element->iteratorValid()) {
- // Protect list against concurrent access
- boost::interprocess::scoped_lock<boost::mutex> lock(mutex_);
- // Move the element to the end of the list.
- lru_.splice(lru_.end(), lru_, element->getLruIterator());
- // Update the iterator in the element to point to it. We can
- // offset from end() as a list has a bidirectional iterator.
- iterator i = lru_.end();
- element->setLruIterator(--i);
- }
- }
- } // namespace nsas
- } // namespace isc
- #endif // __LRU_LIST_H
|