Mercurial > hg > orthanc-stone
changeset 2279:6572b73c65c5 refactoring
begin refactoring oracle
line wrap: on
line diff
--- a/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp Fri Aug 14 11:01:24 2026 +0200 +++ b/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp Thu Sep 17 20:10:13 2026 +0200 @@ -54,6 +54,39 @@ static int frameIndex = 0; + +// TODO Refactoring +#include "../../../../OrthancStone/Sources/Platforms/Native/NativeEnvironment.h" +#include "../../../../OrthancStone/Sources/Oracle/SleepOracleCommand.h" +#include "../../../../OrthancStone/Sources/Oracle/ThreadedOracle.h" + +static OrthancStone::NativeEnvironment environment_; +static OrthancStone::New::ThreadedOracle oracle_(environment_, 4 /* threads */); + + +class Toto : public OrthancStone::IOracleClient +{ +public: + virtual void HandleSuccessFromOracle(const OrthancStone::IOracleCommand& command, + const Orthanc::IDynamicObject& result) + { + LOG(ERROR) << "success!"; + } + + virtual void HandleErrorFromOracle(const OrthancStone::IOracleCommand& command, + const Orthanc::OrthancException& error) + { + LOG(ERROR) << "error!"; + } +}; + +static boost::shared_ptr<Toto> toto_(new Toto); +// END TODO Refactoring + + + + + static void ProcessOptions(int argc, char* argv[]) { namespace po = boost::program_options; @@ -181,6 +214,8 @@ context.StartOracle(); + environment_.Start(); + { { std::string font; @@ -261,6 +296,11 @@ { switch (event.key.keysym.sym) { + case SDLK_b: + // TODO Refactoring + oracle_.Submit(toto_, new OrthancStone::SleepOracleCommand(1000)); + break; + case SDLK_f: viewport->ToggleMaximize(); break; @@ -519,6 +559,8 @@ } } context.StopOracle(); + + environment_.Stop(); } }
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Fri Aug 14 11:01:24 2026 +0200 +++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Thu Sep 17 20:10:13 2026 +0200 @@ -4872,6 +4872,35 @@ } + +// TODO Refactoring +#include "../../../OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyEnvironment.h" +#include "../../../OrthancStone/Sources/Oracle/SleepOracleCommand.h" + +static OrthancStone::WebAssemblyEnvironment environment_; +static OrthancStone::New::WebAssemblyOracle oracle_(environment_); + +class Toto : public OrthancStone::IOracleClient +{ +public: + virtual void HandleSuccessFromOracle(const OrthancStone::IOracleCommand& command, + const Orthanc::IDynamicObject& result) + { + LOG(ERROR) << "success!"; + } + + virtual void HandleErrorFromOracle(const OrthancStone::IOracleCommand& command, + const Orthanc::OrthancException& error) + { + LOG(ERROR) << "error!"; + } +}; + +static boost::shared_ptr<Toto> toto_(new Toto); +// END TODO Refactoring + + + extern "C" { int main(int argc, char const *argv[]) @@ -4905,6 +4934,10 @@ } DISPATCH_JAVASCRIPT_EVENT("StoneInitialized"); + + + // TODO Refactoring + oracle_.Submit(toto_, new OrthancStone::SleepOracleCommand(2000)); }
--- a/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake Fri Aug 14 11:01:24 2026 +0200 +++ b/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake Thu Sep 17 20:10:13 2026 +0200 @@ -209,6 +209,8 @@ ${ORTHANC_STONE_ROOT}/Loaders/GenericLoadersContext.cpp ${ORTHANC_STONE_ROOT}/Oracle/GenericOracleRunner.cpp ${ORTHANC_STONE_ROOT}/Oracle/ThreadedOracle.cpp + ${ORTHANC_STONE_ROOT}/Platforms/Native/NativeEnvironment.cpp + ${ORTHANC_STONE_ROOT}/Platforms/Native/RunnableThread.cpp ) endif()
--- a/OrthancStone/Resources/CMake/OrthancStoneWebAssemblyConfiguration.cmake Fri Aug 14 11:01:24 2026 +0200 +++ b/OrthancStone/Resources/CMake/OrthancStoneWebAssemblyConfiguration.cmake Thu Sep 17 20:10:13 2026 +0200 @@ -48,6 +48,7 @@ list(APPEND ORTHANC_STONE_SOURCES ${CMAKE_CURRENT_LIST_DIR}/../../Sources/Platforms/WebAssembly/WebAssemblyCairoViewport.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../Sources/Platforms/WebAssembly/WebAssemblyEnvironment.cpp ${CMAKE_CURRENT_LIST_DIR}/../../Sources/Platforms/WebAssembly/WebAssemblyLoadersContext.cpp ${CMAKE_CURRENT_LIST_DIR}/../../Sources/Platforms/WebAssembly/WebAssemblyOracle.cpp ${CMAKE_CURRENT_LIST_DIR}/../../Sources/Platforms/WebAssembly/WebAssemblyViewport.cpp
--- a/OrthancStone/Sources/Loaders/ILoadersContext.h Fri Aug 14 11:01:24 2026 +0200 +++ b/OrthancStone/Sources/Loaders/ILoadersContext.h Thu Sep 17 20:10:13 2026 +0200 @@ -70,6 +70,7 @@ int priority, IOracleCommand* command /* Takes ownership */) = 0; + private: /** * Cancel all the commands that are waiting in the * "OracleScheduler" queue and that are linked to the given @@ -91,6 +92,9 @@ **/ virtual void CancelAllRequests() = 0; + public: + // TODO Refactoring - Remove this + /** * Add a reference to the given observer in the Stone loaders * context. This can be used to match the lifetime of a loader @@ -102,6 +106,7 @@ **/ virtual void AddLoader(boost::shared_ptr<IObserver> loader) = 0; + private: /** * Returns the number of commands that were scheduled and * processed using the "Schedule()" method. By "processed"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancStone/Sources/Oracle/IEnvironment.h Thu Sep 17 20:10:13 2026 +0200 @@ -0,0 +1,48 @@ +/** + * 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 "IOracleClient.h" + +#include <boost/weak_ptr.hpp> + + +namespace OrthancStone +{ + class IEnvironment : public boost::noncopyable + { + public: + virtual ~IEnvironment() + { + } + + virtual void NotifyOracleSuccess(const boost::weak_ptr<IOracleClient>& client, + IOracleCommand* command /* takes ownership */, + Orthanc::IDynamicObject* result /* takes ownership */) = 0; + + virtual void NotifyOracleError(const boost::weak_ptr<IOracleClient>& client, + IOracleCommand* command /* takes ownership */, + const Orthanc::OrthancException& error) = 0; + }; +}
--- a/OrthancStone/Sources/Oracle/IOracle.h Fri Aug 14 11:01:24 2026 +0200 +++ b/OrthancStone/Sources/Oracle/IOracle.h Thu Sep 17 20:10:13 2026 +0200 @@ -25,6 +25,9 @@ #include "../Messages/IObserver.h" #include "IOracleCommand.h" +#include "IOracleClient.h" + +#include <OrthancException.h> #include <boost/shared_ptr.hpp> @@ -45,4 +48,19 @@ virtual bool Schedule(boost::shared_ptr<IObserver> receiver, IOracleCommand* command) = 0; // Takes ownership }; + + + namespace New + { + class IOracle : public boost::noncopyable + { + public: + virtual ~IOracle() + { + } + + virtual void Submit(const boost::shared_ptr<IOracleClient>& client, + IOracleCommand* command /* takes ownership */) = 0; + }; + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancStone/Sources/Oracle/IOracleClient.h Thu Sep 17 20:10:13 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 + * <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "IOracleCommand.h" + +#include <OrthancException.h> + + +namespace OrthancStone +{ + class IOracleClient : public boost::noncopyable + { + public: + virtual ~IOracleClient() + { + } + + virtual void HandleSuccessFromOracle(const IOracleCommand& command, + const Orthanc::IDynamicObject& result) = 0; + + virtual void HandleErrorFromOracle(const IOracleCommand& command, + const Orthanc::OrthancException& error) = 0; + }; +}
--- a/OrthancStone/Sources/Oracle/ThreadedOracle.cpp Fri Aug 14 11:01:24 2026 +0200 +++ b/OrthancStone/Sources/Oracle/ThreadedOracle.cpp Thu Sep 17 20:10:13 2026 +0200 @@ -432,4 +432,61 @@ } } } + + + + namespace New + { + class ThreadedOracle::SleepRunnable : public Orthanc::IRunnable + { + private: + + + public: + virtual void Run() ORTHANC_OVERRIDE + { + } + }; + + + ThreadedOracle::ThreadedOracle(IEnvironment& environment, + unsigned int countWorkers) : + environment_(environment), + sleepingThread_(new SleepRunnable, 50 /* milliseconds */) + { + threadPool_.SetThreadsCount(countWorkers); + threadPool_.SetDequeueTimeout(50); + } + + + void ThreadedOracle::Submit(const boost::shared_ptr<IOracleClient>& client, + IOracleCommand* command /* takes ownership */) + { + std::unique_ptr<IOracleCommand> protection(command); + + if (command == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + try + { + switch (command->GetType()) + { + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, + "Command type not implemented by the Threaded Oracle: " + + boost::lexical_cast<std::string>(command->GetType())); + } + } + catch (Orthanc::OrthancException& e) + { + environment_.NotifyOracleError(client, protection.release(), e); + } + catch (...) + { + environment_.NotifyOracleError(client, protection.release(), Orthanc::OrthancException(Orthanc::ErrorCode_InternalError)); + } + } + } }
--- a/OrthancStone/Sources/Oracle/ThreadedOracle.h Fri Aug 14 11:01:24 2026 +0200 +++ b/OrthancStone/Sources/Oracle/ThreadedOracle.h Thu Sep 17 20:10:13 2026 +0200 @@ -42,11 +42,14 @@ # include "../Toolbox/ParsedDicomCache.h" #endif -#include "IOracle.h" +#include "../Messages/IMessageEmitter.h" +#include "../Platforms/Native/RunnableThread.h" #include "GenericOracleRunner.h" -#include "../Messages/IMessageEmitter.h" +#include "IEnvironment.h" +#include "IOracle.h" #include <MultiThreading/SharedMessageQueue.h> +#include <MultiThreading/ThreadPool.h> namespace OrthancStone @@ -112,4 +115,38 @@ virtual bool Schedule(boost::shared_ptr<IObserver> receiver, IOracleCommand* command) ORTHANC_OVERRIDE; }; + + + namespace New + { + class ThreadedOracle : public IOracle + { + private: + class SleepCommands; + class SleepRunnable; + + IEnvironment& environment_; + RunnableThread sleepingThread_; + Orthanc::ThreadPool threadPool_; + + public: + ThreadedOracle(IEnvironment& environment, + unsigned int countWorkers); + + void Start() + { + sleepingThread_.Start(); + threadPool_.Start(); + } + + void Stop() + { + threadPool_.Stop(); + sleepingThread_.Stop(); + } + + virtual void Submit(const boost::shared_ptr<IOracleClient>& client, + IOracleCommand* command /* takes ownership */) ORTHANC_OVERRIDE; + }; + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancStone/Sources/Platforms/Native/NativeEnvironment.cpp Thu Sep 17 20:10:13 2026 +0200 @@ -0,0 +1,150 @@ +/** + * 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 "NativeEnvironment.h" + + +namespace OrthancStone +{ + class NativeEnvironment::Completion : public Orthanc::IDynamicObject + { + protected: + boost::weak_ptr<IOracleClient> client_; + std::unique_ptr<IOracleCommand> command_; + + public: + Completion(const boost::weak_ptr<IOracleClient>& client, + IOracleCommand* command /* takes ownership */) : + client_(client), + command_(command) + { + if (command == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + virtual void NotifyClient() = 0; + }; + + + class NativeEnvironment::SuccessCompletion : public Completion + { + private: + std::unique_ptr<Orthanc::IDynamicObject> result_; + + public: + SuccessCompletion(const boost::weak_ptr<IOracleClient>& client, + IOracleCommand* command /* takes ownership */, + Orthanc::IDynamicObject* result /* takes ownership */) : + Completion(client, command), + result_(result) + { + if (result == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + virtual void NotifyClient() ORTHANC_OVERRIDE + { + boost::shared_ptr<IOracleClient> lock(client_.lock()); + + if (lock) + { + lock->HandleSuccessFromOracle(*command_, *result_); + } + } + }; + + + class NativeEnvironment::ErrorCompletion : public Completion + { + private: + Orthanc::OrthancException error_; + + public: + ErrorCompletion(const boost::weak_ptr<IOracleClient>& client, + IOracleCommand* command /* takes ownership */, + const Orthanc::OrthancException& error) : + Completion(client, command), + error_(error) + { + } + + virtual void NotifyClient() ORTHANC_OVERRIDE + { + boost::shared_ptr<IOracleClient> lock(client_.lock()); + + if (lock) + { + lock->HandleErrorFromOracle(*command_, error_); + } + } + }; + + + class NativeEnvironment::OracleRunnable : public Orthanc::IRunnable + { + private: + Orthanc::SharedMessageQueue& queue_; + + public: + OracleRunnable(Orthanc::SharedMessageQueue& queue) : + queue_(queue) + { + } + + virtual void Run() ORTHANC_OVERRIDE + { + std::unique_ptr<Orthanc::IDynamicObject> completion(queue_.Dequeue(50 /* milliseconds */)); + + if (completion.get() != NULL) + { + dynamic_cast<Completion&>(*completion).NotifyClient(); + } + } + }; + + + NativeEnvironment::NativeEnvironment() : + oracleThread_(new OracleRunnable(oracleQueue_), 0 /* time resolution is in Dequeue() */) + { + } + + + void NativeEnvironment::NotifyOracleSuccess(const boost::weak_ptr<IOracleClient>& client, + IOracleCommand* command /* takes ownership */, + Orthanc::IDynamicObject* result /* takes ownership */) + { + oracleQueue_.Enqueue(new SuccessCompletion(client, command, result)); + } + + + void NativeEnvironment::NotifyOracleError(const boost::weak_ptr<IOracleClient>& client, + IOracleCommand* command /* takes ownership */, + const Orthanc::OrthancException& error) + { + oracleQueue_.Enqueue(new ErrorCompletion(client, command, error)); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancStone/Sources/Platforms/Native/NativeEnvironment.h Thu Sep 17 20:10:13 2026 +0200 @@ -0,0 +1,67 @@ +/** + * 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 "../../Oracle/IEnvironment.h" +#include "RunnableThread.h" + +#include <MultiThreading/SharedMessageQueue.h> + + +namespace OrthancStone +{ + class NativeEnvironment : public IEnvironment + { + private: + class OracleRunnable; + class Completion; + class SuccessCompletion; + class ErrorCompletion; + + boost::mutex mutex_; // Main mutex of the application, to go single-threaded + Orthanc::SharedMessageQueue oracleQueue_; + RunnableThread oracleThread_; + + public: + NativeEnvironment(); + + void Start() + { + oracleThread_.Start(); + } + + void Stop() + { + oracleThread_.Stop(); + } + + virtual void NotifyOracleSuccess(const boost::weak_ptr<IOracleClient>& client, + IOracleCommand* command /* takes ownership */, + Orthanc::IDynamicObject* result /* takes ownership */) ORTHANC_OVERRIDE; + + virtual void NotifyOracleError(const boost::weak_ptr<IOracleClient>& client, + IOracleCommand* command /* takes ownership */, + const Orthanc::OrthancException& error) ORTHANC_OVERRIDE; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancStone/Sources/Platforms/Native/RunnableThread.cpp Thu Sep 17 20:10:13 2026 +0200 @@ -0,0 +1,105 @@ +/** + * 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 "RunnableThread.h" + +#include <OrthancException.h> + + +namespace OrthancStone +{ + void RunnableThread::Worker(RunnableThread* that) + { + for (;;) + { + { + boost::mutex::scoped_lock lock(that->mutex_); + if (that->state_ != State_Running) + { + return; + } + } + + that->runnable_->Run(); + + if (that->timeResolution_ != 0) + { + boost::this_thread::sleep(boost::posix_time::milliseconds(that->timeResolution_)); + } + } + } + + + void RunnableThread::StopInternal(bool throws) + { + { + boost::mutex::scoped_lock lock(mutex_); + + if (state_ != State_Running) + { + if (throws) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + else + { + state_ = State_Done; + } + } + + if (thread_.joinable()) + { + thread_.join(); + } + } + + + RunnableThread::RunnableThread(Orthanc::IRunnable* runnable, + unsigned int timeResolution) : + runnable_(runnable), + timeResolution_(timeResolution), + state_(State_Initialization) + { + if (runnable == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + + void RunnableThread::Start() + { + boost::mutex::scoped_lock lock(mutex_); + + if (state_ != State_Initialization) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + state_ = State_Running; + thread_ = boost::thread(Worker, this); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancStone/Sources/Platforms/Native/RunnableThread.h Thu Sep 17 20:10:13 2026 +0200 @@ -0,0 +1,72 @@ +/** + * 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 <OrthancFramework.h> // Needed before IRunnable in Orthanc <= 1.13.0 + +#include <MultiThreading/IRunnable.h> + +#include <boost/thread.hpp> + + +namespace OrthancStone +{ + // This is a thread that periodically executes a runnable + class RunnableThread : public boost::noncopyable + { + private: + enum State + { + State_Initialization, + State_Running, + State_Done + }; + + static void Worker(RunnableThread* that); + + void StopInternal(bool throws); + + boost::mutex mutex_; + std::unique_ptr<Orthanc::IRunnable> runnable_; + unsigned int timeResolution_; + volatile State state_; + boost::thread thread_; + + public: + RunnableThread(Orthanc::IRunnable* runnable, + unsigned int timeResolution); + + ~RunnableThread() + { + StopInternal(false /* no exception in destructors */); + } + + void Start(); + + void Stop() + { + StopInternal(true); + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyEnvironment.cpp Thu Sep 17 20:10:13 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 + * <http://www.gnu.org/licenses/>. + **/ + + +#include "WebAssemblyEnvironment.h" + +namespace OrthancStone +{ + void WebAssemblyEnvironment::NotifyOracleSuccess(const boost::weak_ptr<IOracleClient>& client, + IOracleCommand* command /* takes ownership */, + Orthanc::IDynamicObject* result /* takes ownership */) + { + std::unique_ptr<IOracleCommand> protection(command); + std::unique_ptr<Orthanc::IDynamicObject> protection2(result); + + if (command == NULL || + result == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + else + { + boost::shared_ptr<IOracleClient> lock(client.lock()); + if (lock) + { + lock->HandleSuccessFromOracle(*protection, *protection2); + } + } + } + + + void WebAssemblyEnvironment::NotifyOracleError(const boost::weak_ptr<IOracleClient>& client, + IOracleCommand* command /* takes ownership */, + const Orthanc::OrthancException& error) + { + std::unique_ptr<IOracleCommand> protection(command); + + if (command == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + else + { + boost::shared_ptr<IOracleClient> lock(client.lock()); + if (lock) + { + lock->HandleErrorFromOracle(*protection, error); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyEnvironment.h Thu Sep 17 20:10:13 2026 +0200 @@ -0,0 +1,41 @@ +/** + * 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 "../../Oracle/IEnvironment.h" + +namespace OrthancStone +{ + class WebAssemblyEnvironment : public IEnvironment + { + public: + virtual void NotifyOracleSuccess(const boost::weak_ptr<IOracleClient>& client, + IOracleCommand* command /* takes ownership */, + Orthanc::IDynamicObject* result /* takes ownership */) ORTHANC_OVERRIDE; + + virtual void NotifyOracleError(const boost::weak_ptr<IOracleClient>& client, + IOracleCommand* command /* takes ownership */, + const Orthanc::OrthancException& error) ORTHANC_OVERRIDE; + }; +}
--- a/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyOracle.cpp Fri Aug 14 11:01:24 2026 +0200 +++ b/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyOracle.cpp Thu Sep 17 20:10:13 2026 +0200 @@ -875,4 +875,84 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } } + + + + namespace New + { + class WebAssemblyOracle::TimeoutCallback + { + private: + IEnvironment& environment_; + boost::weak_ptr<IOracleClient> client_; + std::unique_ptr<SleepOracleCommand> command_; + + public: + TimeoutCallback(IEnvironment& environment, + const boost::weak_ptr<IOracleClient>& 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<TimeoutCallback> callback(reinterpret_cast<TimeoutCallback*>(userData)); + callback->Notify(); + } + }; + + + void WebAssemblyOracle::Submit(const boost::shared_ptr<IOracleClient>& client, + IOracleCommand* command /* takes ownership */) + { + // TODO Refactoring - Use "priority" + + std::unique_ptr<IOracleCommand> protection(command); + + if (command == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + try + { + switch (command->GetType()) + { + case IOracleCommand::Type_Sleep: + { + unsigned int timeoutMS = dynamic_cast<SleepOracleCommand*>(command)->GetDelay(); + emscripten_set_timeout(TimeoutCallback::Callback, timeoutMS, + new TimeoutCallback(environment_, client, dynamic_cast<SleepOracleCommand*>(protection.release()))); + break; + } + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, + "Command type not implemented by the WebAssembly Oracle: " + command->GetType()); + } + } + catch (Orthanc::OrthancException& e) + { + environment_.NotifyOracleError(client, protection.release(), e); + } + catch (...) + { + environment_.NotifyOracleError(client, protection.release(), Orthanc::OrthancException(Orthanc::ErrorCode_InternalError)); + } + } + } }
--- a/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyOracle.h Fri Aug 14 11:01:24 2026 +0200 +++ b/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyOracle.h Thu Sep 17 20:10:13 2026 +0200 @@ -35,6 +35,7 @@ #include "../../Messages/IObservable.h" #include "../../Messages/IMessageEmitter.h" +#include "../../Oracle/IEnvironment.h" #include "../../Oracle/IOracle.h" #if ORTHANC_ENABLE_DCMTK == 1 @@ -153,4 +154,25 @@ bool HasPixelData() const; }; }; + + + namespace New + { + class WebAssemblyOracle : public IOracle + { + private: + class TimeoutCallback; + + IEnvironment& environment_; + + public: + WebAssemblyOracle(IEnvironment& environment) : + environment_(environment) + { + } + + virtual void Submit(const boost::shared_ptr<IOracleClient>& client, + IOracleCommand* command /* takes ownership */) ORTHANC_OVERRIDE; + }; + } }
