[Tizen](Vector) Finalize VectorThread if application terminated + Wait all lottie... 12/316512/2 accepted/tizen/7.0/unified/20240823.014549
authorEunki, Hong <eunkiki.hong@samsung.com>
Tue, 13 Aug 2024 04:35:51 +0000 (13:35 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Thu, 22 Aug 2024 09:50:26 +0000 (18:50 +0900)
Let we don't do any additional progress after application terminated.

Also, let we keep the VectorAnimationThread lifetime until all
working VectorAnimationTask are completed.

Since that task use VectorAnimationThread as reference, we cannot release
the memory of VectorAnimationManager memory. So just call finalize API.

Change-Id: I6f33c3f5863d8ad3ad9a08d45b134501582d87be
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-manager.cpp
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-manager.h
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.cpp
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.cpp
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.h
dali-toolkit/internal/visuals/visual-factory-cache.cpp
dali-toolkit/internal/visuals/visual-factory-cache.h
dali-toolkit/internal/visuals/visual-factory-impl.cpp
dali-toolkit/internal/visuals/visual-factory-impl.h

index 11c6f2237dce887fd9ad79204350738d6139d597..67dec487dd6bcb60764bb3ee77c6abe6f9005126 100644 (file)
@@ -43,22 +43,14 @@ VectorAnimationManager::VectorAnimationManager()
 : mEventCallbacks(),
   mLifecycleObservers(),
   mVectorAnimationThread(nullptr),
-  mProcessorRegistered(false)
+  mProcessorRegistered(false),
+  mDestroyed(false)
 {
 }
 
 VectorAnimationManager::~VectorAnimationManager()
 {
-  for(auto&& iter : mEventCallbacks)
-  {
-    delete iter;
-  }
-  mEventCallbacks.clear();
-
-  if(mProcessorRegistered && Adaptor::IsAvailable())
-  {
-    Adaptor::Get().UnregisterProcessor(*this, true);
-  }
+  Finalize();
 
   for(auto observer : mLifecycleObservers)
   {
@@ -93,12 +85,19 @@ VectorAnimationThread& VectorAnimationManager::GetVectorAnimationThread()
 
 void VectorAnimationManager::RegisterEventCallback(CallbackBase* callback)
 {
-  mEventCallbacks.push_back(callback);
+  if(DALI_LIKELY(!mDestroyed))
+  {
+    mEventCallbacks.push_back(callback);
 
-  if(!mProcessorRegistered)
+    if(!mProcessorRegistered && DALI_LIKELY(Adaptor::IsAvailable()))
+    {
+      Adaptor::Get().RegisterProcessor(*this, true); // Use post processor to trigger after layoutting
+      mProcessorRegistered = true;
+    }
+  }
+  else
   {
-    Adaptor::Get().RegisterProcessor(*this, true); // Use post processor to trigger after layoutting
-    mProcessorRegistered = true;
+    delete callback; // No longer needed.
   }
 }
 
@@ -123,14 +122,45 @@ void VectorAnimationManager::UnregisterEventCallback(CallbackBase* callback)
   }
 }
 
+void VectorAnimationManager::Finalize()
+{
+  if(DALI_LIKELY(!mDestroyed))
+  {
+    DALI_LOG_DEBUG_INFO("Finalizing Vector Animation Manager.\n");
+    mDestroyed = true;
+
+    for(auto&& iter : mEventCallbacks)
+    {
+      delete iter;
+    }
+
+    mEventCallbacks.clear();
+
+    if(mProcessorRegistered && Adaptor::IsAvailable())
+    {
+      Adaptor::Get().UnregisterProcessor(*this, true);
+      mProcessorRegistered = false;
+    }
+
+    if(mVectorAnimationThread)
+    {
+      mVectorAnimationThread->Finalize();
+    }
+  }
+}
+
 void VectorAnimationManager::Process(bool postProcessor)
 {
-  for(auto&& iter : mEventCallbacks)
+  if(DALI_LIKELY(!mDestroyed))
   {
-    CallbackBase::Execute(*iter);
-    delete iter;
+    for(auto&& iter : mEventCallbacks)
+    {
+      CallbackBase::Execute(*iter);
+      delete iter;
+    }
+
+    mEventCallbacks.clear();
   }
-  mEventCallbacks.clear();
 
   Adaptor::Get().UnregisterProcessor(*this, true);
   mProcessorRegistered = false;
index fe6b87a42007f87a382b4da71ea0489476540d43..d3daad058bb053359255893c214a270f8d90f169 100644 (file)
@@ -88,6 +88,11 @@ public:
    */
   void UnregisterEventCallback(CallbackBase* callback);
 
+  /**
+   * @brief Finalize the manager. This will stop the animation thread and clear all resources.
+   */
+  void Finalize();
+
 protected: // Implementation of Processor
   /**
    * @copydoc Dali::Integration::Processor::Process()
@@ -105,7 +110,8 @@ private:
   std::vector<CallbackBase*>             mEventCallbacks;
   std::vector<LifecycleObserver*>        mLifecycleObservers;
   std::unique_ptr<VectorAnimationThread> mVectorAnimationThread;
-  bool                                   mProcessorRegistered;
+  bool                                   mProcessorRegistered : 1;
+  bool                                   mDestroyed : 1;
 };
 
 } // namespace Internal
index f3385f401cbc7c3a983e2bc5f86104448f78287f..2e18fb5ef7d59fbed9408a308c4784a9b93959ea 100644 (file)
@@ -579,7 +579,6 @@ bool VectorAnimationTask::Rasterize(bool& keepAnimation)
   // Forcely trigger render once if need.
   if(mNotifyAfterRasterization || mNeedForceRenderOnceTrigger)
   {
-    Mutex::ScopedLock lock(mMutex);
     mVectorAnimationThread.RequestForceRenderOnce();
     mNeedForceRenderOnceTrigger = false;
   }
index 241eb7443dde0dbb1052fd42b12648be8d90b1ad..02822355a65aec3385129e8bb3bdd16bfa68a3f5 100644 (file)
@@ -23,6 +23,7 @@
 #include <dali/devel-api/adaptor-framework/thread-settings.h>
 #include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <dali/integration-api/debug.h>
+#include <dali/integration-api/trace.h>
 #include <thread>
 
 namespace Dali
@@ -79,15 +80,7 @@ VectorAnimationThread::~VectorAnimationThread()
   // Stop the thread
   {
     ConditionalWait::ScopedLock lock(mConditionalWait);
-    // Wait until some event thread trigger relative job finished.
-    {
-      Mutex::ScopedLock eventTriggerLock(mEventTriggerMutex);
-      Mutex::ScopedLock taskCompletedLock(mTaskCompletedMutex);
-      Mutex::ScopedLock animationTasksLock(mAnimationTasksMutex);
-      DALI_LOG_DEBUG_INFO("Mark VectorAnimationThread destroyed\n");
-      mDestroyThread = true;
-    }
-    mNeedToSleep = false;
+    Finalize();
     mConditionalWait.Notify(lock);
   }
 
@@ -102,6 +95,41 @@ VectorAnimationThread::~VectorAnimationThread()
   DALI_LOG_DEBUG_INFO("VectorAnimationThread Join request\n");
 
   Join();
+
+  // We need to wait all working tasks are completed before destructing this thread.
+  while(mWorkingTasks.size() > 0)
+  {
+    DALI_LOG_DEBUG_INFO("Still waiting WorkingTasks [%zu]\n", mWorkingTasks.size());
+    ConditionalWait::ScopedLock lock(mConditionalWait);
+
+    // ConditionalWait notifyed when task complete.
+    // If task complete, then remove working tasks list and then wait again, until all working tasks are completed.
+    decltype(mCompletedTasksQueue) completedTasksQueue;
+    {
+      Mutex::ScopedLock taskCompletedLock(mTaskCompletedMutex);
+      completedTasksQueue.swap(mCompletedTasksQueue);
+    }
+    if(completedTasksQueue.empty())
+    {
+      mConditionalWait.Wait(lock);
+      // Task completed may have been added to the queue while we were waiting.
+      {
+        Mutex::ScopedLock taskCompletedLock(mTaskCompletedMutex);
+        completedTasksQueue.swap(mCompletedTasksQueue);
+      }
+    }
+
+    DALI_LOG_DEBUG_INFO("Completed task queue [%zu]\n", completedTasksQueue.size());
+
+    for(auto& taskPair : completedTasksQueue)
+    {
+      auto workingIter = std::find(mWorkingTasks.begin(), mWorkingTasks.end(), taskPair.first);
+      if(workingIter != mWorkingTasks.end())
+      {
+        mWorkingTasks.erase(workingIter);
+      }
+    }
+  }
 }
 
 /// Event thread called
@@ -125,14 +153,13 @@ void VectorAnimationThread::OnTaskCompleted(VectorAnimationTaskPtr task, bool su
 
   Mutex::ScopedLock taskCompletedLock(mTaskCompletedMutex);
 
-  if(DALI_LIKELY(!mDestroyThread))
-  {
-    mCompletedTasksQueue.emplace_back(task, success && keepAnimation);
+  // DevNote : We need to add task queue, and notify even if mDestroyThread is true.
+  // Since we should make ensure that all working tasks are completed before destroying the thread.
+  mCompletedTasksQueue.emplace_back(task, success && keepAnimation);
 
-    // wake up the animation thread.
-    // Note that we should not make mNeedToSleep as false now.
-    mConditionalWait.Notify(lock);
-  }
+  // wake up the animation thread.
+  // Note that we should not make mNeedToSleep as false now.
+  mConditionalWait.Notify(lock);
 }
 
 /// VectorAnimationThread::SleepThread called, Mutex SleepThread::mAwakeCallbackMutex is locked
@@ -191,13 +218,28 @@ void VectorAnimationThread::RequestForceRenderOnce()
   }
 }
 
+/// Event thread called
+void VectorAnimationThread::Finalize()
+{
+  Mutex::ScopedLock eventTriggerLock(mEventTriggerMutex);
+  Mutex::ScopedLock taskCompletedLock(mTaskCompletedMutex);
+  Mutex::ScopedLock animationTasksLock(mAnimationTasksMutex);
+  // Wait until some event thread trigger, and tasks relative job finished.
+  if(DALI_LIKELY(!mDestroyThread))
+  {
+    DALI_LOG_DEBUG_INFO("Mark VectorAnimationThread destroyed\n");
+    mDestroyThread = true;
+  }
+  mNeedToSleep = false;
+}
+
 /// VectorAnimationThread called
 void VectorAnimationThread::Run()
 {
   SetThreadName("VectorAnimationThread");
   mLogFactory.InstallLogFunction();
 
-  while(!mDestroyThread)
+  while(DALI_LIKELY(!mDestroyThread))
   {
     Rasterize();
   }
@@ -248,16 +290,18 @@ bool VectorAnimationThread::MoveTasksToAnimation(VectorAnimationTaskPtr task, bo
 /// VectorAnimationThread called
 void VectorAnimationThread::MoveTasksToCompleted(VectorAnimationTaskPtr task, bool keepAnimation)
 {
-  if(DALI_LIKELY(!mDestroyThread))
-  {
-    bool needRasterize = false;
+  // DevNote : We need to consume task queue, and notify even if mDestroyThread is true.
+  // Since we should make ensure that all working tasks are completed before destroying the thread.
+  bool needRasterize = false;
 
-    auto workingTask = std::find(mWorkingTasks.begin(), mWorkingTasks.end(), task);
-    if(workingTask != mWorkingTasks.end())
-    {
-      mWorkingTasks.erase(workingTask);
-    }
+  auto workingIter = std::find(mWorkingTasks.begin(), mWorkingTasks.end(), task);
+  if(workingIter != mWorkingTasks.end())
+  {
+    mWorkingTasks.erase(workingIter);
+  }
 
+  if(DALI_LIKELY(!mDestroyThread))
+  {
     // Check pending task
     {
       Mutex::ScopedLock animationTasksLock(mAnimationTasksMutex);
index c1c510a354d063dd6c018a624b1bf8363f4ac53a..9d04e24bb74936e116725f840014153915b5e810 100644 (file)
@@ -95,6 +95,11 @@ public:
    */
   void RequestForceRenderOnce();
 
+  /**
+   * @brief Finalize the thread.
+   */
+  void Finalize();
+
 protected:
   /**
    * @brief The entry function of the animation thread.
index 30010917896737a90e62e8271feb7bf95da639d6..7d6cb7a40064f2997b69726585447c0f1cffc25a 100644 (file)
@@ -148,6 +148,14 @@ VectorAnimationManager& VisualFactoryCache::GetVectorAnimationManager()
   return *mVectorAnimationManager;
 }
 
+void VisualFactoryCache::FinalizeVectorAnimationManager()
+{
+  if(mVectorAnimationManager)
+  {
+    mVectorAnimationManager->Finalize();
+  }
+}
+
 Geometry VisualFactoryCache::CreateGridGeometry(Uint16Pair gridSize)
 {
   uint16_t gridWidth  = gridSize.GetWidth();
index 0edda01191e0765b6934f78cb8323570077ded28..cf5e59a81d2109ac1784c655d3961d8564936554 100644 (file)
@@ -256,6 +256,12 @@ public:
    */
   VectorAnimationManager& GetVectorAnimationManager();
 
+  /**
+   * @brief Finalize vector animation manager.
+   * It will be called when application is terminated.
+   */
+  void FinalizeVectorAnimationManager();
+
 protected:
   /**
    * Undefined copy constructor.
index 60fb25efca31489f74980d7dccc72cffb9d3df7a..427261debf939d12b258f12c44dbc1d7f2c92eb9 100644 (file)
@@ -19,6 +19,7 @@
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/scripting/scripting.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <dali/integration-api/debug.h>
 #include <dali/public-api/object/property-array.h>
 #include <dali/public-api/object/type-registry-helper.h>
@@ -80,13 +81,22 @@ VisualFactory::VisualFactory(bool debugEnabled)
   mImageVisualShaderFactory(),
   mTextVisualShaderFactory(),
   mSlotDelegate(this),
+  mLifecycleController(Dali::LifecycleController::Get()),
   mDebugEnabled(debugEnabled),
   mPreMultiplyOnLoad(true)
 {
+  if(DALI_LIKELY(mLifecycleController))
+  {
+    mLifecycleController.TerminateSignal().Connect(this, &VisualFactory::OnApplicationTerminated);
+  }
 }
 
 VisualFactory::~VisualFactory()
 {
+  if(DALI_LIKELY(mLifecycleController))
+  {
+    mLifecycleController.TerminateSignal().Disconnect(this, &VisualFactory::OnApplicationTerminated);
+  }
 }
 
 void VisualFactory::OnStyleChangedSignal(Toolkit::StyleManager styleManager, StyleChange::Type type)
@@ -437,6 +447,16 @@ TextVisualShaderFactory& VisualFactory::GetTextVisualShaderFactory()
   return *mTextVisualShaderFactory;
 }
 
+void VisualFactory::OnApplicationTerminated()
+{
+  if(mFactoryCache)
+  {
+    mFactoryCache->FinalizeVectorAnimationManager();
+  }
+  // We don't need to keep it anymore
+  mLifecycleController.Reset();
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
index 616b8d04a9ea4743dd3ebe1564043d91e79274be..516f899edce6636b060f4df7e698700f7929fa7e 100644 (file)
@@ -19,6 +19,7 @@
 
 // EXTERNAL INCLUDES
 #include <dali/public-api/object/base-object.h>
+#include <dali/devel-api/adaptor-framework/lifecycle-controller.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/styling/style-manager-devel.h>
@@ -40,7 +41,7 @@ class TextVisualShaderFactory;
 /**
  * @copydoc Toolkit::VisualFactory
  */
-class VisualFactory : public BaseObject
+class VisualFactory : public BaseObject, public ConnectionTracker
 {
 public:
   /**
@@ -118,6 +119,11 @@ private:
    */
   TextVisualShaderFactory& GetTextVisualShaderFactory();
 
+  /**
+   * @brief Callbacks called when application is terminated.
+   */
+  void OnApplicationTerminated();
+
   VisualFactory(const VisualFactory&) = delete;
 
   VisualFactory& operator=(const VisualFactory& rhs) = delete;
@@ -127,6 +133,7 @@ private:
   std::unique_ptr<ImageVisualShaderFactory> mImageVisualShaderFactory;
   std::unique_ptr<TextVisualShaderFactory>  mTextVisualShaderFactory;
   SlotDelegate<VisualFactory>               mSlotDelegate;
+  Dali::LifecycleController                 mLifecycleController;
   bool                                      mDebugEnabled : 1;
   bool                                      mPreMultiplyOnLoad : 1; ///< Local store for this flag
 };