changeset 2308:e499cac2a048 refactoring

created a new version of the DICOM cache
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 29 Sep 2026 11:20:29 +0200
parents 4c1342615306
children 4d24c9a525b4
files Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp OrthancStone/Sources/Platforms/Native/GenericOracleRunner.cpp OrthancStone/Sources/Platforms/Native/GenericOracleRunner.h OrthancStone/Sources/Platforms/Native/ThreadedOracle.cpp OrthancStone/Sources/Platforms/Native/ThreadedOracle.h OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyOracle.cpp OrthancStone/Sources/StoneApplication.cpp OrthancStone/Sources/Toolbox/ParsedDicomCache.cpp OrthancStone/Sources/Toolbox/ParsedDicomCache.h
diffstat 9 files changed, 213 insertions(+), 48 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp	Tue Sep 29 10:10:40 2026 +0200
+++ b/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp	Tue Sep 29 11:20:29 2026 +0200
@@ -56,6 +56,7 @@
 
 
 // TODO Refactoring
+#include "../../../../OrthancStone/Sources/Oracle/ParseDicomFromFileCommand.h"
 #include "../../../../OrthancStone/Sources/Oracle/SleepOracleCommand.h"
 #include "../../../../OrthancStone/Sources/StoneApplication.h"
 
@@ -221,6 +222,7 @@
 
       OrthancStone::StoneApplication::Configuration configuration;
       configuration.SetRemoteOrthancParameters(orthancWebService);
+      configuration.SetRootDirectory("/tmp");  // TODO Refactoring - Remove this
 
       OrthancStone::StoneApplication::Initialize(configuration);
 
@@ -325,6 +327,13 @@
                       command->SetUri("/system/");
                       oracle.Submit(environment, toto_, command.release());
                     }
+
+                    for (unsigned int i = 0; i < 10; i++)
+                    {
+                      DicomSource source;
+                      source.SetDicomDirSource();
+                      oracle.Submit(environment, toto_, new OrthancStone::ParseDicomFromFileCommand(source, "hand.dcm"));
+                    }
                     break;
                   }
 
--- a/OrthancStone/Sources/Platforms/Native/GenericOracleRunner.cpp	Tue Sep 29 10:10:40 2026 +0200
+++ b/OrthancStone/Sources/Platforms/Native/GenericOracleRunner.cpp	Tue Sep 29 11:20:29 2026 +0200
@@ -40,8 +40,6 @@
 #  include "../../Oracle/ParseDicomSuccessMessage.h"
 #  include <dcmtk/dcmdata/dcdeftag.h>
 #  include <dcmtk/dcmdata/dcfilefo.h>
-static unsigned int BUCKET_DICOMDIR = 0;
-static unsigned int BUCKET_SOP = 1;
 #endif
 
 #include <Compression/GzipCompressor.h>
@@ -54,6 +52,17 @@
 #include <boost/filesystem.hpp>
 
 
+static std::string GetFileSystemCacheKey(const std::string& path)
+{
+  return "file|" + path;
+}
+
+
+static std::string GetSopInstanceUidCacheKey(const std::string& uid)
+{
+  return "sop|" + uid;
+}
+
 
 namespace OrthancStone
 {
@@ -333,32 +342,32 @@
                                       "Cannot parse file: " + path);
     }
   }
+#endif
 
   
+#if ORTHANC_ENABLE_DCMTK == 1
   static void RunInternal(IOracleCallback& callback,
-                          boost::shared_ptr<ParsedDicomCache> cache,
+                          boost::shared_ptr<New::ParsedDicomCache> cache,
                           const std::string& root,
                           const ParseDicomFromFileCommand& command)
   {
     const std::string path = GetPath(root, command.GetPath());
-
-#if 0
-    // TODO Refactoring - Reactivate the cache!!!
+    const std::string cacheKey = GetFileSystemCacheKey(path);
 
     if (cache)
     {
-      ParsedDicomCache::Reader reader(*cache, BUCKET_DICOMDIR, path);
-      if (reader.IsValid() &&
-          (!command.IsPixelDataIncluded() ||
-           reader.HasPixelData()))
+      New::ParsedDicomCache::Accessor accessor(*cache, cacheKey);
+
+      if (accessor.IsValid())
       {
+        assert(accessor.HasPixelData());
+
         // Reuse the DICOM file from the cache
-        callback.NotifySuccess(new ParseDicomSuccessMessage(command, command.GetSource(), reader.GetDicom(),
-                                                            reader.GetFileSize(), reader.HasPixelData()));
+        callback.NotifySuccess(new ParseDicomSuccessMessage(command, command.GetSource(), accessor.GetDicom(),
+                                                            accessor.GetFileSize(), accessor.HasPixelData()));
         return;
       }
     }
-#endif
 
     uint64_t fileSize;
     boost::shared_ptr<Orthanc::ParsedDicomFile> parsed(ParseDicom(fileSize, path, command.IsPixelDataIncluded()));
@@ -372,43 +381,37 @@
     callback.NotifySuccess(new ParseDicomSuccessMessage(command, command.GetSource(), parsed,
                                                         static_cast<size_t>(fileSize), command.IsPixelDataIncluded()));
 
-#if 0
-    // TODO Refactoring - Reactivate the cache!!!
-    if (cache)
+    if (cache &&
+        command.IsPixelDataIncluded())
     {
-      // Store it into the cache for future use
-      
-      // Invalidate to overwrite DICOM instance that would already
-      // be stored without pixel data
-      cache->Invalidate(BUCKET_DICOMDIR, path);
-      
-      cache->Acquire(BUCKET_DICOMDIR, path, parsed.release(),
-                     static_cast<size_t>(fileSize), command.IsPixelDataIncluded());
+      cache->Store(cacheKey, parsed, static_cast<size_t>(fileSize), command.IsPixelDataIncluded());
     }
+  }
 #endif
-  }
+  
 
-  
+#if ORTHANC_ENABLE_DCMTK == 1
   static void RunInternal(IOracleCallback& callback,
-                          boost::shared_ptr<ParsedDicomCache> cache,
+                          boost::shared_ptr<New::ParsedDicomCache> cache,
                           const Orthanc::WebServiceParameters& orthanc,
                           const ParseDicomFromWadoCommand& command)
   {
-#if 0
-    // TODO Refactoring - Reactivate the cache!!!
+    const std::string cacheKey = GetSopInstanceUidCacheKey(command.GetSopInstanceUid());
+
     if (cache)
     {
-      ParsedDicomCache::Reader reader(*cache, BUCKET_SOP, command.GetSopInstanceUid());
-      if (reader.IsValid() &&
-          reader.HasPixelData())
+      New::ParsedDicomCache::Accessor accessor(*cache, cacheKey);
+
+      if (accessor.IsValid())
       {
+        assert(accessor.HasPixelData());
+
         // Reuse the DICOM file from the cache
-        callback.NotifySuccess(new ParseDicomSuccessMessage(command, command.GetSource(), reader.GetDicom(),
-                                                            reader.GetFileSize(), reader.HasPixelData()));
+        callback.NotifySuccess(new ParseDicomSuccessMessage(command, command.GetSource(), accessor.GetDicom(),
+                                                            accessor.GetFileSize(), accessor.HasPixelData()));
         return;
       }
     }
-#endif
 
     std::string answer;
     Orthanc::HttpClient::HttpHeaders answerHeaders;
@@ -434,14 +437,11 @@
     callback.NotifySuccess(new ParseDicomSuccessMessage(command, command.GetSource(), parsed, fileSize,
                                                         true /* pixel data always is included in WADO-RS */));
 
-#if 0
-    // TODO Refactoring - Reactivate the cache!!!
     if (cache)
     {
       // Store it into the cache for future use
-      cache->Acquire(BUCKET_SOP, command.GetSopInstanceUid(), parsed.release(), fileSize, true);
+      cache->Store(cacheKey, parsed, static_cast<size_t>(fileSize), true);
     }
-#endif
   }
 #endif
 
--- a/OrthancStone/Sources/Platforms/Native/GenericOracleRunner.h	Tue Sep 29 10:10:40 2026 +0200
+++ b/OrthancStone/Sources/Platforms/Native/GenericOracleRunner.h	Tue Sep 29 11:20:29 2026 +0200
@@ -48,7 +48,7 @@
     StoneApplication::Configuration  configuration_;
 
 #if ORTHANC_ENABLE_DCMTK == 1
-    boost::shared_ptr<ParsedDicomCache>  dicomCache_;
+    boost::shared_ptr<New::ParsedDicomCache>  dicomCache_;
 #endif
 
   public:
@@ -58,7 +58,7 @@
     }
 
 #if ORTHANC_ENABLE_DCMTK == 1
-    void SetDicomCache(boost::shared_ptr<ParsedDicomCache> cache)
+    void SetDicomCache(boost::shared_ptr<New::ParsedDicomCache> cache)
     {
       dicomCache_ = cache;
     }
--- a/OrthancStone/Sources/Platforms/Native/ThreadedOracle.cpp	Tue Sep 29 10:10:40 2026 +0200
+++ b/OrthancStone/Sources/Platforms/Native/ThreadedOracle.cpp	Tue Sep 29 11:20:29 2026 +0200
@@ -165,8 +165,9 @@
     }
     else
     {
-      LOG(INFO) << "The DICOM cache size is set to " << configuration.GetDicomCacheSize() << " bytes";
-      dicomCache_.reset(new ParsedDicomCache(configuration.GetDicomCacheSize()));
+      LOG(INFO) << "The DICOM cache size is set to "
+                << (static_cast<float>(configuration.GetDicomCacheSize()) / static_cast<float>(1024 * 1024)) << " MB";
+      dicomCache_.reset(new New::ParsedDicomCache(configuration.GetDicomCacheSize()));
     }
   }
 
--- a/OrthancStone/Sources/Platforms/Native/ThreadedOracle.h	Tue Sep 29 10:10:40 2026 +0200
+++ b/OrthancStone/Sources/Platforms/Native/ThreadedOracle.h	Tue Sep 29 11:20:29 2026 +0200
@@ -64,7 +64,7 @@
     Orthanc::ThreadPool              threadPool_;
 
 #if ORTHANC_ENABLE_DCMTK == 1
-    boost::shared_ptr<ParsedDicomCache>  dicomCache_;
+    boost::shared_ptr<New::ParsedDicomCache>  dicomCache_;
 #endif
 
     void SubmitInternal(IOracleCallback* callback /* takes ownership */);
--- a/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyOracle.cpp	Tue Sep 29 10:10:40 2026 +0200
+++ b/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyOracle.cpp	Tue Sep 29 11:20:29 2026 +0200
@@ -797,7 +797,8 @@
     }
     else
     {
-      LOG(INFO) << "The DICOM cache size is set to " << configuration.GetDicomCacheSize() << " bytes";
+      LOG(INFO) << "The DICOM cache size is set to "
+                << (static_cast<float>(configuration.GetDicomCacheSize()) / static_cast<float>(1024 * 1024)) << " MB";
       dicomCache_.reset(new ParsedDicomCache(configuration.GetDicomCacheSize()));
     }
 #else
--- a/OrthancStone/Sources/StoneApplication.cpp	Tue Sep 29 10:10:40 2026 +0200
+++ b/OrthancStone/Sources/StoneApplication.cpp	Tue Sep 29 11:20:29 2026 +0200
@@ -45,7 +45,7 @@
     rootDirectory_("."),
     oracleThreadsCount_(4),
     workersTimeResolution_(50),  // By default, time resolution of 50ms
-    dicomCacheSize_(0)  // By default, no DICOM cache
+    dicomCacheSize_(128 * 1024 * 1024)  // By default, use a cache of 128MB
   {
   }
 
--- a/OrthancStone/Sources/Toolbox/ParsedDicomCache.cpp	Tue Sep 29 10:10:40 2026 +0200
+++ b/OrthancStone/Sources/Toolbox/ParsedDicomCache.cpp	Tue Sep 29 11:20:29 2026 +0200
@@ -158,4 +158,67 @@
       return item_->GetMemoryUsage();
     }
   }
+
+
+  namespace New
+  {
+    ParsedDicomCache::Item::Item(const boost::shared_ptr<Orthanc::ParsedDicomFile>& dicom,
+                                 size_t fileSize,
+                                 bool hasPixelData) :
+      dicom_(dicom),
+      fileSize_(fileSize),
+      hasPixelData_(hasPixelData)
+    {
+      if (dicom == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      }
+    }
+
+
+    void ParsedDicomCache::Store(const std::string& key,
+                                 const boost::shared_ptr<Orthanc::ParsedDicomFile>& dicom,
+                                 size_t fileSize,
+                                 bool hasPixelData)
+    {
+      if (dicom == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      }
+      else
+      {
+        cache_.Store(key, boost::shared_ptr<Item>(new Item(dicom, fileSize, hasPixelData)), fileSize);
+      }
+    }
+
+
+    ParsedDicomCache::Accessor::Accessor(ParsedDicomCache& cache,
+                                         const std::string& key) :
+      item_(cache.cache_.GetCachedValue(key))
+    {
+      if (item_)
+      {
+        lock_.reset(new Orthanc::Mutex::ScopedLock(dynamic_cast<Item&>(*item_).GetMutex()));
+      }
+    }
+
+
+    bool ParsedDicomCache::Accessor::IsValid() const
+    {
+      return (item_ ? true : false);
+    }
+
+
+    const ParsedDicomCache::Item& ParsedDicomCache::Accessor::GetItem() const
+    {
+      if (item_)
+      {
+        return dynamic_cast<Item&>(*item_);
+      }
+      else
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      }
+    }
+  }
 }
--- a/OrthancStone/Sources/Toolbox/ParsedDicomCache.h	Tue Sep 29 10:10:40 2026 +0200
+++ b/OrthancStone/Sources/Toolbox/ParsedDicomCache.h	Tue Sep 29 11:20:29 2026 +0200
@@ -23,12 +23,13 @@
 
 #pragma once
 
-#include <Cache/MemoryObjectCache.h>
+#include <Cache/SharedObjectCache.h>
+#include <Cache/MemoryObjectCache.h>  // TODO Refactoring - Remove this
 #include <DicomParsing/ParsedDicomFile.h>
 
 namespace OrthancStone
 {
-  class ParsedDicomCache : public boost::noncopyable
+  class ParsedDicomCache : public boost::noncopyable  // TODO Refactoring - Remove this
   {
   private:
     class Item;
@@ -81,4 +82,94 @@
       size_t GetFileSize() const;
     };
   };
+
+
+  namespace New
+  {
+    class ParsedDicomCache : public boost::noncopyable
+    {
+    private:
+      class Item : public Orthanc::IDynamicObject
+      {
+      private:
+        Orthanc::Mutex                               mutex_;
+        boost::shared_ptr<Orthanc::ParsedDicomFile>  dicom_;
+        size_t                                       fileSize_;
+        bool                                         hasPixelData_;
+
+      public:
+        Item(const boost::shared_ptr<Orthanc::ParsedDicomFile>& dicom,
+             size_t fileSize,
+             bool hasPixelData);
+
+        Orthanc::Mutex& GetMutex()
+        {
+          return mutex_;
+        }
+
+        const boost::shared_ptr<Orthanc::ParsedDicomFile>& GetDicom() const
+        {
+          return dicom_;
+        }
+
+        size_t GetFileSize() const
+        {
+          return fileSize_;
+        }
+
+        bool HasPixelData() const
+        {
+          return hasPixelData_;
+        }
+      };
+
+      Orthanc::SharedObjectCache  cache_;
+
+    public:
+      ParsedDicomCache(uint64_t capacity) :
+        cache_(capacity)
+      {
+      }
+
+      void Store(const std::string& key,
+                 const boost::shared_ptr<Orthanc::ParsedDicomFile>& dicom,
+                 size_t fileSize,
+                 bool hasPixelData);
+
+      void Invalidate(const std::string& key)
+      {
+        cache_.Invalidate(key);
+      }
+
+      class Accessor : public boost::noncopyable
+      {
+      private:
+        boost::shared_ptr<Orthanc::IDynamicObject>   item_;
+        std::unique_ptr<Orthanc::Mutex::ScopedLock>  lock_;
+
+        const Item& GetItem() const;
+
+      public:
+        Accessor(ParsedDicomCache& cache,
+                 const std::string& key);
+
+        bool IsValid() const;
+
+        const boost::shared_ptr<Orthanc::ParsedDicomFile>& GetDicom() const
+        {
+          return GetItem().GetDicom();
+        }
+
+        size_t GetFileSize() const
+        {
+          return GetItem().GetFileSize();
+        }
+
+        bool HasPixelData() const
+        {
+          return GetItem().HasPixelData();
+        }
+      };
+    };
+  }
 }