changeset 3926:2910b0d30fe0 transcoding

Allow concurrent calls to the custom image decoders provided by the plugins
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 14 May 2020 07:37:44 +0200
parents dd112d2b83f0
children 4cdc875510d1
files NEWS OrthancServer/OrthancRestApi/OrthancRestResources.cpp Plugins/Engine/OrthancPlugins.cpp
diffstat 3 files changed, 34 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Wed May 13 18:12:34 2020 +0200
+++ b/NEWS	Thu May 14 07:37:44 2020 +0200
@@ -1,10 +1,17 @@
 Pending changes in the mainline
 ===============================
 
+General
+-------
+
+* DICOM transcoding over the REST API
+* Transcoding from compressed to uncompressed transfer syntaxes over DICOM
+  C-STORE SCU (if the remote modality doesn't support compressed syntaxes)
+
 REST API
 --------
 
-* API version has been upgraded to 7
+* API version upgraded to 7
 * Improved:
   - "/instances/../modify": it is now possible to "Keep" the "SOPInstanceUID".  
     Note that it was already possible to "Replace" it.
@@ -12,8 +19,9 @@
   - "/queries/.../answers/../retrieve": "TargetAet" not mandatory anymore
     (defaults to the local AET)
 * Changes:
-  - "/instances/.../modify", ".../archive", ".../media",
-    "/tools/create-media" and "/tools/create-archive": New option "Transcode"
+  - "/instances/.../modify": New option "Transcode"
+  - ".../archive", ".../media", "/tools/create-media" and
+    "/tools/create-archive": New option "Transcode"
   - "/ordered-slices": reverted the change introduced in 1.5.8 and go-back 
     to 1.5.7 behaviour.
 
@@ -33,6 +41,7 @@
   - OrthancPluginSerializeDicomInstance()
   - OrthancPluginTranscodeDicomInstance()
 * "OrthancPluginDicomInstance" structure wrapped in "OrthancPluginCppWrapper.h"
+* Allow concurrent calls to the custom image decoders provided by the plugins
 
 Maintenance
 -----------
--- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Wed May 13 18:12:34 2020 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Thu May 14 07:37:44 2020 +0200
@@ -43,6 +43,7 @@
 #include "../../Core/Images/Image.h"
 #include "../../Core/Images/ImageProcessing.h"
 #include "../../Core/Logging.h"
+#include "../../Core/MultiThreading/Semaphore.h"
 #include "../DefaultDicomImageDecoder.h"
 #include "../OrthancConfiguration.h"
 #include "../Search/DatabaseLookup.h"
@@ -56,6 +57,14 @@
 #include <boost/math/special_functions/round.hpp>
 
 
+/**
+ * This semaphore is used to limit the number of concurrent HTTP
+ * requests on CPU-intensive routes of the REST API, in order to
+ * prevent exhaustion of resources (new in Orthanc 1.7.0).
+ **/
+static Orthanc::Semaphore throttlingSemaphore_(4);  // TODO => PARAMETER?
+
+
 namespace Orthanc
 {
   static void AnswerDicomAsJson(RestApiCall& call,
@@ -938,6 +947,8 @@
   template <enum ImageExtractionMode mode>
   static void GetImage(RestApiGetCall& call)
   {
+    Semaphore::Locker locker(throttlingSemaphore_);
+        
     GetImageHandler handler(mode);
     IDecodedFrameHandler::Apply(call, handler);
   }
@@ -945,6 +956,8 @@
 
   static void GetRenderedFrame(RestApiGetCall& call)
   {
+    Semaphore::Locker locker(throttlingSemaphore_);
+        
     RenderedFrameHandler handler;
     IDecodedFrameHandler::Apply(call, handler);
   }
@@ -952,6 +965,8 @@
 
   static void GetMatlabImage(RestApiGetCall& call)
   {
+    Semaphore::Locker locker(throttlingSemaphore_);
+        
     ServerContext& context = OrthancRestApi::GetContext(call);
 
     std::string frameId = call.GetUriComponent("frame", "0");
--- a/Plugins/Engine/OrthancPlugins.cpp	Wed May 13 18:12:34 2020 +0200
+++ b/Plugins/Engine/OrthancPlugins.cpp	Thu May 14 07:37:44 2020 +0200
@@ -71,12 +71,13 @@
 #include "PluginsEnumerations.h"
 #include "PluginsJob.h"
 
-#include <boost/regex.hpp> 
+#include <boost/regex.hpp>
 #include <dcmtk/dcmdata/dcdict.h>
 #include <dcmtk/dcmdata/dcdicent.h>
 
 #define ERROR_MESSAGE_64BIT "A 64bit version of the Orthanc API is necessary"
 
+
 namespace Orthanc
 {
   static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target,
@@ -918,7 +919,7 @@
     boost::recursive_mutex changeCallbackMutex_;
     boost::mutex findCallbackMutex_;
     boost::mutex worklistCallbackMutex_;
-    boost::mutex decodeImageCallbackMutex_;
+    boost::shared_mutex decodeImageCallbackMutex_;  // Changed from "boost::mutex" in Orthanc 1.7.0
     boost::mutex jobsUnserializersMutex_;
     boost::mutex refreshMetricsMutex_;
     boost::mutex storageCommitmentScpMutex_;
@@ -2109,7 +2110,7 @@
     const _OrthancPluginDecodeImageCallback& p = 
       *reinterpret_cast<const _OrthancPluginDecodeImageCallback*>(parameters);
 
-    boost::mutex::scoped_lock lock(pimpl_->decodeImageCallbackMutex_);
+    boost::unique_lock<boost::shared_mutex> lock(pimpl_->decodeImageCallbackMutex_);
 
     pimpl_->decodeImageCallbacks_.push_back(p.callback);
     LOG(INFO) << "Plugin has registered a callback to decode DICOM images (" 
@@ -2809,19 +2810,12 @@
         
       case _OrthancPluginService_GetInstanceDecodedFrame:
       {
-        bool hasDecoderPlugin;
-
-        {
-          boost::mutex::scoped_lock lock(pimpl_->decodeImageCallbackMutex_);
-          hasDecoderPlugin = !pimpl_->decodeImageCallbacks_.empty();
-        }
-
         std::unique_ptr<ImageAccessor> decoded;
         if (p.targetImage == NULL)
         {
           throw OrthancException(ErrorCode_NullPointer);
         }
-        else if (hasDecoderPlugin)
+        else if (HasCustomImageDecoder())
         {
           // TODO - This call could be speeded up the future, if a
           // "decoding context" gets introduced in the decoder plugins          
@@ -4826,7 +4820,7 @@
 
   bool OrthancPlugins::HasCustomImageDecoder()
   {
-    boost::mutex::scoped_lock lock(pimpl_->decodeImageCallbackMutex_);
+    boost::shared_lock<boost::shared_mutex> lock(pimpl_->decodeImageCallbackMutex_);
     return !pimpl_->decodeImageCallbacks_.empty();
   }
 
@@ -4835,7 +4829,7 @@
                                                size_t size,
                                                unsigned int frame)
   {
-    boost::mutex::scoped_lock lock(pimpl_->decodeImageCallbackMutex_);
+    boost::shared_lock<boost::shared_mutex> lock(pimpl_->decodeImageCallbackMutex_);
 
     for (PImpl::DecodeImageCallbacks::const_iterator
            decoder = pimpl_->decodeImageCallbacks_.begin();