changeset 2314:891aff56bbee refactoring

removed class GenericLoadersContext
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 30 Sep 2026 13:56:32 +0200
parents 46e57967b285
children de240abfeeeb
files Applications/Samples/Common/RtViewerApp.cpp Applications/Samples/Common/RtViewerApp.h Applications/Samples/Sdl/RtViewer/RtViewerSdl.cpp Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp Applications/Samples/WebAssembly/RtViewer/RtViewerWasm.cpp OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake OrthancStone/Sources/Loaders/DicomStructureSetLoader.cpp OrthancStone/Sources/Loaders/DicomStructureSetLoader.h OrthancStone/Sources/Loaders/GenericLoadersContext.cpp OrthancStone/Sources/Loaders/GenericLoadersContext.h OrthancStone/Sources/Loaders/LoaderStateMachine.cpp OrthancStone/Sources/Loaders/LoaderStateMachine.h OrthancStone/Sources/Loaders/OrthancMultiframeVolumeLoader.cpp OrthancStone/Sources/Loaders/OrthancMultiframeVolumeLoader.h OrthancStone/Sources/Loaders/OrthancSeriesVolumeProgressiveLoader.cpp OrthancStone/Sources/Loaders/OrthancSeriesVolumeProgressiveLoader.h
diffstat 16 files changed, 166 insertions(+), 376 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Samples/Common/RtViewerApp.cpp	Wed Sep 30 11:50:48 2026 +0200
+++ b/Applications/Samples/Common/RtViewerApp.cpp	Wed Sep 30 13:56:32 2026 +0200
@@ -80,8 +80,9 @@
     }
   }
 
-  RtViewerApp::RtViewerApp() :
+  RtViewerApp::RtViewerApp(StoneApplication::Context& context) :
     // Create the volumes that will be filled later on
+    context_(context),
     ctVolume_(boost::make_shared<DicomVolumeImage>()),
     doseVolume_(boost::make_shared<DicomVolumeImage>()),
     undoStack_(new UndoStack),
@@ -89,9 +90,9 @@
   {
   }
 
-  boost::shared_ptr<RtViewerApp> RtViewerApp::Create()
+  boost::shared_ptr<RtViewerApp> RtViewerApp::Create(StoneApplication::Context& context)
   {
-    boost::shared_ptr<RtViewerApp> thisOne(new RtViewerApp());
+    boost::shared_ptr<RtViewerApp> thisOne(new RtViewerApp(context));
     return thisOne;
   }
 
@@ -128,7 +129,7 @@
       // "true" means use progressive quality (jpeg 50 --> jpeg 90 --> 16-bit raw)
       // "false" means only using hi quality
       // TODO: add flag for quality
-      ctLoader_ = OrthancSeriesVolumeProgressiveLoader::Create(*loadersContext_, ctVolume_, true);
+      ctLoader_ = OrthancSeriesVolumeProgressiveLoader::Create(context_, ctVolume_, true);
       
       // better priority for CT vs dose and struct
       ctLoader_->SetSchedulingPriority(-100);
@@ -137,8 +138,8 @@
       // we need to store the CT loader to ask from geometry details later on when geometry is loaded
       geometryProvider_ = ctLoader_;
 
-      doseLoader_ = OrthancMultiframeVolumeLoader::Create(*loadersContext_, doseVolume_);
-      rtstructLoader_ = DicomStructureSetLoader::Create(*loadersContext_);
+      doseLoader_ = OrthancMultiframeVolumeLoader::Create(context_, doseVolume_);
+      rtstructLoader_ = DicomStructureSetLoader::Create(context_);
     }
 
     /**
--- a/Applications/Samples/Common/RtViewerApp.h	Wed Sep 30 11:50:48 2026 +0200
+++ b/Applications/Samples/Common/RtViewerApp.h	Wed Sep 30 13:56:32 2026 +0200
@@ -23,7 +23,6 @@
 #pragma once
 
 #include "../../../OrthancStone/Sources/Loaders/DicomStructureSetLoader.h"
-#include "../../../OrthancStone/Sources/Loaders/ILoadersContext.h"
 #include "../../../OrthancStone/Sources/Loaders/OrthancMultiframeVolumeLoader.h"
 #include "../../../OrthancStone/Sources/Loaders/OrthancSeriesVolumeProgressiveLoader.h"
 #include "../../../OrthancStone/Sources/Messages/IMessageEmitter.h"
@@ -31,6 +30,7 @@
 #include "../../../OrthancStone/Sources/Messages/ObserverBase.h"
 #include "../../../OrthancStone/Sources/Oracle/OracleCommandExceptionMessage.h"
 #include "../../../OrthancStone/Sources/Scene2DViewport/ViewportController.h"
+#include "../../../OrthancStone/Sources/StoneApplication.h"
 #include "../../../OrthancStone/Sources/Viewport/DefaultViewportInteractor.h"
 #include "../../../OrthancStone/Sources/Viewport/IViewport.h"
 #include "../../../OrthancStone/Sources/Volumes/DicomVolumeImage.h"
@@ -72,11 +72,10 @@
 
 #if ORTHANC_ENABLE_SDL
   public:
-    void RunSdl(int argc, char* argv[]);
+    void RunSdl();
     void SdlRunLoop(const std::vector<boost::shared_ptr<RtViewerView> >& views,
                     DefaultViewportInteractor& interactor);
   private:
-    void ProcessOptions(int argc, char* argv[]);
     void HandleApplicationEvent(const SDL_Event& event);
 #elif ORTHANC_ENABLE_WASM
   public:
@@ -96,12 +95,12 @@
 
     VolumeImageGeometry GetMainGeometry();
 
-    static boost::shared_ptr<RtViewerApp> Create();
+    static boost::shared_ptr<RtViewerApp> Create(StoneApplication::Context& context);
 
     void CreateView(const std::string& canvasId, VolumeProjection projection);
 
   protected:
-    RtViewerApp();
+    RtViewerApp(StoneApplication::Context& context);
 
   private:
     void CreateLoaders();
@@ -146,8 +145,7 @@
     boost::shared_ptr<OrthancMultiframeVolumeLoader> doseLoader_;
     boost::shared_ptr<DicomStructureSetLoader>  rtstructLoader_;
 
-    /** encapsulates resources shared by loaders */
-    boost::shared_ptr<ILoadersContext>                  loadersContext_;
+    StoneApplication::Context&   context_;
 
     /**
     another interface to the ctLoader object (that also implements the IVolumeSlicer interface), that serves as the 
--- a/Applications/Samples/Sdl/RtViewer/RtViewerSdl.cpp	Wed Sep 30 11:50:48 2026 +0200
+++ b/Applications/Samples/Sdl/RtViewer/RtViewerSdl.cpp	Wed Sep 30 13:56:32 2026 +0200
@@ -21,12 +21,12 @@
 
 #include "../../Common/RtViewerApp.h"
 #include "../../Common/RtViewerView.h"
+#include "../../Common/SampleHelpers.h"
 #include "../SdlHelpers.h"
 
 #include <EmbeddedResources.h>
 
 // Stone of Orthanc includes
-#include "../../../../OrthancStone/Sources/Loaders/GenericLoadersContext.h"
 #include "../../../../OrthancStone/Sources/OpenGL/OpenGLIncludes.h"
 #include "../../../../OrthancStone/Sources/Platforms/Sdl/SdlOpenGLContext.h"
 #include "../../../../OrthancStone/Sources/StoneApplication.h"
@@ -95,7 +95,8 @@
     // only used in WASM at the moment
   }
 
-  void RtViewerApp::ProcessOptions(int argc, char* argv[])
+  static void ProcessOptions(std::map<std::string, std::string>& arguments,
+                             int argc, char* argv[])
   {
     namespace po = boost::program_options;
     po::options_description desc("Usage");
@@ -135,54 +136,12 @@
       std::string key = it->first;
       const po::variable_value& value = it->second;
       const std::string& strValue = value.as<std::string>();
-      SetArgument(key, strValue);
+      arguments[key] = strValue;
     }
   }
 
-  void RtViewerApp::RunSdl(int argc, char* argv[])
+  void RtViewerApp::RunSdl()
   {
-    ProcessOptions(argc, argv);
-
-    /**
-      Url of the Orthanc instance
-      Typically, in a native application (Qt, SDL), it will be an absolute URL like "http://localhost:8042". In 
-      wasm on the browser, it could be an absolute URL, provided you do not have cross-origin problems, or a relative
-      URL. In our wasm samples, it is set to "..", because we set up either a reverse proxy or an Orthanc ServeFolders
-      plugin that serves the main web application from an URL like "http://localhost:8042/stone-rtviewer" (with ".." 
-      leading to the main Orthanc root URL)
-    */
-    std::string orthancUrl = arguments_["orthanc"];
-
-    StoneApplication::Configuration configuration;
-
-    {
-      Orthanc::WebServiceParameters p;
-      if (HasArgument("orthanc"))
-      {
-        p.SetUrl(orthancUrl);
-      }
-      if (HasArgument("user"))
-      {
-        ORTHANC_ASSERT(HasArgument("password"));
-        p.SetCredentials(GetArgument("user"), GetArgument("password"));
-      } 
-      else
-      {
-        ORTHANC_ASSERT(!HasArgument("password"));
-      }
-      configuration.SetRemoteOrthancParameters(p);
-    }
-
-    OrthancStone::StoneApplication::Initialize(configuration);
-
-    /**
-    Create the shared loaders context
-    */
-    loadersContext_.reset(new GenericLoadersContext(1, 4, 1));
-
-    // we are in SDL --> downcast to concrete type
-    boost::shared_ptr<GenericLoadersContext> loadersContext = boost::dynamic_pointer_cast<GenericLoadersContext>(loadersContext_);
-
     CreateLoaders();
 
     /**
@@ -453,6 +412,30 @@
   }
 }
 
+
+
+static std::string GetArgument(const std::map<std::string, std::string>& arguments,
+                               const std::string& key)
+{
+  std::map<std::string, std::string>::const_iterator found = arguments.find(key);
+  if (found == arguments.end())
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+  }
+  else
+  {
+    return found->second;
+  }
+}
+
+
+static bool HasArgument(const std::map<std::string, std::string>& arguments,
+                        const std::string& key)
+{
+  return (arguments.find(key) != arguments.end());
+}
+
+
 boost::weak_ptr<OrthancStone::RtViewerApp> g_app;
 
 /**
@@ -469,9 +452,51 @@
 
   try
   {
-    boost::shared_ptr<OrthancStone::RtViewerApp> app = OrthancStone::RtViewerApp::Create();
+    std::map<std::string, std::string> arguments;
+    OrthancStone::ProcessOptions(arguments, argc, argv);
+
+    /**
+      Url of the Orthanc instance
+      Typically, in a native application (Qt, SDL), it will be an absolute URL like "http://localhost:8042". In
+      wasm on the browser, it could be an absolute URL, provided you do not have cross-origin problems, or a relative
+      URL. In our wasm samples, it is set to "..", because we set up either a reverse proxy or an Orthanc ServeFolders
+      plugin that serves the main web application from an URL like "http://localhost:8042/stone-rtviewer" (with ".."
+      leading to the main Orthanc root URL)
+    */
+    std::string orthancUrl = arguments["orthanc"];
+
+    OrthancStone::StoneApplication::Configuration configuration;
+
+    {
+      Orthanc::WebServiceParameters p;
+      if (HasArgument(arguments, "orthanc"))
+      {
+        p.SetUrl(orthancUrl);
+      }
+      if (HasArgument(arguments, "user"))
+      {
+        ORTHANC_ASSERT(HasArgument(arguments, "password"));
+        p.SetCredentials(GetArgument(arguments, "user"), GetArgument(arguments, "password"));
+      }
+      else
+      {
+        ORTHANC_ASSERT(!HasArgument(arguments, "password"));
+      }
+      configuration.SetRemoteOrthancParameters(p);
+    }
+
+    OrthancStone::StoneApplication::Initialize(configuration);
+
+    boost::shared_ptr<OrthancStone::RtViewerApp> app = OrthancStone::RtViewerApp::Create(
+      OrthancStone::StoneApplication::GetInstance());
+
+    for (std::map<std::string, std::string>::const_iterator it = arguments.begin(); it != arguments.end(); ++it)
+    {
+      app->SetArgument(it->first, it->second);
+    }
+
     g_app = app;
-    app->RunSdl(argc,argv);
+    app->RunSdl();
   }
   catch (Orthanc::OrthancException& e)
   {
--- a/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp	Wed Sep 30 11:50:48 2026 +0200
+++ b/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp	Wed Sep 30 13:56:32 2026 +0200
@@ -29,7 +29,6 @@
 #include "../SdlHelpers.h"
 #include "../../Common/SampleHelpers.h"
 
-#include "../../../../OrthancStone/Sources/Loaders/GenericLoadersContext.h"
 #include "../../../../OrthancStone/Sources/Platforms/Sdl/SdlViewport.h"
 #include "../../../../OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.h"
 #include "../../../../OrthancStone/Sources/Scene2DViewport/AngleMeasureTool.h"
@@ -426,8 +425,6 @@
         OrthancStone::SdlCairoViewport::Create("Stone of Orthanc", 800, 600);
 #endif
 
-      OrthancStone::GenericLoadersContext context(1, 4, 1);
-
       {
         {
           std::string font;
--- a/Applications/Samples/WebAssembly/RtViewer/RtViewerWasm.cpp	Wed Sep 30 11:50:48 2026 +0200
+++ b/Applications/Samples/WebAssembly/RtViewer/RtViewerWasm.cpp	Wed Sep 30 13:56:32 2026 +0200
@@ -21,10 +21,8 @@
 
 #include "../../Common/RtViewerApp.h"
 #include "../../Common/RtViewerView.h"
-#include "../../Common/SampleHelpers.h"
 
 // Stone of Orthanc includes
-#include "../../../../OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyLoadersContext.h"
 #include "../../../../OrthancStone/Sources/Platforms/WebAssembly/WebGLViewport.h"
 #include "../../../../OrthancStone/Sources/StoneApplication.h"
 #include "../../../../OrthancStone/Sources/StoneException.h"
@@ -157,12 +155,6 @@
 
     OrthancStone::StoneApplication::Initialize(configuration);
 
-    loadersContext_.reset(new WebAssemblyLoadersContext(1, 4, 1));
-
-    // we are in WASM --> downcast to concrete type
-    boost::shared_ptr<WebAssemblyLoadersContext> loadersContext = 
-      boost::dynamic_pointer_cast<WebAssemblyLoadersContext>(loadersContext_);
-
     CreateLoaders();
     
     CreateView("RtViewer_Axial", VolumeProjection_Axial);
@@ -199,8 +191,11 @@
 
       LOG(INFO) << "Endianness: " << Orthanc::EnumerationToString(Orthanc::Toolbox::DetectEndianness());
 
-      g_app = OrthancStone::RtViewerApp::Create();
-  
+      OrthancStone::StoneApplication::Configuration configuration;
+      OrthancStone::StoneApplication::Initialize(configuration);
+
+      g_app = OrthancStone::RtViewerApp::Create(OrthancStone::StoneApplication::GetInstance());
+
       DISPATCH_JAVASCRIPT_EVENT("WasmModuleInitialized");
     }
     EXTERN_CATCH_EXCEPTIONS;
--- a/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake	Wed Sep 30 11:50:48 2026 +0200
+++ b/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake	Wed Sep 30 13:56:32 2026 +0200
@@ -219,7 +219,6 @@
 
 if (NOT ORTHANC_SANDBOXED AND ENABLE_THREADS AND ENABLE_WEB_CLIENT)
   list(APPEND ORTHANC_STONE_SOURCES
-    ${ORTHANC_STONE_ROOT}/Loaders/GenericLoadersContext.cpp
     ${ORTHANC_STONE_ROOT}/Platforms/Native/GenericOracleRunner.cpp
     ${ORTHANC_STONE_ROOT}/Platforms/Native/ThreadedOracle.cpp
     ${ORTHANC_STONE_ROOT}/Platforms/Native/NativeEnvironment.cpp
--- a/OrthancStone/Sources/Loaders/DicomStructureSetLoader.cpp	Wed Sep 30 11:50:48 2026 +0200
+++ b/OrthancStone/Sources/Loaders/DicomStructureSetLoader.cpp	Wed Sep 30 13:56:32 2026 +0200
@@ -69,7 +69,7 @@
 
   protected:
     explicit RestInstanceLookupHandler(DicomStructureSetLoader& loader) :
-      LoaderStateMachine(loader.loadersContext_),
+      LoaderStateMachine(loader.context_),
       loader_(loader)
     {
     }
@@ -378,24 +378,21 @@
   };
     
 
-  DicomStructureSetLoader::DicomStructureSetLoader(
-    ILoadersContext& loadersContext) 
-    : LoaderStateMachine(loadersContext)
-    , loadersContext_(loadersContext)
-    , revision_(0)
-    , countProcessedInstances_(0)
-    , countReferencedInstances_(0)
-    , structuresReady_(false)
+  DicomStructureSetLoader::DicomStructureSetLoader(StoneApplication::Context& context) :
+    LoaderStateMachine(context),
+    context_(context),
+    revision_(0),
+    countProcessedInstances_(0),
+    countReferencedInstances_(0),
+    structuresReady_(false)
   {
     // the default handler to retrieve slice geometry is RestInstanceLookupHandler
     instanceLookupHandler_ = RestInstanceLookupHandler::Create(*this);
   }
     
-  boost::shared_ptr<DicomStructureSetLoader> DicomStructureSetLoader::Create(ILoadersContext& loadersContext)
+  boost::shared_ptr<DicomStructureSetLoader> DicomStructureSetLoader::Create(StoneApplication::Context& context)
   {
-    boost::shared_ptr<DicomStructureSetLoader> obj(
-      new DicomStructureSetLoader(
-        loadersContext));
+    boost::shared_ptr<DicomStructureSetLoader> obj(new DicomStructureSetLoader(context));
     obj->LoaderStateMachine::PostConstructor();
     return obj;
   }
--- a/OrthancStone/Sources/Loaders/DicomStructureSetLoader.h	Wed Sep 30 11:50:48 2026 +0200
+++ b/OrthancStone/Sources/Loaders/DicomStructureSetLoader.h	Wed Sep 30 13:56:32 2026 +0200
@@ -63,7 +63,7 @@
     class RestInstanceLookupHandler;
 
     static boost::shared_ptr<DicomStructureSetLoader> Create(
-      ILoadersContext& loadersContext);
+      StoneApplication::Context& context);
 
     void SetInstanceLookupHandler(boost::shared_ptr<IInstanceLookupHandler> instanceLookupHandler)
     {
@@ -109,7 +109,7 @@
     // Only state of LoaderStateMachine
     class LoadStructure;           // 1st state
     
-    ILoadersContext&                    loadersContext_;
+    StoneApplication::Context&          context_;
     std::unique_ptr<DicomStructureSet>  content_;
     uint64_t                            revision_;
     std::string                         instanceId_;
@@ -143,6 +143,6 @@
     void RetrieveReferencedSlices(const std::set<std::string>& nonEmptyInstances);
 
   protected:
-    explicit DicomStructureSetLoader(ILoadersContext& loadersContext);
+    explicit DicomStructureSetLoader(StoneApplication::Context& context);
   };
 }
--- a/OrthancStone/Sources/Loaders/GenericLoadersContext.cpp	Wed Sep 30 11:50:48 2026 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,135 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2023 Osimis S.A., Belgium
- * Copyright (C) 2021-2026 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program. If not, see
- * <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "GenericLoadersContext.h"
-
-#include "../Platforms/Native/ThreadedOracle.h"
-#include "../StoneApplication.h"
-
-#include <boost/thread.hpp>
-
-
-namespace OrthancStone
-{
-  class GenericLoadersContext::Locker : public ILoadersContext::ILock
-  {
-  private:
-    GenericLoadersContext& that_;
-    boost::recursive_mutex::scoped_lock lock_;
-
-  public:
-    explicit Locker(GenericLoadersContext& that) :
-      that_(that),
-      lock_(that.mutex_)
-    {
-      if (!that_.scheduler_)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-    }
-      
-    virtual ILoadersContext& GetContext() const ORTHANC_OVERRIDE
-    {
-      return that_;
-    };
-
-    virtual void AddLoader(boost::shared_ptr<IObserver> loader) ORTHANC_OVERRIDE
-    {
-      that_.loaders_.push_back(loader);
-    }
-
-    virtual IObservable& GetOracleObservable() const ORTHANC_OVERRIDE
-    {
-      return StoneApplication::GetInstance().GetOracleObservable();
-    }
-
-    virtual void Schedule(boost::shared_ptr<IObserver> receiver,
-                          int priority,
-                          IOracleCommand* command /* Takes ownership */) ORTHANC_OVERRIDE
-    {
-      that_.scheduler_->Schedule(receiver, priority, command);
-    };
-
-    virtual void CancelRequests(boost::shared_ptr<IObserver> receiver) ORTHANC_OVERRIDE
-    {
-      that_.scheduler_->CancelRequests(receiver);
-    }
-
-    virtual void CancelAllRequests() ORTHANC_OVERRIDE
-    {
-      that_.scheduler_->CancelAllRequests();
-    }
-
-    virtual void GetStatistics(uint64_t& scheduledCommands,
-                               uint64_t& processedCommands) ORTHANC_OVERRIDE
-    {
-      scheduledCommands = that_.scheduler_->GetTotalScheduled();
-      processedCommands = that_.scheduler_->GetTotalProcessed();
-    }
-  };
-
-
-  GenericLoadersContext::GenericLoadersContext(unsigned int maxHighPriority,
-                                               unsigned int maxStandardPriority,
-                                               unsigned int maxLowPriority)
-  {
-    scheduler_ = OracleScheduler::Create(StoneApplication::GetInstance(), maxHighPriority, maxStandardPriority, maxLowPriority);
-
-    if (!scheduler_)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
-  }
-
-
-  GenericLoadersContext::~GenericLoadersContext()
-  {
-    LOG(INFO) << "scheduled commands: " << scheduler_->GetTotalScheduled()
-                 << ", processed commands: " << scheduler_->GetTotalProcessed();
-    scheduler_.reset();
-    //LOG(INFO) << "counter: " << scheduler_.use_count();
-  }
-
-  
-  void GenericLoadersContext::WaitUntilComplete()
-  {
-    for (;;)
-    {
-      {
-        boost::recursive_mutex::scoped_lock lock(mutex_);
-        if (scheduler_ &&
-            scheduler_->GetTotalScheduled() == scheduler_->GetTotalProcessed())
-        {
-          return;
-        }
-      }
-
-      boost::this_thread::sleep(boost::posix_time::milliseconds(100));
-    }
-  }
-   
-  ILoadersContext::ILock* GenericLoadersContext::Lock()
-  {
-    return new Locker(*this);
-  }
-}
--- a/OrthancStone/Sources/Loaders/GenericLoadersContext.h	Wed Sep 30 11:50:48 2026 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2023 Osimis S.A., Belgium
- * Copyright (C) 2021-2026 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program. If not, see
- * <http://www.gnu.org/licenses/>.
- **/
-
-#pragma once
-
-#include "../Messages/IMessageEmitter.h"
-#include "ILoadersContext.h"
-#include "DicomSource.h"
-#include "OracleScheduler.h"
-
-#include <boost/thread/recursive_mutex.hpp>
-#include <list>
-
-namespace OrthancStone
-{
-  // TODO Refactoring - Remove this class
-  class GenericLoadersContext : public ILoadersContext
-  {
-  private:
-    class Locker;
-
-    // "Recursive mutex" is necessary, to be able to run
-    // "ILoaderFactory" from a message handler triggered by
-    // "EmitMessage()"
-    boost::recursive_mutex  mutex_;  // TODO Refactoring - This is redundant with IEnvironment
-
-    boost::shared_ptr<OracleScheduler>  scheduler_;
-
-    // Necessary to keep the loaders persistent (including global
-    // function promises), after the function that created them is
-    // left. This avoids creating one global variable for each loader.
-    std::list< boost::shared_ptr<IObserver> >  loaders_; 
-
-  public:
-    GenericLoadersContext(unsigned int maxHighPriority,
-                          unsigned int maxStandardPriority,
-                          unsigned int maxLowPriority);
-
-    virtual ~GenericLoadersContext();
-   
-    virtual ILock* Lock() ORTHANC_OVERRIDE;
-
-    void WaitUntilComplete();
-  };
-}
--- a/OrthancStone/Sources/Loaders/LoaderStateMachine.cpp	Wed Sep 30 11:50:48 2026 +0200
+++ b/OrthancStone/Sources/Loaders/LoaderStateMachine.cpp	Wed Sep 30 13:56:32 2026 +0200
@@ -101,11 +101,8 @@
         ") < simultaneousDownloads_ (" << simultaneousDownloads_ << 
         ") --> will Schedule command addr " << std::hex << nextCommand << std::dec;
 
-      {
-        std::unique_ptr<ILoadersContext::ILock> lock(loadersContext_.Lock());
-        boost::shared_ptr<IObserver> observer(GetSharedObserver());
-        lock->Schedule(observer, 0, nextCommand); // TODO: priority!
-      }
+      context_.Schedule(GetSharedObserver(), 0, nextCommand); // TODO: priority!
+
       pendingCommands_.pop_front();
 
       activeCommands_++;
@@ -165,12 +162,11 @@
   }
 
 
-  LoaderStateMachine::LoaderStateMachine(
-    ILoadersContext& loadersContext)
-    : loadersContext_(loadersContext)
-    , active_(false)
-    , simultaneousDownloads_(4)
-    , activeCommands_(0)
+  LoaderStateMachine::LoaderStateMachine(StoneApplication::Context& context) :
+    context_(context),
+    active_(false),
+    simultaneousDownloads_(4),
+    activeCommands_(0)
   {
     LOG(TRACE) << "LoaderStateMachine(" << std::hex << this 
                << std::dec << ")::LoaderStateMachine()";
@@ -178,20 +174,15 @@
 
   void LoaderStateMachine::PostConstructor()
   {
-    std::unique_ptr<ILoadersContext::ILock>
-      lock(loadersContext_.Lock());
-
-    IObservable& observable = lock->GetOracleObservable();
-
     // TODO => Move this out of constructor
     Register<OrthancRestApiCommand::SuccessMessage>(
-      observable, &LoaderStateMachine::HandleSuccessMessage);
+      context_.GetOracleObservable(), &LoaderStateMachine::HandleSuccessMessage);
     Register<GetOrthancImageCommand::SuccessMessage>(
-      observable, &LoaderStateMachine::HandleSuccessMessage);
+      context_.GetOracleObservable(), &LoaderStateMachine::HandleSuccessMessage);
     Register<GetOrthancWebViewerJpegCommand::SuccessMessage>(
-      observable, &LoaderStateMachine::HandleSuccessMessage);
+      context_.GetOracleObservable(), &LoaderStateMachine::HandleSuccessMessage);
     Register<OracleCommandExceptionMessage>(
-      observable, &LoaderStateMachine::HandleExceptionMessage);
+      context_.GetOracleObservable(), &LoaderStateMachine::HandleExceptionMessage);
   }
 
   LoaderStateMachine::~LoaderStateMachine()
--- a/OrthancStone/Sources/Loaders/LoaderStateMachine.h	Wed Sep 30 11:50:48 2026 +0200
+++ b/OrthancStone/Sources/Loaders/LoaderStateMachine.h	Wed Sep 30 13:56:32 2026 +0200
@@ -30,6 +30,7 @@
 #include "../Oracle/IOracle.h"
 #include "../Oracle/OracleCommandExceptionMessage.h"
 #include "../Oracle/OrthancRestApiCommand.h"
+#include "../StoneApplication.h"
 
 #include <IDynamicObject.h>
 
@@ -37,8 +38,6 @@
 
 namespace OrthancStone
 {
-  class ILoadersContext;
-
   /**
      This class is supplied with Oracle commands and will schedule up to 
      simultaneousDownloads_ of them at the same time, then will schedule the 
@@ -104,7 +103,7 @@
 
     typedef std::list<IOracleCommand*>  PendingCommands;
 
-    ILoadersContext&  loadersContext_;
+    StoneApplication::Context&  context_;
     bool                            active_;
     unsigned int                    simultaneousDownloads_;
     PendingCommands                 pendingCommands_;
@@ -112,7 +111,7 @@
 
 
   public:
-    explicit LoaderStateMachine(ILoadersContext& loadersContext);
+    explicit LoaderStateMachine(StoneApplication::Context& context);
 
     void PostConstructor();
 
--- a/OrthancStone/Sources/Loaders/OrthancMultiframeVolumeLoader.cpp	Wed Sep 30 11:50:48 2026 +0200
+++ b/OrthancStone/Sources/Loaders/OrthancMultiframeVolumeLoader.cpp	Wed Sep 30 13:56:32 2026 +0200
@@ -562,19 +562,18 @@
     return volume_->GetGeometry();
   }
 
-  OrthancMultiframeVolumeLoader::OrthancMultiframeVolumeLoader(
-    ILoadersContext& loadersContext,
-    boost::shared_ptr<DicomVolumeImage> volume,
-    float outliersHalfRejectionRate) 
-    : LoaderStateMachine(loadersContext)
-    , volume_(volume)
-    , isReversedFrameOffsets_(false)
-    , pixelDataLoaded_(false)
-    , outliersHalfRejectionRate_(outliersHalfRejectionRate)
-    , distributionRawMin_(0)
-    , distributionRawMax_(0)
-    , computedDistributionMin_(0)
-    , computedDistributionMax_(0)
+  OrthancMultiframeVolumeLoader::OrthancMultiframeVolumeLoader(StoneApplication::Context& context,
+                                                               boost::shared_ptr<DicomVolumeImage> volume,
+                                                               float outliersHalfRejectionRate) :
+    LoaderStateMachine(context),
+    volume_(volume),
+    isReversedFrameOffsets_(false),
+    pixelDataLoaded_(false),
+    outliersHalfRejectionRate_(outliersHalfRejectionRate),
+    distributionRawMin_(0),
+    distributionRawMax_(0),
+    computedDistributionMin_(0),
+    computedDistributionMax_(0)
   {
     if (volume.get() == NULL)
     {
@@ -584,17 +583,15 @@
 
 
   boost::shared_ptr<OrthancMultiframeVolumeLoader>
-    OrthancMultiframeVolumeLoader::Create(
-      ILoadersContext& loadersContext, 
-      boost::shared_ptr<DicomVolumeImage> volume, 
-      float outliersHalfRejectionRate /*= 0.0005*/)
+  OrthancMultiframeVolumeLoader::Create(StoneApplication::Context& context, 
+                                        boost::shared_ptr<DicomVolumeImage> volume, 
+                                        float outliersHalfRejectionRate /*= 0.0005*/)
   {
     boost::shared_ptr<OrthancMultiframeVolumeLoader> obj(
-      new OrthancMultiframeVolumeLoader(
-        loadersContext,
-        volume,
-        outliersHalfRejectionRate));
+      new OrthancMultiframeVolumeLoader(context, volume, outliersHalfRejectionRate));
+
     obj->LoaderStateMachine::PostConstructor();
+
     return obj;
   }
 
--- a/OrthancStone/Sources/Loaders/OrthancMultiframeVolumeLoader.h	Wed Sep 30 11:50:48 2026 +0200
+++ b/OrthancStone/Sources/Loaders/OrthancMultiframeVolumeLoader.h	Wed Sep 30 13:56:32 2026 +0200
@@ -23,9 +23,10 @@
 
 #pragma once
 
-#include "LoaderStateMachine.h"
+#include "../StoneApplication.h"
 #include "../Volumes/DicomVolumeImage.h"
 #include "../Volumes/IGeometryProvider.h"
+#include "LoaderStateMachine.h"
 
 #include <boost/shared_ptr.hpp>
 
@@ -98,13 +99,13 @@
     void SetUncompressedPixelData(const std::string& pixelData);
 
   protected:
-    OrthancMultiframeVolumeLoader(ILoadersContext& loadersContext,
+    OrthancMultiframeVolumeLoader(StoneApplication::Context& context,
                                   boost::shared_ptr<DicomVolumeImage> volume,
                                   float outliersHalfRejectionRate);
     
   public:
     static boost::shared_ptr<OrthancMultiframeVolumeLoader> Create(
-      ILoadersContext& loadersContext,
+      StoneApplication::Context& context,
       boost::shared_ptr<DicomVolumeImage> volume,
       float outliersHalfRejectionRate = 0.0005);
 
--- a/OrthancStone/Sources/Loaders/OrthancSeriesVolumeProgressiveLoader.cpp	Wed Sep 30 11:50:48 2026 +0200
+++ b/OrthancStone/Sources/Loaders/OrthancSeriesVolumeProgressiveLoader.cpp	Wed Sep 30 13:56:32 2026 +0200
@@ -323,11 +323,7 @@
 
       command->AcquirePayload(new Orthanc::SingleValueObject<unsigned int>(sliceIndex));
       
-      {
-        std::unique_ptr<ILoadersContext::ILock> lock(loadersContext_.Lock());
-        boost::shared_ptr<IObserver> observer(GetSharedObserver());
-        lock->Schedule(observer, sliceSchedulingPriority_, command.release());
-      }
+      context_.Schedule(GetSharedObserver(), sliceSchedulingPriority_, command.release());
     }
     else
     {
@@ -527,45 +523,40 @@
     sliceSchedulingPriority_ = p;
   }
 
-  OrthancSeriesVolumeProgressiveLoader::OrthancSeriesVolumeProgressiveLoader(
-    ILoadersContext& loadersContext,
-    boost::shared_ptr<DicomVolumeImage> volume,
-    bool progressiveQuality)
-    : loadersContext_(loadersContext)
-    , active_(false)
-    , progressiveQuality_(progressiveQuality)
-    , startCenter_(false)
-    , simultaneousDownloads_(4)
-    , volume_(volume)
-    , sorter_(new BasicFetchingItemsSorter::Factory)
-    , volumeImageReadyInHighQuality_(false)
-    , medadataSchedulingPriority_(0)
-    , sliceSchedulingPriority_(0)
+  OrthancSeriesVolumeProgressiveLoader::OrthancSeriesVolumeProgressiveLoader(StoneApplication::Context& context,
+                                                                             boost::shared_ptr<DicomVolumeImage> volume,
+                                                                             bool progressiveQuality) :
+    context_(context),
+    active_(false),
+    progressiveQuality_(progressiveQuality),
+    startCenter_(false),
+    simultaneousDownloads_(4),
+    volume_(volume),
+    sorter_(new BasicFetchingItemsSorter::Factory),
+    volumeImageReadyInHighQuality_(false),
+    medadataSchedulingPriority_(0),
+    sliceSchedulingPriority_(0)
   {
   }
 
   boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> 
-    OrthancSeriesVolumeProgressiveLoader::Create(
-      ILoadersContext& loadersContext,
-      boost::shared_ptr<DicomVolumeImage> volume,
-      bool progressiveQuality)
+  OrthancSeriesVolumeProgressiveLoader::Create(StoneApplication::Context& context,
+                                               boost::shared_ptr<DicomVolumeImage> volume,
+                                               bool progressiveQuality)
   {
-    std::unique_ptr<ILoadersContext::ILock> lock(loadersContext.Lock());
-
     boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> obj(
-        new OrthancSeriesVolumeProgressiveLoader(
-          loadersContext, volume, progressiveQuality));
+      new OrthancSeriesVolumeProgressiveLoader(context, volume, progressiveQuality));
 
     obj->Register<OrthancRestApiCommand::SuccessMessage>(
-      lock->GetOracleObservable(),
+      context.GetOracleObservable(),
       &OrthancSeriesVolumeProgressiveLoader::LoadGeometry);
 
     obj->Register<GetOrthancImageCommand::SuccessMessage>(
-      lock->GetOracleObservable(),
+      context.GetOracleObservable(),
       &OrthancSeriesVolumeProgressiveLoader::LoadBestQualitySliceContent);
 
     obj->Register<GetOrthancWebViewerJpegCommand::SuccessMessage>(
-      lock->GetOracleObservable(),
+      context.GetOracleObservable(),
       &OrthancSeriesVolumeProgressiveLoader::LoadJpegSliceContent);
 
     return obj;
@@ -613,11 +604,8 @@
 
       std::unique_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand);
       command->SetUri("/series/" + seriesId + "/instances-tags");
-      {
-        std::unique_ptr<ILoadersContext::ILock> lock(loadersContext_.Lock());
-        boost::shared_ptr<IObserver> observer(GetSharedObserver());
-        lock->Schedule(observer, medadataSchedulingPriority_, command.release());
-      }
+
+      context_.Schedule(GetSharedObserver(), medadataSchedulingPriority_, command.release());
     }
   }
   
--- a/OrthancStone/Sources/Loaders/OrthancSeriesVolumeProgressiveLoader.h	Wed Sep 30 11:50:48 2026 +0200
+++ b/OrthancStone/Sources/Loaders/OrthancSeriesVolumeProgressiveLoader.h	Wed Sep 30 13:56:32 2026 +0200
@@ -31,6 +31,7 @@
 #include "../Oracle/GetOrthancWebViewerJpegCommand.h"
 #include "../Oracle/IOracle.h"
 #include "../Oracle/OrthancRestApiCommand.h"
+#include "../StoneApplication.h"
 #include "../Toolbox/SlicesSorter.h"
 #include "../Volumes/DicomVolumeImage.h"
 #include "../Volumes/IVolumeSlicer.h"
@@ -119,7 +120,7 @@
 
     void LoadJpegSliceContent(const GetOrthancWebViewerJpegCommand::SuccessMessage& message);
 
-    ILoadersContext&                                loadersContext_;
+    StoneApplication::Context&                      context_;
     bool                                            active_;
     bool                                            progressiveQuality_;
     bool                                            startCenter_;
@@ -139,7 +140,7 @@
     int sliceSchedulingPriority_;
 
     OrthancSeriesVolumeProgressiveLoader(
-      ILoadersContext& loadersContext,
+      StoneApplication::Context& context,
       boost::shared_ptr<DicomVolumeImage> volume,
       bool progressiveQuality);
   
@@ -150,7 +151,7 @@
     See doc for the progressiveQuality_ field
     */
     static boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> Create(
-      ILoadersContext& context,
+      StoneApplication::Context& context,
       boost::shared_ptr<DicomVolumeImage> volume,
       bool progressiveQuality = false);