//===------------ 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 T) { T->run(); } void InPlaceTaskDispatcher::shutdown() {} #if LLVM_ENABLE_THREADS void DynamicThreadPoolTaskDispatcher::dispatch(std::unique_ptr T) { enum { Normal, Materialization, Idle } TaskKind; if (isa(*T)) TaskKind = Materialization; else if (isa(*T)) TaskKind = Idle; else TaskKind = Normal; { std::lock_guard 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 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 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