From f09181b464aee0cdc28d27c71c4fab9322bd7550 Mon Sep 17 00:00:00 2001 From: "Eunki, Hong" Date: Fri, 31 Mar 2023 15:08:57 +0900 Subject: [PATCH] [Tizen] Revert "AsyncTaskManager overhead reduce" Revert patch This reverts commit 09a85bc561db2d814d5fff48b695d387f75c887c. Change-Id: I601116ee828099a0d034b39b3231be1d5c37a4fc --- .../system/common/async-task-manager-impl.cpp | 392 +++++---------------- .../system/common/async-task-manager-impl.h | 55 ++- 2 files changed, 103 insertions(+), 344 deletions(-) diff --git a/dali/internal/system/common/async-task-manager-impl.cpp b/dali/internal/system/common/async-task-manager-impl.cpp index 16f44ba..fa9b8c8 100644 --- a/dali/internal/system/common/async-task-manager-impl.cpp +++ b/dali/internal/system/common/async-task-manager-impl.cpp @@ -24,9 +24,6 @@ #include #include #include -#include - -#include namespace Dali { @@ -47,7 +44,7 @@ size_t GetNumberOfThreads(const char* environmentVariable, size_t defaultValue) { auto numberString = EnvironmentVariable::GetEnvironmentVariable(environmentVariable); auto numberOfThreads = numberString ? std::strtoul(numberString, nullptr, 10) : 0; - constexpr auto MAX_NUMBER_OF_THREADS = 16u; + constexpr auto MAX_NUMBER_OF_THREADS = 10u; DALI_ASSERT_DEBUG(numberOfThreads < MAX_NUMBER_OF_THREADS); return (numberOfThreads > 0 && numberOfThreads < MAX_NUMBER_OF_THREADS) ? numberOfThreads : defaultValue; } @@ -62,14 +59,10 @@ size_t GetNumberOfLowPriorityThreads(const char* environmentVariable, size_t def #if defined(DEBUG_ENABLED) Debug::Filter* gAsyncTasksManagerLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_ASYNC_TASK_MANAGER"); - -uint32_t gThreadId = 0u; // Only for debug #endif } // unnamed namespace -// AsyncTaskThread - AsyncTaskThread::AsyncTaskThread(AsyncTaskManager& asyncTaskManager) : mConditionalWait(), mAsyncTaskManager(asyncTaskManager), @@ -119,16 +112,7 @@ bool AsyncTaskThread::Request() void AsyncTaskThread::Run() { -#if defined(DEBUG_ENABLED) - uint32_t threadId = gThreadId++; - { - char temp[100]; - snprintf(temp, 100, "AsyncTaskThread[%u]", threadId); - SetThreadName(temp); - } -#else SetThreadName("AsyncTaskThread"); -#endif mLogFactory.InstallLogFunction(); while(!mDestroyThread) @@ -140,99 +124,17 @@ void AsyncTaskThread::Run() if(!mDestroyThread) { mIsThreadIdle = true; - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::General, "Thread[%u] wait\n", threadId); mConditionalWait.Wait(lock); - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::General, "Thread[%u] awake\n", threadId); } } else { - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::General, "Thread[%u] Process task [%p]\n", threadId, task.Get()); task->Process(); - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::General, "Thread[%u] Complete task [%p]\n", threadId, task.Get()); - if(!mDestroyThread) - { - mAsyncTaskManager.CompleteTask(task); - } + mAsyncTaskManager.CompleteTask(task); } } } -// AsyncTaskManager::CacheImpl - -struct AsyncTaskManager::CacheImpl -{ - CacheImpl(AsyncTaskManager& manager) - : mManager(manager) - { - } - -public: - // Insert / Erase task cache API. - - /** - * @brief Insert cache that input task. - * @pre Mutex be locked. - */ - template - static void InsertTaskCache(CacheContainer& cacheMap, AsyncTaskPtr task, Iterator iterator) - { - cacheMap[task.Get()].push_back(iterator); - } - - /** - * @brief Erase cache that input task. - * @pre Mutex be locked. - */ - template - static void EraseTaskCache(CacheContainer& cacheMap, AsyncTaskPtr task, Iterator iterator) - { - auto mapIter = cacheMap.find(task.Get()); - if(mapIter != cacheMap.end()) - { - auto& cacheContainer = (*mapIter).second; - auto cacheIter = std::find(cacheContainer.begin(), cacheContainer.end(), iterator); - - if(cacheIter != cacheContainer.end()) - { - cacheContainer.erase(cacheIter); - if(cacheContainer.empty()) - { - cacheMap.erase(mapIter); - } - } - } - } - - /** - * @brief Erase all cache that input task. - * @pre Mutex be locked. - */ - template - static void EraseAllTaskCache(CacheContainer& cacheMap, AsyncTaskPtr task) - { - auto mapIter = cacheMap.find(task.Get()); - if(mapIter != cacheMap.end()) - { - cacheMap.erase(mapIter); - } - } - -public: - AsyncTaskManager& mManager; ///< Owner of this CacheImpl. - - // TODO : Can't we use std::set instead of std::vector? - // It will be slowdown If someone AddTask multiple times for same task. - using TaskCacheContainer = std::unordered_map>; - using RunningTaskCacheContainer = std::unordered_map>; - - TaskCacheContainer mWaitingTasksCache; ///< The cache of tasks and iterator for waiting to async process. Must be locked under mWaitingTasksMutex. - RunningTaskCacheContainer mRunningTasksCache; ///< The cache of tasks and iterator for running tasks. Must be locked under mRunningTasksMutex. - TaskCacheContainer mCompletedTasksCache; ///< The cache of tasks and iterator for completed async process. Must be locked under mCompletedTasksMutex. -}; - -// AsyncTaskManager - Dali::AsyncTaskManager AsyncTaskManager::Get() { Dali::AsyncTaskManager manager; @@ -261,8 +163,6 @@ Dali::AsyncTaskManager AsyncTaskManager::Get() 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), - mCacheImpl(new CacheImpl(*this)), mTrigger(new EventThreadCallback(MakeCallback(this, &AsyncTaskManager::TasksCompleted))), mProcessorRegistered(false) { @@ -283,29 +183,14 @@ void AsyncTaskManager::AddTask(AsyncTaskPtr task) if(task) { // Lock while adding task to the queue - Mutex::ScopedLock lock(mWaitingTasksMutex); + Mutex::ScopedLock lock(mMutex); - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "AddTask [%p]\n", task.Get()); + mWaitingTasks.push_back(task); - // push back into waiting queue. - auto waitingIter = mWaitingTasks.insert(mWaitingTasks.end(), task); - CacheImpl::InsertTaskCache(mCacheImpl->mWaitingTasksCache, task, waitingIter); - - if(task->GetPriorityType() == AsyncTask::PriorityType::HIGH) + // Finish all Running threads are working + if(mRunningTasks.size() >= mTasks.GetElementCount()) { - // Increase the number of waiting tasks for high priority. - ++mWaitingHighProirityTaskCounts; - } - - { - // For thread safety - Mutex::ScopedLock lock(mRunningTasksMutex); // We can lock this mutex under mWaitingTasksMutex. - - // Finish all Running threads are working - if(mRunningTasks.size() >= mTasks.GetElementCount()) - { - return; - } + return; } } @@ -335,241 +220,114 @@ void AsyncTaskManager::RemoveTask(AsyncTaskPtr task) { if(task) { - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "RemoveTask [%p]\n", task.Get()); - - // Check whether we need to unregister processor. - // If there is some non-empty queue exist, we don't need to check unregister. - bool needCheckUnregisterProcessor = true; + // Lock while remove task from the queue + Mutex::ScopedLock lock(mMutex); + if(!mWaitingTasks.empty()) { - // Lock while remove task from the queue - Mutex::ScopedLock lock(mWaitingTasksMutex); - - auto mapIter = mCacheImpl->mWaitingTasksCache.find(task.Get()); - if(mapIter != mCacheImpl->mWaitingTasksCache.end()) + for(std::vector::iterator it = mWaitingTasks.begin(); it != mWaitingTasks.end();) { - for(auto iterator : mapIter->second) + if((*it) && (*it) == task) { - if((*iterator)->GetPriorityType() == AsyncTask::PriorityType::HIGH) - { - // Decrease the number of waiting tasks for high priority. - --mWaitingHighProirityTaskCounts; - } - mWaitingTasks.erase(iterator); + it = mWaitingTasks.erase(it); } - CacheImpl::EraseAllTaskCache(mCacheImpl->mWaitingTasksCache, task); - } - - if(!mWaitingTasks.empty()) - { - needCheckUnregisterProcessor = false; - } - } - - { - // Lock while remove task from the queue - Mutex::ScopedLock lock(mRunningTasksMutex); - - auto mapIter = mCacheImpl->mRunningTasksCache.find(task.Get()); - if(mapIter != mCacheImpl->mRunningTasksCache.end()) - { - for(auto iterator : mapIter->second) + else { - // We cannot erase container. Just mark as erased. - // Note : mAvaliableLowPriorityTaskCounts will be increased after process finished. - iterator->second = true; + it++; } - CacheImpl::EraseAllTaskCache(mCacheImpl->mRunningTasksCache, task); - } - - if(!mRunningTasks.empty()) - { - needCheckUnregisterProcessor = false; } } + if(!mRunningTasks.empty()) { - // Lock while remove task from the queue - Mutex::ScopedLock lock(mCompletedTasksMutex); - - auto mapIter = mCacheImpl->mCompletedTasksCache.find(task.Get()); - if(mapIter != mCacheImpl->mCompletedTasksCache.end()) + for(auto iter = mRunningTasks.begin(), endIter = mRunningTasks.end(); iter != endIter; ++iter) { - for(auto iterator : mapIter->second) + if((*iter).first == task) { - mCompletedTasks.erase(iterator); + (*iter).second = true; } - CacheImpl::EraseAllTaskCache(mCacheImpl->mCompletedTasksCache, task); - } - - if(!mCompletedTasks.empty()) - { - needCheckUnregisterProcessor = false; } } - // UnregisterProcessor required to lock mutex. Call that API only if required. - if(needCheckUnregisterProcessor) - { - UnregisterProcessor(); - } - } -} - -AsyncTaskPtr AsyncTaskManager::PopNextCompletedTask() -{ - // Lock while popping task out from the queue - Mutex::ScopedLock lock(mCompletedTasksMutex); - - if(mCompletedTasks.empty()) - { - return AsyncTaskPtr(); - } - - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "PopNextCompletedTask, completed task count : [%zu]\n", mCompletedTasks.size()); - - auto next = mCompletedTasks.begin(); - AsyncTaskPtr nextTask = *next; - CacheImpl::EraseTaskCache(mCacheImpl->mCompletedTasksCache, nextTask, next); - mCompletedTasks.erase(next); - - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::General, "Pickup completed [%p]\n", nextTask.Get()); - - return nextTask; -} - -void AsyncTaskManager::UnregisterProcessor() -{ - if(mProcessorRegistered && Dali::Adaptor::IsAvailable()) - { - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "UnregisterProcessor begin\n"); - // Keep processor at least 1 task exist. - // Please be careful the order of mutex, to avoid dead lock. - // TODO : Should we lock all mutex rightnow? - Mutex::ScopedLock lock1(mWaitingTasksMutex); - if(mWaitingTasks.empty()) + if(!mCompletedTasks.empty()) { - Mutex::ScopedLock lock2(mRunningTasksMutex); // We can lock this mutex under mWaitingTasksMutex. - if(mRunningTasks.empty()) + for(std::vector::iterator it = mCompletedTasks.begin(); it != mCompletedTasks.end();) { - Mutex::ScopedLock lock3(mCompletedTasksMutex); // We can lock this mutex under mWaitingTasksMutex and mRunningTasksMutex. - if(mCompletedTasks.empty()) + if((*it) && (*it) == task) + { + it = mCompletedTasks.erase(it); + } + else { - Dali::Adaptor::Get().UnregisterProcessor(*this); - mProcessorRegistered = false; + it++; } } } - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "UnregisterProcessor end (registed? %d)\n", mProcessorRegistered); - } -} - -void AsyncTaskManager::TasksCompleted() -{ - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "TasksCompleted begin\n"); - while(AsyncTaskPtr task = PopNextCompletedTask()) - { - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "Execute callback [%p]\n", task.Get()); - CallbackBase::Execute(*(task->GetCompletedCallback()), task); } - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "TasksCompleted end\n"); UnregisterProcessor(); } -void AsyncTaskManager::Process(bool postProcessor) -{ - TasksCompleted(); -} - -/// Worker thread called AsyncTaskPtr AsyncTaskManager::PopNextTaskToProcess() { // Lock while popping task out from the queue - Mutex::ScopedLock lock(mWaitingTasksMutex); - - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "PopNextTaskToProcess, waiting task count : [%zu]\n", mWaitingTasks.size()); + Mutex::ScopedLock lock(mMutex); // pop out the next task from the queue AsyncTaskPtr nextTask = nullptr; - // Fast cut if all waiting tasks are LOW priority, and we cannot excute low task anymore. - if(mWaitingHighProirityTaskCounts == 0u && !mWaitingTasks.empty()) - { - // For thread safety - Mutex::ScopedLock lock(mRunningTasksMutex); // We can lock this mutex under mWaitingTasksMutex. - - if(mAvaliableLowPriorityTaskCounts == 0u) - { - // There are no avaliabe tasks to run now. Return nullptr. - return nextTask; - } - } - for(auto iter = mWaitingTasks.begin(), endIter = mWaitingTasks.end(); iter != endIter; ++iter) { if((*iter)->IsReady()) { const auto priorityType = (*iter)->GetPriorityType(); - bool taskAvaliable = priorityType == AsyncTask::PriorityType::HIGH; // Task always valid if it's priority is high - if(!taskAvaliable) - { - // For thread safety - Mutex::ScopedLock lock(mRunningTasksMutex); // We can lock this mutex under mWaitingTasksMutex. - - taskAvaliable = (mAvaliableLowPriorityTaskCounts > 0u); // priority is low, but we can use it. - } + const bool taskAvaliable = (priorityType == AsyncTask::PriorityType::HIGH) || // Task always valid if it's priority is high + (mAvaliableLowPriorityTaskCounts > 0u); // or priority is low, but we can use it. if(taskAvaliable) { nextTask = *iter; // Add Running queue - { - // Lock while popping task out from the queue - Mutex::ScopedLock lock(mRunningTasksMutex); // We can lock this mutex under mWaitingTasksMutex. - - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "Waiting -> Running [%p]\n", nextTask.Get()); - - auto runningIter = mRunningTasks.insert(mRunningTasks.end(), std::make_pair(nextTask, false)); - CacheImpl::InsertTaskCache(mCacheImpl->mRunningTasksCache, nextTask, runningIter); - - // Decrease avaliable task counts if it is low priority - if(priorityType == AsyncTask::PriorityType::LOW) - { - // We are under running task mutex. We can decrease it. - --mAvaliableLowPriorityTaskCounts; - } - } + mRunningTasks.push_back(std::make_pair(nextTask, false)); + mWaitingTasks.erase(iter); - if(priorityType == AsyncTask::PriorityType::HIGH) + // Decrease avaliable task counts if it is low priority + if(priorityType == AsyncTask::PriorityType::LOW) { - // Decrease the number of waiting tasks for high priority. - --mWaitingHighProirityTaskCounts; + --mAvaliableLowPriorityTaskCounts; } - - CacheImpl::EraseTaskCache(mCacheImpl->mWaitingTasksCache, nextTask, iter); - mWaitingTasks.erase(iter); break; } } } - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::General, "Pickup process [%p]\n", nextTask.Get()); + return nextTask; +} + +AsyncTaskPtr AsyncTaskManager::PopNextCompletedTask() +{ + // Lock while popping task out from the queue + Mutex::ScopedLock lock(mMutex); + + if(mCompletedTasks.empty()) + { + return AsyncTaskPtr(); + } + + std::vector::iterator next = mCompletedTasks.begin(); + AsyncTaskPtr nextTask = *next; + mCompletedTasks.erase(next); return nextTask; } -/// Worker thread called void AsyncTaskManager::CompleteTask(AsyncTaskPtr task) { // Lock while adding task to the queue { - Mutex::ScopedLock lock(mRunningTasksMutex); - - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "CompleteTask [%p]\n", task.Get()); + Mutex::ScopedLock lock(mMutex); - // Note : The number of mRunningTasks size will not be over than thread count. Just linear iterate. for(auto iter = mRunningTasks.begin(), endIter = mRunningTasks.end(); iter != endIter; ++iter) { if((*iter).first == task) @@ -578,27 +336,19 @@ void AsyncTaskManager::CompleteTask(AsyncTaskPtr task) { if(task->GetCallbackInvocationThread() == AsyncTask::ThreadType::MAIN_THREAD) { - Mutex::ScopedLock lock(mCompletedTasksMutex); // We can lock this mutex under mRunningTasksMutex. - - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "Running -> Completed [%p]\n", task.Get()); - - auto completedIter = mCompletedTasks.insert(mCompletedTasks.end(), task); - CacheImpl::InsertTaskCache(mCacheImpl->mCompletedTasksCache, task, completedIter); + mCompletedTasks.push_back(task); } - - CacheImpl::EraseTaskCache(mCacheImpl->mRunningTasksCache, task, iter); } + // Delete this task in running queue + mRunningTasks.erase(iter); + // Increase avaliable task counts if it is low priority const auto priorityType = task->GetPriorityType(); if(priorityType == AsyncTask::PriorityType::LOW) { - // We are under running task mutex. We can increase it. ++mAvaliableLowPriorityTaskCounts; } - - // Delete this task in running queue - mRunningTasks.erase(iter); break; } } @@ -607,17 +357,41 @@ void AsyncTaskManager::CompleteTask(AsyncTaskPtr task) // wake up the main thread if(task->GetCallbackInvocationThread() == AsyncTask::ThreadType::MAIN_THREAD) { - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "Trigger main thread\n"); mTrigger->Trigger(); } else { - DALI_LOG_INFO(gAsyncTasksManagerLogFilter, Debug::Verbose, "Execute callback on worker thread [%p]\n", task.Get()); CallbackBase::Execute(*(task->GetCompletedCallback()), task); } } -// AsyncTaskManager::TaskHelper +void AsyncTaskManager::UnregisterProcessor() +{ + if(mProcessorRegistered && Dali::Adaptor::IsAvailable()) + { + Mutex::ScopedLock lock(mMutex); + if(mWaitingTasks.empty() && mCompletedTasks.empty() && mRunningTasks.empty()) + { + Dali::Adaptor::Get().UnregisterProcessor(*this); + mProcessorRegistered = false; + } + } +} + +void AsyncTaskManager::TasksCompleted() +{ + while(AsyncTaskPtr task = PopNextCompletedTask()) + { + CallbackBase::Execute(*(task->GetCompletedCallback()), task); + } + + UnregisterProcessor(); +} + +void AsyncTaskManager::Process(bool postProcessor) +{ + TasksCompleted(); +} AsyncTaskManager::TaskHelper::TaskHelper(AsyncTaskManager& asyncTaskManager) : TaskHelper(std::unique_ptr(new AsyncTaskThread(asyncTaskManager)), asyncTaskManager) diff --git a/dali/internal/system/common/async-task-manager-impl.h b/dali/internal/system/common/async-task-manager-impl.h index c33f275..2b31789 100644 --- a/dali/internal/system/common/async-task-manager-impl.h +++ b/dali/internal/system/common/async-task-manager-impl.h @@ -24,7 +24,6 @@ #include #include #include -#include #include #include @@ -118,6 +117,13 @@ public: void RemoveTask(AsyncTaskPtr task); /** + * Pop the next task out from the queue. + * + * @return The next task to be processed. + */ + AsyncTaskPtr PopNextTaskToProcess(); + + /** * Pop the next task out from the completed queue, called by main thread. * * @return The next task in the completed queue. @@ -125,6 +131,13 @@ public: AsyncTaskPtr PopNextCompletedTask(); /** + * Pop the next task out from the running queue and add this task to the completed queue. + * + * @param[in] task The task added to the queue. + */ + void CompleteTask(AsyncTaskPtr task); + + /** * @brief Unregister a previously registered processor */ void UnregisterProcessor(); @@ -139,21 +152,6 @@ public: */ void Process(bool postProcessor) override; -public: // Worker thread called method - /** - * Pop the next task out from the queue. - * - * @return The next task to be processed. - */ - AsyncTaskPtr PopNextTaskToProcess(); - - /** - * Pop the next task out from the running queue and add this task to the completed queue. - * - * @param[in] task The task added to the queue. - */ - void CompleteTask(AsyncTaskPtr task); - private: /** * @brief Helper class to keep the relation between AsyncTaskThread and corresponding container @@ -200,31 +198,18 @@ private: AsyncTaskManager& operator=(const AsyncTaskManager& manager); private: - using AsyncTaskContainer = std::list; + std::vector mWaitingTasks; //The queue of the tasks waiting to async process + std::vector mCompletedTasks; //The queue of the tasks with the async process - using AsyncTaskPair = std::pair; - using AsyncRunningTaskContainer = std::list; - - AsyncTaskContainer mWaitingTasks; ///< The queue of the tasks waiting to async process. Must be locked under mWaitingTasksMutex. - AsyncRunningTaskContainer mRunningTasks; ///< The queue of the running tasks. Must be locked under mRunningTasksMutex. - AsyncTaskContainer mCompletedTasks; ///< The queue of the tasks with the async process. Must be locked under mCompletedTasksMutex. + using AsyncTaskPair = std::pair; + std::vector mRunningTasks; ///< The queue of the running tasks RoundRobinContainerView mTasks; - uint32_t mAvaliableLowPriorityTaskCounts; ///< The number of tasks that can be processed for priority type LOW. - ///< Be used to select next wating task determining algorithm. - ///< Note : For thread safety, Please set/get this value under mRunningTasksMutex scope. - uint32_t mWaitingHighProirityTaskCounts; ///< The number of tasks that waiting now for priority type HIGH. + uint32_t mAvaliableLowPriorityTaskCounts; ///< The number of tasks that can be processed for proirity type LOW. ///< Be used to select next wating task determining algorithm. - ///< Note : For thread safety, Please set/get this value under mWaitingTasksMutex scope. - - Dali::Mutex mWaitingTasksMutex; ///< Mutex for mWaitingTasks. We can lock mRunningTasksMutex and mCompletedTasksMutex under this scope. - 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 CacheImpl; - std::unique_ptr mCacheImpl; ///< Cache interface for AsyncTaskManager. + Dali::Mutex mMutex; std::unique_ptr mTrigger; bool mProcessorRegistered; }; -- 2.7.4