From 1dc1137604a286a1910dea9cf2933f77180f8770 Mon Sep 17 00:00:00 2001 From: "Eunki, Hong" Date: Wed, 8 Mar 2023 08:33:58 +0900 Subject: [PATCH] Add PriorityType for each Task There can be problem if all heavy tasks grep all threads so something lightweight tasks processed late. This patch let user give some hint about priority. So make low-priority don't use whole threads. Now we can leave at least 2 threads to excute high priority tasks Change-Id: I6bb469ce1949ef243a61975af6b6659d8d3982fd Signed-off-by: Eunki, Hong --- .../system/common/async-task-manager-impl.cpp | 50 +++++++++++++++++++--- .../system/common/async-task-manager-impl.h | 5 ++- .../adaptor-framework/async-task-manager.cpp | 10 ++++- .../adaptor-framework/async-task-manager.h | 35 ++++++++++++++- 4 files changed, 90 insertions(+), 10 deletions(-) diff --git a/dali/internal/system/common/async-task-manager-impl.cpp b/dali/internal/system/common/async-task-manager-impl.cpp index 55ebe7a..fa9b8c8 100644 --- a/dali/internal/system/common/async-task-manager-impl.cpp +++ b/dali/internal/system/common/async-task-manager-impl.cpp @@ -16,7 +16,7 @@ */ // CLASS HEADER -#include "async-task-manager-impl.h" +#include // EXTERNAL INCLUDES #include @@ -36,6 +36,10 @@ namespace constexpr auto DEFAULT_NUMBER_OF_ASYNC_THREADS = size_t{8u}; constexpr auto NUMBER_OF_ASYNC_THREADS_ENV = "DALI_ASYNC_MANAGER_THREAD_POOL_SIZE"; +// The number of threads for low priority task. +constexpr auto DEFAULT_NUMBER_OF_LOW_PRIORITY_THREADS = size_t{6u}; +constexpr auto NUMBER_OF_LOW_PRIORITY_THREADS_ENV = "DALI_ASYNC_MANAGER_LOW_PRIORITY_THREAD_POOL_SIZE"; + size_t GetNumberOfThreads(const char* environmentVariable, size_t defaultValue) { auto numberString = EnvironmentVariable::GetEnvironmentVariable(environmentVariable); @@ -45,6 +49,14 @@ size_t GetNumberOfThreads(const char* environmentVariable, size_t defaultValue) return (numberOfThreads > 0 && numberOfThreads < MAX_NUMBER_OF_THREADS) ? numberOfThreads : defaultValue; } +size_t GetNumberOfLowPriorityThreads(const char* environmentVariable, size_t defaultValue, size_t maxValue) +{ + auto numberString = EnvironmentVariable::GetEnvironmentVariable(environmentVariable); + auto numberOfThreads = numberString ? std::strtoul(numberString, nullptr, 10) : 0; + DALI_ASSERT_DEBUG(numberOfThreads <= maxValue); + return (numberOfThreads > 0 && numberOfThreads <= maxValue) ? numberOfThreads : std::min(defaultValue, maxValue); +} + #if defined(DEBUG_ENABLED) Debug::Filter* gAsyncTasksManagerLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_ASYNC_TASK_MANAGER"); #endif @@ -150,6 +162,7 @@ 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())), mTrigger(new EventThreadCallback(MakeCallback(this, &AsyncTaskManager::TasksCompleted))), mProcessorRegistered(false) { @@ -167,9 +180,11 @@ AsyncTaskManager::~AsyncTaskManager() void AsyncTaskManager::AddTask(AsyncTaskPtr task) { + if(task) { // Lock while adding task to the queue Mutex::ScopedLock lock(mMutex); + mWaitingTasks.push_back(task); // Finish all Running threads are working @@ -203,9 +218,11 @@ void AsyncTaskManager::AddTask(AsyncTaskPtr task) void AsyncTaskManager::RemoveTask(AsyncTaskPtr task) { + if(task) { // Lock while remove task from the queue Mutex::ScopedLock lock(mMutex); + if(!mWaitingTasks.empty()) { for(std::vector::iterator it = mWaitingTasks.begin(); it != mWaitingTasks.end();) @@ -263,12 +280,25 @@ AsyncTaskPtr AsyncTaskManager::PopNextTaskToProcess() { if((*iter)->IsReady()) { - nextTask = *iter; + const auto priorityType = (*iter)->GetPriorityType(); + 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. - // Add Running queue - mRunningTasks.push_back(std::make_pair(nextTask, false)); - mWaitingTasks.erase(iter); - break; + if(taskAvaliable) + { + nextTask = *iter; + + // Add Running queue + mRunningTasks.push_back(std::make_pair(nextTask, false)); + mWaitingTasks.erase(iter); + + // Decrease avaliable task counts if it is low priority + if(priorityType == AsyncTask::PriorityType::LOW) + { + --mAvaliableLowPriorityTaskCounts; + } + break; + } } } @@ -297,6 +327,7 @@ void AsyncTaskManager::CompleteTask(AsyncTaskPtr task) // Lock while adding task to the queue { Mutex::ScopedLock lock(mMutex); + for(auto iter = mRunningTasks.begin(), endIter = mRunningTasks.end(); iter != endIter; ++iter) { if((*iter).first == task) @@ -311,6 +342,13 @@ void AsyncTaskManager::CompleteTask(AsyncTaskPtr task) // 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) + { + ++mAvaliableLowPriorityTaskCounts; + } break; } } diff --git a/dali/internal/system/common/async-task-manager-impl.h b/dali/internal/system/common/async-task-manager-impl.h index 2007779..2b31789 100644 --- a/dali/internal/system/common/async-task-manager-impl.h +++ b/dali/internal/system/common/async-task-manager-impl.h @@ -2,7 +2,7 @@ #define DALI_INTERNAL_ASYNC_TASK_MANAGER_H /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -206,6 +206,9 @@ private: RoundRobinContainerView mTasks; + uint32_t mAvaliableLowPriorityTaskCounts; ///< The number of tasks that can be processed for proirity type LOW. + ///< Be used to select next wating task determining algorithm. + Dali::Mutex mMutex; std::unique_ptr mTrigger; bool mProcessorRegistered; diff --git a/dali/public-api/adaptor-framework/async-task-manager.cpp b/dali/public-api/adaptor-framework/async-task-manager.cpp index 2a2d58b..3d19b7e 100644 --- a/dali/public-api/adaptor-framework/async-task-manager.cpp +++ b/dali/public-api/adaptor-framework/async-task-manager.cpp @@ -15,15 +15,16 @@ */ // CLASS HEADER -#include "async-task-manager.h" +#include // INTERNAL INCLUDES #include namespace Dali { -AsyncTask::AsyncTask(CallbackBase* callback, ThreadType threadType) +AsyncTask::AsyncTask(CallbackBase* callback, PriorityType priority, ThreadType threadType) : mCompletedCallback(std::unique_ptr(callback)), + mPriorityType(priority), mThreadType(threadType) { } @@ -38,6 +39,11 @@ AsyncTask::ThreadType AsyncTask::GetCallbackInvocationThread() return mThreadType; } +AsyncTask::PriorityType AsyncTask::GetPriorityType() const +{ + return mPriorityType; +} + AsyncTaskManager::AsyncTaskManager() = default; AsyncTaskManager::~AsyncTaskManager() = default; diff --git a/dali/public-api/adaptor-framework/async-task-manager.h b/dali/public-api/adaptor-framework/async-task-manager.h index dd3ebc0..10a30fc 100644 --- a/dali/public-api/adaptor-framework/async-task-manager.h +++ b/dali/public-api/adaptor-framework/async-task-manager.h @@ -54,12 +54,37 @@ public: }; /** + * @brief The priority of this task what user think. + * To avoid long term tasks (like remote image download) block whole threads, + * Let user set the priority type of this task. + * + * Low priority task means it doesn't need to process by FIFO logic. + * So we make that Low priority don't take whole threads. + * + * Task selection algorithm defined internally. + * + * @note Task cannot change the priority type after create. + * + * @SINCE_2_2.17 + */ + enum class PriorityType + { + HIGH = 0, ///< Highest priority to process task. @SINCE_2_2.17 + LOW = 1, ///< Lowest priority to process task. @SINCE_2_2.17 + + PRIORITY_COUNT, ///< The number of priority type. @SINCE_2_2.17 + + DEFAULT = HIGH, ///< Default priority value if nothing defined. @SINCE_2_2.17 + }; + + /** * Constructor * @SINCE_2_2.3 * @param[in] callback The callback to invoke on task completion, either on the main thread on the worker thread. The ownership of callback is taken by this class. + * @param[in] priority The proirity type of this task. * @param[in] threadType The thread type of invocation callback. */ - AsyncTask(CallbackBase* callback, ThreadType threadType = AsyncTask::ThreadType::MAIN_THREAD); + AsyncTask(CallbackBase* callback, PriorityType priority = PriorityType::DEFAULT, ThreadType threadType = AsyncTask::ThreadType::MAIN_THREAD); /** * Get the complated callback @@ -75,6 +100,13 @@ public: ThreadType GetCallbackInvocationThread(); /** + * Get the priority of this task + * @SINCE_2_2.17 + * @return the type of priority. + */ + PriorityType GetPriorityType() const; + + /** * Destructor. * @SINCE_2_2.3 */ @@ -95,6 +127,7 @@ public: private: std::unique_ptr mCompletedCallback; + const PriorityType mPriorityType; ThreadType mThreadType; // Undefined -- 2.7.4