1 /* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef PANDA_LIBPANDABASE_TASKMANAGER_TASK_MANAGER_H 17 #define PANDA_LIBPANDABASE_TASKMANAGER_TASK_MANAGER_H 18 19 #include "libpandabase/taskmanager/task_queue.h" 20 #include "libpandabase/taskmanager/task_statistics/task_statistics.h" 21 #include "libpandabase/taskmanager/worker_thread.h" 22 #include <vector> 23 #include <map> 24 #include <random> 25 26 namespace panda::taskmanager { 27 /** 28 * Task Manager can register 3 queues with different type of tasks 29 * - GC queue(ECMA) 30 * - GC queue(ArkTS) 31 * - JIT queue 32 */ 33 class TaskScheduler { 34 public: 35 NO_COPY_SEMANTIC(TaskScheduler); 36 NO_MOVE_SEMANTIC(TaskScheduler); 37 38 /** 39 * @brief Creates an instance of TaskScheduler. 40 * @param threads_count - number of worker that will be created be Task Manager 41 * @param task_statistics_type - type of TaskStatistics that will be used in TaskScheduler 42 */ 43 PANDA_PUBLIC_API static TaskScheduler *Create( 44 size_t threadsCount, TaskStatisticsImplType taskStatisticsType = TaskStatisticsImplType::SIMPLE); 45 46 /** 47 * @brief Returns the pointer to TaskScheduler. If you use it before the Create or after Destroy methods, it 48 * will return nullptr. 49 */ 50 [[nodiscard]] PANDA_PUBLIC_API static TaskScheduler *GetTaskScheduler(); 51 52 /// @brief Deletes the existed TaskScheduler. You should not use it if you didn't use Create before. 53 PANDA_PUBLIC_API static void Destroy(); 54 55 /** 56 * @brief Creates and starts workers with registered queues. After this method, you can not register new 57 * queues. 58 */ 59 PANDA_PUBLIC_API void Initialize(); 60 61 /** 62 * @brief Method allocates, constructs and registers TaskQueue. If it already exists, method returns nullptr. 63 * @param task_type - TaskType of future TaskQueue. 64 * @param vm_type - VMType of future TaskQueue. 65 * @param priority - value of priority: 66 * TaskQueueInterface::MIN_PRIORITY <= priority <= TaskQueueInterface::MIN_PRIORITY 67 * @tparam Allocator - allocator of Task that will be used in internal queues. By default is used 68 * std::allocator<Task> 69 */ 70 template <class Allocator = std::allocator<Task>> 71 PANDA_PUBLIC_API TaskQueueInterface *CreateAndRegisterTaskQueue( 72 TaskType taskType, VMType vmType, uint8_t priority = TaskQueueInterface::DEFAULT_PRIORITY) 73 { 74 auto *queue = internal::TaskQueue<Allocator>::Create(taskType, vmType, priority); 75 if (UNLIKELY(queue == nullptr)) { 76 return nullptr; 77 } 78 auto id = RegisterQueue(queue); 79 if (UNLIKELY(id == INVALID_TASKQUEUE_ID)) { 80 internal::TaskQueue<Allocator>::Destroy(queue); 81 return nullptr; 82 } 83 return queue; 84 } 85 86 /** 87 * @brief Method Destroy and Unregister TaskQueue 88 * @param queue - TaskQueueInterface* of TaskQueue. 89 * @tparam Allocator - allocator of Task that will be used to deallocate TaskQueue. Use the same allocator as 90 * you have used in TaskScheduler::CreateAndRegisterTaskQueue method. 91 */ 92 template <class Allocator = std::allocator<Task>> UnregisterAndDestroyTaskQueue(TaskQueueInterface * queue)93 PANDA_PUBLIC_API void UnregisterAndDestroyTaskQueue(TaskQueueInterface *queue) 94 { 95 TaskQueueId id(queue->GetTaskType(), queue->GetVMType()); 96 auto *schedulableQueue = taskQueues_[id]; 97 98 schedulableQueue->UnsetNewTasksCallback(); 99 taskQueues_.erase(id); 100 internal::TaskQueue<Allocator>::Destroy(schedulableQueue); 101 } 102 103 /** 104 * @brief Fills @arg worker (local queues) with tasks. It will stop if the size of the queue is equal to @arg 105 * tasks_count. Method @return bool value that indicates worker end. If it's true, workers should finish after 106 * the execution of tasks. 107 * @param worker - pointer on worker that should be fill will tasks 108 * @param tasks_count - max number of tasks for filling 109 */ 110 bool FillWithTasks(WorkerThread *worker, size_t tasksCount); 111 112 /** 113 * @brief Method returns Task with specified properties. If there are no tasks with that properties method will 114 * return nullopt. 115 * @param properties - TaskProperties of task we want to get. 116 */ 117 [[nodiscard]] PANDA_PUBLIC_API std::optional<Task> GetTaskFromQueue(TaskProperties properties); 118 119 /** 120 * @brief Method waits all tasks with specified properties. This method should be used only from Main Thread and 121 * only for finalization! 122 * @param properties - TaskProperties of tasks we will wait to be completed. 123 */ 124 PANDA_PUBLIC_API void WaitForFinishAllTasksWithProperties(TaskProperties properties); 125 126 /// @brief This method indicates that workers can no longer wait for new tasks and be completed. 127 PANDA_PUBLIC_API void Finalize(); 128 129 PANDA_PUBLIC_API ~TaskScheduler(); 130 131 private: 132 explicit TaskScheduler(size_t workersCount, TaskStatisticsImplType taskStatisticsType); 133 134 /** 135 * @brief Registers a queue that was created externally. It should be valid until all workers finish, and the 136 * queue should have a unique set of TaskType and VMType fields. You can not use this method after Initialize() 137 * method 138 * @param queue: pointer to a valid TaskQueue. 139 * @return TaskQueueId of queue that was added. If queue with same TaskType and VMType is already added, method 140 * returns INVALID_TASKQUEUE_ID 141 */ 142 PANDA_PUBLIC_API TaskQueueId RegisterQueue(internal::SchedulableTaskQueueInterface *queue); 143 144 /** 145 * @brief Method pops one task from internal queues based on priorities. 146 * @return if queue are empty, returns nullopt, otherwise returns task. 147 */ 148 [[nodiscard]] std::optional<Task> GetNextTask() REQUIRES(popFromTaskQueuesLock_); 149 150 /** 151 * @brief Method selects queues for getting tasks by updating selected_queues_ 152 * @param tasks_count - count of tasks to select. 153 */ 154 void SelectNextTasks(size_t tasksCount) REQUIRES(popFromTaskQueuesLock_); 155 156 /** 157 * @brief Method puts tasks to @arg worker. Queue and count of tasks depends on selected_queues_. After 158 * execution of the method selected_queues_ will be empty. 159 * @param worker - pointer on worker that should be fill with tasks 160 * @return count of task that was gotten by worker. 161 */ 162 size_t PutTasksInWorker(WorkerThread *worker) REQUIRES(popFromTaskQueuesLock_); 163 164 /** 165 * @brief This method updates map from kinetic sum of non-empty queues to queues pointer 166 * with the same order as they place in task_queues_. 167 */ 168 void UpdateKineticPriorities() REQUIRES(popFromTaskQueuesLock_); 169 170 /// @brief Checks if task queues are empty 171 bool AreQueuesEmpty() const; 172 173 /// @brief Checks if there are no tasks in queues and workers 174 bool AreNoMoreTasks() const; 175 176 /** 177 * @brief Method increment counter of new tasks and signal worker 178 * @param properties - TaskProperties of task from queue that execute the callback 179 * @param ivalue - the value by which the counter will be increased 180 * @param was_empty - flage that should be true only, if queue was empty before last AddTask 181 */ 182 void IncrementCounterOfAddedTasks(TaskProperties properties, size_t ivalue, bool wasEmpty); 183 184 /** 185 * @brief Method increment counter of finished tasks and signal Finalize waiter 186 * @param counter_map - map from id to count of finished tasks 187 */ 188 void IncrementCounterOfExecutedTasks(const TaskPropertiesCounterMap &counterMap); 189 190 static TaskScheduler *instance_; 191 192 size_t workersCount_; 193 194 /// Pointers to Worker Threads. 195 std::vector<WorkerThread *> workers_; 196 197 /// workers_lock_ is used to synchronize a lot of workers in FillWithTasks method 198 os::memory::Mutex workersLock_; 199 200 /// pop_from_task_queues_lock_ is used to guard popping from queues 201 os::memory::Mutex popFromTaskQueuesLock_; 202 203 /** 204 * Map from TaskType and VMType to queue. 205 * Can be changed only before Initialize methods. 206 * Since we can change the map only before creating the workers, we do not need to synchronize access after 207 * Initialize method 208 */ 209 std::map<TaskQueueId, internal::SchedulableTaskQueueInterface *> taskQueues_; 210 211 /// task_scheduler_state_lock_ is used to check state of task 212 os::memory::RecursiveMutex taskSchedulerStateLock_; 213 214 /** 215 * queues_wait_cond_var_ is used when all registered queues are empty to wait until one of them will have a 216 * task 217 */ 218 os::memory::ConditionVariable queuesWaitCondVar_ GUARDED_BY(taskSchedulerStateLock_); 219 220 /** 221 * This cond var uses to wait for all tasks will be done. 222 * It is used in Finalize() method. 223 */ 224 os::memory::ConditionVariable finishTasksCondVar_ GUARDED_BY(taskSchedulerStateLock_); 225 226 /// start_ is true if we used Initialize method 227 std::atomic_bool start_ {false}; 228 229 /// finish_ is true when TaskScheduler finish Workers and TaskQueues GUARDED_BY(taskSchedulerStateLock_)230 bool finish_ GUARDED_BY(taskSchedulerStateLock_) {false}; 231 232 /// new_tasks_count_ represents count of new tasks 233 TaskPropertiesCounterMap newTasksCount_ GUARDED_BY(taskSchedulerStateLock_); 234 235 /** 236 * finished_tasks_count_ represents count of finished tasks; 237 * Task is finished if: 238 * - it was executed by Worker; 239 * - it was gotten by main thread; 240 */ 241 TaskPropertiesCounterMap finishedTasksCount_ GUARDED_BY(taskSchedulerStateLock_); 242 243 std::mt19937 gen_; 244 245 TaskStatistics *taskStatistics_; 246 247 std::map<TaskQueueId, size_t> selectedQueues_ GUARDED_BY(popFromTaskQueuesLock_); 248 249 std::map<size_t, internal::SchedulableTaskQueueInterface *> kineticPriorities_ GUARDED_BY(popFromTaskQueuesLock_); 250 }; 251 252 } // namespace panda::taskmanager 253 254 #endif // PANDA_LIBPANDABASE_TASKMANAGER_TASK_MANAGER_H 255