// 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 #include // 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 #include #include #include 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 class LruList : boost::noncopyable { public: typedef typename std::list > 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& 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& 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& 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 > 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 object }; // Add entry to the list template void LruList::add(boost::shared_ptr& element) { // Protect list against concurrent access boost::interprocess::scoped_lock 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 void LruList::remove(boost::shared_ptr& 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 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 void LruList::touch(boost::shared_ptr& 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 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