changeset 1621:575f512cdf48

Used a weak_ptr as a cookie to RequestAnimationFrame to prevent accessing a deleted viewport.
author Benjamin Golinvaux <bgo@osimis.io>
date Mon, 02 Nov 2020 20:45:04 +0100
parents 1151e25d7311
children 0f8d6791b403
files Applications/Platforms/WebAssembly/WebAssemblyViewport.cpp Applications/Platforms/WebAssembly/WebAssemblyViewport.h
diffstat 2 files changed, 96 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Platforms/WebAssembly/WebAssemblyViewport.cpp	Mon Nov 02 17:56:49 2020 +0100
+++ b/Applications/Platforms/WebAssembly/WebAssemblyViewport.cpp	Mon Nov 02 20:45:04 2020 +0100
@@ -120,19 +120,27 @@
     }
   };
 
-
   EM_BOOL WebAssemblyViewport::OnRequestAnimationFrame(double time, void *userData)
   {
     LOG(TRACE) << __func__;
-    WebAssemblyViewport* that = reinterpret_cast<WebAssemblyViewport*>(userData);
 
-    if (that->compositor_.get() != NULL &&
-        that->controller_ /* should always be true */)
+    WebAssemblyViewport* that = 
+      WebAssemblyViewport::DereferenceObjectCookie(userData);
+    
+    if (that != NULL)
     {
-      that->animationFrameCallbackIds_.clear();
-      that->Paint(*that->compositor_, *that->controller_);
+      if (that->compositor_.get() != NULL &&
+          that->controller_ /* should always be true */)
+      {
+        that->Paint(*that->compositor_, *that->controller_);
+      }
+    } 
+    else
+    {
+      LOG(INFO) << "WebAssemblyViewport::OnRequestAnimationFrame " 
+        << "-- the WebAssemblyViewport is deleted and Paint will " 
+        << "not be called";
     }
-      
     LOG(TRACE) << "Exiting: " << __func__;
     return true;
   }
@@ -212,10 +220,48 @@
     return true;
   }
 
+  void* WebAssemblyViewport::GetObjectCookie()
+  {
+    if(objectCookie_ != NULL)
+      return objectCookie_;
+    
+    boost::shared_ptr<WebAssemblyViewport>* sharedFromThisPtr = 
+      new boost::shared_ptr<WebAssemblyViewport>();
+    
+    *sharedFromThisPtr = shared_from_this();
+
+    objectCookie_ = reinterpret_cast<void*>(sharedFromThisPtr);
+    
+    return objectCookie_;
+  }
+
+  void WebAssemblyViewport::ReleaseObjectCookie(void* cookie)
+  {
+    WebAssemblyViewport* This = DereferenceObjectCookie(cookie);
+    
+    if (This != NULL)
+      This->objectCookie_ = NULL;
+
+    boost::weak_ptr<WebAssemblyViewport>* weakThisPtr = 
+      reinterpret_cast<boost::weak_ptr<WebAssemblyViewport>*>(cookie);
+    
+    delete weakThisPtr;
+  }
+
+  WebAssemblyViewport* WebAssemblyViewport::DereferenceObjectCookie(void* cookie)
+  {
+    boost::weak_ptr<WebAssemblyViewport>* weakThisPtr = 
+      reinterpret_cast<boost::weak_ptr<WebAssemblyViewport>*>(cookie);
+
+    boost::shared_ptr<WebAssemblyViewport> sharedThis = weakThisPtr->lock();
+
+    return sharedThis.get();
+  }
+
   void WebAssemblyViewport::Invalidate()
   {
     long id = emscripten_request_animation_frame(OnRequestAnimationFrame, 
-                                                 reinterpret_cast<void*>(this));
+                                                 GetObjectCookie());
     animationFrameCallbackIds_.push_back(id);
   }
 
@@ -259,7 +305,8 @@
     interactor_(new DefaultViewportInteractor),
     enableEmscriptenMouseEvents_(enableEmscriptenMouseEvents),
     canvasWidth_(0),
-    canvasHeight_(0)
+    canvasHeight_(0),
+    objectCookie_(NULL)
   {
   }
 
--- a/Applications/Platforms/WebAssembly/WebAssemblyViewport.h	Mon Nov 02 17:56:49 2020 +0100
+++ b/Applications/Platforms/WebAssembly/WebAssemblyViewport.h	Mon Nov 02 20:45:04 2020 +0100
@@ -62,6 +62,7 @@
     unsigned int                          canvasWidth_;
     unsigned int                          canvasHeight_;
     std::vector<long>                     animationFrameCallbackIds_;
+    void*                                 objectCookie_;
 
     static EM_BOOL OnRequestAnimationFrame(double time, void *userData);
     
@@ -100,6 +101,45 @@
 
     void PostConstructor();
 
+
+    /**
+     * This method can be called to retrieve a cookie that can be passed to 
+     * C-style callbacks that expect the object to be passed as a void*
+     * 
+     * This cookie is a resource and must be freed when it is guaranteed 
+     * not to be used anymore, with ReleaseObjectCookie
+     */
+    void* GetObjectCookie();
+
+    /**
+     * This static method can be used to dereference a cookie (i.e. retrieve 
+     * a pointer to the underlying object) that has been created with 
+     * WebAssemblyViewport::GetObjectCookie()
+     * 
+     * If this method returns NULL, it basically means that the 
+     * WebAssemblyViewport has already been deleted and that you should NOT 
+     * attempt to use it!
+     * 
+     * This method never releases the cookie (for other in-flight callbacks
+     * could possibly require to cookie to be valid)
+     * 
+     * If this method is called AFTER ReleaseObjectCookie has been called on
+     * the same cookie, the behavior is undefined and things will CERTAINLY
+     * go wrong.
+     * 
+     * NEVER call this method on a void* that has not been generated by the
+     * GetObjectCookie method of this class
+     */
+    static WebAssemblyViewport* DereferenceObjectCookie(void* cookie);
+
+    /**
+     * This method must be used when the object cookie will not be used anymore.
+     * 
+     * You must call it when you are certain that no entity will attempt to 
+     * dereference the cookie.
+     */
+    static void ReleaseObjectCookie(void* cookie);
+
     void RefreshCanvasSize();
 
     unsigned int GetCanvasWidth() const