changeset 1328:fd616c4a5904 broker

Added mechanism to prevent callbacks from being sent on dead WebAssemblyViewport objects
author Benjamin Golinvaux <bgo@osimis.io>
date Fri, 27 Mar 2020 12:54:27 +0100
parents 4f8db2d202c8
children 8d3e669f01a2
files Framework/Toolbox/GenericToolbox.h Framework/Viewport/WebAssemblyViewport.cpp Framework/Viewport/WebAssemblyViewport.h UnitTestsSources/GenericToolboxTests.cpp
diffstat 4 files changed, 142 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Toolbox/GenericToolbox.h	Wed Mar 25 14:34:27 2020 +0100
+++ b/Framework/Toolbox/GenericToolbox.h	Fri Mar 27 12:54:27 2020 +0100
@@ -20,10 +20,15 @@
 
 #pragma once
 
+#include <Core/Compatibility.h>
+
+#include <boost/shared_ptr.hpp>
+
 #include <string>
 #include <stdint.h>
 #include <math.h>
 
+#include <memory>
 
 namespace OrthancStone
 {
@@ -281,14 +286,52 @@
     /**
     Same as GetRgbValuesFromString
     */
-    bool GetRgbaValuesFromString(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha, const char* text);
+    bool GetRgbaValuesFromString(uint8_t& red,
+                                 uint8_t& green,
+                                 uint8_t& blue,
+                                 uint8_t& alpha,
+                                 const char* text);
 
     /**
     Same as GetRgbValuesFromString
     */
-    inline bool GetRgbaValuesFromString(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha, const std::string& text)
+    inline bool GetRgbaValuesFromString(uint8_t& red, 
+                                        uint8_t& green, 
+                                        uint8_t& blue, 
+                                        uint8_t& alpha, 
+                                        const std::string& text)
     {
       return GetRgbaValuesFromString(red, green, blue, alpha, text.c_str());
     }
+
+
+    template<typename Wrappee> 
+    struct HoldingRef
+    {
+      HoldingRef(Wrappee* object)
+      {
+        // a crash here means that the object is not stored in a shared_ptr
+        // using shared_ptr is mandatory for this
+        object_ = object->shared_from_this();
+      }
+      boost::shared_ptr<Wrappee> object_;
+
+      static void* Wrap(Wrappee* object)
+      {
+        std::unique_ptr<HoldingRef<Wrappee > > up(new HoldingRef<Wrappee>(object));
+        void* userData = reinterpret_cast<void*>(up.release());
+        return userData;
+      }
+
+      static boost::shared_ptr<Wrappee> Unwrap(void* userData)
+      {
+        // the stored shared_ptr will be deleted because of wrapping
+        // the data in a RAII
+        std::unique_ptr<HoldingRef<Wrappee > >
+          up(reinterpret_cast<HoldingRef<Wrappee>*>(userData));
+        boost::shared_ptr<Wrappee> object = up->object_;
+        return object;
+      }
+    };
   }
 }
--- a/Framework/Viewport/WebAssemblyViewport.cpp	Wed Mar 25 14:34:27 2020 +0100
+++ b/Framework/Viewport/WebAssemblyViewport.cpp	Fri Mar 27 12:54:27 2020 +0100
@@ -21,9 +21,12 @@
 
 #include "WebAssemblyViewport.h"
 
+#include "../Toolbox/GenericToolbox.h"
+
 #include <Core/OrthancException.h>
 
 #include <boost/make_shared.hpp>
+#include <boost/enable_shared_from_this.hpp>
 
 namespace OrthancStone
 {
@@ -103,12 +106,12 @@
 
   EM_BOOL WebAssemblyViewport::OnRequestAnimationFrame(double time, void *userData)
   {
-    WebAssemblyViewport& that = *reinterpret_cast<WebAssemblyViewport*>(userData);
+    boost::shared_ptr<WebAssemblyViewport> that = GenericToolbox::HoldingRef<WebAssemblyViewport>::Unwrap(userData);
 
-    if (that.compositor_.get() != NULL &&
-        that.controller_ /* should always be true */)
+    if (that->compositor_.get() != NULL &&
+        that->controller_ /* should always be true */)
     {
-      that.Paint(*that.compositor_, *that.controller_);
+      that->Paint(*that->compositor_, *that->controller_);
     }
       
     return true;
@@ -116,12 +119,12 @@
 
   EM_BOOL WebAssemblyViewport::OnResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
   {
-    WebAssemblyViewport& that = *reinterpret_cast<WebAssemblyViewport*>(userData);
+    boost::shared_ptr<WebAssemblyViewport> that = GenericToolbox::HoldingRef<WebAssemblyViewport>::Unwrap(userData);
 
-    if (that.compositor_.get() != NULL)
+    if (that->compositor_.get() != NULL)
     {
-      that.UpdateSize(*that.compositor_);
-      that.Invalidate();
+      that->UpdateSize(*that->compositor_);
+      that->Invalidate();
     }
       
     return true;
@@ -130,20 +133,20 @@
 
   EM_BOOL WebAssemblyViewport::OnMouseDown(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
   {
-    WebAssemblyViewport& that = *reinterpret_cast<WebAssemblyViewport*>(userData);
+    boost::shared_ptr<WebAssemblyViewport> that = GenericToolbox::HoldingRef<WebAssemblyViewport>::Unwrap(userData);
 
-    LOG(INFO) << "mouse down: " << that.GetFullCanvasId();      
+    LOG(INFO) << "mouse down: " << that->GetFullCanvasId();      
 
-    if (that.compositor_.get() != NULL &&
-        that.interactor_.get() != NULL)
+    if (that->compositor_.get() != NULL &&
+        that->interactor_.get() != NULL)
     {
       PointerEvent pointer;
-      ConvertMouseEvent(pointer, *mouseEvent, *that.compositor_);
+      ConvertMouseEvent(pointer, *mouseEvent, *that->compositor_);
 
-      that.controller_->HandleMousePress(*that.interactor_, pointer,
-                                         that.compositor_->GetCanvasWidth(),
-                                         that.compositor_->GetCanvasHeight());        
-      that.Invalidate();
+      that->controller_->HandleMousePress(*that->interactor_, pointer,
+                                         that->compositor_->GetCanvasWidth(),
+                                         that->compositor_->GetCanvasHeight());        
+      that->Invalidate();
     }
 
     return true;
@@ -152,44 +155,41 @@
     
   EM_BOOL WebAssemblyViewport::OnMouseMove(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
   {
-    WebAssemblyViewport& that = *reinterpret_cast<WebAssemblyViewport*>(userData);
+    boost::shared_ptr<WebAssemblyViewport> that = GenericToolbox::HoldingRef<WebAssemblyViewport>::Unwrap(userData);
 
-    if (that.compositor_.get() != NULL &&
-        that.controller_->HasActiveTracker())
+    if (that->compositor_.get() != NULL &&
+        that->controller_->HasActiveTracker())
     {
       PointerEvent pointer;
-      ConvertMouseEvent(pointer, *mouseEvent, *that.compositor_);
-      if (that.controller_->HandleMouseMove(pointer))
+      ConvertMouseEvent(pointer, *mouseEvent, *that->compositor_);
+      if (that->controller_->HandleMouseMove(pointer))
       {
-        that.Invalidate();
+        that->Invalidate();
       }
     }
 
     return true;
   }
+    
+  EM_BOOL WebAssemblyViewport::OnMouseUp(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
+  {
+    boost::shared_ptr<WebAssemblyViewport> that = GenericToolbox::HoldingRef<WebAssemblyViewport>::Unwrap(userData);
+
+    if (that->compositor_.get() != NULL)
+    {
+      PointerEvent pointer;
+      ConvertMouseEvent(pointer, *mouseEvent, *that->compositor_);
+      that->controller_->HandleMouseRelease(pointer);
+      that->Invalidate();
+    }
+
+    return true;
+  }
 
-    
-  EM_BOOL WebAssemblyViewport::OnMouseUp(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
-  {
-    WebAssemblyViewport& that = *reinterpret_cast<WebAssemblyViewport*>(userData);
-
-    if (that.compositor_.get() != NULL)
-    {
-      PointerEvent pointer;
-      ConvertMouseEvent(pointer, *mouseEvent, *that.compositor_);
-      that.controller_->HandleMouseRelease(pointer);
-      that.Invalidate();
-    }
-
-    return true;
-  }
-
-    
   void WebAssemblyViewport::Invalidate()
   {
-    emscripten_request_animation_frame(OnRequestAnimationFrame, this);
+    emscripten_request_animation_frame(OnRequestAnimationFrame, GenericToolbox::HoldingRef<WebAssemblyViewport>::Wrap(this));
   }
-    
 
   void WebAssemblyViewport::AcquireCompositor(ICompositor* compositor /* takes ownership */)
   {
--- a/Framework/Viewport/WebAssemblyViewport.h	Wed Mar 25 14:34:27 2020 +0100
+++ b/Framework/Viewport/WebAssemblyViewport.h	Fri Mar 27 12:54:27 2020 +0100
@@ -36,7 +36,9 @@
 
 namespace OrthancStone
 {
-  class WebAssemblyViewport : public IViewport
+  class WebAssemblyViewport : public IViewport,
+                              public boost::enable_shared_from_this<WebAssemblyViewport>
+
   {
   private:
     class WasmLock;
@@ -84,6 +86,9 @@
 
     virtual ILock* Lock() ORTHANC_OVERRIDE;
 
+    /**
+    This method takes ownership
+    */
     void AcquireInteractor(IViewportInteractor* interactor);
 
     const std::string& GetShortCanvasId() const
--- a/UnitTestsSources/GenericToolboxTests.cpp	Wed Mar 25 14:34:27 2020 +0100
+++ b/UnitTestsSources/GenericToolboxTests.cpp	Fri Mar 27 12:54:27 2020 +0100
@@ -22,6 +22,7 @@
 
 #include <boost/date_time/posix_time/posix_time.hpp>
 #include <boost/lexical_cast.hpp>
+#include <boost/enable_shared_from_this.hpp>
 
 #include "gtest/gtest.h"
 
@@ -4243,3 +4244,51 @@
   EXPECT_EQ(0, green);
   EXPECT_EQ(0, blue);
 }
+
+static int Test_GenericToolbox_HoldingRef_ctor_trace = 100;
+static int Test_GenericToolbox_HoldingRef_dtor_trace = 1000;
+
+class Test_GenericToolbox_HoldingRef : public boost::enable_shared_from_this<Test_GenericToolbox_HoldingRef>
+{
+public:
+  Test_GenericToolbox_HoldingRef() : answer_(42) { Test_GenericToolbox_HoldingRef_ctor_trace++; }
+  unsigned int answer_;
+
+  ~Test_GenericToolbox_HoldingRef()
+  {
+    Test_GenericToolbox_HoldingRef_dtor_trace--;
+  }
+};
+
+TEST(GenericToolbox, HoldingRef)
+{
+  using namespace OrthancStone::GenericToolbox;
+
+  void* userData = NULL;
+
+  {
+    boost::shared_ptr<Test_GenericToolbox_HoldingRef> test(new Test_GenericToolbox_HoldingRef() );
+    Test_GenericToolbox_HoldingRef* This = test.get();
+    userData = HoldingRef<Test_GenericToolbox_HoldingRef>::Wrap(This);
+    EXPECT_EQ(42u,This->answer_);
+    This->answer_ = 0xdeadbeef;
+    EXPECT_EQ(0xdeadbeef,This->answer_);
+  }
+  
+  EXPECT_EQ(101,Test_GenericToolbox_HoldingRef_ctor_trace);
+  EXPECT_EQ(1000,Test_GenericToolbox_HoldingRef_dtor_trace);
+  
+  {
+    boost::shared_ptr<Test_GenericToolbox_HoldingRef> test2 = HoldingRef<Test_GenericToolbox_HoldingRef>::Unwrap(userData);
+    EXPECT_EQ(0xdeadbeef,test2->answer_);
+  }
+  
+  EXPECT_EQ(1001,Test_GenericToolbox_HoldingRef_dtor_trace); // make sure wrapper was deleted once
+}
+
+
+
+
+
+
+