# HG changeset patch # User Sebastien Jodogne # Date 1789773860 -7200 # Node ID 2329f970cde78fc14cb1a5cb75b15fd47a82fc9f # Parent 7a169e34a0333577f205ea461ca961e8f039064d native oracle: sleep diff -r 7a169e34a033 -r 2329f970cde7 Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp --- a/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp Thu Sep 17 20:17:50 2026 +0200 +++ b/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp Sat Sep 19 01:24:20 2026 +0200 @@ -61,7 +61,7 @@ #include "../../../../OrthancStone/Sources/Oracle/ThreadedOracle.h" static OrthancStone::NativeEnvironment environment_; -static OrthancStone::New::ThreadedOracle oracle_(environment_, 4 /* threads */); +static OrthancStone::New::ThreadedOracle oracle_(4 /* threads */); class Toto : public OrthancStone::IOracleClient @@ -215,6 +215,7 @@ context.StartOracle(); environment_.Start(); + oracle_.Start(); { { @@ -298,7 +299,7 @@ { case SDLK_b: // TODO Refactoring - oracle_.Submit(toto_, new OrthancStone::SleepOracleCommand(1000)); + oracle_.Submit(environment_, toto_, new OrthancStone::SleepOracleCommand(1000)); break; case SDLK_f: @@ -560,6 +561,7 @@ } context.StopOracle(); + oracle_.Stop(); environment_.Stop(); } } diff -r 7a169e34a033 -r 2329f970cde7 Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp --- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Thu Sep 17 20:17:50 2026 +0200 +++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Sat Sep 19 01:24:20 2026 +0200 @@ -4878,7 +4878,7 @@ #include "../../../OrthancStone/Sources/Oracle/SleepOracleCommand.h" static OrthancStone::WebAssemblyEnvironment environment_; -static OrthancStone::New::WebAssemblyOracle oracle_(environment_); +static OrthancStone::New::WebAssemblyOracle oracle_; class Toto : public OrthancStone::IOracleClient { @@ -4937,7 +4937,7 @@ // TODO Refactoring - oracle_.Submit(toto_, new OrthancStone::SleepOracleCommand(2000)); + oracle_.Submit(environment_, toto_, new OrthancStone::SleepOracleCommand(2000)); } diff -r 7a169e34a033 -r 2329f970cde7 OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake --- a/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake Thu Sep 17 20:17:50 2026 +0200 +++ b/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake Sat Sep 19 01:24:20 2026 +0200 @@ -266,6 +266,7 @@ ${ORTHANC_STONE_ROOT}/Oracle/GetOrthancImageCommand.cpp ${ORTHANC_STONE_ROOT}/Oracle/GetOrthancWebViewerJpegCommand.cpp ${ORTHANC_STONE_ROOT}/Oracle/HttpCommand.cpp + ${ORTHANC_STONE_ROOT}/Oracle/OracleCallback.cpp ${ORTHANC_STONE_ROOT}/Oracle/OracleCommandBase.cpp ${ORTHANC_STONE_ROOT}/Oracle/OrthancRestApiCommand.cpp ${ORTHANC_STONE_ROOT}/Oracle/ParseDicomFromFileCommand.cpp diff -r 7a169e34a033 -r 2329f970cde7 OrthancStone/Sources/Oracle/IOracle.h --- a/OrthancStone/Sources/Oracle/IOracle.h Thu Sep 17 20:17:50 2026 +0200 +++ b/OrthancStone/Sources/Oracle/IOracle.h Sat Sep 19 01:24:20 2026 +0200 @@ -24,8 +24,9 @@ #pragma once #include "../Messages/IObserver.h" +#include "IEnvironment.h" +#include "IOracleClient.h" #include "IOracleCommand.h" -#include "IOracleClient.h" #include @@ -59,7 +60,8 @@ { } - virtual void Submit(const boost::shared_ptr& client, + virtual void Submit(IEnvironment& environment, + const boost::shared_ptr& client, IOracleCommand* command /* takes ownership */) = 0; }; } diff -r 7a169e34a033 -r 2329f970cde7 OrthancStone/Sources/Oracle/OracleCallback.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancStone/Sources/Oracle/OracleCallback.cpp Sat Sep 19 01:24:20 2026 +0200 @@ -0,0 +1,70 @@ +/** + * 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 + * . + **/ + + +#include "OracleCallback.h" + + +namespace OrthancStone +{ + OracleCallback::OracleCallback(IEnvironment& environment, + const boost::shared_ptr& client, + IOracleCommand* command /* takes ownership */) : + environment_(environment), + client_(client), + command_(command) + { + if (!client || + command == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + + void OracleCallback::NotifySuccess(Orthanc::IDynamicObject* result) + { + std::unique_ptr protection(result); + + if (command_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + environment_.NotifyOracleSuccess(client_, command_.release(), protection.release()); + } + } + + + void OracleCallback::NotifyError(const Orthanc::OrthancException& error) + { + if (command_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + environment_.NotifyOracleError(client_, command_.release(), error); + } + } +} diff -r 7a169e34a033 -r 2329f970cde7 OrthancStone/Sources/Oracle/OracleCallback.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancStone/Sources/Oracle/OracleCallback.h Sat Sep 19 01:24:20 2026 +0200 @@ -0,0 +1,46 @@ +/** + * 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 + * . + **/ + + +#pragma once + +#include "IEnvironment.h" + +namespace OrthancStone +{ + class OracleCallback : public boost::noncopyable + { + private: + IEnvironment& environment_; + boost::weak_ptr client_; + std::unique_ptr command_; + + public: + OracleCallback(IEnvironment& environment, + const boost::shared_ptr& client, + IOracleCommand* command /* takes ownership */); + + void NotifySuccess(Orthanc::IDynamicObject* result); + + void NotifyError(const Orthanc::OrthancException& error); + }; +} diff -r 7a169e34a033 -r 2329f970cde7 OrthancStone/Sources/Oracle/ThreadedOracle.cpp --- a/OrthancStone/Sources/Oracle/ThreadedOracle.cpp Thu Sep 17 20:17:50 2026 +0200 +++ b/OrthancStone/Sources/Oracle/ThreadedOracle.cpp Sat Sep 19 01:24:20 2026 +0200 @@ -23,6 +23,7 @@ #include "ThreadedOracle.h" +#include "OracleCallback.h" #include "SleepOracleCommand.h" #include @@ -132,7 +133,7 @@ const boost::posix_time::ptime now = boost::posix_time::microsec_clock::local_time(); Content stillSleeping; - + for (Content::iterator it = content_.begin(); it != content_.end(); ++it) { if (*it != NULL && @@ -440,18 +441,91 @@ class ThreadedOracle::SleepRunnable : public Orthanc::IRunnable { private: + class Item : public boost::noncopyable + { + private: + OracleCallback callback_; + boost::posix_time::ptime expiration_; + public: + Item(IEnvironment& environment, + const boost::shared_ptr& client, + SleepOracleCommand* command) : + callback_(environment, client, command) + { + expiration_ = (boost::posix_time::microsec_clock::local_time() + + boost::posix_time::milliseconds(command->GetDelay())); + } + + const boost::posix_time::ptime& GetExpirationTime() const + { + return expiration_; + } + + OracleCallback& GetCallback() + { + return callback_; + } + }; + + typedef std::list 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(IEnvironment& environment, + const boost::shared_ptr& client, + SleepOracleCommand* command /* takes ownership */) + { + boost::mutex::scoped_lock lock(mutex_); + content_.push_back(new Item(environment, client, command)); + } + + + // 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; + + for (Content::iterator it = content_.begin(); it != content_.end(); ++it) + { + if (*it != NULL && + (*it)->GetExpirationTime() <= now) + { + (*it)->GetCallback().NotifySuccess(new Orthanc::IDynamicObject); + delete *it; + *it = NULL; + } + else + { + stillSleeping.push_back(*it); + } + } + + // Compact the still-sleeping commands + content_ = stillSleeping; } }; - ThreadedOracle::ThreadedOracle(IEnvironment& environment, - unsigned int countWorkers) : - environment_(environment), + ThreadedOracle::ThreadedOracle(unsigned int countWorkers) : sleepingThread_(new SleepRunnable, 50 /* milliseconds */) { threadPool_.SetThreadsCount(countWorkers); @@ -459,12 +533,28 @@ } - void ThreadedOracle::Submit(const boost::shared_ptr& client, + void ThreadedOracle::Start() + { + sleepingThread_.Start(); + threadPool_.Start(); + } + + + void ThreadedOracle::Stop() + { + threadPool_.Stop(); + sleepingThread_.Stop(); + } + + + void ThreadedOracle::Submit(IEnvironment& environment, + const boost::shared_ptr& client, IOracleCommand* command /* takes ownership */) { std::unique_ptr protection(command); - if (command == NULL) + if (!client || + command == NULL) { throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); } @@ -473,6 +563,13 @@ { switch (command->GetType()) { + case IOracleCommand::Type_Sleep: + { + SleepRunnable& runnable = dynamic_cast(sleepingThread_.GetRunnable()); + runnable.Add(environment, client, dynamic_cast(protection.release())); + break; + } + default: throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, "Command type not implemented by the Threaded Oracle: " + @@ -481,11 +578,11 @@ } catch (Orthanc::OrthancException& e) { - environment_.NotifyOracleError(client, protection.release(), e); + environment.NotifyOracleError(client, protection.release(), e); } catch (...) { - environment_.NotifyOracleError(client, protection.release(), Orthanc::OrthancException(Orthanc::ErrorCode_InternalError)); + environment.NotifyOracleError(client, protection.release(), Orthanc::OrthancException(Orthanc::ErrorCode_InternalError)); } } } diff -r 7a169e34a033 -r 2329f970cde7 OrthancStone/Sources/Oracle/ThreadedOracle.h --- a/OrthancStone/Sources/Oracle/ThreadedOracle.h Thu Sep 17 20:17:50 2026 +0200 +++ b/OrthancStone/Sources/Oracle/ThreadedOracle.h Sat Sep 19 01:24:20 2026 +0200 @@ -122,30 +122,20 @@ class ThreadedOracle : public IOracle { private: - class SleepCommands; class SleepRunnable; - IEnvironment& environment_; RunnableThread sleepingThread_; Orthanc::ThreadPool threadPool_; public: - ThreadedOracle(IEnvironment& environment, - unsigned int countWorkers); + ThreadedOracle(unsigned int countWorkers); + + void Start(); - void Start() - { - sleepingThread_.Start(); - threadPool_.Start(); - } + void Stop(); - void Stop() - { - threadPool_.Stop(); - sleepingThread_.Stop(); - } - - virtual void Submit(const boost::shared_ptr& client, + virtual void Submit(IEnvironment& environment, + const boost::shared_ptr& client, IOracleCommand* command /* takes ownership */) ORTHANC_OVERRIDE; }; } diff -r 7a169e34a033 -r 2329f970cde7 OrthancStone/Sources/Platforms/Native/NativeEnvironment.h --- a/OrthancStone/Sources/Platforms/Native/NativeEnvironment.h Thu Sep 17 20:17:50 2026 +0200 +++ b/OrthancStone/Sources/Platforms/Native/NativeEnvironment.h Sat Sep 19 01:24:20 2026 +0200 @@ -39,7 +39,7 @@ class SuccessCompletion; class ErrorCompletion; - boost::mutex mutex_; // Main mutex of the application, to go single-threaded + boost::recursive_mutex mutex_; // Main mutex of the application, to go single-threaded Orthanc::SharedMessageQueue oracleQueue_; RunnableThread oracleThread_; diff -r 7a169e34a033 -r 2329f970cde7 OrthancStone/Sources/Platforms/Native/RunnableThread.h --- a/OrthancStone/Sources/Platforms/Native/RunnableThread.h Thu Sep 17 20:17:50 2026 +0200 +++ b/OrthancStone/Sources/Platforms/Native/RunnableThread.h Sat Sep 19 01:24:20 2026 +0200 @@ -69,5 +69,10 @@ { StopInternal(true); } + + Orthanc::IRunnable& GetRunnable() const + { + return *runnable_; + } }; } diff -r 7a169e34a033 -r 2329f970cde7 OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyOracle.cpp --- a/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyOracle.cpp Thu Sep 17 20:17:50 2026 +0200 +++ b/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyOracle.cpp Sat Sep 19 01:24:20 2026 +0200 @@ -29,6 +29,7 @@ # include #endif +#include "../../Oracle/OracleCallback.h" #include "../../Toolbox/StoneToolbox.h" #include @@ -880,43 +881,15 @@ namespace New { - class WebAssemblyOracle::TimeoutCallback + static void TimeoutCallback(void *userData) { - private: - IEnvironment& environment_; - boost::weak_ptr client_; - std::unique_ptr command_; - - public: - TimeoutCallback(IEnvironment& environment, - const boost::weak_ptr& client, - SleepOracleCommand* command) : - environment_(environment), - client_(client), - command_(command) - { - if (command == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - } - - void Notify() - { - assert(environment_.get() != NULL); - assert(command_.get() != NULL); - environment_.NotifyOracleSuccess(client_, command_.release(), new Orthanc::IDynamicObject); - } - - static void Callback(void *userData) - { - std::unique_ptr callback(reinterpret_cast(userData)); - callback->Notify(); - } - }; + std::unique_ptr callback(reinterpret_cast(userData)); + callback->NotifySuccess(new Orthanc::IDynamicObject); + } - void WebAssemblyOracle::Submit(const boost::shared_ptr& client, + void WebAssemblyOracle::Submit(IEnvironment& environment, + const boost::shared_ptr& client, IOracleCommand* command /* takes ownership */) { // TODO Refactoring - Use "priority" @@ -935,23 +908,24 @@ case IOracleCommand::Type_Sleep: { unsigned int timeoutMS = dynamic_cast(command)->GetDelay(); - emscripten_set_timeout(TimeoutCallback::Callback, timeoutMS, - new TimeoutCallback(environment_, client, dynamic_cast(protection.release()))); + emscripten_set_timeout(TimeoutCallback, timeoutMS, + new OracleCallback(environment, client, dynamic_cast(protection.release()))); break; } default: throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, - "Command type not implemented by the WebAssembly Oracle: " + command->GetType()); + "Command type not implemented by the WebAssembly Oracle: " + + boost::lexical_cast(command->GetType())); } } catch (Orthanc::OrthancException& e) { - environment_.NotifyOracleError(client, protection.release(), e); + environment.NotifyOracleError(client, protection.release(), e); } catch (...) { - environment_.NotifyOracleError(client, protection.release(), Orthanc::OrthancException(Orthanc::ErrorCode_InternalError)); + environment.NotifyOracleError(client, protection.release(), Orthanc::OrthancException(Orthanc::ErrorCode_InternalError)); } } } diff -r 7a169e34a033 -r 2329f970cde7 OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyOracle.h --- a/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyOracle.h Thu Sep 17 20:17:50 2026 +0200 +++ b/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyOracle.h Sat Sep 19 01:24:20 2026 +0200 @@ -160,18 +160,9 @@ { class WebAssemblyOracle : public IOracle { - private: - class TimeoutCallback; - - IEnvironment& environment_; - public: - WebAssemblyOracle(IEnvironment& environment) : - environment_(environment) - { - } - - virtual void Submit(const boost::shared_ptr& client, + virtual void Submit(IEnvironment& environment, + const boost::shared_ptr& client, IOracleCommand* command /* takes ownership */) ORTHANC_OVERRIDE; }; }