From 3b6d6a1f56e03277bb3a06e6e67148208cb3e0c1 Mon Sep 17 00:00:00 2001 From: Marc Rousavy Date: Wed, 11 Feb 2026 18:51:58 +0100 Subject: [PATCH 1/2] fix: Fix `dispose()` stalling because of `Dispatcher` --- packages/skia/cpp/api/JsiSkImage.h | 21 --------------------- packages/skia/cpp/api/JsiSkPicture.h | 21 --------------------- packages/skia/cpp/api/JsiSkSurface.h | 21 --------------------- 3 files changed, 63 deletions(-) diff --git a/packages/skia/cpp/api/JsiSkImage.h b/packages/skia/cpp/api/JsiSkImage.h index 437b54e298..3069e89609 100644 --- a/packages/skia/cpp/api/JsiSkImage.h +++ b/packages/skia/cpp/api/JsiSkImage.h @@ -4,7 +4,6 @@ #include #include -#include "JsiSkDispatcher.h" #include "JsiSkHostObjects.h" #include "JsiSkImageInfo.h" #include "JsiSkMatrix.h" @@ -64,9 +63,6 @@ inline SkSamplingOptions SamplingOptionsFromValue(jsi::Runtime &runtime, } class JsiSkImage : public JsiSkWrappingSkPtrHostObject { -private: - std::shared_ptr _dispatcher; - public: // TODO-API: Properties? JSI_HOST_FUNCTION(width) { return static_cast(getObject()->width()); } @@ -287,23 +283,6 @@ class JsiSkImage : public JsiSkWrappingSkPtrHostObject { const sk_sp image) : JsiSkWrappingSkPtrHostObject(std::move(context), std::move(image)) { - // Get the dispatcher for the current thread - _dispatcher = Dispatcher::getDispatcher(); - // Process any pending operations - _dispatcher->processQueue(); - } - -protected: - void releaseResources() override { - // Queue deletion on the creation thread if needed - auto image = getObjectUnchecked(); - if (image && _dispatcher) { - _dispatcher->run([image]() { - // Image will be deleted when this lambda is destroyed - }); - } - // Clear the object - JsiSkWrappingSkPtrHostObject::releaseResources(); } public: diff --git a/packages/skia/cpp/api/JsiSkPicture.h b/packages/skia/cpp/api/JsiSkPicture.h index 168df1fc2e..62938cc80e 100644 --- a/packages/skia/cpp/api/JsiSkPicture.h +++ b/packages/skia/cpp/api/JsiSkPicture.h @@ -3,7 +3,6 @@ #include #include "JsiSkData.h" -#include "JsiSkDispatcher.h" #include "JsiSkHostObjects.h" #include "JsiSkMatrix.h" #include "JsiSkRect.h" @@ -21,30 +20,10 @@ namespace RNSkia { namespace jsi = facebook::jsi; class JsiSkPicture : public JsiSkWrappingSkPtrHostObject { -private: - std::shared_ptr _dispatcher; - public: JsiSkPicture(std::shared_ptr context, const sk_sp picture) : JsiSkWrappingSkPtrHostObject(context, picture) { - // Get the dispatcher for the current thread - _dispatcher = Dispatcher::getDispatcher(); - // Process any pending operations - _dispatcher->processQueue(); - } - -protected: - void releaseResources() override { - // Queue deletion on the creation thread if needed - auto picture = getObjectUnchecked(); - if (picture && _dispatcher) { - _dispatcher->run([picture]() { - // Picture will be deleted when this lambda is destroyed - }); - } - // Clear the object - JsiSkWrappingSkPtrHostObject::releaseResources(); } public: diff --git a/packages/skia/cpp/api/JsiSkSurface.h b/packages/skia/cpp/api/JsiSkSurface.h index 96defe0280..59a97c329c 100644 --- a/packages/skia/cpp/api/JsiSkSurface.h +++ b/packages/skia/cpp/api/JsiSkSurface.h @@ -7,7 +7,6 @@ #include -#include "JsiSkDispatcher.h" #include "JsiSkHostObjects.h" #include "JsiTextureInfo.h" @@ -31,31 +30,11 @@ namespace RNSkia { namespace jsi = facebook::jsi; class JsiSkSurface : public JsiSkWrappingSkPtrHostObject { -private: - std::shared_ptr _dispatcher; - public: JsiSkSurface(std::shared_ptr context, sk_sp surface) : JsiSkWrappingSkPtrHostObject(std::move(context), std::move(surface)) { - // Get the dispatcher for the current thread - _dispatcher = Dispatcher::getDispatcher(); - // Process any pending operations - _dispatcher->processQueue(); - } - -protected: - void releaseResources() override { - // Queue deletion on the creation thread if needed - auto surface = getObjectUnchecked(); - if (surface && _dispatcher) { - _dispatcher->run([surface]() { - // Surface will be deleted when this lambda is destroyed - }); - } - // Clear the object - JsiSkWrappingSkPtrHostObject::releaseResources(); } public: From 9eee995678414808b65df377f8bb5ce0e89fc874 Mon Sep 17 00:00:00 2001 From: Marc Rousavy Date: Wed, 11 Feb 2026 19:24:01 +0100 Subject: [PATCH 2/2] delete `JsiSkDispatcher` --- packages/skia/android/CMakeLists.txt | 1 - packages/skia/cpp/api/JsiSkDispatcher.cpp | 9 -- packages/skia/cpp/api/JsiSkDispatcher.h | 149 ---------------------- 3 files changed, 159 deletions(-) delete mode 100644 packages/skia/cpp/api/JsiSkDispatcher.cpp delete mode 100644 packages/skia/cpp/api/JsiSkDispatcher.h diff --git a/packages/skia/android/CMakeLists.txt b/packages/skia/android/CMakeLists.txt index d72f0cb5b0..759a2fb213 100644 --- a/packages/skia/android/CMakeLists.txt +++ b/packages/skia/android/CMakeLists.txt @@ -148,7 +148,6 @@ add_library( "${PROJECT_SOURCE_DIR}/../cpp/api/third_party/base64.cpp" "${PROJECT_SOURCE_DIR}/../cpp/api/third_party/SkottieUtils.cpp" - "${PROJECT_SOURCE_DIR}/../cpp/api/JsiSkDispatcher.cpp" ${BACKEND_SOURCES} ) diff --git a/packages/skia/cpp/api/JsiSkDispatcher.cpp b/packages/skia/cpp/api/JsiSkDispatcher.cpp deleted file mode 100644 index 47b83595cd..0000000000 --- a/packages/skia/cpp/api/JsiSkDispatcher.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "JsiSkDispatcher.h" - -namespace RNSkia { - -// Thread-local storage definition -thread_local std::shared_ptr - Dispatcher::_threadDispatcher; - -} // namespace RNSkia \ No newline at end of file diff --git a/packages/skia/cpp/api/JsiSkDispatcher.h b/packages/skia/cpp/api/JsiSkDispatcher.h deleted file mode 100644 index bfecd22310..0000000000 --- a/packages/skia/cpp/api/JsiSkDispatcher.h +++ /dev/null @@ -1,149 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace RNSkia { - -/** - * Thread-local dispatcher for managing deferred operations. - * Each thread gets its own dispatcher instance for queueing operations - * to be executed on that specific thread. - */ -class Dispatcher { -private: - using Operation = std::function; - - struct DispatcherData { - std::queue operationQueue; - std::mutex queueMutex; - }; - - // Thread-local storage for dispatcher data - static thread_local std::shared_ptr _threadDispatcher; - - // Global registry of all dispatchers by thread ID - static inline std::mutex _registryMutex; - static inline std::unordered_map> - _dispatcherRegistry; - - std::shared_ptr _data; - std::thread::id _threadId; - -public: - Dispatcher() : _threadId(std::this_thread::get_id()) { - // Get or create dispatcher data for current thread - if (!_threadDispatcher) { - _threadDispatcher = std::make_shared(); - - // Register in global registry - std::lock_guard lock(_registryMutex); - _dispatcherRegistry[_threadId] = _threadDispatcher; - } - _data = _threadDispatcher; - } - - /** - * Get the dispatcher for the current thread. - * Creates one if it doesn't exist. - */ - static std::shared_ptr getDispatcher() { - return std::make_shared(); - } - - /** - * Get the dispatcher for a specific thread. - * Returns nullptr if that thread doesn't have a dispatcher. - */ - static std::shared_ptr getDispatcher(std::thread::id threadId) { - std::lock_guard lock(_registryMutex); - auto it = _dispatcherRegistry.find(threadId); - if (it != _dispatcherRegistry.end()) { - if (auto data = it->second.lock()) { - auto dispatcher = std::make_shared(); - dispatcher->_data = data; - dispatcher->_threadId = threadId; - return dispatcher; - } - } - return nullptr; - } - - /** - * Queue an operation to be executed on the dispatcher's thread. - * The operation will be executed when processQueue() is called on that - * thread. - */ - void run(Operation op) { - if (!_data) - return; - - std::lock_guard lock(_data->queueMutex); - _data->operationQueue.push(std::move(op)); - } - - /** - * Process all pending operations for the current thread. - * Must be called from the thread that owns this dispatcher. - * Returns the number of operations processed. - */ - size_t processQueue() { - if (!_data) - return 0; - - // Only process if we're on the correct thread - if (std::this_thread::get_id() != _threadId) { - return 0; - } - - std::queue operations; - { - std::lock_guard lock(_data->queueMutex); - operations.swap(_data->operationQueue); - } - - size_t count = operations.size(); - while (!operations.empty()) { - auto &op = operations.front(); - op(); - operations.pop(); - } - - return count; - } - - /** - * Get the number of pending operations. - */ - size_t getPendingCount() const { - if (!_data) - return 0; - - std::lock_guard lock(_data->queueMutex); - return _data->operationQueue.size(); - } - - /** - * Clean up dispatcher for a thread that's shutting down. - */ - static void cleanup() { - if (_threadDispatcher) { - // Process any remaining operations - auto dispatcher = getDispatcher(); - dispatcher->processQueue(); - - // Remove from registry - std::lock_guard lock(_registryMutex); - _dispatcherRegistry.erase(std::this_thread::get_id()); - - _threadDispatcher.reset(); - } - } -}; - -} // namespace RNSkia \ No newline at end of file