changeset 7163:5e8003d46cf5

fixed possible deadlock in ThreadedSetOfInstancesJob
author Alain Mazy <am@orthanc.team>
date Tue, 29 Sep 2026 17:06:35 +0200
parents e72e3adae0cc
children 22083f3fe932
files NEWS OrthancServer/Sources/ServerJobs/ThreadedSetOfInstancesJob.cpp
diffstat 2 files changed, 28 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Tue Sep 29 16:25:10 2026 +0200
+++ b/NEWS	Tue Sep 29 17:06:35 2026 +0200
@@ -32,6 +32,8 @@
   (https://discourse.orthanc-server.org/t/find-worklist-with-localaet-from-modality-configuration/6482)
 * Added new warnings that can be disabled in the configuration: 
   - W101_TruncatingDicomTagTo256Values
+* Fixed a deadlock in Anonymization/Modification jobs: some instances failing to be processed
+  could keep the job in a "processing" state forever.
 * Fix use of the static runtime under Visual Studio (/MT), which broke in 1.12.10
 * New CMake options: 
   - "THIRD_PARTY_DOWNLOADS_ROOT_URL" to specify another webserver to downloads the third-parties from.
--- a/OrthancServer/Sources/ServerJobs/ThreadedSetOfInstancesJob.cpp	Tue Sep 29 16:25:10 2026 +0200
+++ b/OrthancServer/Sources/ServerJobs/ThreadedSetOfInstancesJob.cpp	Tue Sep 29 17:06:35 2026 +0200
@@ -46,11 +46,12 @@
 namespace Orthanc
 {
   static const char* EXIT_WORKER_MESSAGE = "exit";
+  static const unsigned int WORKER_DEQUEUE_TIMEOUT_MS = 100;
 
-   ThreadedSetOfInstancesJob::ThreadedSetOfInstancesJob(ServerContext& context,
-                                                        bool hasPostProcessing,
-                                                        bool keepSource,
-                                                        size_t workersCount) :
+  ThreadedSetOfInstancesJob::ThreadedSetOfInstancesJob(ServerContext& context,
+                                                       bool hasPostProcessing,
+                                                       bool keepSource,
+                                                       size_t workersCount) :
     hasPostProcessing_(hasPostProcessing),
     started_(false),
     stopRequested_(false),
@@ -260,9 +261,27 @@
 
     while (true)
     {
-      std::unique_ptr<SingleValueObject<std::string> > instanceId(dynamic_cast<SingleValueObject<std::string>*>(that->instancesToProcessQueue_.Dequeue(0)));
-      if (that->stopRequested_                // no lock(mutex) to access this variable, this is safe since it's just reading a boolean
-        || instanceId->GetValue() == EXIT_WORKER_MESSAGE)
+      std::unique_ptr<IDynamicObject> message(that->instancesToProcessQueue_.Dequeue(WORKER_DEQUEUE_TIMEOUT_MS));
+      if (that->stopRequested_)                // no lock(mutex) to access this variable, this is safe since it's just reading a boolean
+      {
+        return;
+      }
+
+      if (message.get() == NULL)
+      {
+        continue;  // timeout, nothing to process yet
+      }
+
+      SingleValueObject<std::string>* instanceId = dynamic_cast<SingleValueObject<std::string>*>(message.get());
+      if (instanceId == NULL)
+      {
+        LOG(ERROR) << "Unexpected message type in the instances queue";
+        that->SetErrorCode(ErrorCode_InternalError);
+        that->StopWorkers();
+        return;
+      }
+
+      if (instanceId->GetValue() == EXIT_WORKER_MESSAGE)
       {
         return;
       }