// Copyright 2023 PingCAP, Inc. // // 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. #include #include #include #include #include #include #include #include #include #include #include #include namespace DB { template TaskThreadPool::TaskThreadPool(TaskScheduler & scheduler_, const ThreadPoolConfig & config) : task_queue(Impl::newTaskQueue(config.queue_type)) , scheduler(scheduler_) { RUNTIME_CHECK(config.pool_size > 0); threads.reserve(config.pool_size); for (size_t i = 0; i < config.pool_size; ++i) threads.emplace_back(&TaskThreadPool::loop, this, i); } template void TaskThreadPool::finish() { task_queue->finish(); } template void TaskThreadPool::waitForStop() { for (auto & thread : threads) thread.join(); LOG_INFO(logger, "task thread pool is stopped"); } template void TaskThreadPool::loop(size_t thread_no) { try { CPUAffinityManager::getInstance().bindSelfQueryThread(); doLoop(thread_no); } CATCH_AND_TERMINATE(logger) } template void TaskThreadPool::doLoop(size_t thread_no) { setThreadName(Impl::NAME); metrics.incThreadCnt(); SCOPE_EXIT({ metrics.decThreadCnt(); }); auto thread_no_str = fmt::format("thread_no={}", thread_no); auto thread_logger = logger->getChild(thread_no_str); LOG_INFO(thread_logger, "start loop"); TaskPtr task; while (likely(task_queue->take(task))) { metrics.decPendingTask(); handleTask(task); assert(!task); } LOG_INFO(thread_logger, "loop finished"); } template void TaskThreadPool::handleTask(TaskPtr & task) { assert(task); task->startTraceMemory(); metrics.incExecutingTask(); metrics.elapsedPendingTime(task); auto status_before_exec = task->getStatus(); auto status_after_exec = status_before_exec; UInt64 total_time_spent = 0; while (true) { status_after_exec = Impl::exec(task); total_time_spent += task->profile_info.elapsedFromPrev(); // The executing task should yield if it takes more than `YIELD_MAX_TIME_SPENT_NS`. if (!Impl::isTargetStatus(status_after_exec) || total_time_spent >= YIELD_MAX_TIME_SPENT_NS) break; } task_queue->updateStatistics(task, status_before_exec, total_time_spent); metrics.addExecuteTime(task, total_time_spent); metrics.decExecutingTask(); switch (status_after_exec) { case ExecTaskStatus::RUNNING: task->endTraceMemory(); scheduler.submitToCPUTaskThreadPool(std::move(task)); break; case ExecTaskStatus::IO_IN: case ExecTaskStatus::IO_OUT: task->endTraceMemory(); scheduler.submitToIOTaskThreadPool(std::move(task)); break; case ExecTaskStatus::WAITING: task->endTraceMemory(); scheduler.submitToWaitReactor(std::move(task)); break; case ExecTaskStatus::WAIT_FOR_NOTIFY: task->endTraceMemory(); registerTaskToFuture(std::move(task)); break; case FINISH_STATUS: task->finalize(); task->endTraceMemory(); task.reset(); break; default: UNEXPECTED_STATUS(task->log, status_after_exec); } } template void TaskThreadPool::submit(TaskPtr && task) { metrics.incPendingTask(1); task_queue->submit(std::move(task)); } template void TaskThreadPool::submit(std::vector & tasks) { metrics.incPendingTask(tasks.size()); task_queue->submit(tasks); } template void TaskThreadPool::cancel(const String & query_id, const String & resource_group_name) { task_queue->cancel(query_id, resource_group_name); } template class TaskThreadPool; template class TaskThreadPool; } // namespace DB