changeset 509:e7841864c97c

StableResourcesMonitor
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 16 Aug 2013 14:23:54 +0200
parents c4122c3a47c1
children 3b735fdf320b
files Core/Cache/LeastRecentlyUsedIndex.h Core/Cache/MemoryCache.cpp UnitTests/MemoryCache.cpp
diffstat 3 files changed, 244 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/Core/Cache/LeastRecentlyUsedIndex.h	Fri Aug 16 10:24:49 2013 +0200
+++ b/Core/Cache/LeastRecentlyUsedIndex.h	Fri Aug 16 14:23:54 2013 +0200
@@ -73,12 +73,16 @@
      **/
     void Add(T id, Payload payload = Payload());
 
+    void AddOrMakeMostRecent(T id, Payload payload = Payload());
+
     /**
      * When accessing an element of the cache, this method tags the
      * element as the most recently used.
      * \param id The most recently accessed item.
      **/
-    void TagAsMostRecent(T id);
+    void MakeMostRecent(T id);
+
+    void MakeMostRecent(T id, Payload updatedPayload);
 
     /**
      * Remove an element from the cache index.
@@ -90,11 +94,7 @@
      * Get the oldest element in the cache and remove it.
      * \return The oldest item.
      **/
-    T RemoveOldest()
-    {
-      Payload p;
-      return RemoveOldest(p);
-    }
+    T RemoveOldest();
 
     /**
      * Get the oldest element in the cache, remove it and return the
@@ -191,7 +191,7 @@
 
 
   template <typename T, typename Payload>
-  void LeastRecentlyUsedIndex<T, Payload>::TagAsMostRecent(T id)
+  void LeastRecentlyUsedIndex<T, Payload>::MakeMostRecent(T id)
   {
     if (!Contains(id))
     {
@@ -212,6 +212,53 @@
 
 
   template <typename T, typename Payload>
+  void LeastRecentlyUsedIndex<T, Payload>::AddOrMakeMostRecent(T id, Payload payload)
+  {
+    typename Index::iterator it = index_.find(id);
+
+    if (it != index_.end())
+    {
+      // Already existing. Make it most recent.
+      std::pair<T, Payload> item = *(it->second);
+      item.second = payload;
+      queue_.erase(it->second);
+      queue_.push_front(item);
+    }
+    else
+    {
+      // New item
+      queue_.push_front(std::make_pair(id, payload));
+    }
+
+    index_[id] = queue_.begin();
+
+    CheckInvariants();
+  }
+
+
+  template <typename T, typename Payload>
+  void LeastRecentlyUsedIndex<T, Payload>::MakeMostRecent(T id, Payload updatedPayload)
+  {
+    if (!Contains(id))
+    {
+      throw OrthancException(ErrorCode_InexistentItem);
+    }
+
+    typename Index::iterator it = index_.find(id);
+    assert(it != index_.end());
+
+    std::pair<T, Payload> item = *(it->second);
+    item.second = updatedPayload;
+    
+    queue_.erase(it->second);
+    queue_.push_front(item);
+    index_[id] = queue_.begin();
+
+    CheckInvariants();
+  }
+
+
+  template <typename T, typename Payload>
   Payload LeastRecentlyUsedIndex<T, Payload>::Invalidate(T id)
   {
     if (!Contains(id))
@@ -254,6 +301,27 @@
 
 
   template <typename T, typename Payload>
+  T LeastRecentlyUsedIndex<T, Payload>::RemoveOldest()
+  {
+    if (IsEmpty())
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+
+    std::pair<T, Payload> item = queue_.back();
+    T oldest = item.first;
+
+    queue_.pop_back();
+    assert(index_.find(oldest) != index_.end());
+    index_.erase(oldest);
+
+    CheckInvariants();
+
+    return oldest;
+  }
+
+
+  template <typename T, typename Payload>
   const T& LeastRecentlyUsedIndex<T, Payload>::GetOldest() const
   {
     if (IsEmpty())
--- a/Core/Cache/MemoryCache.cpp	Fri Aug 16 10:24:49 2013 +0200
+++ b/Core/Cache/MemoryCache.cpp	Fri Aug 16 14:23:54 2013 +0200
@@ -46,7 +46,7 @@
     {
       VLOG(1) << "Reusing a cache page";
       assert(p != NULL);
-      index_.TagAsMostRecent(id);
+      index_.MakeMostRecent(id);
       return *p;
     }
 
--- a/UnitTests/MemoryCache.cpp	Fri Aug 16 10:24:49 2013 +0200
+++ b/UnitTests/MemoryCache.cpp	Fri Aug 16 14:23:54 2013 +0200
@@ -17,12 +17,12 @@
   r.Add("c");
   r.Add("b");
 
-  r.TagAsMostRecent("a");
-  r.TagAsMostRecent("d");
-  r.TagAsMostRecent("b");
-  r.TagAsMostRecent("c");
-  r.TagAsMostRecent("d");
-  r.TagAsMostRecent("c");
+  r.MakeMostRecent("a");
+  r.MakeMostRecent("d");
+  r.MakeMostRecent("b");
+  r.MakeMostRecent("c");
+  r.MakeMostRecent("d");
+  r.MakeMostRecent("c");
 
   ASSERT_EQ("a", r.GetOldest());
   ASSERT_EQ("a", r.RemoveOldest());
@@ -49,12 +49,12 @@
   r.Add("c", 422);
   r.Add("d", 423);
 
-  r.TagAsMostRecent("a");
-  r.TagAsMostRecent("d");
-  r.TagAsMostRecent("b");
-  r.TagAsMostRecent("c");
-  r.TagAsMostRecent("d");
-  r.TagAsMostRecent("c");
+  r.MakeMostRecent("a");
+  r.MakeMostRecent("d");
+  r.MakeMostRecent("b");
+  r.MakeMostRecent("c");
+  r.MakeMostRecent("d");
+  r.MakeMostRecent("c");
 
   ASSERT_TRUE(r.Contains("b"));
   ASSERT_EQ(421, r.Invalidate("b"));
@@ -81,6 +81,60 @@
 }
 
 
+TEST(LRU, PayloadUpdate)
+{
+  Orthanc::LeastRecentlyUsedIndex<std::string, int> r;
+  
+  r.Add("a", 420);
+  r.Add("b", 421);
+  r.Add("d", 423);
+
+  r.MakeMostRecent("a", 424);
+  r.MakeMostRecent("d", 421);
+
+  ASSERT_EQ("b", r.GetOldest());
+  ASSERT_EQ(421, r.GetOldestPayload());
+  r.RemoveOldest();
+
+  ASSERT_EQ("a", r.GetOldest());
+  ASSERT_EQ(424, r.GetOldestPayload());
+  r.RemoveOldest();
+
+  ASSERT_EQ("d", r.GetOldest());
+  ASSERT_EQ(421, r.GetOldestPayload());
+  r.RemoveOldest();
+
+  ASSERT_TRUE(r.IsEmpty());
+}
+
+
+
+TEST(LRU, PayloadUpdateBis)
+{
+  Orthanc::LeastRecentlyUsedIndex<std::string, int> r;
+  
+  r.AddOrMakeMostRecent("a", 420);
+  r.AddOrMakeMostRecent("b", 421);
+  r.AddOrMakeMostRecent("d", 423);
+  r.AddOrMakeMostRecent("a", 424);
+  r.AddOrMakeMostRecent("d", 421);
+
+  ASSERT_EQ("b", r.GetOldest());
+  ASSERT_EQ(421, r.GetOldestPayload());
+  r.RemoveOldest();
+
+  ASSERT_EQ("a", r.GetOldest());
+  ASSERT_EQ(424, r.GetOldestPayload());
+  r.RemoveOldest();
+
+  ASSERT_EQ("d", r.GetOldest());
+  ASSERT_EQ(421, r.GetOldestPayload());
+  r.RemoveOldest();
+
+  ASSERT_TRUE(r.IsEmpty());
+}
+
+
 
 
 namespace
@@ -141,3 +195,105 @@
 
   ASSERT_EQ("45 42 43 47 44 42 ", provider.log_);
 }
+
+
+#include "../OrthancServer/ServerEnumerations.h"
+
+namespace
+{
+  struct Payload
+  {
+    Orthanc::ResourceType type_;
+    boost::posix_time::ptime time_;
+
+    Payload() : type_(Orthanc::ResourceType_Instance)
+    {
+    }
+
+    Payload(Orthanc::ResourceType type) : type_(type)
+    {
+      time_ = boost::posix_time::second_clock::local_time();
+    }
+
+    unsigned int GetAge() const
+    {
+      return (boost::posix_time::second_clock::local_time() - time_).total_seconds();
+    }
+  };
+
+
+  class StableResourcesMonitor
+  {
+  private:
+    bool done_;
+    boost::mutex mutex_;
+    unsigned int stableTimeout_;
+    Orthanc::LeastRecentlyUsedIndex<std::string, Payload>  unstableResources_;
+    boost::thread thread_;
+
+    static void Thread(StableResourcesMonitor* that)
+    {
+      static const unsigned int SLEEP = 1;  // Check for stable resources each second
+
+      while (!that->done_)
+      {
+        boost::this_thread::sleep(boost::posix_time::seconds(SLEEP));
+
+        boost::mutex::scoped_lock lock(that->mutex_);
+        while (!that->unstableResources_.IsEmpty() &&
+               that->unstableResources_.GetOldestPayload().GetAge() > that->stableTimeout_)
+        {
+          // This DICOM resource has not received any new instance for
+          // some time. It can be considered as stable.
+          
+          Payload payload;
+          std::string id = that->unstableResources_.RemoveOldest(payload);
+
+          LOG(INFO) << "Stable resource: " << id << " (type " << payload.type_ << ")";
+        }
+      }
+
+      LOG(INFO) << "Closing the monitor for stable resources";
+    }
+
+  public:
+    StableResourcesMonitor(unsigned int stableTimeout)
+    {
+      done_ = false;
+      stableTimeout_ = stableTimeout;
+      thread_ = boost::thread(Thread, this);
+    }
+
+    ~StableResourcesMonitor()
+    {
+      done_ = true;
+
+      if (thread_.joinable())
+      {
+        thread_.join();
+      }
+    }
+
+    void ResourceUpdated(const std::string& id,
+                         Orthanc::ResourceType type)
+    {
+      assert(type == Orthanc::ResourceType_Patient ||
+             type == Orthanc::ResourceType_Study ||
+             type == Orthanc::ResourceType_Series);
+
+      boost::mutex::scoped_lock lock(mutex_);
+      unstableResources_.AddOrMakeMostRecent(id, type);
+    }
+  };
+}
+
+TEST(LRU, Hello)
+{
+  StableResourcesMonitor m(5);
+  boost::this_thread::sleep(boost::posix_time::seconds(1));
+  m.ResourceUpdated("Hello", Orthanc::ResourceType_Study);
+  m.ResourceUpdated("World", Orthanc::ResourceType_Series);
+  boost::this_thread::sleep(boost::posix_time::seconds(2));
+  m.ResourceUpdated("Hello", Orthanc::ResourceType_Study);
+  boost::this_thread::sleep(boost::posix_time::seconds(10));
+}