diff OrthancFramework/Sources/Cache/MemoryStringCache.h @ 5420:d37dff2c0028 am-new-cache

Optimized the MemoryStringCache to prevent loading the same file multiple times if multiple users request the same file at the same time
author Alain Mazy <am@osimis.io>
date Mon, 13 Nov 2023 17:01:59 +0100
parents 0ea402b4d901
children c65e036d649b
line wrap: on
line diff
--- a/OrthancFramework/Sources/Cache/MemoryStringCache.h	Thu Nov 09 08:51:01 2023 +0100
+++ b/OrthancFramework/Sources/Cache/MemoryStringCache.h	Mon Nov 13 17:01:59 2023 +0100
@@ -23,28 +23,71 @@
 
 #pragma once
 
-#include "MemoryObjectCache.h"
+#include "../OrthancFramework.h"
+#include "ICacheable.h"
+#include "LeastRecentlyUsedIndex.h"
+
+#include <boost/thread/condition_variable.hpp>
+#include <boost/thread/mutex.hpp>
+
 
 namespace Orthanc
 {
   /**
-   * Facade object around "MemoryObjectCache" that caches a dictionary
+   * Class that caches a dictionary
    * of strings, using the "fetch/add" paradigm of memcached.
    * 
+   * Starting from 1.12.2, if multiple clients are trying to access
+   * an inexistent item at the same time, only one of them will load it
+   * and the others will wait until the first one has loaded the data.
+   * 
+   * The MemoryStringCache is only accessible through an Accessor.
+   * 
    * Note: this class is thread safe
    **/
   class ORTHANC_PUBLIC MemoryStringCache : public boost::noncopyable
   {
+  public:
+    class Accessor : public boost::noncopyable
+    {
+      MemoryStringCache& cache_;
+      bool                shouldAdd_;  // when this accessor is the one who should load and add the data
+      std::string         keyToAdd_;
+    public:
+      Accessor(MemoryStringCache& cache);
+      ~Accessor();
+
+      bool Fetch(std::string& value, const std::string& key);
+      void Add(const std::string& key, const std::string& value);
+      void Add(const std::string& key,const char* buffer, size_t size);
+    };
+
   private:
     class StringValue;
 
-    MemoryObjectCache  cache_;
+    boost::mutex              cacheMutex_;  // note: we can not use recursive_mutex with condition_variable
+    boost::condition_variable cacheCond_;
+    std::set<std::string>     itemsBeingLoaded_;
+
+    size_t currentSize_;
+    size_t maxSize_;
+    LeastRecentlyUsedIndex<std::string, StringValue*>  content_;
+
+    void Recycle(size_t targetSize);
 
   public:
+    MemoryStringCache();
+
+    ~MemoryStringCache();
+
     size_t GetMaximumSize();
     
     void SetMaximumSize(size_t size);
 
+    void Invalidate(const std::string& key);
+
+
+  private:
     void Add(const std::string& key,
              const std::string& value);
 
@@ -52,9 +95,12 @@
              const void* buffer,
              size_t size);
 
-    void Invalidate(const std::string& key);
-
     bool Fetch(std::string& value,
                const std::string& key);
+
+    void RemoveFromItemsBeingLoaded(const std::string& key);
+    void RemoveFromItemsBeingLoadedInternal(const std::string& key);
+
+    void AddToItemsBeingLoadedInternal(const std::string& key);
   };
 }