changeset 5411:ca9cf4d46883

ServerContextLock transformed into ServerContextReference /usr/bin/env /bin/sh /tmp/Microsoft-MIEngine-Cmd-owukncoo.ob4
author Alain Mazy <am@osimis.io>
date Wed, 08 Nov 2023 08:34:47 +0100
parents 16cbfefa15e9
children 1a351a141653
files NEWS OrthancServer/Plugins/Engine/OrthancPlugins.cpp OrthancServer/Sources/main.cpp
diffstat 3 files changed, 59 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Tue Nov 07 12:52:37 2023 +0100
+++ b/NEWS	Wed Nov 08 08:34:47 2023 +0100
@@ -4,6 +4,10 @@
 General
 -------
 
+* Performance:
+  - Allow multiple plugins to use the plugin SDK at the same time.  In previous versions,
+    functions like instance transcoding or instance reading where mutually exclusive.
+    This can bring some significant improvements particularly in viewers.
 * Housekeeper plugin:
   - Update to rebuild the cache of the DicomWeb plugin when updating to DicomWeb 1.15.
   - New trigger configuration: "DicomWebCacheChange"
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Tue Nov 07 12:52:37 2023 +0100
+++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Wed Nov 08 08:34:47 2023 +0100
@@ -1090,7 +1090,10 @@
   class OrthancPlugins::PImpl
   {
   private:
-    boost::mutex   contextMutex_;
+    boost::mutex              contextMutex_;
+    boost::condition_variable contextCond_;
+    size_t                    contextRefCount_;
+
     ServerContext* context_;
     
   public:
@@ -1458,21 +1461,38 @@
     };
 
 
-    class ServerContextLock
+    // This class ensures that the Context remains valid while being used.
+    // But it does not prevent multiple users to use the context at the same time.
+    // (new behavior in 1.12.2.  In previous version, only one user could use the "locked" context)
+    class ServerContextReference
     {
     private:
-      boost::mutex::scoped_lock  lock_;
       ServerContext* context_;
+      boost::mutex&  mutex_;
+      boost::condition_variable& cond_;
+      size_t& refCount_;
 
     public:
-      explicit ServerContextLock(PImpl& that) : 
-        lock_(that.contextMutex_),
-        context_(that.context_)
+      explicit ServerContextReference(PImpl& that) : 
+        context_(that.context_),
+        mutex_(that.contextMutex_),
+        cond_(that.contextCond_),
+        refCount_(that.contextRefCount_)
       {
         if (context_ == NULL)
         {
           throw OrthancException(ErrorCode_DatabaseNotInitialized);
         }
+
+        boost::mutex::scoped_lock lock(mutex_);
+        refCount_++;
+      }
+
+      ~ServerContextReference()
+      {
+        boost::mutex::scoped_lock lock(mutex_);
+        refCount_++;
+        cond_.notify_one();
       }
 
       ServerContext& GetContext()
@@ -1485,7 +1505,13 @@
 
     void SetServerContext(ServerContext* context)
     {
+      // update only the context while nobody is using it
       boost::mutex::scoped_lock lock(contextMutex_);
+
+      while (contextRefCount_ > 0)
+      {
+        contextCond_.wait(lock);
+      }
       context_ = context;
     }
 
@@ -1554,6 +1580,7 @@
     unsigned int maxDatabaseRetries_;   // New in Orthanc 1.9.2
 
     explicit PImpl(const std::string& databaseServerIdentifier) : 
+      contextRefCount_(0),
       context_(NULL), 
       findCallback_(NULL),
       worklistCallback_(NULL),
@@ -1600,7 +1627,7 @@
       {
         static const char* LUA_CALLBACK = "IncomingWorklistRequestFilter";
 
-        PImpl::ServerContextLock lock(*that_.pimpl_);
+        PImpl::ServerContextReference lock(*that_.pimpl_);
         LuaScripting::Lock lua(lock.GetContext().GetLuaScripting());
 
         if (!lua.GetLua().IsExistingFunction(LUA_CALLBACK))
@@ -3156,7 +3183,7 @@
     std::string dicom;
 
     {
-      PImpl::ServerContextLock lock(*pimpl_);
+      PImpl::ServerContextReference lock(*pimpl_);
       lock.GetContext().ReadDicom(dicom, p.instanceId);
     }
 
@@ -3197,7 +3224,7 @@
     IHttpHandler* handler;
 
     {
-      PImpl::ServerContextLock lock(*pimpl_);
+      PImpl::ServerContextReference lock(*pimpl_);
       handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
     }
 
@@ -3230,7 +3257,7 @@
     IHttpHandler* handler;
 
     {
-      PImpl::ServerContextLock lock(*pimpl_);
+      PImpl::ServerContextReference lock(*pimpl_);
       handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!p.afterPlugins);
     }
       
@@ -3254,7 +3281,7 @@
     IHttpHandler* handler;
 
     {
-      PImpl::ServerContextLock lock(*pimpl_);
+      PImpl::ServerContextReference lock(*pimpl_);
       handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
     }
       
@@ -3281,7 +3308,7 @@
     IHttpHandler* handler;
 
     {
-      PImpl::ServerContextLock lock(*pimpl_);
+      PImpl::ServerContextReference lock(*pimpl_);
       handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
     }
       
@@ -3340,7 +3367,7 @@
     std::vector<std::string> result;
 
     {
-      PImpl::ServerContextLock lock(*pimpl_);
+      PImpl::ServerContextReference lock(*pimpl_);
       lock.GetContext().GetIndex().LookupIdentifierExact(result, level, tag, p.argument);
     }
 
@@ -3635,7 +3662,7 @@
 
         std::unique_ptr<ImageAccessor> decoded;
         {
-          PImpl::ServerContextLock lock(*pimpl_);
+          PImpl::ServerContextReference lock(*pimpl_);
           decoded.reset(lock.GetContext().DecodeDicomFrame(instance, p.frameIndex));
         }
         
@@ -3718,7 +3745,7 @@
 
       case OrthancPluginImageFormat_Dicom:
       {
-        PImpl::ServerContextLock lock(*pimpl_);
+        PImpl::ServerContextReference lock(*pimpl_);
         image.reset(lock.GetContext().DecodeDicomFrame(p.data, p.size, 0));
         break;
       }
@@ -4043,7 +4070,7 @@
     IHttpHandler* handler;
 
     {
-      PImpl::ServerContextLock lock(*pimpl_);
+      PImpl::ServerContextReference lock(*pimpl_);
       handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!p.afterPlugins);
     }
     
@@ -4265,7 +4292,7 @@
       std::string content;
 
       {
-        PImpl::ServerContextLock lock(*pimpl_);
+        PImpl::ServerContextReference lock(*pimpl_);
         lock.GetContext().ReadDicom(content, p.instanceId);
       }
 
@@ -4394,7 +4421,7 @@
 
       case _OrthancPluginService_DecodeDicomImage:
       {
-        PImpl::ServerContextLock lock(*pimpl_);
+        PImpl::ServerContextReference lock(*pimpl_);
         result.reset(lock.GetContext().DecodeDicomFrame(p.constBuffer, p.bufferSize, p.frameIndex));
         break;
       }
@@ -4447,7 +4474,7 @@
         std::string buffer;
 
         {
-          PImpl::ServerContextLock lock(*pimpl_);
+          PImpl::ServerContextReference lock(*pimpl_);
           lock.GetContext().ReadDicom(buffer, params.instanceId);
         }
 
@@ -4464,7 +4491,7 @@
           std::string buffer;
         
           {
-            PImpl::ServerContextLock lock(*pimpl_);
+            PImpl::ServerContextReference lock(*pimpl_);
             if (!lock.GetContext().ReadDicomUntilPixelData(buffer, params.instanceId))
             {
               lock.GetContext().ReadDicom(buffer, params.instanceId);
@@ -4482,7 +4509,7 @@
           ValueRepresentation pixelDataVR = parsed->GuessPixelDataValueRepresentation();
 
           {
-            PImpl::ServerContextLock lock(*pimpl_);
+            PImpl::ServerContextReference lock(*pimpl_);
 
             std::string s;
             int64_t revision;  // unused
@@ -4813,7 +4840,7 @@
         {
           // TODO - Plugins can only access global properties of their
           // own Orthanc server (no access to the shared global properties)
-          PImpl::ServerContextLock lock(*pimpl_);
+          PImpl::ServerContextReference lock(*pimpl_);
           lock.GetContext().GetIndex().SetGlobalProperty(static_cast<GlobalProperty>(p.property),
                                                          false /* not shared */, p.value);
           return true;
@@ -4830,7 +4857,7 @@
         {
           // TODO - Plugins can only access global properties of their
           // own Orthanc server (no access to the shared global properties)
-          PImpl::ServerContextLock lock(*pimpl_);
+          PImpl::ServerContextReference lock(*pimpl_);
           result = lock.GetContext().GetIndex().GetGlobalProperty(static_cast<GlobalProperty>(p.property),
                                                                   false /* not shared */, p.value);
         }
@@ -5297,7 +5324,7 @@
 
         std::string uuid;
 
-        PImpl::ServerContextLock lock(*pimpl_);
+        PImpl::ServerContextReference lock(*pimpl_);
         lock.GetContext().GetJobsEngine().GetRegistry().Submit
           (uuid, reinterpret_cast<PluginsJob*>(p.job), p.priority);
         
@@ -5320,7 +5347,7 @@
           *reinterpret_cast<const _OrthancPluginSetMetricsValue*>(parameters);
 
         {
-          PImpl::ServerContextLock lock(*pimpl_);
+          PImpl::ServerContextReference lock(*pimpl_);
           lock.GetContext().GetMetricsRegistry().SetFloatValue(p.name, p.value, Plugins::Convert(p.type));
         }
 
@@ -5333,7 +5360,7 @@
           *reinterpret_cast<const _OrthancPluginSetMetricsIntegerValue*>(parameters);
 
         {
-          PImpl::ServerContextLock lock(*pimpl_);
+          PImpl::ServerContextReference lock(*pimpl_);
           lock.GetContext().GetMetricsRegistry().SetIntegerValue(p.name, p.value, Plugins::Convert(p.type));
         }
 
@@ -5425,7 +5452,7 @@
           bool success;
           
           {
-            PImpl::ServerContextLock lock(*pimpl_);
+            PImpl::ServerContextReference lock(*pimpl_);
             success = lock.GetContext().Transcode(
               transcoded, source, syntaxes, true /* allow new sop */);
           }
--- a/OrthancServer/Sources/main.cpp	Tue Nov 07 12:52:37 2023 +0100
+++ b/OrthancServer/Sources/main.cpp	Wed Nov 08 08:34:47 2023 +0100
@@ -1606,6 +1606,7 @@
       lock.GetConfiguration().LoadModalitiesAndPeers();
     }
 
+    // this function exits only when Orthanc stops or resets
     return ConfigureHttpHandler(context, plugins, loadJobsFromDatabase);
   }
 }