Add AsyncTaskManager 86/284586/8
authortscholb <scholb.kim@samsung.com>
Mon, 21 Nov 2022 08:34:48 +0000 (17:34 +0900)
committertscholb <scholb.kim@samsung.com>
Thu, 24 Nov 2022 09:50:26 +0000 (18:50 +0900)
Change-Id: I0a455873e747c36a8404a380dbfca72a00f529d5

dali/internal/system/common/async-task-manager-impl.cpp [new file with mode: 0644]
dali/internal/system/common/async-task-manager-impl.h [new file with mode: 0644]
dali/internal/system/file.list
dali/public-api/adaptor-framework/async-task-manager.cpp [new file with mode: 0644]
dali/public-api/adaptor-framework/async-task-manager.h [new file with mode: 0644]
dali/public-api/adaptor-framework/round-robin-container-view.h [new file with mode: 0644]
dali/public-api/file.list

diff --git a/dali/internal/system/common/async-task-manager-impl.cpp b/dali/internal/system/common/async-task-manager-impl.cpp
new file mode 100644 (file)
index 0000000..22e274c
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2022 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include "async-task-manager-impl.h"
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/common/singleton-service.h>
+#include <dali/devel-api/adaptor-framework/environment-variable.h>
+#include <dali/devel-api/adaptor-framework/thread-settings.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
+#include <dali/integration-api/debug.h>
+
+namespace Dali
+{
+namespace Internal
+{
+namespace Adaptor
+{
+namespace
+{
+constexpr auto DEFAULT_NUMBER_OF_ASYNC_THREADS = size_t{8u};
+constexpr auto NUMBER_OF_ASYNC_THREADS_ENV     = "DALI_ASYNC_MANAGER_THREAD_POOL_SIZE";
+
+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 = 10u;
+  DALI_ASSERT_DEBUG(numberOfThreads < MAX_NUMBER_OF_THREADS);
+  return (numberOfThreads > 0 && numberOfThreads < MAX_NUMBER_OF_THREADS) ? numberOfThreads : defaultValue;
+}
+
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gAsyncTasksManagerLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_ASYNC_TASK_MANAGER");
+#endif
+
+} // unnamed namespace
+
+AsyncTaskThread::AsyncTaskThread(AsyncTaskManager& asyncTaskManager)
+: mConditionalWait(),
+  mAsyncTaskManager(asyncTaskManager),
+  mLogFactory(Dali::Adaptor::Get().GetLogFactory()),
+  mDestroyThread(false),
+  mIsThreadStarted(false),
+  mIsThreadIdle(true)
+{
+}
+
+AsyncTaskThread::~AsyncTaskThread()
+{
+  // Stop the thread
+  {
+    ConditionalWait::ScopedLock lock(mConditionalWait);
+    mDestroyThread = true;
+    mConditionalWait.Notify(lock);
+  }
+
+  Join();
+}
+
+bool AsyncTaskThread::Request()
+{
+  if(!mIsThreadStarted)
+  {
+    Start();
+    mIsThreadStarted = true;
+  }
+
+  {
+    // Lock while adding task to the queue
+    ConditionalWait::ScopedLock lock(mConditionalWait);
+
+    if(mIsThreadIdle)
+    {
+      mIsThreadIdle = false;
+
+      // wake up the thread
+      mConditionalWait.Notify(lock);
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void AsyncTaskThread::Run()
+{
+  SetThreadName("AsyncTaskThread");
+  mLogFactory.InstallLogFunction();
+
+  while(!mDestroyThread)
+  {
+    AsyncTaskPtr task = mAsyncTaskManager.PopNextTaskToProcess();
+    if(!task)
+    {
+      ConditionalWait::ScopedLock lock(mConditionalWait);
+      mIsThreadIdle = true;
+      mConditionalWait.Wait(lock);
+    }
+    else
+    {
+      task->Process();
+      mAsyncTaskManager.CompleteTask(task);
+    }
+  }
+}
+
+Dali::AsyncTaskManager AsyncTaskManager::Get()
+{
+  Dali::AsyncTaskManager manager;
+  SingletonService singletonService(SingletonService::Get());
+  if(singletonService)
+  {
+    // Check whether the async task manager is already created
+    Dali::BaseHandle handle = singletonService.GetSingleton(typeid(Dali::AsyncTaskManager));
+    if(handle)
+    {
+      // If so, downcast the handle of singleton
+      manager = Dali::AsyncTaskManager(dynamic_cast<Internal::Adaptor::AsyncTaskManager*>(handle.GetObjectPtr()));
+    }
+
+    if(!manager)
+    {
+      // If not, create the async task manager and register it as a singleton
+      Internal::Adaptor::AsyncTaskManager* internalAsyncTaskManager = new Internal::Adaptor::AsyncTaskManager();
+      manager = Dali::AsyncTaskManager(internalAsyncTaskManager);
+      singletonService.Register(typeid(manager), manager);
+    }
+  }
+  return manager;
+}
+
+AsyncTaskManager::AsyncTaskManager()
+: mTasks(GetNumberOfThreads(NUMBER_OF_ASYNC_THREADS_ENV, DEFAULT_NUMBER_OF_ASYNC_THREADS), [&]() { return TaskHelper(*this); }),
+  mTrigger(new EventThreadCallback(MakeCallback(this, &AsyncTaskManager::TasksCompleted))),
+  mProcessorRegistered(false)
+{
+}
+
+AsyncTaskManager::~AsyncTaskManager()
+{
+  if(mProcessorRegistered)
+  {
+    Dali::Adaptor::Get().UnregisterProcessor(*this);
+  }
+
+  mTasks.Clear();
+}
+
+void AsyncTaskManager::AddTask(AsyncTaskPtr task)
+{
+  {
+    // Lock while adding task to the queue
+    Mutex::ScopedLock lock(mMutex);
+    mWaitingTasks.push_back(task);
+
+    // Finish all Running threads are working
+    if(mRunningTasks.size() >= mTasks.GetElementCount())
+    {
+      return;
+    }
+  }
+
+  size_t count = mTasks.GetElementCount();
+  size_t index = 0;
+  while(index++ < count)
+  {
+    auto processHelperIt = mTasks.GetNext();
+    DALI_ASSERT_ALWAYS(processHelperIt != mTasks.End());
+    if(processHelperIt->Request())
+    {
+      break;
+    }
+    // If all threads are busy, then it's ok just to push the task because they will try to get the next job.
+  }
+
+  if(!mProcessorRegistered)
+  {
+    Dali::Adaptor::Get().RegisterProcessor(*this);
+    mProcessorRegistered = true;
+  }
+
+  return;
+}
+
+void AsyncTaskManager::RemoveTask(AsyncTaskPtr 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();)
+      {
+        if((*it) && (*it) == task)
+        {
+          it = mWaitingTasks.erase(it);
+        }
+        else
+        {
+          it++;
+        }
+      }
+    }
+
+    if(!mRunningTasks.empty())
+    {
+      for(auto iter = mRunningTasks.begin(), endIter = mRunningTasks.end(); iter != endIter; ++iter)
+      {
+        if((*iter).first == task)
+        {
+          (*iter).second = true;
+        }
+      }
+    }
+
+    if(!mCompletedTasks.empty())
+    {
+      for(std::vector<AsyncTaskPtr>::iterator it = mCompletedTasks.begin(); it != mCompletedTasks.end();)
+      {
+        if((*it) && (*it) == task)
+        {
+          it = mCompletedTasks.erase(it);
+        }
+        else
+        {
+          it++;
+        }
+      }
+    }
+  }
+
+  UnregisterProcessor();
+}
+
+AsyncTaskPtr AsyncTaskManager::PopNextTaskToProcess()
+{
+  // Lock while popping task out from the queue
+  Mutex::ScopedLock lock(mMutex);
+
+  // pop out the next task from the queue
+  AsyncTaskPtr nextTask = nullptr;
+
+  for(auto iter = mWaitingTasks.begin(), endIter = mWaitingTasks.end(); iter != endIter; ++iter)
+  {
+    if((*iter)->IsReady())
+    {
+      nextTask = *iter;
+
+      // Add Running queue
+      mRunningTasks.push_back(std::make_pair(nextTask, false));
+      mWaitingTasks.erase(iter);
+      break;
+    }
+  }
+
+  return nextTask;
+}
+
+AsyncTaskPtr AsyncTaskManager::PopNextCompletedTask()
+{
+  // Lock while popping task out from the queue
+  Mutex::ScopedLock lock(mMutex);
+
+  if(mCompletedTasks.empty())
+  {
+    return AsyncTaskPtr();
+  }
+
+  std::vector<AsyncTaskPtr>::iterator next     = mCompletedTasks.begin();
+  AsyncTaskPtr                        nextTask = *next;
+  mCompletedTasks.erase(next);
+
+  return nextTask;
+}
+
+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)
+    {
+      if(!(*iter).second)
+      {
+        mCompletedTasks.push_back(task);
+      }
+
+      // Delete this task in running queue
+      mRunningTasks.erase(iter);
+      break;
+    }
+  }
+
+  // wake up the main thread
+  mTrigger->Trigger();
+}
+
+void AsyncTaskManager::UnregisterProcessor()
+{
+  if(mProcessorRegistered)
+  {
+    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<AsyncTaskThread>(new AsyncTaskThread(asyncTaskManager)), asyncTaskManager)
+{
+}
+
+AsyncTaskManager::TaskHelper::TaskHelper(TaskHelper&& rhs)
+: TaskHelper(std::move(rhs.mProcessor), rhs.mAsyncTaskManager)
+{
+}
+
+AsyncTaskManager::TaskHelper::TaskHelper(std::unique_ptr<AsyncTaskThread> processor, AsyncTaskManager& asyncTaskManager)
+: mProcessor(std::move(processor)),
+  mAsyncTaskManager(asyncTaskManager)
+{
+}
+
+bool AsyncTaskManager::TaskHelper::Request()
+{
+  return mProcessor->Request();
+}
+} // namespace Adaptor
+
+} // namespace Internal
+
+} // namespace Dali
diff --git a/dali/internal/system/common/async-task-manager-impl.h b/dali/internal/system/common/async-task-manager-impl.h
new file mode 100644 (file)
index 0000000..2007779
--- /dev/null
@@ -0,0 +1,238 @@
+#ifndef DALI_INTERNAL_ASYNC_TASK_MANAGER_H
+#define DALI_INTERNAL_ASYNC_TASK_MANAGER_H
+
+/*
+ * Copyright (c) 2022 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/event-thread-callback.h>
+#include <dali/devel-api/threading/conditional-wait.h>
+#include <dali/devel-api/threading/mutex.h>
+#include <dali/devel-api/threading/thread.h>
+#include <dali/integration-api/adaptor-framework/log-factory-interface.h>
+#include <dali/integration-api/processor-interface.h>
+#include <dali/public-api/object/base-object.h>
+#include <memory>
+
+// INTERNAL INCLUDES
+#include <dali/public-api/adaptor-framework/async-task-manager.h>
+#include <dali/public-api/adaptor-framework/round-robin-container-view.h>
+
+namespace Dali
+{
+namespace Internal
+{
+namespace Adaptor
+{
+class AsyncTaskManager;
+
+/**
+ * The worker thread for async process
+ */
+class AsyncTaskThread : public Thread
+{
+public:
+  /**
+   * Constructor.
+   */
+  AsyncTaskThread(AsyncTaskManager& asyncTaskManager);
+
+  /**
+   * Destructor.
+   */
+  ~AsyncTaskThread() override;
+
+  /**
+   * @brief Request the thread to process the task.
+   * @return True if the request is successed, otherwise false.
+   */
+  bool Request();
+
+protected:
+  /**
+   * The entry function of the worker thread.
+   */
+  void Run() override;
+
+private:
+  // Undefined
+  AsyncTaskThread(const AsyncTaskThread& thread) = delete;
+
+  // Undefined
+  AsyncTaskThread& operator=(const AsyncTaskThread& thread) = delete;
+
+private:
+  ConditionalWait                  mConditionalWait;
+  AsyncTaskManager&                mAsyncTaskManager;
+  const Dali::LogFactoryInterface& mLogFactory; ///< The log factory
+  bool                             mDestroyThread;
+  bool                             mIsThreadStarted;
+  bool                             mIsThreadIdle;
+};
+
+/**
+ * The manager for async task
+ */
+class AsyncTaskManager : public Dali::BaseObject, public Integration::Processor
+{
+public:
+  /**
+   * Singleton access
+   *
+   * @return The AsyncTaskManager object
+   */
+  static Dali::AsyncTaskManager Get();
+
+  /**
+   * Constructor.
+   */
+  AsyncTaskManager();
+
+  /**
+   * Destructor.
+   */
+  ~AsyncTaskManager() override;
+
+  /**
+   * @copydoc Dali::AsyncTaskManager::AddTask()
+   */
+  void AddTask(AsyncTaskPtr task);
+
+  /**
+   * @copydoc Dali::AsyncTaskManager::RemoveTask()
+   */
+  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.
+   */
+  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();
+
+  /**
+   * Execute the callback registered by tasks in the completed queue
+   */
+  void TasksCompleted();
+
+  /**
+   * @copydoc Dali::Integration::Processor::Process()
+   */
+  void Process(bool postProcessor) override;
+
+private:
+  /**
+   * @brief Helper class to keep the relation between AsyncTaskThread and corresponding container
+   */
+  class TaskHelper
+  {
+  public:
+    /**
+     * @brief Create an TaskHelper.
+     *
+     * @param[in] asyncTaskManager Reference to the AsyncTaskManager
+     */
+    TaskHelper(AsyncTaskManager& asyncTaskManager);
+
+    /**
+     * @brief Request the thread to process the task.
+     * @return True if the request succeeds, otherwise false.
+     */
+    bool Request();
+
+  public:
+    TaskHelper(const TaskHelper&) = delete;
+    TaskHelper& operator=(const TaskHelper&) = delete;
+
+    TaskHelper(TaskHelper&& rhs);
+    TaskHelper& operator=(TaskHelper&& rhs) = delete;
+
+  private:
+    /**
+     * @brief Main constructor that used by all other constructors
+     */
+    TaskHelper(std::unique_ptr<AsyncTaskThread> processor, AsyncTaskManager& asyncTaskManager);
+
+  private:
+    std::unique_ptr<AsyncTaskThread> mProcessor;
+    AsyncTaskManager&                mAsyncTaskManager;
+  };
+
+private:
+  // Undefined
+  AsyncTaskManager(const AsyncTaskManager& manager);
+
+  // Undefined
+  AsyncTaskManager& operator=(const AsyncTaskManager& manager);
+
+private:
+  std::vector<AsyncTaskPtr> mWaitingTasks;   //The queue of the tasks waiting to async process
+  std::vector<AsyncTaskPtr> mCompletedTasks; //The queue of the tasks with the async process
+
+  using AsyncTaskPair = std::pair<AsyncTaskPtr, bool>;
+  std::vector<AsyncTaskPair> mRunningTasks; ///< The queue of the running tasks
+
+  RoundRobinContainerView<TaskHelper> mTasks;
+
+  Dali::Mutex                          mMutex;
+  std::unique_ptr<EventThreadCallback> mTrigger;
+  bool                                 mProcessorRegistered;
+};
+
+} // namespace Adaptor
+
+} // namespace Internal
+
+inline Internal::Adaptor::AsyncTaskManager& GetImplementation(Dali::AsyncTaskManager& obj)
+{
+  DALI_ASSERT_ALWAYS(obj && "AsyncTaskManager is empty");
+
+  Dali::BaseObject& handle = obj.GetBaseObject();
+
+  return static_cast<Internal::Adaptor::AsyncTaskManager&>(handle);
+}
+
+inline const Internal::Adaptor::AsyncTaskManager& GetImplementation(const Dali::AsyncTaskManager& obj)
+{
+  DALI_ASSERT_ALWAYS(obj && "AsyncTaskManager is empty");
+
+  const Dali::BaseObject& handle = obj.GetBaseObject();
+
+  return static_cast<const Internal::Adaptor::AsyncTaskManager&>(handle);
+}
+
+} // namespace Dali
+
+#endif
index 07ca29c..d366736 100644 (file)
@@ -23,6 +23,7 @@ SET( adaptor_system_common_src_files
     ${adaptor_system_dir}/common/thread-controller.cpp
     ${adaptor_system_dir}/common/update-status-logger.cpp
     ${adaptor_system_dir}/common/widget-application-impl.cpp
+    ${adaptor_system_dir}/common/async-task-manager-impl.cpp
 )
 
 # module: system, backend: linux
diff --git a/dali/public-api/adaptor-framework/async-task-manager.cpp b/dali/public-api/adaptor-framework/async-task-manager.cpp
new file mode 100644 (file)
index 0000000..20a1a6c
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2022 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CLASS HEADER
+#include "async-task-manager.h"
+
+// INTERNAL INCLUDES
+#include <dali/internal/system/common/async-task-manager-impl.h>
+
+namespace Dali
+{
+
+AsyncTask::AsyncTask(CallbackBase* callback)
+: mCompletedCallback(std::unique_ptr<CallbackBase>(callback))
+{
+}
+
+CallbackBase* AsyncTask::GetCompletedCallback()
+{
+  return mCompletedCallback.get();
+}
+
+AsyncTaskManager::AsyncTaskManager() = default;
+
+AsyncTaskManager::~AsyncTaskManager() = default;
+
+AsyncTaskManager AsyncTaskManager::Get()
+{
+  return Internal::Adaptor::AsyncTaskManager::Get();
+}
+
+void AsyncTaskManager::AddTask(AsyncTaskPtr task)
+{
+  GetImplementation(*this).AddTask(task);
+}
+
+void AsyncTaskManager::RemoveTask(AsyncTaskPtr task)
+{
+  GetImplementation(*this).RemoveTask(task);
+}
+
+AsyncTaskManager::AsyncTaskManager(Internal::Adaptor::AsyncTaskManager* impl)
+: BaseHandle(impl)
+{
+}
+
+} // namespace Dali
diff --git a/dali/public-api/adaptor-framework/async-task-manager.h b/dali/public-api/adaptor-framework/async-task-manager.h
new file mode 100644 (file)
index 0000000..02021c1
--- /dev/null
@@ -0,0 +1,149 @@
+#ifndef DALI_ASYNC_TASK_MANAGER_H
+#define DALI_ASYNC_TASK_MANAGER_H
+
+/*
+ * Copyright (c) 2022 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/common/intrusive-ptr.h>
+#include <dali/public-api/object/base-handle.h>
+#include <dali/public-api/signals/callback.h>
+#include <memory>
+
+// INTERNAL INCLUDES
+#include <dali/public-api/dali-adaptor-common.h>
+
+namespace Dali
+{
+namespace Internal DALI_INTERNAL
+{
+namespace Adaptor
+{
+class AsyncTaskManager;
+}
+} // namespace DALI_INTERNAL
+
+class AsyncTask;
+using AsyncTaskPtr = IntrusivePtr<AsyncTask>;
+
+/**
+ * The async tasks to be processed in the worker thread.
+ * @SINCE_2_2.3
+ */
+class DALI_ADAPTOR_API AsyncTask : public RefObject
+{
+public:
+  /**
+   * Constructor
+   * @SINCE_2_2.3
+   * @param[in] callback The callback to up the main thread. The ownership of callback is taken by this class
+   */
+  AsyncTask(CallbackBase* callback);
+
+  /**
+   * Get the complated callback
+   * @SINCE_2_2.3
+   */
+  CallbackBase* GetCompletedCallback();
+
+  /**
+   * Destructor.
+   * @SINCE_2_2.3
+   */
+  virtual ~AsyncTask() = default;
+
+  /**
+   * Process the task
+   * @SINCE_2_2.3
+   */
+  virtual void Process() = 0;
+
+  /**
+   * Whether the task is ready to process.
+   * @SINCE_2_2.3
+   * @return True if the task is ready to process.
+   */
+  virtual bool IsReady() = 0;
+
+private:
+  std::unique_ptr<CallbackBase> mCompletedCallback;
+
+  // Undefined
+  AsyncTask(const AsyncTask& task) = delete;
+
+  // Undefined
+  AsyncTask& operator=(const AsyncTask& task) = delete;
+};
+
+/**
+ * The manager for async task
+ * @SINCE_2_2.3
+ */
+class DALI_ADAPTOR_API AsyncTaskManager : public BaseHandle
+{
+public:
+  /**
+   * Constructor.
+   * @SINCE_2_2.3
+   */
+  AsyncTaskManager();
+
+  /**
+   * Destructor.
+   * @SINCE_2_2.3
+   */
+  ~AsyncTaskManager();
+
+  /**
+   * @brief Gets the singleton of AsyncTaskManager object.
+   *
+   * @SINCE_2_2.3
+   * @return A handle to the AsyncTaskManager
+   */
+  static AsyncTaskManager Get();
+
+  /**
+   * @brief Add the async task into the waiting queue, called by main thread.
+   *
+   * @SINCE_2_2.3
+   * @param[in] task The task added to the queue.
+   */
+  void AddTask(AsyncTaskPtr task);
+
+  /**
+   * @brief Remove the task from the waiting queue, called by main thread.
+   *
+   * @SINCE_2_2.3
+   * @param[in] task The task pointer.
+   */
+  void RemoveTask(AsyncTaskPtr task);
+
+public:
+  /// @cond internal
+  /**
+   * @brief Allows the creation of a AsyncTaskManager handle from an internal pointer.
+   *
+   * @note Not intended for application developers
+   * @SINCE_2_2.3
+   * @param[in] impl A pointer to the object
+   */
+  explicit DALI_INTERNAL AsyncTaskManager(Internal::Adaptor::AsyncTaskManager* impl);
+  /// @endcond
+};
+
+} // namespace Dali
+
+#endif
diff --git a/dali/public-api/adaptor-framework/round-robin-container-view.h b/dali/public-api/adaptor-framework/round-robin-container-view.h
new file mode 100644 (file)
index 0000000..66d1e32
--- /dev/null
@@ -0,0 +1,128 @@
+#ifndef DALI_ROUND_ROBIN_CONTAINER_VIEW_H
+#define DALI_ROUND_ROBIN_CONTAINER_VIEW_H
+
+/*
+ * Copyright (c) 2022 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <cstddef>
+#include <vector>
+
+namespace Dali
+{
+/**
+ * @brief RoundRobinContainerView is a view to a container that allows iterating through the elements cyclically.
+ */
+template<typename T>
+class RoundRobinContainerView
+{
+public:
+  using ContainerType = std::vector<T>;
+
+  /**
+   * @brief Constructs a new RoundRobinControlView with the given number elements using the provided factory.
+   * @param[in] numberOfElements The number of elements in the container
+   * @param[in] factory          Factory function of functor that will be used to create instances of the elements
+   */
+  template<typename FactoryType>
+  RoundRobinContainerView(size_t numberOfElements, const FactoryType& factory)
+  : mElements(),
+    mNextIndex{}
+  {
+    mElements.reserve(numberOfElements);
+    for(unsigned i = {}; i < numberOfElements; ++i)
+    {
+      mElements.push_back(factory());
+    }
+  }
+
+  /**
+   * @brief Clear all elements.
+   */
+  void Clear()
+  {
+    mElements.clear();
+  }
+
+  /**
+   * @brief Reset the position of the iterator returned by GetNext() to the first element.
+   */
+  void Reset()
+  {
+    mNextIndex = 0u;
+  }
+
+  /**
+   * @brief Returns the next element on the container.
+   * @return Iterator for the next element
+   */
+  typename ContainerType::iterator GetNext()
+  {
+    SetValidNextIndex();
+
+    return mElements.begin() + mNextIndex++;
+  }
+
+  /**
+   * @brief Returns the iterator to the end of the container.
+   *
+   * Can be used to compare against GetNext() to check if the container is empty.
+   *
+   * @return The container end() element
+   */
+  typename ContainerType::const_iterator End() const
+  {
+    return mElements.cend();
+  }
+
+  /**
+   * @brief Returns the element count.
+   * @return The element count
+   */
+  size_t GetElementCount() const
+  {
+    return mElements.size();
+  }
+
+  // default members
+  ~RoundRobinContainerView() = default;
+
+  RoundRobinContainerView(const RoundRobinContainerView&) = delete;
+  RoundRobinContainerView& operator=(const RoundRobinContainerView&) = delete;
+  RoundRobinContainerView(RoundRobinContainerView&&)                 = default;
+  RoundRobinContainerView& operator=(RoundRobinContainerView&&) = default;
+
+private:
+  /**
+   * @brief Check the current index and reset if necessary.
+   */
+  void SetValidNextIndex()
+  {
+    if(mNextIndex >= mElements.size())
+    {
+      Reset();
+    }
+  }
+
+private:
+  ContainerType mElements;  //< container of elements
+  size_t        mNextIndex; //< index to the next element to be viewed
+};
+
+} // namespace Dali
+
+#endif // DALI_ROUND_ROBIN_CONTAINER_VIEW_H
index cd52e1f..f13b5ff 100644 (file)
@@ -11,6 +11,7 @@ SET( adaptor_public_api_src_files
   ${adaptor_public_api_dir}/adaptor-framework/widget.cpp
   ${adaptor_public_api_dir}/adaptor-framework/widget-application.cpp
   ${adaptor_public_api_dir}/adaptor-framework/widget-impl.cpp
+  ${adaptor_public_api_dir}/adaptor-framework/async-task-manager.cpp
   ${adaptor_public_api_dir}/capture/capture.cpp
   ${adaptor_public_api_dir}/dali-adaptor-version.cpp
 )
@@ -38,9 +39,10 @@ SET( public_api_adaptor_framework_header_files
   ${adaptor_public_api_dir}/adaptor-framework/widget-application.h
   ${adaptor_public_api_dir}/adaptor-framework/widget-impl.h
   ${adaptor_public_api_dir}/adaptor-framework/window-enumerations.h
+  ${adaptor_public_api_dir}/adaptor-framework/round-robin-container-view.h
+  ${adaptor_public_api_dir}/adaptor-framework/async-task-manager.h
 )
 
-
 SET( public_dali_capture_header_files
   ${adaptor_public_api_dir}/capture/capture.h
 )