# HG changeset patch # User Benjamin Golinvaux # Date 1604346304 -3600 # Node ID 575f512cdf488013ecc6c3d611d8bf6a47d1d074 # Parent 1151e25d731187c55653089321195e780943dbbf Used a weak_ptr as a cookie to RequestAnimationFrame to prevent accessing a deleted viewport. diff -r 1151e25d7311 -r 575f512cdf48 Applications/Platforms/WebAssembly/WebAssemblyViewport.cpp --- 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(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* sharedFromThisPtr = + new boost::shared_ptr(); + + *sharedFromThisPtr = shared_from_this(); + + objectCookie_ = reinterpret_cast(sharedFromThisPtr); + + return objectCookie_; + } + + void WebAssemblyViewport::ReleaseObjectCookie(void* cookie) + { + WebAssemblyViewport* This = DereferenceObjectCookie(cookie); + + if (This != NULL) + This->objectCookie_ = NULL; + + boost::weak_ptr* weakThisPtr = + reinterpret_cast*>(cookie); + + delete weakThisPtr; + } + + WebAssemblyViewport* WebAssemblyViewport::DereferenceObjectCookie(void* cookie) + { + boost::weak_ptr* weakThisPtr = + reinterpret_cast*>(cookie); + + boost::shared_ptr sharedThis = weakThisPtr->lock(); + + return sharedThis.get(); + } + void WebAssemblyViewport::Invalidate() { long id = emscripten_request_animation_frame(OnRequestAnimationFrame, - reinterpret_cast(this)); + GetObjectCookie()); animationFrameCallbackIds_.push_back(id); } @@ -259,7 +305,8 @@ interactor_(new DefaultViewportInteractor), enableEmscriptenMouseEvents_(enableEmscriptenMouseEvents), canvasWidth_(0), - canvasHeight_(0) + canvasHeight_(0), + objectCookie_(NULL) { } diff -r 1151e25d7311 -r 575f512cdf48 Applications/Platforms/WebAssembly/WebAssemblyViewport.h --- 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 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