Add PriorityType for each Task 79/289579/1
authorEunki, Hong <eunkiki.hong@samsung.com>
Tue, 7 Mar 2023 23:33:58 +0000 (08:33 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Fri, 10 Mar 2023 03:27:15 +0000 (12:27 +0900)
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 <eunkiki.hong@samsung.com>
dali/internal/system/common/async-task-manager-impl.cpp
dali/internal/system/common/async-task-manager-impl.h
dali/public-api/adaptor-framework/async-task-manager.cpp
dali/public-api/adaptor-framework/async-task-manager.h

index 55ebe7a..fa9b8c8 100644 (file)
@@ -16,7 +16,7 @@
  */
 
 // CLASS HEADER
-#include "async-task-manager-impl.h"
+#include <dali/internal/system/common/async-task-manager-impl.h>
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/adaptor-framework/environment-variable.h>
@@ -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<AsyncTaskPtr>::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;
       }
     }
index 2007779..2b31789 100644 (file)
@@ -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<TaskHelper> 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<EventThreadCallback> mTrigger;
   bool                                 mProcessorRegistered;
index 2a2d58b..3d19b7e 100644 (file)
  */
 
 // CLASS HEADER
-#include "async-task-manager.h"
+#include <dali/public-api/adaptor-framework/async-task-manager.h>
 
 // INTERNAL INCLUDES
 #include <dali/internal/system/common/async-task-manager-impl.h>
 
 namespace Dali
 {
-AsyncTask::AsyncTask(CallbackBase* callback, ThreadType threadType)
+AsyncTask::AsyncTask(CallbackBase* callback, PriorityType priority, ThreadType threadType)
 : mCompletedCallback(std::unique_ptr<CallbackBase>(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;
index dd3ebc0..10a30fc 100644 (file)
@@ -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<CallbackBase> mCompletedCallback;
+  const PriorityType            mPriorityType;
   ThreadType                    mThreadType;
 
   // Undefined