mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-30 05:05:19 -05:00
feat: Added basic ability to interrupt long running tasks
This commit is contained in:
@@ -150,15 +150,6 @@ namespace hex {
|
||||
|
||||
}
|
||||
|
||||
namespace Tasks {
|
||||
|
||||
Task createTask(const std::string &unlocalizedName, u64 maxValue);
|
||||
|
||||
void doLater(const std::function<void()> &function);
|
||||
std::vector<std::function<void()>> &getDeferredCalls();
|
||||
|
||||
}
|
||||
|
||||
namespace System {
|
||||
|
||||
namespace impl {
|
||||
|
||||
@@ -2,40 +2,100 @@
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <list>
|
||||
#include <cstdio>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <list>
|
||||
|
||||
namespace hex {
|
||||
|
||||
class TaskHolder;
|
||||
class TaskManager;
|
||||
|
||||
class Task {
|
||||
public:
|
||||
Task() = default;
|
||||
Task(const std::string &unlocalizedName, u64 maxValue);
|
||||
Task(std::string unlocalizedName, u64 maxValue, std::function<void(Task &)> function);
|
||||
|
||||
Task(const Task&) = delete;
|
||||
Task(Task &&other) noexcept;
|
||||
~Task();
|
||||
|
||||
Task(Task &&other) noexcept;
|
||||
void update(u64 value = 0);
|
||||
void setMaxValue(u64 value);
|
||||
|
||||
void setMaxValue(u64 maxValue);
|
||||
void update(u64 currValue);
|
||||
void finish();
|
||||
[[nodiscard]] bool isFinished() const;
|
||||
[[nodiscard]] bool hadException() const;
|
||||
[[nodiscard]] bool wasInterrupted() const;
|
||||
void clearException();
|
||||
|
||||
[[nodiscard]] double getProgress() const;
|
||||
[[nodiscard]] const std::string &getUnlocalizedName();
|
||||
[[nodiscard]] u64 getValue() const;
|
||||
[[nodiscard]] u64 getMaxValue() const;
|
||||
|
||||
[[nodiscard]] const std::string &getName() const;
|
||||
|
||||
[[nodiscard]] bool isPending() const;
|
||||
|
||||
static size_t getRunningTaskCount();
|
||||
static std::list<Task *> &getRunningTasks() { return Task::s_runningTasks; }
|
||||
static std::mutex &getTaskMutex() { return Task::s_taskMutex; }
|
||||
void interrupt();
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
u64 m_maxValue = 0, m_currValue = 0;
|
||||
void finish();
|
||||
void interruption();
|
||||
void exception();
|
||||
|
||||
static std::list<Task *> s_runningTasks;
|
||||
static std::mutex s_taskMutex;
|
||||
private:
|
||||
mutable std::mutex m_mutex;
|
||||
|
||||
std::string m_unlocalizedName;
|
||||
u64 m_currValue, m_maxValue;
|
||||
std::thread m_thread;
|
||||
|
||||
bool m_shouldInterrupt = false;
|
||||
|
||||
bool m_interrupted = false;
|
||||
bool m_finished = false;
|
||||
bool m_hadException = false;
|
||||
|
||||
struct TaskInterruptor { virtual ~TaskInterruptor() = default; };
|
||||
|
||||
friend class TaskHolder;
|
||||
friend class TaskManager;
|
||||
};
|
||||
|
||||
class TaskHolder {
|
||||
public:
|
||||
TaskHolder() = default;
|
||||
explicit TaskHolder(std::weak_ptr<Task> task) : m_task(std::move(task)) { }
|
||||
|
||||
[[nodiscard]] bool isRunning() const;
|
||||
[[nodiscard]] bool hadException() const;
|
||||
[[nodiscard]] bool wasInterrupted() const;
|
||||
|
||||
void interrupt();
|
||||
private:
|
||||
std::weak_ptr<Task> m_task;
|
||||
};
|
||||
|
||||
class TaskManager {
|
||||
public:
|
||||
TaskManager() = delete;
|
||||
|
||||
constexpr static auto NoProgress = 0;
|
||||
|
||||
static TaskHolder createTask(std::string name, u64 maxValue, std::function<void(Task &)> function);
|
||||
static void collectGarbage();
|
||||
|
||||
static size_t getRunningTaskCount();
|
||||
static std::list<std::shared_ptr<Task>> &getRunningTasks();
|
||||
|
||||
static void doLater(const std::function<void()> &function);
|
||||
static void runDeferredCalls();
|
||||
private:
|
||||
static std::mutex s_deferredCallsMutex;
|
||||
|
||||
static std::list<std::shared_ptr<Task>> s_tasks;
|
||||
static std::list<std::function<void()>> s_deferredCalls;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -236,7 +236,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
void setCurrentProvider(u32 index) {
|
||||
if (Task::getRunningTaskCount() > 0)
|
||||
if (TaskManager::getRunningTaskCount() > 0)
|
||||
return;
|
||||
|
||||
if (index < s_providers.size() && s_currentProvider != index) {
|
||||
@@ -266,7 +266,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
void add(prv::Provider *provider, bool skipLoadInterface) {
|
||||
if (Task::getRunningTaskCount() > 0)
|
||||
if (TaskManager::getRunningTaskCount() > 0)
|
||||
return;
|
||||
|
||||
if (skipLoadInterface)
|
||||
@@ -282,7 +282,7 @@ namespace hex {
|
||||
if (provider == nullptr)
|
||||
return;
|
||||
|
||||
if (Task::getRunningTaskCount() > 0)
|
||||
if (TaskManager::getRunningTaskCount() > 0)
|
||||
return;
|
||||
|
||||
if (!noQuestions) {
|
||||
@@ -319,29 +319,6 @@ namespace hex {
|
||||
|
||||
}
|
||||
|
||||
|
||||
namespace ImHexApi::Tasks {
|
||||
|
||||
Task createTask(const std::string &unlocalizedName, u64 maxValue) {
|
||||
return { unlocalizedName, maxValue };
|
||||
}
|
||||
|
||||
void doLater(const std::function<void()> &function) {
|
||||
static std::mutex tasksMutex;
|
||||
std::scoped_lock lock(tasksMutex);
|
||||
|
||||
getDeferredCalls().push_back(function);
|
||||
}
|
||||
|
||||
std::vector<std::function<void()>> &getDeferredCalls() {
|
||||
static std::vector<std::function<void()>> deferredCalls;
|
||||
|
||||
return deferredCalls;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
namespace ImHexApi::System {
|
||||
|
||||
namespace impl {
|
||||
|
||||
@@ -6,66 +6,174 @@
|
||||
|
||||
namespace hex {
|
||||
|
||||
std::list<Task *> Task::s_runningTasks;
|
||||
std::mutex Task::s_taskMutex;
|
||||
std::mutex TaskManager::s_deferredCallsMutex;
|
||||
|
||||
Task::Task(const std::string &unlocalizedName, u64 maxValue) : m_name(LangEntry(unlocalizedName)), m_maxValue(maxValue), m_currValue(0) {
|
||||
std::scoped_lock lock(Task::s_taskMutex);
|
||||
std::list<std::shared_ptr<Task>> TaskManager::s_tasks;
|
||||
std::list<std::function<void()>> TaskManager::s_deferredCalls;
|
||||
|
||||
Task::s_runningTasks.push_back(this);
|
||||
}
|
||||
Task::Task(std::string unlocalizedName, u64 maxValue, std::function<void(Task &)> function)
|
||||
: m_unlocalizedName(std::move(unlocalizedName)), m_currValue(0), m_maxValue(maxValue) {
|
||||
this->m_thread = std::thread([this, func = std::move(function)] {
|
||||
try {
|
||||
func(*this);
|
||||
} catch (const TaskInterruptor &) {
|
||||
this->interruption();
|
||||
} catch (...) {
|
||||
this->exception();
|
||||
}
|
||||
|
||||
Task::~Task() {
|
||||
this->finish();
|
||||
this->finish();
|
||||
});
|
||||
}
|
||||
|
||||
Task::Task(hex::Task &&other) noexcept {
|
||||
std::scoped_lock lock(Task::s_taskMutex);
|
||||
std::scoped_lock thisLock(this->m_mutex);
|
||||
std::scoped_lock otherLock(other.m_mutex);
|
||||
|
||||
this->m_thread = std::move(other.m_thread);
|
||||
this->m_unlocalizedName = std::move(other.m_unlocalizedName);
|
||||
|
||||
this->m_name = other.m_name;
|
||||
this->m_maxValue = other.m_maxValue;
|
||||
this->m_currValue = other.m_currValue;
|
||||
|
||||
auto it = std::find(Task::s_runningTasks.begin(), Task::s_runningTasks.end(), &other);
|
||||
if (it != Task::s_runningTasks.end()) {
|
||||
*it = this;
|
||||
}
|
||||
this->m_finished = other.m_finished;
|
||||
this->m_hadException = other.m_hadException;
|
||||
this->m_interrupted = other.m_interrupted;
|
||||
this->m_shouldInterrupt = other.m_shouldInterrupt;
|
||||
}
|
||||
|
||||
Task::~Task() {
|
||||
this->interrupt();
|
||||
this->m_thread.join();
|
||||
}
|
||||
|
||||
void Task::update(u64 value) {
|
||||
std::scoped_lock lock(this->m_mutex);
|
||||
|
||||
this->m_currValue = value;
|
||||
|
||||
if (this->m_shouldInterrupt)
|
||||
throw TaskInterruptor();
|
||||
}
|
||||
|
||||
void Task::setMaxValue(u64 value) {
|
||||
std::scoped_lock lock(this->m_mutex);
|
||||
|
||||
this->m_maxValue = value;
|
||||
}
|
||||
|
||||
|
||||
void Task::interrupt() {
|
||||
std::scoped_lock lock(this->m_mutex);
|
||||
|
||||
this->m_shouldInterrupt = true;
|
||||
}
|
||||
|
||||
bool Task::isFinished() const {
|
||||
std::scoped_lock lock(this->m_mutex);
|
||||
|
||||
return this->m_finished;
|
||||
}
|
||||
|
||||
bool Task::hadException() const {
|
||||
std::scoped_lock lock(this->m_mutex);
|
||||
|
||||
return this->m_hadException;
|
||||
}
|
||||
|
||||
bool Task::wasInterrupted() const {
|
||||
std::scoped_lock lock(this->m_mutex);
|
||||
|
||||
return this->m_interrupted;
|
||||
}
|
||||
|
||||
void Task::clearException() {
|
||||
std::scoped_lock lock(this->m_mutex);
|
||||
|
||||
this->m_hadException = false;
|
||||
}
|
||||
|
||||
const std::string &Task::getUnlocalizedName() {
|
||||
return this->m_unlocalizedName;
|
||||
}
|
||||
|
||||
u64 Task::getValue() const {
|
||||
return this->m_currValue;
|
||||
}
|
||||
|
||||
u64 Task::getMaxValue() const {
|
||||
return this->m_maxValue;
|
||||
}
|
||||
|
||||
void Task::finish() {
|
||||
std::scoped_lock lock(Task::s_taskMutex);
|
||||
std::scoped_lock lock(this->m_mutex);
|
||||
|
||||
Task::s_runningTasks.remove(this);
|
||||
this->m_finished = true;
|
||||
}
|
||||
|
||||
void Task::setMaxValue(u64 maxValue) {
|
||||
this->m_maxValue = maxValue;
|
||||
void Task::interruption() {
|
||||
std::scoped_lock lock(this->m_mutex);
|
||||
|
||||
this->m_interrupted = true;
|
||||
}
|
||||
|
||||
void Task::update(u64 currValue) {
|
||||
if (this->m_currValue < this->m_maxValue)
|
||||
this->m_currValue = currValue;
|
||||
void Task::exception() {
|
||||
std::scoped_lock lock(this->m_mutex);
|
||||
|
||||
this->m_hadException = true;
|
||||
}
|
||||
|
||||
double Task::getProgress() const {
|
||||
if (this->m_maxValue == 0)
|
||||
return 100;
|
||||
|
||||
return static_cast<double>(this->m_currValue) / static_cast<double>(this->m_maxValue);
|
||||
bool TaskHolder::isRunning() const {
|
||||
return !m_task.expired() && !m_task.lock()->isFinished();
|
||||
}
|
||||
|
||||
bool Task::isPending() const {
|
||||
return this->m_maxValue == 0;
|
||||
bool TaskHolder::hadException() const {
|
||||
return m_task.expired() || m_task.lock()->hadException();
|
||||
}
|
||||
|
||||
const std::string &Task::getName() const {
|
||||
return this->m_name;
|
||||
bool TaskHolder::wasInterrupted() const {
|
||||
return m_task.expired() || m_task.lock()->wasInterrupted();
|
||||
}
|
||||
|
||||
size_t Task::getRunningTaskCount() {
|
||||
std::scoped_lock lock(Task::s_taskMutex);
|
||||
void TaskHolder::interrupt() {
|
||||
if (!this->m_task.expired())
|
||||
this->m_task.lock()->interrupt();
|
||||
}
|
||||
|
||||
return Task::s_runningTasks.size();
|
||||
|
||||
TaskHolder TaskManager::createTask(std::string name, u64 maxValue, std::function<void(Task &)> function) {
|
||||
s_tasks.emplace_back(std::make_shared<Task>(std::move(name), maxValue, std::move(function)));
|
||||
|
||||
return TaskHolder(s_tasks.back());
|
||||
}
|
||||
|
||||
void TaskManager::collectGarbage() {
|
||||
std::erase_if(s_tasks, [](const auto &task) { return task->isFinished() && !task->hadException(); });
|
||||
}
|
||||
|
||||
std::list<std::shared_ptr<Task>> &TaskManager::getRunningTasks() {
|
||||
return s_tasks;
|
||||
}
|
||||
|
||||
size_t TaskManager::getRunningTaskCount() {
|
||||
return s_tasks.size();
|
||||
}
|
||||
|
||||
|
||||
void TaskManager::doLater(const std::function<void()> &function) {
|
||||
std::scoped_lock lock(s_deferredCallsMutex);
|
||||
|
||||
s_deferredCalls.push_back(function);
|
||||
}
|
||||
|
||||
void TaskManager::runDeferredCalls() {
|
||||
std::scoped_lock lock(s_deferredCallsMutex);
|
||||
|
||||
for (const auto &call : s_deferredCalls)
|
||||
call();
|
||||
|
||||
s_deferredCalls.clear();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
#include <imgui_impl_opengl3_loader.h>
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
|
||||
namespace ImGui {
|
||||
|
||||
int UpdateStringSizeCallback(ImGuiInputTextCallbackData *data) {
|
||||
@@ -536,7 +538,7 @@ namespace ImGui {
|
||||
const ImGuiStyle &style = g.Style;
|
||||
|
||||
ImVec2 pos = window->DC.CursorPos + ImVec2(0, yOffset);
|
||||
ImVec2 size = CalcItemSize(ImVec2(100, 5), 100, g.FontSize + style.FramePadding.y * 2.0f);
|
||||
ImVec2 size = CalcItemSize(ImVec2(100, 5) * hex::ImHexApi::System::getGlobalScale(), 100, g.FontSize + style.FramePadding.y * 2.0f);
|
||||
ImRect bb(pos, pos + size);
|
||||
ItemSize(size, 0);
|
||||
if (!ItemAdd(bb, 0))
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
ImVec2 View::getMinSize() const {
|
||||
return scaled(ImVec2(480, 720));
|
||||
return scaled(ImVec2(10, 10));
|
||||
}
|
||||
|
||||
ImVec2 View::getMaxSize() const {
|
||||
|
||||
Reference in New Issue
Block a user