| //===------------ TaskDispatch.cpp - ORC task dispatch utils --------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/ExecutionEngine/Orc/TaskDispatch.h" |
| #include "llvm/Config/llvm-config.h" // for LLVM_ENABLE_THREADS |
| #include "llvm/ExecutionEngine/Orc/Core.h" |
| |
| namespace llvm { |
| namespace orc { |
| |
| char Task::ID = 0; |
| char GenericNamedTask::ID = 0; |
| char IdleTask::ID = 0; |
| |
| const char *GenericNamedTask::DefaultDescription = "Generic Task"; |
| |
| void Task::anchor() {} |
| void IdleTask::anchor() {} |
| |
| TaskDispatcher::~TaskDispatcher() = default; |
| |
| void InPlaceTaskDispatcher::dispatch(std::unique_ptr<Task> T) { T->run(); } |
| |
| void InPlaceTaskDispatcher::shutdown() {} |
| |
| #if LLVM_ENABLE_THREADS |
| void DynamicThreadPoolTaskDispatcher::dispatch(std::unique_ptr<Task> T) { |
| |
| enum { Normal, Materialization, Idle } TaskKind; |
| |
| if (isa<MaterializationTask>(*T)) |
| TaskKind = Materialization; |
| else if (isa<IdleTask>(*T)) |
| TaskKind = Idle; |
| else |
| TaskKind = Normal; |
| |
| { |
| std::lock_guard<std::mutex> Lock(DispatchMutex); |
| |
| // Reject new tasks if they're dispatched after a call to shutdown. |
| if (Shutdown) |
| return; |
| |
| if (TaskKind == Materialization) { |
| |
| // If this is a materialization task and there are too many running |
| // already then queue this one up and return early. |
| if (!canRunMaterializationTaskNow()) |
| return MaterializationTaskQueue.push_back(std::move(T)); |
| |
| // Otherwise record that we have a materialization task running. |
| ++NumMaterializationThreads; |
| } else if (TaskKind == Idle) { |
| if (!canRunIdleTaskNow()) |
| return IdleTaskQueue.push_back(std::move(T)); |
| } |
| |
| ++Outstanding; |
| } |
| |
| std::thread([this, T = std::move(T), TaskKind]() mutable { |
| while (true) { |
| |
| // Run the task. |
| T->run(); |
| |
| // Reset the task to free any resources. We need this to happen *before* |
| // we notify anyone (via Outstanding) that this thread is done to ensure |
| // that we don't proceed with JIT shutdown while still holding resources. |
| // (E.g. this was causing "Dangling SymbolStringPtr" assertions). |
| T.reset(); |
| |
| // Check the work queue state and either proceed with the next task or |
| // end this thread. |
| std::lock_guard<std::mutex> Lock(DispatchMutex); |
| |
| if (TaskKind == Materialization) |
| --NumMaterializationThreads; |
| --Outstanding; |
| |
| if (!MaterializationTaskQueue.empty() && canRunMaterializationTaskNow()) { |
| // If there are any materialization tasks running then steal that work. |
| T = std::move(MaterializationTaskQueue.front()); |
| MaterializationTaskQueue.pop_front(); |
| TaskKind = Materialization; |
| ++NumMaterializationThreads; |
| ++Outstanding; |
| } else if (!IdleTaskQueue.empty() && canRunIdleTaskNow()) { |
| T = std::move(IdleTaskQueue.front()); |
| IdleTaskQueue.pop_front(); |
| TaskKind = Idle; |
| ++Outstanding; |
| } else { |
| if (Outstanding == 0) |
| OutstandingCV.notify_all(); |
| return; |
| } |
| } |
| }).detach(); |
| } |
| |
| void DynamicThreadPoolTaskDispatcher::shutdown() { |
| std::unique_lock<std::mutex> Lock(DispatchMutex); |
| Shutdown = true; |
| OutstandingCV.wait(Lock, [this]() { return Outstanding == 0; }); |
| } |
| |
| bool DynamicThreadPoolTaskDispatcher::canRunMaterializationTaskNow() { |
| return !MaxMaterializationThreads || |
| (NumMaterializationThreads < *MaxMaterializationThreads); |
| } |
| |
| bool DynamicThreadPoolTaskDispatcher::canRunIdleTaskNow() { |
| return !MaxMaterializationThreads || |
| (Outstanding < *MaxMaterializationThreads); |
| } |
| |
| #endif |
| |
| } // namespace orc |
| } // namespace llvm |