refactor: Task Manager related code
This commit is contained in:
parent
1be9e8c5b1
commit
37ac1b66dd
@ -166,6 +166,8 @@ namespace hex {
|
||||
|
||||
private:
|
||||
static void runner(const std::stop_token &stopToken);
|
||||
|
||||
static TaskHolder createTask(std::string name, u64 maxValue, bool background, std::function<void(Task &)> function);
|
||||
};
|
||||
|
||||
}
|
@ -82,8 +82,11 @@ namespace hex {
|
||||
}
|
||||
|
||||
void Task::update(u64 value) {
|
||||
// Update the current progress value of the task
|
||||
this->m_currValue.store(value, std::memory_order_relaxed);
|
||||
|
||||
// Check if the task has been interrupted by the main thread and if yes,
|
||||
// throw an exception that is generally not caught by the task
|
||||
if (this->m_shouldInterrupt.load(std::memory_order_relaxed)) [[unlikely]]
|
||||
throw TaskInterruptor();
|
||||
}
|
||||
@ -96,6 +99,7 @@ namespace hex {
|
||||
void Task::interrupt() {
|
||||
this->m_shouldInterrupt = true;
|
||||
|
||||
// Call the interrupt callback on the current thread if one is set
|
||||
if (this->m_interruptCallback)
|
||||
this->m_interruptCallback();
|
||||
}
|
||||
@ -157,59 +161,62 @@ namespace hex {
|
||||
void Task::exception(const char *message) {
|
||||
std::scoped_lock lock(this->m_mutex);
|
||||
|
||||
// Store information about the caught exception
|
||||
this->m_exceptionMessage = message;
|
||||
this->m_hadException = true;
|
||||
}
|
||||
|
||||
|
||||
bool TaskHolder::isRunning() const {
|
||||
if (this->m_task.expired())
|
||||
auto task = this->m_task.lock();
|
||||
if (!task)
|
||||
return false;
|
||||
|
||||
auto task = this->m_task.lock();
|
||||
return !task->isFinished();
|
||||
}
|
||||
|
||||
bool TaskHolder::hadException() const {
|
||||
if (this->m_task.expired())
|
||||
auto task = this->m_task.lock();
|
||||
if (!task)
|
||||
return false;
|
||||
|
||||
auto task = this->m_task.lock();
|
||||
return !task->hadException();
|
||||
}
|
||||
|
||||
bool TaskHolder::shouldInterrupt() const {
|
||||
if (this->m_task.expired())
|
||||
auto task = this->m_task.lock();
|
||||
if (!task)
|
||||
return false;
|
||||
|
||||
auto task = this->m_task.lock();
|
||||
return !task->shouldInterrupt();
|
||||
}
|
||||
|
||||
bool TaskHolder::wasInterrupted() const {
|
||||
if (this->m_task.expired())
|
||||
auto task = this->m_task.lock();
|
||||
if (!task)
|
||||
return false;
|
||||
|
||||
auto task = this->m_task.lock();
|
||||
return !task->wasInterrupted();
|
||||
}
|
||||
|
||||
void TaskHolder::interrupt() const {
|
||||
if (this->m_task.expired())
|
||||
auto task = this->m_task.lock();
|
||||
if (!task)
|
||||
return;
|
||||
|
||||
auto task = this->m_task.lock();
|
||||
task->interrupt();
|
||||
}
|
||||
|
||||
u32 TaskHolder::getProgress() const {
|
||||
if (this->m_task.expired())
|
||||
return 0;
|
||||
|
||||
auto task = this->m_task.lock();
|
||||
if (!task)
|
||||
return false;
|
||||
|
||||
// If the max value is 0, the task has no progress
|
||||
if (task->getMaxValue() == 0)
|
||||
return 0;
|
||||
|
||||
// Calculate the progress of the task from 0 to 100
|
||||
return u32((task->getValue() * 100) / task->getMaxValue());
|
||||
}
|
||||
|
||||
@ -219,49 +226,74 @@ namespace hex {
|
||||
|
||||
log::debug("Initializing task manager thread pool with {} workers.", threadCount);
|
||||
|
||||
// Create worker threads
|
||||
for (u32 i = 0; i < threadCount; i++)
|
||||
s_workers.emplace_back(TaskManager::runner);
|
||||
}
|
||||
|
||||
void TaskManager::exit() {
|
||||
for (auto &task : s_tasks)
|
||||
// Interrupt all tasks
|
||||
for (auto &task : s_tasks) {
|
||||
task->interrupt();
|
||||
}
|
||||
|
||||
// Ask worker threads to exit after finishing their task
|
||||
for (auto &thread : s_workers)
|
||||
thread.request_stop();
|
||||
|
||||
// Wake up all the idle worker threads so they can exit
|
||||
s_jobCondVar.notify_all();
|
||||
|
||||
// Wait for all worker threads to exit
|
||||
s_workers.clear();
|
||||
|
||||
s_tasks.clear();
|
||||
s_taskQueue.clear();
|
||||
}
|
||||
|
||||
void TaskManager::runner(const std::stop_token &stopToken) {
|
||||
while (true) {
|
||||
std::shared_ptr<Task> task;
|
||||
|
||||
// Set the thread name to "Idle Task" while waiting for a task
|
||||
setThreadName("Idle Task");
|
||||
|
||||
{
|
||||
// Wait for a task to be added to the queue
|
||||
std::unique_lock lock(s_queueMutex);
|
||||
s_jobCondVar.wait(lock, [&] {
|
||||
return !s_taskQueue.empty() || stopToken.stop_requested();
|
||||
});
|
||||
|
||||
// Check if the thread should exit
|
||||
if (stopToken.stop_requested())
|
||||
break;
|
||||
|
||||
// Grab the next task from the queue
|
||||
task = std::move(s_taskQueue.front());
|
||||
s_taskQueue.pop_front();
|
||||
}
|
||||
|
||||
try {
|
||||
// Set the thread name to the name of the task
|
||||
setThreadName(Lang(task->m_unlocalizedName));
|
||||
|
||||
// Execute the task
|
||||
task->m_function(*task);
|
||||
setThreadName("Idle Task");
|
||||
log::debug("Finished task {}", task->m_unlocalizedName);
|
||||
|
||||
log::debug("Task '{}' finished", task->m_unlocalizedName);
|
||||
} catch (const Task::TaskInterruptor &) {
|
||||
// Handle the task being interrupted by user request
|
||||
task->interruption();
|
||||
} catch (const std::exception &e) {
|
||||
log::error("Exception in task {}: {}", task->m_unlocalizedName, e.what());
|
||||
log::error("Exception in task '{}': {}", task->m_unlocalizedName, e.what());
|
||||
|
||||
// Handle the task throwing an uncaught exception
|
||||
task->exception(e.what());
|
||||
} catch (...) {
|
||||
log::error("Exception in task {}", task->m_unlocalizedName);
|
||||
log::error("Exception in task '{}'", task->m_unlocalizedName);
|
||||
|
||||
// Handle the task throwing an uncaught exception of unknown type
|
||||
task->exception("Unknown Exception");
|
||||
}
|
||||
|
||||
@ -269,39 +301,43 @@ namespace hex {
|
||||
}
|
||||
}
|
||||
|
||||
TaskHolder TaskManager::createTask(std::string name, u64 maxValue, std::function<void(Task &)> function) {
|
||||
log::debug("Creating task {}", name);
|
||||
std::unique_lock lock(s_queueMutex);
|
||||
auto task = std::make_shared<Task>(std::move(name), maxValue, false, std::move(function));
|
||||
TaskHolder TaskManager::createTask(std::string name, u64 maxValue, bool background, std::function<void(Task&)> function) {
|
||||
std::scoped_lock lock(s_queueMutex);
|
||||
|
||||
// Construct new task
|
||||
auto task = std::make_shared<Task>(std::move(name), maxValue, background, std::move(function));
|
||||
|
||||
s_tasks.emplace_back(task);
|
||||
s_taskQueue.emplace_back(task);
|
||||
|
||||
// Add task to the queue for the worker to pick up
|
||||
s_taskQueue.emplace_back(std::move(task));
|
||||
|
||||
s_jobCondVar.notify_one();
|
||||
|
||||
return TaskHolder(s_tasks.back());
|
||||
}
|
||||
|
||||
|
||||
TaskHolder TaskManager::createTask(std::string name, u64 maxValue, std::function<void(Task &)> function) {
|
||||
log::debug("Creating task {}", name);
|
||||
return createTask(std::move(name), maxValue, false, std::move(function));
|
||||
}
|
||||
|
||||
TaskHolder TaskManager::createBackgroundTask(std::string name, std::function<void(Task &)> function) {
|
||||
log::debug("Creating background task {}", name);
|
||||
std::unique_lock lock(s_queueMutex);
|
||||
|
||||
auto task = std::make_shared<Task>(std::move(name), 0, true, std::move(function));
|
||||
s_tasks.emplace_back(task);
|
||||
s_taskQueue.emplace_back(task);
|
||||
|
||||
s_jobCondVar.notify_one();
|
||||
|
||||
return TaskHolder(s_tasks.back());
|
||||
return createTask(std::move(name), 0, true, std::move(function));
|
||||
}
|
||||
|
||||
void TaskManager::collectGarbage() {
|
||||
{
|
||||
std::unique_lock lock1(s_queueMutex);
|
||||
std::erase_if(s_tasks, [](const auto &task) { return task->isFinished() && !task->hadException(); });
|
||||
std::scoped_lock lock(s_queueMutex);
|
||||
std::erase_if(s_tasks, [](const auto &task) {
|
||||
return task->isFinished() && !task->hadException();
|
||||
});
|
||||
}
|
||||
|
||||
if (s_tasks.empty()) {
|
||||
std::unique_lock lock2(s_deferredCallsMutex);
|
||||
std::scoped_lock lock(s_deferredCallsMutex);
|
||||
for (auto &call : s_tasksFinishedCallbacks)
|
||||
call();
|
||||
s_tasksFinishedCallbacks.clear();
|
||||
@ -314,7 +350,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
size_t TaskManager::getRunningTaskCount() {
|
||||
std::unique_lock lock(s_queueMutex);
|
||||
std::scoped_lock lock(s_queueMutex);
|
||||
|
||||
return std::count_if(s_tasks.begin(), s_tasks.end(), [](const auto &task){
|
||||
return !task->isBackgroundTask();
|
||||
@ -322,7 +358,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
size_t TaskManager::getRunningBackgroundTaskCount() {
|
||||
std::unique_lock lock(s_queueMutex);
|
||||
std::scoped_lock lock(s_queueMutex);
|
||||
|
||||
return std::count_if(s_tasks.begin(), s_tasks.end(), [](const auto &task){
|
||||
return task->isBackgroundTask();
|
||||
|
@ -63,9 +63,11 @@ namespace hex::init {
|
||||
// destructors to be called correctly. To prevent crashes when ImHex exits, we need to delete all shared data
|
||||
|
||||
EventManager::post<EventImHexClosing>();
|
||||
|
||||
EventManager::clear();
|
||||
|
||||
// Terminate all asynchronous tasks
|
||||
TaskManager::exit();
|
||||
|
||||
while (ImHexApi::Provider::isValid())
|
||||
ImHexApi::Provider::remove(ImHexApi::Provider::get());
|
||||
ContentRegistry::Provider::impl::getEntries().clear();
|
||||
@ -114,8 +116,6 @@ namespace hex::init {
|
||||
|
||||
ShortcutManager::clearShortcuts();
|
||||
|
||||
TaskManager::getRunningTasks().clear();
|
||||
|
||||
ContentRegistry::DataProcessorNode::impl::getEntries().clear();
|
||||
|
||||
ContentRegistry::DataFormatter::impl::getEntries().clear();
|
||||
|
@ -116,14 +116,12 @@ namespace {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Deinitializes ImHex by running all exit tasks and terminating all asynchronous tasks
|
||||
* @brief Deinitializes ImHex by running all exit tasks
|
||||
*/
|
||||
void deinitializeImHex() {
|
||||
// Run exit tasks
|
||||
init::runExitTasks();
|
||||
|
||||
// Terminate all asynchronous tasks
|
||||
TaskManager::exit();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,9 +84,12 @@ namespace hex::plugin::builtin {
|
||||
const auto &tasks = TaskManager::getRunningTasks();
|
||||
const auto &frontTask = tasks.front();
|
||||
|
||||
const auto progress = frontTask->getMaxValue() == 0 ? 1 : float(frontTask->getValue()) / frontTask->getMaxValue();
|
||||
if (frontTask == nullptr)
|
||||
return;
|
||||
|
||||
ImHexApi::System::setTaskBarProgress(ImHexApi::System::TaskProgressState::Progress, ImHexApi::System::TaskProgressType::Normal, progress * 100);
|
||||
const auto progress = frontTask->getMaxValue() == 0 ? 1 : float(frontTask->getValue()) / float(frontTask->getMaxValue());
|
||||
|
||||
ImHexApi::System::setTaskBarProgress(ImHexApi::System::TaskProgressState::Progress, ImHexApi::System::TaskProgressType::Normal, u32(progress * 100));
|
||||
|
||||
const auto widgetStart = ImGui::GetCursorPos();
|
||||
{
|
||||
@ -113,7 +116,7 @@ namespace hex::plugin::builtin {
|
||||
ImGui::SameLine();
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
ImGui::SameLine();
|
||||
ImGuiExt::SmallProgressBar(frontTask->getMaxValue() == 0 ? 1 : (float(frontTask->getValue()) / frontTask->getMaxValue()), (ImGui::GetTextLineHeightWithSpacing() - 5_scaled) / 2);
|
||||
ImGuiExt::SmallProgressBar(task->getMaxValue() == 0 ? 1 : (float(task->getValue()) / float(task->getMaxValue())), (ImGui::GetTextLineHeightWithSpacing() - 5_scaled) / 2);
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||
|
Loading…
x
Reference in New Issue
Block a user