changeset 53:c2dc924f1a63 wasm

removing threading out of the framework
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 27 Apr 2017 16:57:49 +0200
parents 37e504582af6
children 01aa453d4d5b
files Applications/BasicApplicationContext.cpp Applications/BasicApplicationContext.h Applications/IBasicApplication.cpp Applications/Samples/TestPatternApplication.h Applications/Sdl/SdlEngine.cpp Applications/Sdl/SdlEngine.h Framework/Layers/ILayerRenderer.h Framework/Layers/ILayerRendererFactory.h Framework/Layers/SiblingSliceLocationFactory.cpp Framework/Layers/SiblingSliceLocationFactory.h Framework/Toolbox/DownloadStack.cpp Framework/Toolbox/DownloadStack.h Framework/Toolbox/ISeriesLoader.h Framework/Toolbox/IThreadSafety.h Framework/Toolbox/ObserversRegistry.h Framework/Toolbox/OrthancSeriesLoader.h Framework/Toolbox/ParallelSlices.h Framework/Toolbox/ParallelSlicesCursor.cpp Framework/Toolbox/ParallelSlicesCursor.h Framework/Toolbox/SharedValue.h Framework/Toolbox/SliceGeometry.h Framework/Toolbox/ViewportGeometry.h Framework/Viewport/IMouseTracker.h Framework/Viewport/IStatusBar.h Framework/Viewport/IViewport.h Framework/Viewport/WidgetViewport.cpp Framework/Viewport/WidgetViewport.h Framework/Volumes/ISliceableVolume.h Framework/Volumes/ImageBuffer3D.h Framework/Volumes/VolumeImage.h Framework/Widgets/EmptyWidget.cpp Framework/Widgets/IWidget.h Framework/Widgets/IWorldSceneInteractor.h Framework/Widgets/IWorldSceneMouseTracker.h Framework/Widgets/LayeredSceneWidget.cpp Framework/Widgets/LayeredSceneWidget.h Framework/Widgets/LayoutWidget.cpp Framework/Widgets/LayoutWidget.h Framework/Widgets/WorldSceneWidget.cpp Framework/Widgets/WorldSceneWidget.h Resources/Graveyard/Threading/IThreadSafety.h Resources/Graveyard/Threading/SharedValue.h
diffstat 42 files changed, 427 insertions(+), 399 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/BasicApplicationContext.cpp	Thu Apr 27 14:50:40 2017 +0200
+++ b/Applications/BasicApplicationContext.cpp	Thu Apr 27 16:57:49 2017 +0200
@@ -27,8 +27,24 @@
 
 namespace OrthancStone
 {
+  void BasicApplicationContext::UpdateThread(BasicApplicationContext* that)
+  {
+    while (!that->stopped_)
+    {
+      {
+        ViewportLocker locker(*that);
+        locker.GetViewport().UpdateContent();
+      }
+      
+      boost::this_thread::sleep(boost::posix_time::milliseconds(that->updateDelay_));
+    }
+  }
+  
+
   BasicApplicationContext::BasicApplicationContext(OrthancPlugins::IOrthancConnection& orthanc) :
-    orthanc_(orthanc)
+    orthanc_(orthanc),
+    stopped_(true),
+    updateDelay_(100)   // By default, 100ms between each refresh of the content
   {
   }
 
@@ -119,11 +135,24 @@
     }
 
     viewport_.Start();
+
+    if (viewport_.HasUpdateContent())
+    {
+      stopped_ = false;
+      updateThread_ = boost::thread(UpdateThread, this);
+    }
   }
 
 
   void BasicApplicationContext::Stop()
   {
+    stopped_ = true;
+    
+    if (updateThread_.joinable())
+    {
+      updateThread_.join();
+    }
+    
     viewport_.Stop();
 
     for (Volumes::iterator it = volumes_.begin(); it != volumes_.end(); ++it)
--- a/Applications/BasicApplicationContext.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Applications/BasicApplicationContext.h	Thu Apr 27 16:57:49 2017 +0200
@@ -27,6 +27,7 @@
 #include "../../Framework/Toolbox/DicomStructureSet.h"
 
 #include <list>
+#include <boost/thread.hpp>
 
 namespace OrthancStone
 {
@@ -37,25 +38,46 @@
     typedef std::list<IWorldSceneInteractor*>  Interactors;
     typedef std::list<DicomStructureSet*>      StructureSets;
 
+    static void UpdateThread(BasicApplicationContext* that);
+
     OrthancPlugins::IOrthancConnection&  orthanc_;
 
+    boost::mutex     viewportMutex_;
     WidgetViewport   viewport_;
     Volumes          volumes_;
     Interactors      interactors_;
     StructureSets    structureSets_;
+    boost::thread    updateThread_;
+    bool             stopped_;
+    unsigned int     updateDelay_;
 
   public:
+    class ViewportLocker : public boost::noncopyable
+    {
+    private:
+      boost::mutex::scoped_lock  lock_;
+      IViewport&                 viewport_;
+
+    public:
+      ViewportLocker(BasicApplicationContext& that) :
+        lock_(that.viewportMutex_),
+        viewport_(that.viewport_)
+      {
+      }
+
+      IViewport& GetViewport() const
+      {
+        return viewport_;
+      }
+    };
+
+    
     BasicApplicationContext(OrthancPlugins::IOrthancConnection& orthanc);
 
     ~BasicApplicationContext();
 
     IWidget& SetCentralWidget(IWidget* widget);   // Takes ownership
 
-    IViewport& GetViewport()
-    {
-      return viewport_;
-    }
-
     OrthancPlugins::IOrthancConnection& GetOrthancConnection()
     {
       return orthanc_;
@@ -72,5 +94,10 @@
     void Start();
 
     void Stop();
+
+    void SetUpdateDelay(unsigned int delay)  // In milliseconds
+    {
+      updateDelay_ = delay;
+    }
   };
 }
--- a/Applications/IBasicApplication.cpp	Thu Apr 27 14:50:40 2017 +0200
+++ b/Applications/IBasicApplication.cpp	Thu Apr 27 16:57:49 2017 +0200
@@ -214,7 +214,11 @@
       BasicApplicationContext context(orthanc);
 
       application.Initialize(context, statusBar, parameters);
-      context.GetViewport().SetStatusBar(statusBar);
+
+      {
+        BasicApplicationContext::ViewportLocker locker(context);
+        locker.GetViewport().SetStatusBar(statusBar);
+      }
 
       std::string title = application.GetTitle();
       if (title.empty())
@@ -232,7 +236,7 @@
         LOG(WARNING) << "Starting the application";
 
         SdlWindow window(title.c_str(), width, height, opengl);
-        SdlEngine sdl(window, context.GetViewport());
+        SdlEngine sdl(window, context);
 
         sdl.Run();
 
@@ -248,7 +252,11 @@
 
       LOG(WARNING) << "The application has stopped";
 
-      context.GetViewport().ResetStatusBar();
+      {
+        BasicApplicationContext::ViewportLocker locker(context);
+        locker.GetViewport().ResetStatusBar();
+      }
+      
       application.Finalize();
     }
     catch (Orthanc::OrthancException& e)
--- a/Applications/Samples/TestPatternApplication.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Applications/Samples/TestPatternApplication.h	Thu Apr 27 16:57:49 2017 +0200
@@ -54,9 +54,10 @@
         layout->SetPadding(10);
         layout->SetBackgroundCleared(true);
         layout->AddWidget(new TestCairoWidget(parameters["animate"].as<bool>()));
-        layout->AddWidget(new TestWorldSceneWidget);
+        layout->AddWidget(new TestWorldSceneWidget(parameters["animate"].as<bool>()));
 
         context.SetCentralWidget(layout.release());
+        context.SetUpdateDelay(25);  // If animation, update the content each 25ms
       }
     };
   }
--- a/Applications/Sdl/SdlEngine.cpp	Thu Apr 27 14:50:40 2017 +0200
+++ b/Applications/Sdl/SdlEngine.cpp	Thu Apr 27 16:57:49 2017 +0200
@@ -38,7 +38,14 @@
 
     viewportChanged_ = false;
 
-    if (buffering_.RenderOffscreen(viewport_))
+    bool updated;
+    
+    {
+      BasicApplicationContext::ViewportLocker locker(context_);
+      updated = buffering_.RenderOffscreen(locker.GetViewport());
+    }
+
+    if (updated)
     {
       // Do not notify twice when a new frame was rendered, to avoid
       // spoiling the SDL event queue
@@ -119,10 +126,11 @@
   }
 
 
-  void SdlEngine::SetSize(unsigned int width,
+  void SdlEngine::SetSize(BasicApplicationContext::ViewportLocker& locker,
+                          unsigned int width,
                           unsigned int height)
   {
-    buffering_.SetSize(width, height, viewport_);
+    buffering_.SetSize(width, height, locker.GetViewport());
     viewportChanged_ = true;
     Refresh();
   }
@@ -146,16 +154,18 @@
 
 
   SdlEngine::SdlEngine(SdlWindow& window,
-                       IViewport& viewport) :
+                       BasicApplicationContext& context) :
     window_(window),
-    viewport_(viewport),
+    context_(context),
     continue_(true)
   {
     refreshEvent_ = SDL_RegisterEvents(1);
 
-    SetSize(window_.GetWidth(), window_.GetHeight());
-
-    viewport_.Register(*this);
+    {
+      BasicApplicationContext::ViewportLocker locker(context_);
+      SetSize(locker, window_.GetWidth(), window_.GetHeight());
+      locker.GetViewport().Register(*this);
+    }
 
     renderThread_ = boost::thread(RenderThread, this);
   }
@@ -165,7 +175,10 @@
   {
     Stop();
 
-    viewport_.Unregister(*this);
+    {
+      BasicApplicationContext::ViewportLocker locker(context_);
+      locker.GetViewport().Unregister(*this);
+    }
   }
 
 
@@ -183,6 +196,8 @@
 
       while (SDL_PollEvent(&event))
       {
+        BasicApplicationContext::ViewportLocker locker(context_);
+
         if (event.type == SDL_QUIT) 
         {
           stop = true;
@@ -199,15 +214,15 @@
           switch (event.button.button)
           {
             case SDL_BUTTON_LEFT:
-              viewport_.MouseDown(MouseButton_Left, event.button.x, event.button.y, modifiers);
+              locker.GetViewport().MouseDown(MouseButton_Left, event.button.x, event.button.y, modifiers);
               break;
             
             case SDL_BUTTON_RIGHT:
-              viewport_.MouseDown(MouseButton_Right, event.button.x, event.button.y, modifiers);
+              locker.GetViewport().MouseDown(MouseButton_Right, event.button.x, event.button.y, modifiers);
               break;
             
             case SDL_BUTTON_MIDDLE:
-              viewport_.MouseDown(MouseButton_Middle, event.button.x, event.button.y, modifiers);
+              locker.GetViewport().MouseDown(MouseButton_Middle, event.button.x, event.button.y, modifiers);
               break;
 
             default:
@@ -216,26 +231,26 @@
         }
         else if (event.type == SDL_MOUSEMOTION)
         {
-          viewport_.MouseMove(event.button.x, event.button.y);
+          locker.GetViewport().MouseMove(event.button.x, event.button.y);
         }
         else if (event.type == SDL_MOUSEBUTTONUP)
         {
-          viewport_.MouseUp();
+          locker.GetViewport().MouseUp();
         }
         else if (event.type == SDL_WINDOWEVENT)
         {
           switch (event.window.event)
           {
             case SDL_WINDOWEVENT_LEAVE:
-              viewport_.MouseLeave();
+              locker.GetViewport().MouseLeave();
               break;
 
             case SDL_WINDOWEVENT_ENTER:
-              viewport_.MouseEnter();
+              locker.GetViewport().MouseEnter();
               break;
 
             case SDL_WINDOWEVENT_SIZE_CHANGED:
-              SetSize(event.window.data1, event.window.data2);
+              SetSize(locker, event.window.data1, event.window.data2);
               break;
 
             default:
@@ -251,11 +266,11 @@
 
           if (event.wheel.y > 0)
           {
-            viewport_.MouseWheel(MouseWheelDirection_Up, x, y, modifiers);
+            locker.GetViewport().MouseWheel(MouseWheelDirection_Up, x, y, modifiers);
           }
           else if (event.wheel.y < 0)
           {
-            viewport_.MouseWheel(MouseWheelDirection_Down, x, y, modifiers);
+            locker.GetViewport().MouseWheel(MouseWheelDirection_Down, x, y, modifiers);
           }
         }
         else if (event.type == SDL_KEYDOWN)
@@ -264,32 +279,32 @@
 
           switch (event.key.keysym.sym)
           {
-            case SDLK_a:  viewport_.KeyPressed('a', modifiers);  break;
-            case SDLK_b:  viewport_.KeyPressed('b', modifiers);  break;
-            case SDLK_c:  viewport_.KeyPressed('c', modifiers);  break;
-            case SDLK_d:  viewport_.KeyPressed('d', modifiers);  break;
-            case SDLK_e:  viewport_.KeyPressed('e', modifiers);  break;
-            case SDLK_f:  window_.ToggleMaximize();              break;
-            case SDLK_g:  viewport_.KeyPressed('g', modifiers);  break;
-            case SDLK_h:  viewport_.KeyPressed('h', modifiers);  break;
-            case SDLK_i:  viewport_.KeyPressed('i', modifiers);  break;
-            case SDLK_j:  viewport_.KeyPressed('j', modifiers);  break;
-            case SDLK_k:  viewport_.KeyPressed('k', modifiers);  break;
-            case SDLK_l:  viewport_.KeyPressed('l', modifiers);  break;
-            case SDLK_m:  viewport_.KeyPressed('m', modifiers);  break;
-            case SDLK_n:  viewport_.KeyPressed('n', modifiers);  break;
-            case SDLK_o:  viewport_.KeyPressed('o', modifiers);  break;
-            case SDLK_p:  viewport_.KeyPressed('p', modifiers);  break;
-            case SDLK_q:  stop = true;                           break;
-            case SDLK_r:  viewport_.KeyPressed('r', modifiers);  break;
-            case SDLK_s:  viewport_.KeyPressed('s', modifiers);  break;
-            case SDLK_t:  viewport_.KeyPressed('t', modifiers);  break;
-            case SDLK_u:  viewport_.KeyPressed('u', modifiers);  break;
-            case SDLK_v:  viewport_.KeyPressed('v', modifiers);  break;
-            case SDLK_w:  viewport_.KeyPressed('w', modifiers);  break;
-            case SDLK_x:  viewport_.KeyPressed('x', modifiers);  break;
-            case SDLK_y:  viewport_.KeyPressed('y', modifiers);  break;
-            case SDLK_z:  viewport_.KeyPressed('z', modifiers);  break;
+            case SDLK_a:  locker.GetViewport().KeyPressed('a', modifiers);  break;
+            case SDLK_b:  locker.GetViewport().KeyPressed('b', modifiers);  break;
+            case SDLK_c:  locker.GetViewport().KeyPressed('c', modifiers);  break;
+            case SDLK_d:  locker.GetViewport().KeyPressed('d', modifiers);  break;
+            case SDLK_e:  locker.GetViewport().KeyPressed('e', modifiers);  break;
+            case SDLK_f:  window_.ToggleMaximize();                         break;
+            case SDLK_g:  locker.GetViewport().KeyPressed('g', modifiers);  break;
+            case SDLK_h:  locker.GetViewport().KeyPressed('h', modifiers);  break;
+            case SDLK_i:  locker.GetViewport().KeyPressed('i', modifiers);  break;
+            case SDLK_j:  locker.GetViewport().KeyPressed('j', modifiers);  break;
+            case SDLK_k:  locker.GetViewport().KeyPressed('k', modifiers);  break;
+            case SDLK_l:  locker.GetViewport().KeyPressed('l', modifiers);  break;
+            case SDLK_m:  locker.GetViewport().KeyPressed('m', modifiers);  break;
+            case SDLK_n:  locker.GetViewport().KeyPressed('n', modifiers);  break;
+            case SDLK_o:  locker.GetViewport().KeyPressed('o', modifiers);  break;
+            case SDLK_p:  locker.GetViewport().KeyPressed('p', modifiers);  break;
+            case SDLK_q:  stop = true;                                      break;
+            case SDLK_r:  locker.GetViewport().KeyPressed('r', modifiers);  break;
+            case SDLK_s:  locker.GetViewport().KeyPressed('s', modifiers);  break;
+            case SDLK_t:  locker.GetViewport().KeyPressed('t', modifiers);  break;
+            case SDLK_u:  locker.GetViewport().KeyPressed('u', modifiers);  break;
+            case SDLK_v:  locker.GetViewport().KeyPressed('v', modifiers);  break;
+            case SDLK_w:  locker.GetViewport().KeyPressed('w', modifiers);  break;
+            case SDLK_x:  locker.GetViewport().KeyPressed('x', modifiers);  break;
+            case SDLK_y:  locker.GetViewport().KeyPressed('y', modifiers);  break;
+            case SDLK_z:  locker.GetViewport().KeyPressed('z', modifiers);  break;
 
             default:
               break;
--- a/Applications/Sdl/SdlEngine.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Applications/Sdl/SdlEngine.h	Thu Apr 27 16:57:49 2017 +0200
@@ -25,6 +25,7 @@
 
 #include "SdlBuffering.h"
 #include "../BinarySemaphore.h"
+#include "../BasicApplicationContext.h"
 
 #include <boost/thread.hpp>
 
@@ -33,14 +34,14 @@
   class SdlEngine : public IViewport::IChangeObserver
   {
   private:
-    SdlWindow&        window_;
-    IViewport&        viewport_;
-    SdlBuffering      buffering_;
-    boost::thread     renderThread_;
-    bool              continue_;
-    BinarySemaphore   renderFrame_;
-    uint32_t          refreshEvent_;
-    bool              viewportChanged_;
+    SdlWindow&                window_;
+    BasicApplicationContext&  context_;
+    SdlBuffering              buffering_;
+    boost::thread             renderThread_;
+    bool                      continue_;
+    BinarySemaphore           renderFrame_;
+    uint32_t                  refreshEvent_;
+    bool                      viewportChanged_;
 
     void RenderFrame();
 
@@ -49,7 +50,8 @@
     static KeyboardModifiers GetKeyboardModifiers(const uint8_t* keyboardState,
                                                   const int scancodeCount);
 
-    void SetSize(unsigned int width,
+    void SetSize(BasicApplicationContext::ViewportLocker& locker,
+                 unsigned int width,
                  unsigned int height);
 
     void Stop();
@@ -58,7 +60,7 @@
 
   public:
     SdlEngine(SdlWindow& window,
-              IViewport& viewport);
+              BasicApplicationContext& context);
   
     virtual ~SdlEngine();
 
--- a/Framework/Layers/ILayerRenderer.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Layers/ILayerRenderer.h	Thu Apr 27 16:57:49 2017 +0200
@@ -22,15 +22,18 @@
 #pragma once
 
 #include "../Viewport/CairoContext.h"
-#include "../Toolbox/IThreadSafety.h"
 #include "../Toolbox/ViewportGeometry.h"
 #include "RenderStyle.h"
 
 namespace OrthancStone
 {
-  class ILayerRenderer : public IThreadUnsafe
+  class ILayerRenderer : public boost::noncopyable
   {
   public:
+    virtual ~ILayerRenderer()
+    {
+    }
+    
     virtual bool RenderLayer(CairoContext& context,
                              const ViewportGeometry& view) = 0;
 
--- a/Framework/Layers/ILayerRendererFactory.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Layers/ILayerRendererFactory.h	Thu Apr 27 16:57:49 2017 +0200
@@ -27,7 +27,7 @@
 
 namespace OrthancStone
 {
-  class ILayerRendererFactory : public IThreadUnsafe
+  class ILayerRendererFactory : public boost::noncopyable
   {
   public:
     virtual ~ILayerRendererFactory()
--- a/Framework/Layers/SiblingSliceLocationFactory.cpp	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Layers/SiblingSliceLocationFactory.cpp	Thu Apr 27 16:57:49 2017 +0200
@@ -49,7 +49,6 @@
 
   void SiblingSliceLocationFactory::SetLayerIndex(size_t layerIndex)
   {
-    boost::mutex::scoped_lock lock(mutex_);
     hasLayerIndex_ = true;
     layerIndex_ = layerIndex;
   }
@@ -57,21 +56,18 @@
 
   void SiblingSliceLocationFactory::SetStyle(const RenderStyle& style)
   {
-    boost::mutex::scoped_lock lock(mutex_);
     style_ = style;
   }
 
 
   RenderStyle SiblingSliceLocationFactory::GetRenderStyle()
   {
-    boost::mutex::scoped_lock lock(mutex_);
     return style_;
   }
 
 
   void SiblingSliceLocationFactory::SetSlice(const SliceGeometry& slice)
   {
-    boost::mutex::scoped_lock lock(mutex_);
     slice_ = slice;
 
     if (hasLayerIndex_)
@@ -84,21 +80,14 @@
   ILayerRenderer* SiblingSliceLocationFactory::CreateLayerRenderer(const SliceGeometry& viewportSlice)
   {
     Vector p, d;
-    RenderStyle style;
 
+    // Compute the line of intersection between the two slices
+    if (!GeometryToolbox::IntersectTwoPlanes(p, d, 
+                                             slice_.GetOrigin(), slice_.GetNormal(),
+                                             viewportSlice.GetOrigin(), viewportSlice.GetNormal()))
     {
-      boost::mutex::scoped_lock lock(mutex_);
-
-      style = style_;
-
-      // Compute the line of intersection between the two slices
-      if (!GeometryToolbox::IntersectTwoPlanes(p, d, 
-                                               slice_.GetOrigin(), slice_.GetNormal(),
-                                               viewportSlice.GetOrigin(), viewportSlice.GetNormal()))
-      {
-        // The two slice are parallel, don't try and display the intersection
-        return NULL;
-      }
+      // The two slice are parallel, don't try and display the intersection
+      return NULL;
     }
 
     double x1, y1, x2, y2;
@@ -113,7 +102,7 @@
                                              sx1, sy1, sx2, sy2))
     {
       std::auto_ptr<ILayerRenderer> layer(new LineLayerRenderer(x1, y1, x2, y2));
-      layer->SetLayerStyle(style);
+      layer->SetLayerStyle(style_);
       return layer.release();
     }
     else
--- a/Framework/Layers/SiblingSliceLocationFactory.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Layers/SiblingSliceLocationFactory.h	Thu Apr 27 16:57:49 2017 +0200
@@ -30,7 +30,6 @@
     public LayeredSceneWidget::ISliceObserver
   {
   private:
-    boost::mutex          mutex_;
     LayeredSceneWidget&   owner_;
     LayeredSceneWidget&   sibling_;
     SliceGeometry         slice_;
--- a/Framework/Toolbox/DownloadStack.cpp	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Toolbox/DownloadStack.cpp	Thu Apr 27 16:57:49 2017 +0200
@@ -99,8 +99,6 @@
 
   bool DownloadStack::Pop(unsigned int& value)
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     assert(CheckInvariants());
 
     if (firstNode_ == NIL)
--- a/Framework/Toolbox/DownloadStack.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Toolbox/DownloadStack.h	Thu Apr 27 16:57:49 2017 +0200
@@ -23,7 +23,8 @@
 
 #include <vector>
 #include <boost/noncopyable.hpp>
-#include <boost/thread/mutex.hpp>
+
+#include <boost/thread/mutex.hpp>   // TODO remove
 
 namespace OrthancStone
 {
@@ -40,7 +41,6 @@
       bool  dequeued_;
     };
 
-    boost::mutex        mutex_;
     std::vector<Node>   nodes_;
     int                 firstNode_;
 
@@ -63,8 +63,8 @@
       
     public:
       Writer(DownloadStack& that) :
-        that_(that),
-        lock_(that.mutex_)
+        that_(that) 
+        //lock_(that.mutex_)
       {
       }
       
--- a/Framework/Toolbox/ISeriesLoader.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Toolbox/ISeriesLoader.h	Thu Apr 27 16:57:49 2017 +0200
@@ -23,16 +23,18 @@
 
 #include "ParallelSlices.h"
 
-#include "IThreadSafety.h"
 #include "../../Resources/Orthanc/Core/Images/ImageAccessor.h"
 #include "../../Resources/Orthanc/Plugins/Samples/Common/IDicomDataset.h"
 
 namespace OrthancStone
 {
-  // This class is NOT thread-safe
-  class ISeriesLoader : public IThreadUnsafe
+  class ISeriesLoader : public boost::noncopyable
   {
   public:
+    virtual ~ISeriesLoader()
+    {
+    }
+    
     virtual ParallelSlices& GetGeometry() = 0;
 
     virtual Orthanc::PixelFormat GetPixelFormat() = 0;
--- a/Framework/Toolbox/IThreadSafety.h	Thu Apr 27 14:50:40 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017 Osimis, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero 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
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include <boost/noncopyable.hpp>
-
-namespace OrthancStone
-{
-  /**
-   * Dummy interface to explicitely tag the interfaces whose derived
-   * class must be thread-safe. The different methods of such classes
-   * might be simlultaneously invoked by several threads, and should
-   * be properly protected by mutexes.
-   **/
-  class IThreadSafe : public boost::noncopyable
-  {
-  public:
-    virtual ~IThreadSafe()
-    {
-    }
-  };
-
-
-  /**
-   * Dummy interface to explicitely tag the interfaces that are NOT
-   * expected to be thread-safe. The Orthanc Stone framework ensures
-   * that at most one method of such classes will be invoked at a
-   * given time. Such classes are automatically protected by the
-   * Orthanc Stone framework wherever required.
-   **/
-  class IThreadUnsafe : public boost::noncopyable
-  {
-  public:
-    virtual ~IThreadUnsafe()
-    {
-    }
-  };
-}
--- a/Framework/Toolbox/ObserversRegistry.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Toolbox/ObserversRegistry.h	Thu Apr 27 16:57:49 2017 +0200
@@ -24,7 +24,6 @@
 #include "../../Resources/Orthanc/Core/OrthancException.h"
 
 #include <boost/noncopyable.hpp>
-#include <boost/thread/mutex.hpp>
 #include <set>
 
 namespace OrthancStone
@@ -47,32 +46,18 @@
 
     typedef std::set<Observer*>  Observers;
 
-    boost::mutex  mutex_;
-    Observers     observers_;
-    bool          empty_;
+    Observers  observers_;
 
   public:
-    ObserversRegistry() : empty_(true)
-    {
-    }
-
     template <typename Functor>
     void Notify(const Source* source,
                 Functor& functor)
     {
-      if (empty_)
-      {
-        // Optimization to avoid the unnecessary locking of the mutex
-        return;
-      }
-
       if (source == NULL)
       {
         throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
       }
 
-      boost::mutex::scoped_lock lock(mutex_);
-
       for (typename Observers::const_iterator observer = observers_.begin();
            observer != observers_.end(); ++observer)
       {
@@ -95,31 +80,17 @@
 
     void Register(Observer& observer)
     {
-      empty_ = false;
-
-      boost::mutex::scoped_lock lock(mutex_);
       observers_.insert(&observer);
     }
 
     void Unregister(Observer& observer)
     {
-      boost::mutex::scoped_lock lock(mutex_);
       observers_.erase(&observer);
-
-      if (observers_.empty())
-      {
-        empty_ = true;
-      }
     }
 
     bool IsEmpty()
     {
-#if 0
-      boost::mutex::scoped_lock lock(mutex_);
       return observers_.empty();
-#else
-      return empty_;
-#endif
     }
   };
 }
--- a/Framework/Toolbox/OrthancSeriesLoader.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Toolbox/OrthancSeriesLoader.h	Thu Apr 27 16:57:49 2017 +0200
@@ -28,10 +28,9 @@
 
 namespace OrthancStone
 {
-  // This class is NOT thread-safe
-  // It sorts the slices from a given series, give access to their
-  // geometry and individual frames, making the assumption that there
-  // is a single frame in each instance of the series
+  // This class sorts the slices from a given series, give access to
+  // their geometry and individual frames, making the assumption that
+  // there is a single frame in each instance of the series
   class OrthancSeriesLoader : public ISeriesLoader
   {
   private:
--- a/Framework/Toolbox/ParallelSlices.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Toolbox/ParallelSlices.h	Thu Apr 27 16:57:49 2017 +0200
@@ -25,8 +25,7 @@
 
 namespace OrthancStone
 {
-  // This class is NOT thread-safe
-  class ParallelSlices
+  class ParallelSlices : public boost::noncopyable
   {
   private:
     Vector                       normal_;
--- a/Framework/Toolbox/ParallelSlicesCursor.cpp	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Toolbox/ParallelSlicesCursor.cpp	Thu Apr 27 16:57:49 2017 +0200
@@ -40,8 +40,6 @@
 
   size_t ParallelSlicesCursor::GetSliceCount()
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     if (slices_.get() == NULL)
     {
       return 0;
@@ -55,8 +53,6 @@
 
   SliceGeometry ParallelSlicesCursor::GetSlice(size_t slice)
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     if (slices_.get() == NULL)
     {
       return SliceGeometry();
@@ -70,8 +66,6 @@
 
   void ParallelSlicesCursor::SetGeometry(const ParallelSlices& slices)
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     slices_.reset(new ParallelSlices(slices));
 
     currentSlice_ = GetDefaultSlice();
@@ -80,8 +74,6 @@
 
   SliceGeometry ParallelSlicesCursor::GetCurrentSlice()
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     if (slices_.get() != NULL &&
         currentSlice_ < slices_->GetSliceCount())
     {
@@ -96,8 +88,6 @@
 
   bool ParallelSlicesCursor::SetDefaultSlice()
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     size_t slice = GetDefaultSlice();
 
     if (currentSlice_ != slice)
@@ -115,8 +105,6 @@
   bool ParallelSlicesCursor::ApplyOffset(SliceOffsetMode mode,
                                          int offset)
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     if (slices_.get() == NULL)
     {
       return false;
@@ -215,8 +203,6 @@
 
   bool ParallelSlicesCursor::LookupSliceContainingPoint(const Vector& p)
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     size_t slice;
     double distance;
 
--- a/Framework/Toolbox/ParallelSlicesCursor.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Toolbox/ParallelSlicesCursor.h	Thu Apr 27 16:57:49 2017 +0200
@@ -24,15 +24,11 @@
 #include "ParallelSlices.h"
 #include "../Enumerations.h"
 
-#include <boost/thread/mutex.hpp>
-
 namespace OrthancStone
 {
-  // This class is thread-safe
   class ParallelSlicesCursor : public boost::noncopyable
   {
   private:
-    boost::mutex                   mutex_;
     std::auto_ptr<ParallelSlices>  slices_;
     size_t                         currentSlice_;
 
--- a/Framework/Toolbox/SharedValue.h	Thu Apr 27 14:50:40 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017 Osimis, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero 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
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include <boost/noncopyable.hpp>
-#include <boost/thread/mutex.hpp>
-
-namespace OrthancStone
-{
-  // A value that is protected by a mutex, in order to be shared by
-  // multiple threads
-  template <typename T>
-  class SharedValue : public boost::noncopyable
-  {
-  private:
-    boost::mutex   mutex_;
-    T              value_;
-    
-  public:
-    class Locker : public boost::noncopyable
-    {
-    private:
-      boost::mutex::scoped_lock  lock_;
-      T&                         value_;
-
-    public:
-      Locker(SharedValue& shared) :
-        lock_(shared.mutex_),
-        value_(shared.value_)
-      {
-      }
-
-      T& GetValue() const
-      {
-        return value_;
-      }
-    };
-  };
-}
--- a/Framework/Toolbox/SliceGeometry.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Toolbox/SliceGeometry.h	Thu Apr 27 16:57:49 2017 +0200
@@ -26,7 +26,7 @@
 
 namespace OrthancStone
 {
-  // Geometry of a 3D plane, NOT thread-safe
+  // Geometry of a 3D plane
   class SliceGeometry
   {
   private:
--- a/Framework/Toolbox/ViewportGeometry.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Toolbox/ViewportGeometry.h	Thu Apr 27 16:57:49 2017 +0200
@@ -25,7 +25,6 @@
 
 namespace OrthancStone
 {
-  // Not thread-safe
   class ViewportGeometry
   {
   private:
--- a/Framework/Viewport/IMouseTracker.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Viewport/IMouseTracker.h	Thu Apr 27 16:57:49 2017 +0200
@@ -22,14 +22,16 @@
 #pragma once
 
 #include "CairoSurface.h"
-#include "../Toolbox/IThreadSafety.h"
 
 namespace OrthancStone
 {
-  // Not thread-safe
-  class IMouseTracker : public IThreadUnsafe
+  class IMouseTracker : public boost::noncopyable
   {
   public:
+    virtual ~IMouseTracker()
+    {
+    }
+    
     virtual void Render(Orthanc::ImageAccessor& surface) = 0;
 
     virtual void MouseUp() = 0;
--- a/Framework/Viewport/IStatusBar.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Viewport/IStatusBar.h	Thu Apr 27 16:57:49 2017 +0200
@@ -22,14 +22,17 @@
 #pragma once
 
 #include <string>
-#include "../Toolbox/IThreadSafety.h"
+#include <boost/noncopyable.hpp>
 
 namespace OrthancStone
 {
-  // This class must be thread-safe
-  class IStatusBar : public IThreadSafe
+  class IStatusBar : public boost::noncopyable
   {
   public:
+    virtual ~IStatusBar()
+    {
+    }
+    
     virtual void ClearMessage() = 0;
 
     virtual void SetMessage(const std::string& message) = 0;
--- a/Framework/Viewport/IViewport.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Viewport/IViewport.h	Thu Apr 27 16:57:49 2017 +0200
@@ -21,7 +21,6 @@
 
 #pragma once
 
-#include "../Toolbox/IThreadSafety.h"
 #include "IStatusBar.h"
 #include "../Enumerations.h"
 
@@ -29,8 +28,7 @@
 
 namespace OrthancStone
 {
-  // This class must be thread-safe
-  class IViewport : public IThreadSafe
+  class IViewport : public boost::noncopyable
   {
   public:
     class IChangeObserver : public boost::noncopyable
@@ -43,6 +41,10 @@
       virtual void NotifyChange(const IViewport& scene) = 0;
     };
 
+    virtual ~IViewport()
+    {
+    }
+
     virtual void Register(IChangeObserver& observer) = 0;
 
     virtual void Unregister(IChangeObserver& observer) = 0;
--- a/Framework/Viewport/WidgetViewport.cpp	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Viewport/WidgetViewport.cpp	Thu Apr 27 16:57:49 2017 +0200
@@ -50,8 +50,6 @@
 
   void WidgetViewport::SetStatusBar(IStatusBar& statusBar)
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     statusBar_ = &statusBar;
 
     if (centralWidget_.get() != NULL)
@@ -63,8 +61,6 @@
 
   void WidgetViewport::ResetStatusBar()
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     statusBar_ = NULL;
 
     if (centralWidget_.get() != NULL)
@@ -81,8 +77,6 @@
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
 
-    boost::mutex::scoped_lock lock(mutex_);
-
     if (widget == NULL)
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
@@ -117,8 +111,6 @@
 
   void WidgetViewport::Start()
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     if (centralWidget_.get() != NULL)
     {
       centralWidget_->Start();
@@ -130,8 +122,6 @@
 
   void WidgetViewport::Stop()
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     started_ = false;
 
     if (centralWidget_.get() != NULL)
@@ -144,8 +134,6 @@
   void WidgetViewport::SetSize(unsigned int width,
                                unsigned int height)
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     background_.SetSize(width, height);
 
     if (centralWidget_.get() != NULL)
@@ -159,8 +147,6 @@
 
   bool WidgetViewport::Render(Orthanc::ImageAccessor& surface)
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     if (!started_ ||
         centralWidget_.get() == NULL)
     {
@@ -201,8 +187,6 @@
                                  int y,
                                  KeyboardModifiers modifiers)
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     if (!started_)
     {
       return;
@@ -226,8 +210,6 @@
 
   void WidgetViewport::MouseUp()
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     if (!started_)
     {
       return;
@@ -245,8 +227,6 @@
   void WidgetViewport::MouseMove(int x, 
                                  int y) 
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     if (!started_)
     {
       return;
@@ -267,7 +247,6 @@
 
   void WidgetViewport::MouseEnter()
   {
-    boost::mutex::scoped_lock lock(mutex_);
     isMouseOver_ = true;
     observers_.NotifyChange(this);
   }
@@ -275,7 +254,6 @@
 
   void WidgetViewport::MouseLeave()
   {
-    boost::mutex::scoped_lock lock(mutex_);
     isMouseOver_ = false;
 
     if (started_ &&
@@ -294,8 +272,6 @@
                                   int y,
                                   KeyboardModifiers modifiers)
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     if (!started_)
     {
       return;
@@ -312,8 +288,6 @@
   void WidgetViewport::KeyPressed(char key,
                                   KeyboardModifiers modifiers)
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     if (centralWidget_.get() != NULL &&
         mouseTracker_.get() == NULL)
     {
@@ -324,8 +298,6 @@
 
   bool WidgetViewport::HasUpdateContent()
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     if (centralWidget_.get() != NULL)
     {
       return centralWidget_->HasUpdateContent();
@@ -339,8 +311,6 @@
 
   void WidgetViewport::UpdateContent()
   {
-    boost::mutex::scoped_lock lock(mutex_);
-
     if (centralWidget_.get() != NULL)
     {
       centralWidget_->UpdateContent();
--- a/Framework/Viewport/WidgetViewport.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Viewport/WidgetViewport.h	Thu Apr 27 16:57:49 2017 +0200
@@ -25,6 +25,8 @@
 #include "../Toolbox/ObserversRegistry.h"
 #include "../Widgets/IWidget.h"
 
+#include <memory>
+
 namespace OrthancStone
 {
   class WidgetViewport : 
@@ -32,7 +34,6 @@
     public IWidget::IChangeObserver    
   {
   private:
-    boost::mutex                  mutex_;
     std::auto_ptr<IWidget>        centralWidget_;
     IStatusBar*                   statusBar_;
     ObserversRegistry<IViewport>  observers_;
--- a/Framework/Volumes/ISliceableVolume.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Volumes/ISliceableVolume.h	Thu Apr 27 16:57:49 2017 +0200
@@ -21,14 +21,13 @@
 
 #pragma once
 
-#include "../Toolbox/IThreadSafety.h"
+#include <boost/noncopyable.hpp>
 
 namespace OrthancStone
 {
-  class ISliceableVolume : public IThreadSafe
+  class ISliceableVolume : public boost::noncopyable
   {
   public:
-    // Must be thread-safe
     class IChangeObserver : public boost::noncopyable
     {
     public:
@@ -39,6 +38,10 @@
       virtual void NotifyChange(const ISliceableVolume& volume) = 0;
     };
 
+    virtual ~ISliceableVolume()
+    {
+    }
+
     virtual void Register(IChangeObserver& observer) = 0;
 
     virtual void Unregister(IChangeObserver& observer) = 0;
--- a/Framework/Volumes/ImageBuffer3D.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Volumes/ImageBuffer3D.h	Thu Apr 27 16:57:49 2017 +0200
@@ -22,7 +22,6 @@
 #pragma once
 
 #include "../Enumerations.h"
-#include "../Toolbox/IThreadSafety.h"
 #include "../Toolbox/SliceGeometry.h"
 #include "../Toolbox/ParallelSlices.h"
 
@@ -36,7 +35,7 @@
 
 namespace OrthancStone
 {
-  class ImageBuffer3D : public IThreadSafe
+  class ImageBuffer3D : public boost::noncopyable
   {
   private:
     typedef boost::shared_mutex          Mutex;
--- a/Framework/Volumes/VolumeImage.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Volumes/VolumeImage.h	Thu Apr 27 16:57:49 2017 +0200
@@ -35,9 +35,13 @@
   class VolumeImage : public ISliceableVolume
   {
   public:
-    class IDownloadPolicy : public IThreadSafe
+    class IDownloadPolicy : public boost::noncopyable
     {
     public:
+      virtual ~IDownloadPolicy()
+      {
+      }
+      
       virtual void Initialize(ImageBuffer3D& buffer,
                               ISeriesLoader& loader) = 0;
 
--- a/Framework/Widgets/EmptyWidget.cpp	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Widgets/EmptyWidget.cpp	Thu Apr 27 16:57:49 2017 +0200
@@ -22,6 +22,7 @@
 #include "EmptyWidget.h"
 
 #include "../../Resources/Orthanc/Core/Images/ImageProcessing.h"
+#include "../../Resources/Orthanc/Core/OrthancException.h"
 
 namespace OrthancStone
 {
--- a/Framework/Widgets/IWidget.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Widgets/IWidget.h	Thu Apr 27 16:57:49 2017 +0200
@@ -27,7 +27,7 @@
 
 namespace OrthancStone
 {
-  class IWidget : public IThreadUnsafe
+  class IWidget : public boost::noncopyable
   {
   public:
     class IChangeObserver : public boost::noncopyable
@@ -40,6 +40,10 @@
       virtual void NotifyChange(const IWidget& widget) = 0;
     };
 
+    virtual ~IWidget()
+    {
+    }
+
     virtual void SetStatusBar(IStatusBar& statusBar) = 0;
 
     virtual void ResetStatusBar() = 0;
--- a/Framework/Widgets/IWorldSceneInteractor.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Widgets/IWorldSceneInteractor.h	Thu Apr 27 16:57:49 2017 +0200
@@ -32,9 +32,13 @@
 {
   class WorldSceneWidget;
 
-  class IWorldSceneInteractor : public IThreadSafe
+  class IWorldSceneInteractor : public boost::noncopyable
   {
   public:
+    virtual ~IWorldSceneInteractor()
+    {
+    }
+    
     virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
                                                         const SliceGeometry& slice,
                                                         const ViewportGeometry& view,
--- a/Framework/Widgets/IWorldSceneMouseTracker.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Widgets/IWorldSceneMouseTracker.h	Thu Apr 27 16:57:49 2017 +0200
@@ -21,14 +21,17 @@
 
 #pragma once
 
-#include "../Toolbox/IThreadSafety.h"
 #include "../Viewport/CairoContext.h"
 
 namespace OrthancStone
 {
-  class IWorldSceneMouseTracker : public IThreadUnsafe
+  class IWorldSceneMouseTracker : public boost::noncopyable
   {
   public:
+    virtual ~IWorldSceneMouseTracker()
+    {
+    }
+    
     virtual void Render(CairoContext& context,
                         double zoom) = 0;
     
--- a/Framework/Widgets/LayeredSceneWidget.cpp	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Widgets/LayeredSceneWidget.cpp	Thu Apr 27 16:57:49 2017 +0200
@@ -26,6 +26,8 @@
 
 #include "../../Resources/Orthanc/Core/OrthancException.h"
 
+#include <boost/thread/condition_variable.hpp>   // TODO Remove
+
 namespace OrthancStone
 {
   class LayeredSceneWidget::Renderers : public boost::noncopyable
--- a/Framework/Widgets/LayeredSceneWidget.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Widgets/LayeredSceneWidget.h	Thu Apr 27 16:57:49 2017 +0200
@@ -25,13 +25,13 @@
 
 #include "../Layers/ILayerRendererFactory.h"
 
+#include <boost/thread/mutex.hpp>  // TODO remove
 
 namespace OrthancStone
 {
   class LayeredSceneWidget : public WorldSceneWidget
   {
   public:
-    // Must be thread-safe
     class ISliceObserver : public boost::noncopyable
     {
     public:
--- a/Framework/Widgets/LayoutWidget.cpp	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Widgets/LayoutWidget.cpp	Thu Apr 27 16:57:49 2017 +0200
@@ -82,15 +82,25 @@
     int                     top_;
     unsigned int            width_;
     unsigned int            height_;
+    bool                    hasUpdate_;
 
   public:
     ChildWidget(IWidget* widget) :
-      widget_(widget)
+      widget_(widget),
+      hasUpdate_(widget->HasUpdateContent())
     {
       assert(widget != NULL);
       SetEmpty();
     }
 
+    void UpdateContent()
+    {
+      if (hasUpdate_)
+      {
+        widget_->UpdateContent();
+      }
+    }
+
     IWidget& GetWidget() const
     {
       return *widget_;
@@ -348,6 +358,11 @@
 
     ComputeChildrenExtents();
 
+    if (widget->HasUpdateContent())
+    {
+      hasUpdateContent_ = true;
+    }
+
     return *widget;
   }
 
@@ -473,4 +488,20 @@
       children_[i]->GetWidget().KeyPressed(key, modifiers);
     }
   }
+
+  
+  void LayoutWidget::UpdateContent()
+  {
+    if (hasUpdateContent_)
+    {
+      for (size_t i = 0; i < children_.size(); i++)
+      {
+        children_[i]->UpdateContent();
+      }
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+  }
 }
--- a/Framework/Widgets/LayoutWidget.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Widgets/LayoutWidget.h	Thu Apr 27 16:57:49 2017 +0200
@@ -23,6 +23,8 @@
 
 #include "WidgetBase.h"
 
+#include <vector>
+#include <memory>
 
 namespace OrthancStone
 {
@@ -45,6 +47,7 @@
     unsigned int                  paddingRight_;
     unsigned int                  paddingBottom_;
     unsigned int                  paddingInternal_;
+    bool                          hasUpdateContent_;
 
     void ComputeChildrenExtents();
 
@@ -123,5 +126,12 @@
 
     virtual void KeyPressed(char key,
                             KeyboardModifiers modifiers);
+
+    virtual bool HasUpdateContent() const
+    {
+      return hasUpdateContent_;
+    }
+
+    virtual void UpdateContent();
   };
 }
--- a/Framework/Widgets/WorldSceneWidget.cpp	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Widgets/WorldSceneWidget.cpp	Thu Apr 27 16:57:49 2017 +0200
@@ -129,8 +129,7 @@
       downX_(x),
       downY_(y)
     {
-      SharedValue<ViewportGeometry>::Locker locker(that_.view_);
-      locker.GetValue().GetPan(previousPanX_, previousPanY_);
+      that_.view_.GetPan(previousPanX_, previousPanY_);
     }
 
     virtual void Render(Orthanc::ImageAccessor& surface)
@@ -144,11 +143,10 @@
     virtual void MouseMove(int x, 
                            int y)
     {
-      SharedValue<ViewportGeometry>::Locker locker(that_.view_);
-      locker.GetValue().SetPan(previousPanX_ + x - downX_,
-                               previousPanY_ + y - downY_);
+      that_.view_.SetPan(previousPanX_ + x - downX_,
+                         previousPanY_ + y - downY_);
 
-      ViewChangeFunctor functor(locker.GetValue());
+      ViewChangeFunctor functor(that_.view_);
       that_.observers_.Notify(&that_, functor);
     }
   };
@@ -172,9 +170,8 @@
       downX_(x),
       downY_(y)
     {
-      SharedValue<ViewportGeometry>::Locker locker(that_.view_);
-      oldZoom_ = locker.GetValue().GetZoom();
-      MapMouseToScene(centerX_, centerY_, locker.GetValue(), downX_, downY_);
+      oldZoom_ = that_.view_.GetZoom();
+      MapMouseToScene(centerX_, centerY_, that_.view_, downX_, downY_);
     }
 
     virtual void Render(Orthanc::ImageAccessor& surface)
@@ -191,15 +188,13 @@
       static const double MIN_ZOOM = -4;
       static const double MAX_ZOOM = 4;
 
-      SharedValue<ViewportGeometry>::Locker locker(that_.view_);
-
-      if (locker.GetValue().GetDisplayHeight() <= 3)
+      if (that_.view_.GetDisplayHeight() <= 3)
       {
         return;   // Cannot zoom on such a small image
       }
 
       double dy = (static_cast<double>(y - downY_) / 
-                   static_cast<double>(locker.GetValue().GetDisplayHeight() - 1)); // In the range [-1,1]
+                   static_cast<double>(that_.view_.GetDisplayHeight() - 1)); // In the range [-1,1]
       double z;
 
       // Linear interpolation from [-1, 1] to [MIN_ZOOM, MAX_ZOOM]
@@ -218,19 +213,19 @@
 
       z = pow(2.0, z);
 
-      locker.GetValue().SetZoom(oldZoom_ * z);
+      that_.view_.SetZoom(oldZoom_ * z);
 
       // Correct the pan so that the original click point is kept at
       // the same location on the display
       double panX, panY;
-      locker.GetValue().GetPan(panX, panY);
+      that_.view_.GetPan(panX, panY);
 
       int tx, ty;
-      locker.GetValue().MapSceneToDisplay(tx, ty, centerX_, centerY_);
-      locker.GetValue().SetPan(panX + static_cast<double>(downX_ - tx),
+      that_.view_.MapSceneToDisplay(tx, ty, centerX_, centerY_);
+      that_.view_.SetPan(panX + static_cast<double>(downX_ - tx),
                                panY + static_cast<double>(downY_ - ty));
 
-      ViewChangeFunctor functor(locker.GetValue());
+      ViewChangeFunctor functor(that_.view_);
       that_.observers_.Notify(&that_, functor);
     }
   };
@@ -238,16 +233,8 @@
 
   bool WorldSceneWidget::RenderCairo(CairoContext& context)
   {
-    ViewportGeometry view;
-
-    {
-      SharedValue<ViewportGeometry>::Locker locker(view_);
-      view = locker.GetValue();
-    }
-
-    view.ApplyTransform(context);
-
-    return RenderScene(context, view);
+    view_.ApplyTransform(context);
+    return RenderScene(context, view_);
   }
 
 
@@ -264,11 +251,11 @@
   }
 
 
-  void WorldSceneWidget::SetSceneExtent(SharedValue<ViewportGeometry>::Locker& locker)
+  void WorldSceneWidget::SetSceneExtent(ViewportGeometry& view)
   {
     double x1, y1, x2, y2;
     GetSceneExtent(x1, y1, x2, y2);
-    locker.GetValue().SetSceneExtent(x1, y1, x2, y2);
+    view.SetSceneExtent(x1, y1, x2, y2);
   }
 
 
@@ -277,21 +264,18 @@
   {
     CairoWidget::SetSize(width, height);
 
-    {
-      SharedValue<ViewportGeometry>::Locker locker(view_);
-      locker.GetValue().SetDisplaySize(width, height);
+    view_.SetDisplaySize(width, height);
 
-      if (observers_.IsEmpty())
-      {
-        // Without a size observer, use the default view
-        locker.GetValue().SetDefaultView();
-      }
-      else
-      {
-        // With a size observer, let it decide which view to use
-        SizeChangeFunctor functor(locker.GetValue());
-        observers_.Notify(this, functor);
-      }
+    if (observers_.IsEmpty())
+    {
+      // Without a size observer, use the default view
+      view_.SetDefaultView();
+    }
+    else
+    {
+      // With a size observer, let it decide which view to use
+      SizeChangeFunctor functor(view_);
+      observers_.Notify(this, functor);
     }
   }
 
@@ -309,57 +293,41 @@
 
   void WorldSceneWidget::Start()
   {
-    ViewportGeometry geometry;
-
-    {
-      SharedValue<ViewportGeometry>::Locker locker(view_);
-      SetSceneExtent(locker);
-      geometry = locker.GetValue();
-    }
-
+    SetSceneExtent(view_);
+    
     WidgetBase::Start();
 
-    ViewChangeFunctor functor(geometry);
+    ViewChangeFunctor functor(view_);
     observers_.Notify(this, functor);
   }
       
 
   void WorldSceneWidget::SetDefaultView()
   {
-    ViewportGeometry geometry;
-
-    {
-      SharedValue<ViewportGeometry>::Locker locker(view_);
-      SetSceneExtent(locker);
-      locker.GetValue().SetDefaultView();
-      geometry = locker.GetValue();
-    }
+    SetSceneExtent(view_);
+    view_.SetDefaultView();
 
     NotifyChange();
 
-    ViewChangeFunctor functor(geometry);
+    ViewChangeFunctor functor(view_);
     observers_.Notify(this, functor);
   }
 
 
   void WorldSceneWidget::SetView(const ViewportGeometry& view)
   {
-    {
-      SharedValue<ViewportGeometry>::Locker locker(view_);
-      locker.GetValue() = view;
-    }
+    view_ = view;
 
     NotifyChange();
 
-    ViewChangeFunctor functor(view);
+    ViewChangeFunctor functor(view_);
     observers_.Notify(this, functor);
   }
 
 
   ViewportGeometry WorldSceneWidget::GetView()
   {
-    SharedValue<ViewportGeometry>::Locker locker(view_);
-    return locker.GetValue();
+    return view_;
   }
 
 
@@ -368,15 +336,15 @@
                                                       int y,
                                                       KeyboardModifiers modifiers)
   {
-    ViewportGeometry view = GetView();
+    double sceneX, sceneY;
+    MapMouseToScene(sceneX, sceneY, view_, x, y);
 
-    double sceneX, sceneY;
-    MapMouseToScene(sceneX, sceneY, view, x, y);
+    std::auto_ptr<IWorldSceneMouseTracker> tracker
+      (CreateMouseSceneTracker(view_, button, sceneX, sceneY, modifiers));
 
-    std::auto_ptr<IWorldSceneMouseTracker> tracker(CreateMouseSceneTracker(view, button, sceneX, sceneY, modifiers));
     if (tracker.get() != NULL)
     {
-      return new SceneMouseTracker(view, tracker.release());
+      return new SceneMouseTracker(view_, tracker.release());
     }
 
     switch (button)
--- a/Framework/Widgets/WorldSceneWidget.h	Thu Apr 27 14:50:40 2017 +0200
+++ b/Framework/Widgets/WorldSceneWidget.h	Thu Apr 27 16:57:49 2017 +0200
@@ -24,7 +24,6 @@
 #include "CairoWidget.h"
 #include "IWorldSceneInteractor.h"
 
-#include "../Toolbox/SharedValue.h"
 #include "../Toolbox/ViewportGeometry.h"
 
 namespace OrthancStone
@@ -32,7 +31,6 @@
   class WorldSceneWidget : public CairoWidget
   {
   public:
-    // Must be thread-safe
     class IWorldObserver : public boost::noncopyable
     {
     public:
@@ -57,9 +55,9 @@
 
     typedef ObserversRegistry<WorldSceneWidget, IWorldObserver>  Observers;
 
-    SharedValue<ViewportGeometry>  view_;
-    Observers                      observers_;
-    IWorldSceneInteractor*         interactor_;
+    ViewportGeometry       view_;
+    Observers              observers_;
+    IWorldSceneInteractor* interactor_;
 
 
   protected:
@@ -72,7 +70,7 @@
                                       int x,
                                       int y);
 
-    void SetSceneExtent(SharedValue<ViewportGeometry>::Locker& locker);
+    void SetSceneExtent(ViewportGeometry& geometry);
 
   public:
     WorldSceneWidget() :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Graveyard/Threading/IThreadSafety.h	Thu Apr 27 16:57:49 2017 +0200
@@ -0,0 +1,57 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero 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
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include <boost/noncopyable.hpp>
+
+namespace OrthancStone
+{
+  /**
+   * Dummy interface to explicitely tag the interfaces whose derived
+   * class must be thread-safe. The different methods of such classes
+   * might be simlultaneously invoked by several threads, and should
+   * be properly protected by mutexes.
+   **/
+  class IThreadSafe : public boost::noncopyable
+  {
+  public:
+    virtual ~IThreadSafe()
+    {
+    }
+  };
+
+
+  /**
+   * Dummy interface to explicitely tag the interfaces that are NOT
+   * expected to be thread-safe. The Orthanc Stone framework ensures
+   * that at most one method of such classes will be invoked at a
+   * given time. Such classes are automatically protected by the
+   * Orthanc Stone framework wherever required.
+   **/
+  class IThreadUnsafe : public boost::noncopyable
+  {
+  public:
+    virtual ~IThreadUnsafe()
+    {
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Graveyard/Threading/SharedValue.h	Thu Apr 27 16:57:49 2017 +0200
@@ -0,0 +1,58 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero 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
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include <boost/noncopyable.hpp>
+#include <boost/thread/mutex.hpp>
+
+namespace OrthancStone
+{
+  // A value that is protected by a mutex, in order to be shared by
+  // multiple threads
+  template <typename T>
+  class SharedValue : public boost::noncopyable
+  {
+  private:
+    boost::mutex   mutex_;
+    T              value_;
+    
+  public:
+    class Locker : public boost::noncopyable
+    {
+    private:
+      boost::mutex::scoped_lock  lock_;
+      T&                         value_;
+
+    public:
+      Locker(SharedValue& shared) :
+        lock_(shared.mutex_),
+        value_(shared.value_)
+      {
+      }
+
+      T& GetValue() const
+      {
+        return value_;
+      }
+    };
+  };
+}