From: dongsug.song Date: Mon, 6 Nov 2023 10:09:31 +0000 (+0900) Subject: [Tizen] Add TasksCompleted signal at AsyncTaskManager X-Git-Tag: accepted/tizen/unified/20231107.172904~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=13b4983b3bb1b43dfdecc48e7fd6d262564cb3b3;p=platform%2Fcore%2Fuifw%2Fdali-adaptor.git [Tizen] Add TasksCompleted signal at AsyncTaskManager This reverts commit e25a5c002505e218250b16ae6c6f843a2e21d48b. Change-Id: Ieee91d4b33f464d03370c7fd6a28071d268dcfdf --- diff --git a/dali/internal/system/common/async-task-manager-impl.cpp b/dali/internal/system/common/async-task-manager-impl.cpp index 9fcf9b4..dabb4ae 100644 --- a/dali/internal/system/common/async-task-manager-impl.cpp +++ b/dali/internal/system/common/async-task-manager-impl.cpp @@ -161,6 +161,335 @@ void AsyncTaskThread::Run() } } +// AsyncTaskManager::TasksCompletedImpl + +struct AsyncTaskManager::TasksCompletedImpl +{ + TasksCompletedImpl(AsyncTaskManager& manager) + : mManager(manager), + mEmitCompletedTaskRegistered(false) + { + } + +public: + /** + * @brief Create new tasks completed id and. + * @post AppendTaskTrace or CheckTasksCompletedCallbackCompleted should be called. + * @param[in] callback The callback that want to be executed when we notify that all tasks completed. + */ + Dali::AsyncTaskManager::TasksCompletedId GenerateTasksCompletedId(CallbackBase* callback) + { + // Lock while adding tasks completed callback list to the queue + Mutex::ScopedLock lock(mTasksCompletedCallbacksMutex); + + auto id = mTasksCompletedCount++; + DALI_ASSERT_ALWAYS(mTasksCompletedCallbackList.find(id) == mTasksCompletedCallbackList.end()); + + mTasksCompletedCallbackList.insert({id, CallbackData(callback)}); + + DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "GenerateTasksCompletedId id[%u] callback[%p]\n", id, callback); + return id; + } + + /** + * @brief Append task that will be trace. + * @post RemoveTaskTrace should be called. + * @param[in] id The id of tasks completed. + * @param[in] task The task want to trace. + */ + void AppendTaskTrace(Dali::AsyncTaskManager::TasksCompletedId id, AsyncTaskPtr task) + { + DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "AppendTaskTrace id[%u] task[%p]\n", id, task.Get()); + + // Lock while adding tasks completed callback list to the queue + Mutex::ScopedLock lock(mTasksCompletedCallbacksMutex); + + auto iter = mTasksCompletedCallbackList.find(id); + if(iter == mTasksCompletedCallbackList.end()) + { + // This task is already erased. Ignore. + return; + } + + auto& callbackData = iter->second; + + auto jter = callbackData.mTasks.find(task.Get()); + + if(jter != callbackData.mTasks.end()) + { + // Increase reference count. + ++(jter->second); + } + else + { + callbackData.mTasks.insert({task.Get(), 1u}); + } + } + + /** + * @brief Remove all task that were traced. + * @param[in] task The task want to remove trace. + * @param[in] taskCount The number of tasks that will be removed. + */ + void RemoveTaskTrace(AsyncTaskPtr task, uint32_t count = 1u) + { + if(count == 0u) + { + return; + } + DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "RemoveTaskTrace task[%p] remove count[%u]\n", task.Get(), count); + + // Lock while removing tasks completed callback list to the queue + Mutex::ScopedLock lock(mTasksCompletedCallbacksMutex); + + for(auto iter = mTasksCompletedCallbackList.begin(); iter != mTasksCompletedCallbackList.end();) + { + auto& callbackData = iter->second; + bool eraseCallbackData = false; + + auto jter = callbackData.mTasks.find(task.Get()); + + if(jter != callbackData.mTasks.end()) + { + DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "RemoveTaskTrace id[%u] task[%p], current refcount[%u]\n", iter->first, task.Get(), (jter->second)); + + if(jter->second <= count) + { + callbackData.mTasks.erase(jter); + + DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "RemoveTaskTrace id[%u] task erased. remained tasks[%zu]", iter->first, callbackData.mTasks.size()); + + if(callbackData.mTasks.empty()) + { + eraseCallbackData = true; + + // Move callback base into list. + // (To avoid task container changed during callback emit) + RegisterTasksCompletedCallback(std::move(callbackData.mCallback), iter->first); + + DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "id[%u] completed!\n", iter->first); + + iter = mTasksCompletedCallbackList.erase(iter); + } + } + else + { + jter->second -= count; + } + } + + if(!eraseCallbackData) + { + ++iter; + } + } + } + + /** + * @brief Check whether current TasksCompletedId completed or not. + * @param[in] id The id of tasks completed. + * @return True if all tasks are completed so we need to execute callback soon. False otherwise. + */ + bool CheckTasksCompletedCallbackCompleted(Dali::AsyncTaskManager::TasksCompletedId id) + { + DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "CheckTasksCompletedCallbackCompleted[%u]\n", id); + + // Lock while removing tasks completed callback list to the queue + Mutex::ScopedLock lock(mTasksCompletedCallbacksMutex); + + auto iter = mTasksCompletedCallbackList.find(id); + if(iter != mTasksCompletedCallbackList.end()) + { + auto& callbackData = iter->second; + if(callbackData.mTasks.empty()) + { + // Move callback base into list. + // (To avoid task container changed during callback emit) + RegisterTasksCompletedCallback(std::move(callbackData.mCallback), iter->first); + + DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "id[%u] completed!\n", iter->first); + + iter = mTasksCompletedCallbackList.erase(iter); + + return true; + } + } + + return false; + } + + /** + * @brief Remove taskS completed callbacks by id. + * @param[in] id The id of taskS completed. + * @return True if taskS completed id removed. False otherwise. + */ + bool RemoveTasksCompleted(Dali::AsyncTaskManager::TasksCompletedId id) + { + DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "RemoveTasksCompleted[%u]\n", id); + + // Lock while removing taskS completed callback list to the queue + Mutex::ScopedLock lock(mTasksCompletedCallbacksMutex); + + auto iter = mTasksCompletedCallbackList.find(id); + if(iter == mTasksCompletedCallbackList.end()) + { + // This task is already erased, or completed. + // Erase from completed excute callback list. + + // Lock while removing excute callback list to the queue + Mutex::ScopedLock lock(mExcuteCallbacksMutex); + + for(auto iter = mExcuteCallbackList.begin(); iter != mExcuteCallbackList.end();) + { + if(iter->second == id) + { + iter = mExcuteCallbackList.erase(iter); + + return true; + } + else + { + ++iter; + } + } + + // This task is alread erased and completed. Ignore. + return false; + } + + mTasksCompletedCallbackList.erase(iter); + + return true; + } + + /** + * @brief Emit all completed callbacks. + */ + void EmitCompletedTasks() + { + ExecuteCallbackContainer executeCallbackList; + { + // Lock while removing excute callback list to the queue + Mutex::ScopedLock lock(mExcuteCallbacksMutex); + + mEmitCompletedTaskRegistered = false; + + // Copy callback lists, for let we execute callbacks out of mutex + executeCallbackList = std::move(mExcuteCallbackList); + mExcuteCallbackList.clear(); + } + + if(!executeCallbackList.empty()) + { + DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "Excute callback count[%zu]\n", executeCallbackList.size()); + // Execute all callbacks + for(auto&& callbackPair : executeCallbackList) + { + auto& callback = callbackPair.first; + auto id = callbackPair.second; + + DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "Excute taskS completed callback[%p] for id[%u]\n", callback.get(), id); + + Dali::CallbackBase::Execute(*callback, id); + } + + DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "Excute callback end\n"); + } + } + + /** + * @brief Check whether there is some completed signal what we need to trace, or not. + * @return True if mTasksCompletedCallbackList is not empty. False otherwise. + */ + bool IsTasksCompletedCallbackExist() + { + Mutex::ScopedLock lock(mTasksCompletedCallbacksMutex); + return !mTasksCompletedCallbackList.empty(); + } + + /** + * @brief Check whether there is some completed signal what we need to execute, or not. + * @return True if mExcuteCallbackList is not empty. False otherwise. + */ + bool IsExecuteCallbackExist() + { + Mutex::ScopedLock lock(mExcuteCallbacksMutex); + return !mExcuteCallbackList.empty(); + } + +private: + void RegisterTasksCompletedCallback(std::unique_ptr callback, Dali::AsyncTaskManager::TasksCompletedId id) + { + DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "TasksCompleted[%u] need to be execute with callback[%p]\n", id, callback.get()); + + // Lock while adding excute callback list to the queue + Mutex::ScopedLock lock(mExcuteCallbacksMutex); + + mExcuteCallbackList.emplace_back(std::move(callback), id); + + if(!mEmitCompletedTaskRegistered) + { + mEmitCompletedTaskRegistered = true; + + DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "Register processor\n"); + mManager.RegisterProcessor(); + } + } + +private: + struct CallbackData + { + public: + CallbackData(CallbackBase* callback) + : mCallback(callback), + mTasks() + { + } + + CallbackData(CallbackData&& rhs) noexcept + : mCallback(std::move(rhs.mCallback)), + mTasks(std::move(rhs.mTasks)) + { + } + + CallbackData& operator=(CallbackData&& rhs) noexcept + { + if(this != &rhs) + { + mCallback = std::move(rhs.mCallback); + mTasks = std::move(rhs.mTasks); + } + + return *this; + } + + private: + // Delete copy operator. + CallbackData(const CallbackData& rhs) = delete; + CallbackData& operator=(const CallbackData& rhs) = delete; + + public: + std::unique_ptr mCallback; + std::unordered_map mTasks; + }; + +private: + AsyncTaskManager& mManager; ///< Owner of this CacheImpl. + + Dali::AsyncTaskManager::TasksCompletedId mTasksCompletedCount{0u}; + + using TasksCompletedContainer = std::unordered_map; + TasksCompletedContainer mTasksCompletedCallbackList; + + using ExecuteCallbackContainer = std::vector, Dali::AsyncTaskManager::TasksCompletedId>>; + ExecuteCallbackContainer mExcuteCallbackList; + + Dali::Mutex mTasksCompletedCallbacksMutex; ///< Mutex for mTasksCompletedCallbackList. We can lock mExcuteCallbacksMutex under this scope. + Dali::Mutex mExcuteCallbacksMutex; ///< Mutex for mExcuteCallbackList. + + bool mEmitCompletedTaskRegistered : 1; +}; + // AsyncTaskManager::CacheImpl struct AsyncTaskManager::CacheImpl @@ -266,6 +595,7 @@ AsyncTaskManager::AsyncTaskManager() : mTasks(GetNumberOfThreads(NUMBER_OF_ASYNC_THREADS_ENV, DEFAULT_NUMBER_OF_ASYNC_THREADS), [&]() { return TaskHelper(*this); }), mAvaliableLowPriorityTaskCounts(GetNumberOfLowPriorityThreads(NUMBER_OF_LOW_PRIORITY_THREADS_ENV, DEFAULT_NUMBER_OF_LOW_PRIORITY_THREADS, mTasks.GetElementCount())), mWaitingHighProirityTaskCounts(0u), + mTasksCompletedImpl(new TasksCompletedImpl(*this)), mCacheImpl(new CacheImpl(*this)), mTrigger(new EventThreadCallback(MakeCallback(this, &AsyncTaskManager::TasksCompleted))), mProcessorRegistered(false) @@ -337,11 +667,7 @@ void AsyncTaskManager::AddTask(AsyncTaskPtr task) } // Register Process (Since mTrigger execute too late timing if event thread running a lots of events.) - if(!mProcessorRegistered && Dali::Adaptor::IsAvailable()) - { - Dali::Adaptor::Get().RegisterProcessor(*this); - mProcessorRegistered = true; - } + RegisterProcessor(); return; } @@ -356,6 +682,8 @@ void AsyncTaskManager::RemoveTask(AsyncTaskPtr task) // If there is some non-empty queue exist, we don't need to unregister processor. bool needCheckUnregisterProcessor = true; + uint32_t removedCount = 0u; + { // Lock while remove task from the queue Mutex::ScopedLock lock(mWaitingTasksMutex); @@ -372,6 +700,7 @@ void AsyncTaskManager::RemoveTask(AsyncTaskPtr task) --mWaitingHighProirityTaskCounts; } mWaitingTasks.erase(iterator); + ++removedCount; } CacheImpl::EraseAllTaskCache(mCacheImpl->mWaitingTasksCache, task); } @@ -395,6 +724,7 @@ void AsyncTaskManager::RemoveTask(AsyncTaskPtr task) // We cannot erase container. Just mark as canceled. // Note : mAvaliableLowPriorityTaskCounts will be increased after process finished. (*iterator).second = RunningTaskState::CANCELED; + ++removedCount; } } @@ -415,6 +745,7 @@ void AsyncTaskManager::RemoveTask(AsyncTaskPtr task) { DALI_ASSERT_DEBUG(iterator->first == task); mCompletedTasks.erase(iterator); + ++removedCount; } CacheImpl::EraseAllTaskCache(mCacheImpl->mCompletedTasksCache, task); } @@ -425,6 +756,12 @@ void AsyncTaskManager::RemoveTask(AsyncTaskPtr task) } } + // Remove TasksCompleted callback trace + if(removedCount > 0u) + { + mTasksCompletedImpl->RemoveTaskTrace(task, removedCount); + } + // UnregisterProcessor required to lock mutex. Call this API only if required. if(needCheckUnregisterProcessor) { @@ -433,6 +770,82 @@ void AsyncTaskManager::RemoveTask(AsyncTaskPtr task) } } +Dali::AsyncTaskManager::TasksCompletedId AsyncTaskManager::SetCompletedCallback(CallbackBase* callback, Dali::AsyncTaskManager::CompletedCallbackTraceMask mask) +{ + // mTasksCompletedImpl will take ownership of callback. + Dali::AsyncTaskManager::TasksCompletedId tasksCompletedId = mTasksCompletedImpl->GenerateTasksCompletedId(callback); + + bool taskAdded = false; ///< Flag whether at least one task tracing now. + + DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "SetCompletedCallback id : %u, mask : %d\n", tasksCompletedId, static_cast(mask)); + + // Please be careful the order of mutex, to avoid dead lock. + { + Mutex::ScopedLock lockWait(mWaitingTasksMutex); + { + Mutex::ScopedLock lockRunning(mRunningTasksMutex); // We can lock this mutex under mWaitingTasksMutex. + { + Mutex::ScopedLock lockComplete(mCompletedTasksMutex); // We can lock this mutex under mWaitingTasksMutex and mRunningTasksMutex. + + // Collect all tasks from waiting tasks + for(auto& task : mWaitingTasks) + { + auto checkMask = (task->GetCallbackInvocationThread() == Dali::AsyncTask::ThreadType::MAIN_THREAD ? Dali::AsyncTaskManager::CompletedCallbackTraceMask::THREAD_MASK_MAIN : Dali::AsyncTaskManager::CompletedCallbackTraceMask::THREAD_MASK_WORKER) | + (task->GetPriorityType() == Dali::AsyncTask::PriorityType::HIGH ? Dali::AsyncTaskManager::CompletedCallbackTraceMask::PRIORITY_MASK_HIGH : Dali::AsyncTaskManager::CompletedCallbackTraceMask::PRIORITY_MASK_LOW); + + if((checkMask & mask) == checkMask) + { + taskAdded = true; + mTasksCompletedImpl->AppendTaskTrace(tasksCompletedId, task); + } + } + + // Collect all tasks from running tasks + for(auto& taskPair : mRunningTasks) + { + auto& task = taskPair.first; + auto checkMask = (task->GetCallbackInvocationThread() == Dali::AsyncTask::ThreadType::MAIN_THREAD ? Dali::AsyncTaskManager::CompletedCallbackTraceMask::THREAD_MASK_MAIN : Dali::AsyncTaskManager::CompletedCallbackTraceMask::THREAD_MASK_WORKER) | + (task->GetPriorityType() == Dali::AsyncTask::PriorityType::HIGH ? Dali::AsyncTaskManager::CompletedCallbackTraceMask::PRIORITY_MASK_HIGH : Dali::AsyncTaskManager::CompletedCallbackTraceMask::PRIORITY_MASK_LOW); + + if((checkMask & mask) == checkMask) + { + taskAdded = true; + mTasksCompletedImpl->AppendTaskTrace(tasksCompletedId, task); + } + } + + // Collect all tasks from complete tasks + for(auto& taskPair : mCompletedTasks) + { + auto& task = taskPair.first; + auto checkMask = (task->GetCallbackInvocationThread() == Dali::AsyncTask::ThreadType::MAIN_THREAD ? Dali::AsyncTaskManager::CompletedCallbackTraceMask::THREAD_MASK_MAIN : Dali::AsyncTaskManager::CompletedCallbackTraceMask::THREAD_MASK_WORKER) | + (task->GetPriorityType() == Dali::AsyncTask::PriorityType::HIGH ? Dali::AsyncTaskManager::CompletedCallbackTraceMask::PRIORITY_MASK_HIGH : Dali::AsyncTaskManager::CompletedCallbackTraceMask::PRIORITY_MASK_LOW); + + if((checkMask & mask) == checkMask) + { + taskAdded = true; + mTasksCompletedImpl->AppendTaskTrace(tasksCompletedId, task); + } + } + } + } + } + + // If there is nothing to check task, just excute callback right now. + if(!taskAdded) + { + DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "CompletedCallback id[%u] executed now due to no task exist\n", tasksCompletedId); + + mTasksCompletedImpl->CheckTasksCompletedCallbackCompleted(tasksCompletedId); + } + return tasksCompletedId; +} + +bool AsyncTaskManager::RemoveCompletedCallback(Dali::AsyncTaskManager::TasksCompletedId tasksCompletedId) +{ + return mTasksCompletedImpl->RemoveTasksCompleted(tasksCompletedId); +} + AsyncTaskPtr AsyncTaskManager::PopNextCompletedTask() { std::vector ignoredTaskList; ///< To keep asyncTask reference so we can ensure that destructor called out of mutex. @@ -469,6 +882,15 @@ AsyncTaskPtr AsyncTaskManager::PopNextCompletedTask() return nextCompletedTask; } +void AsyncTaskManager::RegisterProcessor() +{ + if(!mProcessorRegistered && Dali::Adaptor::IsAvailable()) + { + Dali::Adaptor::Get().RegisterProcessor(*this); + mProcessorRegistered = true; + } +} + void AsyncTaskManager::UnregisterProcessor() { if(mProcessorRegistered && Dali::Adaptor::IsAvailable()) @@ -502,10 +924,15 @@ void AsyncTaskManager::TasksCompleted() { DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "Execute callback [%p]\n", task.Get()); CallbackBase::Execute(*(task->GetCompletedCallback()), task); + + // Remove TasksCompleted callback trace + mTasksCompletedImpl->RemoveTaskTrace(task); } UnregisterProcessor(); DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "TasksCompleted end\n"); + + mTasksCompletedImpl->EmitCompletedTasks(); } void AsyncTaskManager::Process(bool postProcessor) @@ -627,6 +1054,18 @@ void AsyncTaskManager::CompleteTask(AsyncTaskPtr&& task) { DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "Execute callback on worker thread [%p]\n", task.Get()); CallbackBase::Execute(*(task->GetCompletedCallback()), task); + + // We need to remove task trace now. + if(mTasksCompletedImpl->IsTasksCompletedCallbackExist()) + { + mTasksCompletedImpl->RemoveTaskTrace(task); + + if(mTasksCompletedImpl->IsExecuteCallbackExist()) + { + // We need to call EmitCompletedTasks(). Trigger main thread. + needTrigger = true; + } + } } // Lock while adding task to the queue diff --git a/dali/internal/system/common/async-task-manager-impl.h b/dali/internal/system/common/async-task-manager-impl.h index 6690098..5efd4f4 100644 --- a/dali/internal/system/common/async-task-manager-impl.h +++ b/dali/internal/system/common/async-task-manager-impl.h @@ -120,6 +120,16 @@ public: void RemoveTask(AsyncTaskPtr task); /** + * @copydoc Dali::AsyncTaskManager::SetCompletedCallback() + */ + Dali::AsyncTaskManager::TasksCompletedId SetCompletedCallback(CallbackBase* callback, Dali::AsyncTaskManager::CompletedCallbackTraceMask mask); + + /** + * @copydoc Dali::AsyncTaskManager::RemoveCompletedCallback() + */ + bool RemoveCompletedCallback(Dali::AsyncTaskManager::TasksCompletedId tasksCompletedId); + + /** * Pop the next task out from the completed queue, called by main thread. * * @return The next task in the completed queue. @@ -127,6 +137,11 @@ public: AsyncTaskPtr PopNextCompletedTask(); /** + * @brief Register processor if we don't registered before. + */ + void RegisterProcessor(); + + /** * @brief Unregister a previously registered processor */ void UnregisterProcessor(); @@ -248,6 +263,9 @@ private: Dali::Mutex mRunningTasksMutex; ///< Mutex for mRunningTasks. We can lock mCompletedTasksMutex under this scope. Dali::Mutex mCompletedTasksMutex; ///< Mutex for mCompletedTasks. We cannot lock any mutex under this scope. + struct TasksCompletedImpl; + std::unique_ptr mTasksCompletedImpl; ///< TaskS completed signal interface for AsyncTaskManager. + struct CacheImpl; std::unique_ptr mCacheImpl; ///< Cache interface for AsyncTaskManager. diff --git a/dali/public-api/adaptor-framework/async-task-manager.cpp b/dali/public-api/adaptor-framework/async-task-manager.cpp index 3d19b7e..b160954 100644 --- a/dali/public-api/adaptor-framework/async-task-manager.cpp +++ b/dali/public-api/adaptor-framework/async-task-manager.cpp @@ -34,7 +34,7 @@ CallbackBase* AsyncTask::GetCompletedCallback() return mCompletedCallback.get(); } -AsyncTask::ThreadType AsyncTask::GetCallbackInvocationThread() +AsyncTask::ThreadType AsyncTask::GetCallbackInvocationThread() const { return mThreadType; } @@ -63,6 +63,16 @@ void AsyncTaskManager::RemoveTask(AsyncTaskPtr task) GetImplementation(*this).RemoveTask(task); } +AsyncTaskManager::TasksCompletedId AsyncTaskManager::SetCompletedCallback(CallbackBase* callback, AsyncTaskManager::CompletedCallbackTraceMask mask) +{ + return GetImplementation(*this).SetCompletedCallback(callback, mask); +} + +bool AsyncTaskManager::RemoveCompletedCallback(AsyncTaskManager::TasksCompletedId tasksCompletedId) +{ + return GetImplementation(*this).RemoveCompletedCallback(tasksCompletedId); +} + AsyncTaskManager::AsyncTaskManager(Internal::Adaptor::AsyncTaskManager* impl) : BaseHandle(impl) { diff --git a/dali/public-api/adaptor-framework/async-task-manager.h b/dali/public-api/adaptor-framework/async-task-manager.h index 10a30fc..087a77a 100644 --- a/dali/public-api/adaptor-framework/async-task-manager.h +++ b/dali/public-api/adaptor-framework/async-task-manager.h @@ -97,7 +97,7 @@ public: * @SINCE_2_2.9 * @return the type of invocation callback. */ - ThreadType GetCallbackInvocationThread(); + ThreadType GetCallbackInvocationThread() const; /** * Get the priority of this task @@ -181,6 +181,71 @@ public: void RemoveTask(AsyncTaskPtr task); public: + using TasksCompletedId = uint32_t; + + enum CompletedCallbackTraceMask + { + THREAD_MASK_MAIN = 1u << 0, ///< Trace only main thread tasks. + THREAD_MASK_WORKER = 1u << 1, ///< Trace only worker thread tasks. + + PRIORITY_MASK_HIGH = 1u << 2, ///< Trace only high priority tasks. + PRIORITY_MASK_LOW = 1u << 3, ///< Trace only low priority tasks. + + THREAD_MASK_ALL = THREAD_MASK_MAIN | THREAD_MASK_WORKER, + PRIORITY_MASK_ALL = PRIORITY_MASK_HIGH | PRIORITY_MASK_LOW, + + MASK_ALL = THREAD_MASK_ALL | PRIORITY_MASK_ALL, + + DEFAULT = MASK_ALL, + }; + + /** + * @brief Set the async tasks completed callback. + * Inputed callback will be emitted after all tasks what user added are completed. + * + * Usage example: + * + * void OnTasksCompleted(TasksCompletedId id); + * auto id0 = AsyncTaskManager::Get().SetCompletedCallback(MakeCallback(OnTasksCompleted), CompletedCallbackTraceMask::MASK_ALL); + * // OnTasksCompleted(id0); called at next Idler. + * + * AsyncTaskManager::Get().AddTask(task1); + * auto id1 = AsyncTaskManager::Get().SetCompletedCallback(MakeCallback(OnTasksCompleted), CompletedCallbackTraceMask::MASK_ALL); + * // OnTasksCompleted(id1); called after task1 completed. + * + * AsyncTaskManager::Get().AddTask(task2WhichIsLowPriority); + * AsyncTaskManager::Get().AddTask(task3WhichIsWorkerThread); + * AsyncTaskManager::Get().AddTask(task4); + * auto id2 = AsyncTaskManager::Get().SetCompletedCallback(MakeCallback(OnTasksCompleted), CompletedCallbackTraceMask::THREAD_MASK_MAIN | CompletedCallbackTraceMask::PRIORITY_MASK_HIGH); + * // OnTasksCompleted(id2); called after task1 and task4 completed. + * + * AsyncTaskManager::Get().RemoveCompletedCallback(id1); + * // OnTasksCompleted(id1); will not be called. + * + * @note The ownership of callback will be hold AsyncTaskManager itself. + * @note The callback will be emmited at Process() timming. + * + * @SINCE_2_2.50 + * @param[in] callback The callback base when all AsyncTasks completed. + * This callback will be void return, and single input argument ; TasksCompletedId. + * @param[in] mask Mask info s.t. what kind of async task we want to detact. + * For example, if we set this value as MASK_ALL & ~PRIORITY_MASK_LOW, we will ignore low priority tasks. + * Default is MASK_ALL. + * @return The unique id for callback. It can be used when we want to remove callback. + */ + TasksCompletedId SetCompletedCallback(CallbackBase* callback, CompletedCallbackTraceMask mask = CompletedCallbackTraceMask::DEFAULT); + + /** + * @brief Remove the async tasks completed callback. + * @note It will not execute setted callback. + * + * @SINCE_2_2.50 + * @param[in] tasksCompletedId The id for callback that want to remove. + * @return True if we success to removed. False if it already removed, or callback already emitted. + */ + bool RemoveCompletedCallback(TasksCompletedId tasksCompletedId); + +public: /// @cond internal /** * @brief Allows the creation of a AsyncTaskManager handle from an internal pointer.