changeset 2298:e057abc1a572 refactoring

removed old ThreadedOracle
author Sebastien Jodogne <s.jodogne@gmail.com>
date Sat, 26 Sep 2026 12:34:25 +0200
parents d852cb4c19fd
children 5e19a6760208
files Applications/Samples/Sdl/RtViewer/RtViewerSdl.cpp Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp OrthancStone/Sources/Loaders/GenericLoadersContext.cpp OrthancStone/Sources/Loaders/GenericLoadersContext.h OrthancStone/Sources/Oracle/ThreadedOracle.cpp OrthancStone/Sources/Oracle/ThreadedOracle.h OrthancStone/Sources/StoneApplication.cpp OrthancStone/Sources/StoneApplication.h
diffstat 8 files changed, 162 insertions(+), 533 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Samples/Sdl/RtViewer/RtViewerSdl.cpp	Sat Sep 26 12:08:39 2026 +0200
+++ b/Applications/Samples/Sdl/RtViewer/RtViewerSdl.cpp	Sat Sep 26 12:34:25 2026 +0200
@@ -178,13 +178,11 @@
     /**
     Create the shared loaders context
     */
-    loadersContext_.reset(new GenericLoadersContext(configuration, 1, 4, 1));
+    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_);
 
-    loadersContext->StartOracle();
-
     CreateLoaders();
 
     /**
@@ -210,7 +208,6 @@
 
 
     SdlRunLoop(views_, interactor);
-    loadersContext->StopOracle();
   }
 
   void RtViewerView::TakeScreenshot(const std::string& target,
--- a/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp	Sat Sep 26 12:08:39 2026 +0200
+++ b/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp	Sat Sep 26 12:34:25 2026 +0200
@@ -222,10 +222,9 @@
       OrthancStone::StoneApplication::Configuration configuration;
       configuration.SetRemoteOrthancParameters(orthancWebService);
 
-      OrthancStone::GenericLoadersContext context(configuration, 1, 4, 1);
       OrthancStone::StoneApplication::Initialize(configuration);
 
-      context.StartOracle();
+      OrthancStone::GenericLoadersContext context(1, 4, 1);
 
       {
         {
@@ -583,7 +582,6 @@
             SDL_Delay(1);
           }
         }
-        context.StopOracle();
       }
     }
   }
--- a/OrthancStone/Sources/Loaders/GenericLoadersContext.cpp	Sat Sep 26 12:08:39 2026 +0200
+++ b/OrthancStone/Sources/Loaders/GenericLoadersContext.cpp	Sat Sep 26 12:34:25 2026 +0200
@@ -23,6 +23,12 @@
 
 #include "GenericLoadersContext.h"
 
+#include "../Oracle/ThreadedOracle.h"
+#include "../StoneApplication.h"
+
+#include <boost/thread.hpp>
+
+
 namespace OrthancStone
 {
   class GenericLoadersContext::Locker : public ILoadersContext::ILock
@@ -93,13 +99,12 @@
   }
 
 
-  GenericLoadersContext::GenericLoadersContext(const StoneApplication::Configuration& configuration,
-                                               unsigned int maxHighPriority,
+  GenericLoadersContext::GenericLoadersContext(unsigned int maxHighPriority,
                                                unsigned int maxStandardPriority,
                                                unsigned int maxLowPriority)
   {
-    oracle_.reset(new ThreadedOracle(configuration, *this));
-    scheduler_ = OracleScheduler::Create(*oracle_, oracleObservable_, *this,
+    dynamic_cast<ThreadedOracle&>(StoneApplication::GetInstance().GetOldOracle()).SetMessageEmitter(*this);
+    scheduler_ = OracleScheduler::Create(StoneApplication::GetInstance().GetOldOracle(), oracleObservable_, *this,
                                          maxHighPriority, maxStandardPriority, maxLowPriority);
 
     if (!scheduler_)
@@ -118,29 +123,6 @@
   }
 
   
-  void GenericLoadersContext::StartOracle()
-  {
-    boost::recursive_mutex::scoped_lock lock(mutex_);
-    oracle_->Start();
-    //LOG(INFO) << "STARTED ORACLE";
-  }
-
-  
-  void GenericLoadersContext::StopOracle()
-  {
-    /**
-     * DON'T lock "mutex_" here, otherwise Stone won't be able to
-     * stop if one command being executed by the oracle has to emit
-     * a message (method "EmitMessage()" would have to lock the
-     * mutex too).
-     **/
-      
-    //LOG(INFO) << "STOPPING ORACLE";
-    oracle_->Stop();
-    //LOG(INFO) << "STOPPED ORACLE";
-  }
-
-  
   void GenericLoadersContext::WaitUntilComplete()
   {
     for (;;)
--- a/OrthancStone/Sources/Loaders/GenericLoadersContext.h	Sat Sep 26 12:08:39 2026 +0200
+++ b/OrthancStone/Sources/Loaders/GenericLoadersContext.h	Sat Sep 26 12:34:25 2026 +0200
@@ -23,12 +23,12 @@
 #pragma once
 
 #include "../Messages/IMessageEmitter.h"
-#include "../Oracle/ThreadedOracle.h"
 #include "ILoadersContext.h"
 #include "DicomSource.h"
 #include "OracleScheduler.h"
 
 #include <boost/thread/recursive_mutex.hpp>
+#include <list>
 
 namespace OrthancStone
 {
@@ -46,7 +46,6 @@
     boost::recursive_mutex  mutex_;
 
     IObservable                         oracleObservable_;
-    std::unique_ptr<ThreadedOracle>     oracle_;
     boost::shared_ptr<OracleScheduler>  scheduler_;
 
     // Necessary to keep the loaders persistent (including global
@@ -58,8 +57,7 @@
                              const IMessage& message) ORTHANC_OVERRIDE;
 
   public:
-    GenericLoadersContext(const StoneApplication::Configuration& configuration,
-                          unsigned int maxHighPriority,
+    GenericLoadersContext(unsigned int maxHighPriority,
                           unsigned int maxStandardPriority,
                           unsigned int maxLowPriority);
 
@@ -67,10 +65,6 @@
    
     virtual ILock* Lock() ORTHANC_OVERRIDE;
 
-    void StartOracle();
-
-    void StopOracle();
-
     void WaitUntilComplete();
   };
 }
--- a/OrthancStone/Sources/Oracle/ThreadedOracle.cpp	Sat Sep 26 12:08:39 2026 +0200
+++ b/OrthancStone/Sources/Oracle/ThreadedOracle.cpp	Sat Sep 26 12:34:25 2026 +0200
@@ -23,6 +23,7 @@
 
 #include "ThreadedOracle.h"
 
+#include "GenericOracleRunner.h"
 #include "OracleCallback.h"
 #include "SleepOracleCommand.h"
 
@@ -31,22 +32,47 @@
 
 namespace OrthancStone
 {
-  class ThreadedOracle::SleepingCommands : public boost::noncopyable
+  class ThreadedOracle::GenericRunnable : public Orthanc::IRunnable
   {
   private:
-    class Item
+    StoneApplication::Configuration       configuration_;
+    std::unique_ptr<GenericOracleRunner>  runner_;
+    std::unique_ptr<IOracleCallback>      callback_;
+
+  public:
+    GenericRunnable(const StoneApplication::Configuration& configuration,
+                    GenericOracleRunner* runner /* takes ownership */,
+                    IOracleCallback* callback /* takes ownership */) :
+      configuration_(configuration),
+      runner_(runner),
+      callback_(callback)
+    {
+      if (runner == NULL ||
+          callback == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      }
+    }
+
+    virtual void Run() ORTHANC_OVERRIDE
+    {
+      runner_->Run(*callback_);
+    }
+  };
+
+
+  class ThreadedOracle::SleepRunnable : public Orthanc::IRunnable
+  {
+  private:
+    class Item : public boost::noncopyable
     {
     private:
-      std::unique_ptr<OldOracleCallback>  callback_;
-      boost::posix_time::ptime            expiration_;
-
-      const SleepOracleCommand& GetCommand() const
-      {
-        return dynamic_cast<const SleepOracleCommand&>(callback_->GetCommand());
-      }
+      std::unique_ptr<IOracleCallback>  callback_;
+      boost::posix_time::ptime          expiration_;
 
     public:
-      explicit Item(OldOracleCallback* callback) :
+      Item(IOracleCallback* callback,
+           unsigned int delay) :
         callback_(callback)
       {
         if (callback == NULL)
@@ -54,8 +80,8 @@
           throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
         }
 
-        expiration_ = (boost::posix_time::microsec_clock::local_time() + 
-                       boost::posix_time::milliseconds(GetCommand().GetDelay()));
+        expiration_ = (boost::posix_time::microsec_clock::local_time() +
+                       boost::posix_time::milliseconds(delay));
       }
 
       const boost::posix_time::ptime& GetExpirationTime() const
@@ -63,9 +89,9 @@
         return expiration_;
       }
 
-      void Awake(IMessageEmitter& emitter)
+      IOracleCallback& GetCallback()
       {
-        callback_->NotifySuccess(new SleepOracleCommand::TimeoutMessage(GetCommand()));
+        return *callback_;
       }
     };
 
@@ -75,7 +101,7 @@
     Content       content_;
 
   public:
-    ~SleepingCommands()
+    ~SleepRunnable()
     {
       for (Content::iterator it = content_.begin(); it != content_.end(); ++it)
       {
@@ -86,13 +112,17 @@
       }
     }
 
-    void Add(OldOracleCallback* callback /* takes ownership */)
+
+    void Add(IOracleCallback* callback,
+             unsigned int delay)
     {
       boost::mutex::scoped_lock lock(mutex_);
-      content_.push_back(new Item(callback));
+      content_.push_back(new Item(callback, delay));
     }
 
-    void AwakeExpired(IMessageEmitter& emitter)
+
+    // Awakes expired sleeps
+    virtual void Run() ORTHANC_OVERRIDE
     {
       boost::mutex::scoped_lock lock(mutex_);
 
@@ -105,7 +135,8 @@
         if (*it != NULL &&
             (*it)->GetExpirationTime() <= now)
         {
-          (*it)->Awake(emitter);
+          const SleepOracleCommand& command = dynamic_cast<const SleepOracleCommand&>((*it)->GetCallback().GetCommand()); // TODO Refactoring - Remove this
+          (*it)->GetCallback().NotifySuccess(new SleepOracleCommand::TimeoutMessage(command));
           delete *it;
           *it = NULL;
         }
@@ -121,119 +152,14 @@
   };
 
 
-  void ThreadedOracle::Step()
-  {
-    std::unique_ptr<Orthanc::IDynamicObject>  object(queue_.Dequeue(100));
-
-    if (object.get() != NULL)
-    {
-      std::unique_ptr<OldOracleCallback> item(dynamic_cast<OldOracleCallback*>(object.release()));
-
-      if (item->GetCommand().GetType() == IOracleCommand::Type_Sleep)
-      {
-        sleepingCommands_->Add(item.release());
-      }
-      else
-      {
-        GenericOracleRunner runner(configuration_);
-
-#if ORTHANC_ENABLE_DCMTK == 1
-        if (dicomCache_)
-        {
-          runner.SetDicomCache(dicomCache_);
-        }
-#endif
-
-        runner.Run(*item);
-      }
-    }
-  }
-
-
-  void ThreadedOracle::Worker(ThreadedOracle* that)
-  {
-    assert(that != NULL);
-      
-    for (;;)
-    {
-      {
-        boost::mutex::scoped_lock lock(that->mutex_);
-        if (that->state_ != State_Running)
-        {
-          return;
-        }
-      }
-
-      that->Step();
-    }
-  }
-
-
-  void ThreadedOracle::SleepingWorker(ThreadedOracle* that)
+  ThreadedOracle::ThreadedOracle(const StoneApplication::Configuration& configuration) :
+    emitter_(NULL),
+    configuration_(configuration),
+    sleepingThread_(new SleepRunnable, configuration.GetWorkersTimeResolution())
   {
-    assert(that != NULL);
-      
-    for (;;)
-    {
-      {
-        boost::mutex::scoped_lock lock(that->mutex_);
-        if (that->state_ != State_Running)
-        {
-          return;
-        }
-      }
-
-      that->sleepingCommands_->AwakeExpired(that->emitter_);
-
-      boost::this_thread::sleep(boost::posix_time::milliseconds(that->configuration_.GetWorkersTimeResolution()));
-    }
-  }
-
-
-  void ThreadedOracle::StopInternal()
-  {
-    {
-      boost::mutex::scoped_lock lock(mutex_);
+    threadPool_.SetThreadsCount(configuration.GetOracleThreadsCount());
+    threadPool_.SetDequeueTimeout(configuration.GetWorkersTimeResolution());
 
-      if (state_ == State_Setup ||
-          state_ == State_Stopped)
-      {
-        return;
-      }
-      else
-      {
-        state_ = State_Stopped;
-      }
-    }
-
-    if (sleepingWorker_.joinable())
-    {
-      sleepingWorker_.join();
-    }
-
-    for (size_t i = 0; i < workers_.size(); i++)
-    {
-      if (workers_[i] != NULL)
-      {
-        if (workers_[i]->joinable())
-        {
-          workers_[i]->join();
-        }
-
-        delete workers_[i];
-      }
-    } 
-  }
-
-
-  ThreadedOracle::ThreadedOracle(const StoneApplication::Configuration& configuration,
-                                 IMessageEmitter& emitter) :
-    configuration_(configuration),
-    emitter_(emitter),
-    state_(State_Setup),
-    workers_(4),
-    sleepingCommands_(new SleepingCommands)
-  {
     if (configuration.GetDicomCacheSize() == 0)
     {
       LOG(WARNING) << "The DICOM cache is disabled";
@@ -246,314 +172,79 @@
   }
 
 
-  ThreadedOracle::~ThreadedOracle()
+  void ThreadedOracle::Start()
+  {
+    sleepingThread_.Start();
+    threadPool_.Start();
+  }
+
+
+  void ThreadedOracle::Stop()
   {
-    if (state_ == State_Running)
+    threadPool_.Stop();
+    sleepingThread_.Stop();
+  }
+
+
+  void ThreadedOracle::SubmitInternal(IOracleCallback* callback /* takes ownership */)
+  {
+    std::unique_ptr<IOracleCallback> protection(callback);
+
+    if (callback == NULL)
     {
-      LOG(ERROR) << "The threaded oracle is still running, explicit call to "
-                 << "Stop() is mandatory to avoid crashes";
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
     }
 
-    try
+    if (protection->GetCommand().GetType() == IOracleCommand::Type_Sleep)
     {
-      StopInternal();
+      const unsigned int delay = dynamic_cast<const SleepOracleCommand&>(protection->GetCommand()).GetDelay();
+      SleepRunnable& runnable = dynamic_cast<SleepRunnable&>(sleepingThread_.GetRunnable());
+      runnable.Add(protection.release(), delay);
     }
-    catch (Orthanc::OrthancException& e)
+    else
     {
-      LOG(ERROR) << "Exception while stopping the threaded oracle: " << e.What();
-    }
-    catch (...)
-    {
-      LOG(ERROR) << "Native exception while stopping the threaded oracle";
+      std::unique_ptr<GenericOracleRunner> runner(new GenericOracleRunner(configuration_));
+
+#if ORTHANC_ENABLE_DCMTK == 1
+      if (dicomCache_)
+      {
+        runner->SetDicomCache(dicomCache_);
+      }
+#endif
+
+      threadPool_.Submit(new GenericRunnable(configuration_, runner.release(), protection.release()));
     }
   }
 
-  
-  void ThreadedOracle::Start()
-  {
-    boost::mutex::scoped_lock lock(mutex_);
 
-    if (state_ != State_Setup)
+  void ThreadedOracle::Submit(IEnvironment& environment,
+                              const boost::shared_ptr<IOracleClient>& client,
+                              IOracleCommand* command /* takes ownership */)
+  {
+    std::unique_ptr<IOracleCommand> protection(command);
+
+    if (!client)
     {
-      LOG(ERROR) << "ThreadedOracle::Start(): (state_ != State_Setup)";
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
     }
-    else
-    {
-      LOG(INFO) << "Starting oracle with " << workers_.size() << " worker threads";
-      state_ = State_Running;
 
-      for (unsigned int i = 0; i < workers_.size(); i++)
-      {
-        workers_[i] = new boost::thread(Worker, this);
-      }
-
-      sleepingWorker_ = boost::thread(SleepingWorker, this);
-    }      
+    SubmitInternal(new OracleCallback(environment, client, protection.release()));
   }
 
 
   bool ThreadedOracle::Schedule(boost::shared_ptr<IObserver> receiver,
                                 IOracleCommand* command)
   {
-    std::unique_ptr<OldOracleCallback> item(new OldOracleCallback(command, receiver, emitter_));
-
-    {
-      boost::mutex::scoped_lock lock(mutex_);
-
-      if (state_ == State_Running)
-      {
-        //LOG(INFO) << "New oracle command queued";
-        queue_.Enqueue(item.release());
-        return true;
-      }
-      else
-      {
-        LOG(TRACE) << "Command not enqueued, as the oracle has stopped";
-        return false;
-      }
-    }
-  }
-
-
-
-  namespace New
-  {
-    class ThreadedOracle::GenericRunnable : public Orthanc::IRunnable
-    {
-    private:
-      StoneApplication::Configuration       configuration_;
-      std::unique_ptr<GenericOracleRunner>  runner_;
-      std::unique_ptr<IOracleCallback>      callback_;
-
-    public:
-      GenericRunnable(const StoneApplication::Configuration& configuration,
-                      GenericOracleRunner* runner /* takes ownership */,
-                      IOracleCallback* callback /* takes ownership */) :
-        configuration_(configuration),
-        runner_(runner),
-        callback_(callback)
-      {
-        if (runner == NULL ||
-            callback == NULL)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-        }
-      }
-
-      virtual void Run() ORTHANC_OVERRIDE
-      {
-        runner_->Run(*callback_);
-      }
-    };
-
-
-    class ThreadedOracle::SleepRunnable : public Orthanc::IRunnable
-    {
-    private:
-      class Item : public boost::noncopyable
-      {
-      private:
-        std::unique_ptr<IOracleCallback>  callback_;
-        boost::posix_time::ptime          expiration_;
-
-      public:
-        Item(IOracleCallback* callback,
-             unsigned int delay) :
-          callback_(callback)
-        {
-          if (callback == NULL)
-          {
-            throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-          }
-
-          expiration_ = (boost::posix_time::microsec_clock::local_time() +
-                         boost::posix_time::milliseconds(delay));
-        }
-
-        const boost::posix_time::ptime& GetExpirationTime() const
-        {
-          return expiration_;
-        }
-
-        IOracleCallback& GetCallback()
-        {
-          return *callback_;
-        }
-      };
-
-      typedef std::list<Item*>  Content;
-
-      boost::mutex  mutex_;
-      Content       content_;
-
-    public:
-      ~SleepRunnable()
-      {
-        for (Content::iterator it = content_.begin(); it != content_.end(); ++it)
-        {
-          if (*it != NULL)
-          {
-            delete *it;
-          }
-        }
-      }
-
-
-      void Add(IOracleCallback* callback,
-               unsigned int delay)
-      {
-        boost::mutex::scoped_lock lock(mutex_);
-        content_.push_back(new Item(callback, delay));
-      }
-
-
-      // Awakes expired sleeps
-      virtual void Run() ORTHANC_OVERRIDE
-      {
-        boost::mutex::scoped_lock lock(mutex_);
-
-        const boost::posix_time::ptime now = boost::posix_time::microsec_clock::local_time();
-
-        Content  stillSleeping;
+    std::unique_ptr<IOracleCommand> protection(command);
 
-        for (Content::iterator it = content_.begin(); it != content_.end(); ++it)
-        {
-          if (*it != NULL &&
-              (*it)->GetExpirationTime() <= now)
-          {
-            const SleepOracleCommand& command = dynamic_cast<const SleepOracleCommand&>((*it)->GetCallback().GetCommand()); // TODO Refactoring - Remove this
-            (*it)->GetCallback().NotifySuccess(new SleepOracleCommand::TimeoutMessage(command));
-            delete *it;
-            *it = NULL;
-          }
-          else
-          {
-            stillSleeping.push_back(*it);
-          }
-        }
-
-        // Compact the still-sleeping commands
-        content_ = stillSleeping;
-      }
-    };
-
-
-    ThreadedOracle::ThreadedOracle(const StoneApplication::Configuration& configuration) :
-      emitter_(NULL),
-      configuration_(configuration),
-      sleepingThread_(new SleepRunnable, configuration.GetWorkersTimeResolution())
+    if (emitter_ == NULL)
     {
-      threadPool_.SetThreadsCount(configuration.GetOracleThreadsCount());
-      threadPool_.SetDequeueTimeout(configuration.GetWorkersTimeResolution());
-
-      if (configuration.GetDicomCacheSize() == 0)
-      {
-        LOG(WARNING) << "The DICOM cache is disabled";
-      }
-      else
-      {
-        LOG(INFO) << "The DICOM cache size is set to " << configuration.GetDicomCacheSize() << " bytes";
-        dicomCache_.reset(new ParsedDicomCache(configuration.GetDicomCacheSize()));
-      }
-    }
-
-
-    ThreadedOracle::ThreadedOracle(const StoneApplication::Configuration& configuration,
-                                   IMessageEmitter& emitter) :
-      emitter_(&emitter),
-      configuration_(configuration),
-      sleepingThread_(new SleepRunnable, configuration.GetWorkersTimeResolution())
-    {
-      threadPool_.SetThreadsCount(configuration.GetOracleThreadsCount());
-      threadPool_.SetDequeueTimeout(configuration.GetWorkersTimeResolution());
-
-      if (configuration.GetDicomCacheSize() == 0)
-      {
-        LOG(WARNING) << "The DICOM cache is disabled";
-      }
-      else
-      {
-        LOG(INFO) << "The DICOM cache size is set to " << configuration.GetDicomCacheSize() << " bytes";
-        dicomCache_.reset(new ParsedDicomCache(configuration.GetDicomCacheSize()));
-      }
-    }
-
-
-    void ThreadedOracle::Start()
-    {
-      sleepingThread_.Start();
-      threadPool_.Start();
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
-
-
-    void ThreadedOracle::Stop()
-    {
-      threadPool_.Stop();
-      sleepingThread_.Stop();
-    }
-
-
-    void ThreadedOracle::SubmitInternal(IOracleCallback* callback /* takes ownership */)
+    else
     {
-      std::unique_ptr<IOracleCallback> protection(callback);
-
-      if (callback == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-      }
-
-      if (protection->GetCommand().GetType() == IOracleCommand::Type_Sleep)
-      {
-        const unsigned int delay = dynamic_cast<const SleepOracleCommand&>(protection->GetCommand()).GetDelay();
-        SleepRunnable& runnable = dynamic_cast<SleepRunnable&>(sleepingThread_.GetRunnable());
-        runnable.Add(protection.release(), delay);
-      }
-      else
-      {
-        std::unique_ptr<GenericOracleRunner> runner(new GenericOracleRunner(configuration_));
-
-#if ORTHANC_ENABLE_DCMTK == 1
-        if (dicomCache_)
-        {
-          runner->SetDicomCache(dicomCache_);
-        }
-#endif
-
-        threadPool_.Submit(new GenericRunnable(configuration_, runner.release(), protection.release()));
-      }
-    }
-
-
-    void ThreadedOracle::Submit(IEnvironment& environment,
-                                const boost::shared_ptr<IOracleClient>& client,
-                                IOracleCommand* command /* takes ownership */)
-    {
-      std::unique_ptr<IOracleCommand> protection(command);
-
-      if (!client)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-      }
-
-      SubmitInternal(new OracleCallback(environment, client, protection.release()));
-    }
-
-
-    bool ThreadedOracle::Schedule(boost::shared_ptr<IObserver> receiver,
-                                  IOracleCommand* command)
-    {
-      std::unique_ptr<IOracleCommand> protection(command);
-
-      if (emitter_ == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
-      else
-      {
-        SubmitInternal(new OldOracleCallback(protection.release(), receiver, *emitter_));
-        return true;
-      }
+      SubmitInternal(new OldOracleCallback(protection.release(), receiver, *emitter_));
+      return true;
     }
   }
 }
--- a/OrthancStone/Sources/Oracle/ThreadedOracle.h	Sat Sep 26 12:08:39 2026 +0200
+++ b/OrthancStone/Sources/Oracle/ThreadedOracle.h	Sat Sep 26 12:34:25 2026 +0200
@@ -45,103 +45,50 @@
 #include "../Messages/IMessageEmitter.h"
 #include "../Platforms/Native/RunnableThread.h"
 #include "../StoneApplication.h"
-#include "GenericOracleRunner.h"
 #include "IOracle.h"
+#include "OracleCallback.h"
 
-#include <MultiThreading/SharedMessageQueue.h>
 #include <MultiThreading/ThreadPool.h>
 
 
 namespace OrthancStone
 {
-  class ThreadedOracle : public IOracle
+  class ThreadedOracle :
+    public ::OrthancStone::IOracle,  // TODO Refactoring - Remove this
+    public ::OrthancStone::New::IOracle
   {
   private:
-    enum State
-    {
-      State_Setup,
-      State_Running,
-      State_Stopped
-    };
-
-    class SleepingCommands;
+    class GenericRunnable;
+    class SleepRunnable;
 
-    StoneApplication::Configuration      configuration_;
-    IMessageEmitter&                     emitter_;
-    Orthanc::SharedMessageQueue          queue_;
-    State                                state_;
-    boost::mutex                         mutex_;
-    std::vector<boost::thread*>          workers_;
-    boost::shared_ptr<SleepingCommands>  sleepingCommands_;
-    boost::thread                        sleepingWorker_;
+    IMessageEmitter*                 emitter_;  // TODO Refactoring - Remove this
+    StoneApplication::Configuration  configuration_;
+    RunnableThread                   sleepingThread_;
+    Orthanc::ThreadPool              threadPool_;
 
 #if ORTHANC_ENABLE_DCMTK == 1
     boost::shared_ptr<ParsedDicomCache>  dicomCache_;
 #endif
-    
-    void Step();
 
-    static void Worker(ThreadedOracle* that);
-
-    static void SleepingWorker(ThreadedOracle* that);
-
-    void StopInternal();
+    void SubmitInternal(IOracleCallback* callback /* takes ownership */);
 
   public:
-    ThreadedOracle(const StoneApplication::Configuration& configuration,
-                   IMessageEmitter& emitter);
+    ThreadedOracle(const StoneApplication::Configuration& configuration);
 
-    virtual ~ThreadedOracle() ORTHANC_OVERRIDE;
+    void SetMessageEmitter(IMessageEmitter& emitter)  // TODO Refactoring - Remove this
+    {
+      emitter_ = &emitter;
+    }
 
     void Start();
 
-    void Stop()
-    {
-      StopInternal();
-    }
+    void Stop();
+
+    virtual void Submit(IEnvironment& environment,
+                        const boost::shared_ptr<IOracleClient>& client,
+                        IOracleCommand* command /* takes ownership */) ORTHANC_OVERRIDE;
 
     virtual bool Schedule(boost::shared_ptr<IObserver> receiver,
                           IOracleCommand* command) ORTHANC_OVERRIDE;
   };
-
-
-  namespace New
-  {
-    class ThreadedOracle :
-      public ::OrthancStone::IOracle,  // TODO Refactoring - Remove this
-      public ::OrthancStone::New::IOracle
-    {
-    private:
-      class GenericRunnable;
-      class SleepRunnable;
-
-      IMessageEmitter*                 emitter_;  // TODO Refactoring - Remove this
-      StoneApplication::Configuration  configuration_;
-      RunnableThread                   sleepingThread_;
-      Orthanc::ThreadPool              threadPool_;
-
-#if ORTHANC_ENABLE_DCMTK == 1
-      boost::shared_ptr<ParsedDicomCache>  dicomCache_;
-#endif
-
-      void SubmitInternal(IOracleCallback* callback /* takes ownership */);
-
-    public:
-      ThreadedOracle(const StoneApplication::Configuration& configuration);
-
-      ThreadedOracle(const StoneApplication::Configuration& configuration,
-                     IMessageEmitter& emitter);  // TODO Refactoring - Remove this
-
-      void Start();
-
-      void Stop();
-
-      virtual void Submit(IEnvironment& environment,
-                          const boost::shared_ptr<IOracleClient>& client,
-                          IOracleCommand* command /* takes ownership */) ORTHANC_OVERRIDE;
-
-      virtual bool Schedule(boost::shared_ptr<IObserver> receiver,
-                            IOracleCommand* command) ORTHANC_OVERRIDE;
-    };
-  }
 }
--- a/OrthancStone/Sources/StoneApplication.cpp	Sat Sep 26 12:08:39 2026 +0200
+++ b/OrthancStone/Sources/StoneApplication.cpp	Sat Sep 26 12:34:25 2026 +0200
@@ -134,7 +134,7 @@
       return environment_;
     }
 
-    New::IOracle& GetOracle()
+    WebAssemblyOracle& GetOracle()
     {
       return oracle_;
     }
@@ -155,7 +155,7 @@
   {
   private:
     NativeEnvironment    environment_;
-    New::ThreadedOracle  oracle_;
+    ThreadedOracle       oracle_;
 
   public:
     PImpl(const Configuration& configuration) :
@@ -168,7 +168,7 @@
       return environment_;
     }
 
-    New::IOracle& GetOracle()
+    ThreadedOracle& GetOracle()
     {
       return oracle_;
     }
@@ -258,6 +258,20 @@
   }
 
 
+  IOracle& StoneApplication::GetOldOracle()
+  {
+    assert(pimpl_ != NULL);
+    return pimpl_->GetOracle();
+  }
+
+
+  New::IOracle& StoneApplication::GetOracle()
+  {
+    assert(pimpl_ != NULL);
+    return pimpl_->GetOracle();
+  }
+
+
   void StoneApplication::Submit(const boost::shared_ptr<IOracleClient>& client,
                                 IOracleCommand* command /* takes ownership */)
   {
--- a/OrthancStone/Sources/StoneApplication.h	Sat Sep 26 12:08:39 2026 +0200
+++ b/OrthancStone/Sources/StoneApplication.h	Sat Sep 26 12:34:25 2026 +0200
@@ -24,6 +24,7 @@
 #pragma once
 
 #include "Oracle/IEnvironment.h"
+#include "Oracle/IOracle.h"
 
 #include <WebServiceParameters.h>
 
@@ -115,6 +116,11 @@
 
     IEnvironment& GetEnvironment();
 
+    IOracle& GetOldOracle();  // TODO Refactoring - Remove this
+
+    New::IOracle& GetOracle();
+
+    // TODO Refactoring - Remove this?
     void Submit(const boost::shared_ptr<IOracleClient>& client,
                 IOracleCommand* command /* takes ownership */);
   };