[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 11c6f22..67dec48 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 fe6b87a..d3daad0 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 f3385f4..2e18fb5 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 241eb74..0282235 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 c1c510a..9d04e24 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 3001091..7d6cb7a 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 0edda01..cf5e59a 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 60fb25e..427261d 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 616b8d0..516f899 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
 };