try to index media immediately on import
Since indexing is supplemental, it can be made a background task that can occur at more or less any time. So we run it as soon as possible.
This commit is contained in:
+166
-66
@@ -23,119 +23,219 @@
|
||||
#include <QDebug>
|
||||
#include <QThread>
|
||||
|
||||
TaskManager TaskManager::instance_;
|
||||
TaskManager* TaskManager::instance_ = nullptr;
|
||||
|
||||
TaskManager::TaskManager()
|
||||
TaskManager::TaskManager() :
|
||||
active_thread_count_(0)
|
||||
{
|
||||
maximum_task_count_ = QThread::idealThreadCount();
|
||||
// Initialize threads to run tasks on
|
||||
threads_.resize(QThread::idealThreadCount());
|
||||
|
||||
for (int i=0;i<threads_.size();i++) {
|
||||
QThread* t = new QThread(this);
|
||||
t->start(QThread::LowPriority);
|
||||
threads_.replace(i, {t, false});
|
||||
}
|
||||
}
|
||||
|
||||
TaskManager::~TaskManager()
|
||||
{
|
||||
Clear();
|
||||
// First send the signal to all tasks to start cancelling
|
||||
foreach (const TaskContainer& task_info, tasks_) {
|
||||
if (task_info.status == kWorking) {
|
||||
task_info.task->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
// Next, signal each thread to quit as its next event in the queue
|
||||
foreach (const ThreadContainer& tc, threads_) {
|
||||
tc.thread->quit();
|
||||
}
|
||||
|
||||
// Wait for each thread's event queue to finish
|
||||
foreach (const ThreadContainer& tc, threads_) {
|
||||
tc.thread->wait();
|
||||
|
||||
// This is technically unnecessary since each QThread is a child of this object, but we may as well
|
||||
delete tc.thread;
|
||||
}
|
||||
|
||||
// Finally delete all task objects (they shouldn't have been deleted by TaskSucceeded() or TaskFailed() because our
|
||||
// event queue shouldn't be active by this point
|
||||
foreach (const TaskContainer& task_info, tasks_) {
|
||||
delete task_info.task;
|
||||
}
|
||||
}
|
||||
|
||||
void TaskManager::CreateInstance()
|
||||
{
|
||||
instance_ = new TaskManager();
|
||||
}
|
||||
|
||||
void TaskManager::DestroyInstance()
|
||||
{
|
||||
delete instance_;
|
||||
}
|
||||
|
||||
TaskManager *TaskManager::instance()
|
||||
{
|
||||
return &instance_;
|
||||
return instance_;
|
||||
}
|
||||
|
||||
void TaskManager::AddTask(TaskPtr t)
|
||||
void TaskManager::AddTask(Task* t)
|
||||
{
|
||||
// Connect Task's status signal to the Callback
|
||||
connect(t.get(), SIGNAL(StatusChanged(Task::Status)), this, SLOT(TaskCallback(Task::Status)));
|
||||
connect(t, &Task::Succeeeded, this, &TaskManager::TaskSucceeded, Qt::QueuedConnection);
|
||||
connect(t, &Task::Failed, this, &TaskManager::TaskFailed, Qt::QueuedConnection);
|
||||
|
||||
// Add the Task to the queue
|
||||
tasks_.append(t);
|
||||
tasks_.append({t, kWaiting});
|
||||
|
||||
// Emit signal that a Task was added
|
||||
emit TaskAdded(t.get());
|
||||
emit TaskAdded(t);
|
||||
|
||||
// Scan through queue and start any Tasks that can (including this one)
|
||||
StartNextWaiting();
|
||||
}
|
||||
|
||||
void TaskManager::Clear()
|
||||
{
|
||||
// Delete Tasks from memory
|
||||
for (int i=0;i<tasks_.size();i++) {
|
||||
tasks_.at(i)->Cancel();
|
||||
}
|
||||
tasks_.clear();
|
||||
}
|
||||
|
||||
void TaskManager::StartNextWaiting()
|
||||
{
|
||||
// Count the tasks that are currently active
|
||||
int working_count = 0;
|
||||
// If there are no tasks in the queue, there is nothing to be done
|
||||
if (tasks_.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i=0;i<tasks_.size();i++) {
|
||||
TaskPtr t = tasks_.at(i);
|
||||
|
||||
if (t->status() == Task::kWorking) {
|
||||
|
||||
// Task is active, add it to the count
|
||||
working_count++;
|
||||
|
||||
} else if (t->status() == Task::kWaiting) {
|
||||
|
||||
// Task is waiting and we have available threads, try to start it
|
||||
if (t->Start()) {
|
||||
// If it started, add it to the working count
|
||||
working_count++;
|
||||
}
|
||||
// If all threads are occupied, nothing to be done
|
||||
if (active_thread_count_ == threads_.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a list of tasks that are waiting
|
||||
QList<Task*> waiting_tasks;
|
||||
foreach (const TaskContainer& task_info, tasks_) {
|
||||
if (task_info.status == kWaiting) {
|
||||
waiting_tasks.append(task_info.task);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the count exceeds our maximum threads, if so stop here
|
||||
if (working_count == maximum_task_count_) {
|
||||
break;
|
||||
// No tasks waiting to start
|
||||
if (waiting_tasks.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For any inactive threads,
|
||||
for (int i=0;i<threads_.size();i++) {
|
||||
if (!threads_.at(i).active) {
|
||||
// This thread is inactive and needs a new Task
|
||||
Task* task = waiting_tasks.takeFirst();
|
||||
|
||||
task->moveToThread(threads_.at(i).thread);
|
||||
|
||||
threads_[i].active = true;
|
||||
active_thread_count_++;
|
||||
|
||||
SetTaskStatus(task, kWorking);
|
||||
|
||||
QMetaObject::invokeMethod(task,
|
||||
"Start",
|
||||
Qt::QueuedConnection);
|
||||
|
||||
if (active_thread_count_ == threads_.size() || waiting_tasks.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TaskManager::DeleteTask(Task *t)
|
||||
{
|
||||
// Cancel the task
|
||||
t->Cancel();
|
||||
if (GetTaskStatus(t) == kWorking) {
|
||||
// Send a signal to the task to cancel, it will likely continue to cancel in the background after it's removed
|
||||
t->Cancel();
|
||||
}
|
||||
|
||||
// Remove instances of Task from queue
|
||||
for (int i=0;i<tasks_.size();i++) {
|
||||
if (tasks_.at(i).get() == t) {
|
||||
emit t->Removed();
|
||||
if (tasks_.at(i).task == t) {
|
||||
tasks_.removeAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
emit t->Removed();
|
||||
|
||||
if (GetTaskStatus(t) != kWorking) {
|
||||
// If the task isn't doing anything, we can simply delete it
|
||||
delete t;
|
||||
}
|
||||
}
|
||||
|
||||
void TaskManager::TaskFinished(Task* task)
|
||||
{
|
||||
// Set this thread's active value to false
|
||||
for (int i=0;i<threads_.size();i++) {
|
||||
if (threads_.at(i).thread == task->thread()) {
|
||||
threads_[i].active = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Decrement the active thread count
|
||||
active_thread_count_--;
|
||||
}
|
||||
|
||||
TaskManager::TaskStatus TaskManager::GetTaskStatus(Task *t)
|
||||
{
|
||||
foreach (const TaskContainer& container, tasks_) {
|
||||
if (container.task == t) {
|
||||
return container.status;
|
||||
}
|
||||
}
|
||||
|
||||
return kError;
|
||||
}
|
||||
|
||||
void TaskManager::SetTaskStatus(Task *t, TaskStatus status)
|
||||
{
|
||||
for (int i=0;i<tasks_.size();i++) {
|
||||
TaskContainer& cont = tasks_[i];
|
||||
|
||||
if (cont.task == t) {
|
||||
cont.status = status;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TaskManager::TaskCallback(Task::Status status)
|
||||
void TaskManager::TaskSucceeded()
|
||||
{
|
||||
if (status == Task::kFinished || status == Task::kError) {
|
||||
// The Task has finished, we can start a new one
|
||||
StartNextWaiting();
|
||||
Task* task_sender = static_cast<Task*>(sender());
|
||||
|
||||
if (status == Task::kFinished) {
|
||||
// The Task was successful, remove this Task from the queue
|
||||
DeleteTask(static_cast<Task*>(sender()));
|
||||
SetTaskStatus(task_sender, kFinished);
|
||||
|
||||
TaskFinished(task_sender);
|
||||
|
||||
// Delete this task
|
||||
DeleteTask(task_sender);
|
||||
}
|
||||
|
||||
void TaskManager::TaskFailed()
|
||||
{
|
||||
Task* task_sender = static_cast<Task*>(sender());
|
||||
|
||||
SetTaskStatus(task_sender, kError);
|
||||
|
||||
TaskFinished(task_sender);
|
||||
|
||||
// If this task has already been deleted, we'll free its memory now
|
||||
bool was_deleted = true;
|
||||
|
||||
for (int i=0;i<tasks_.size();i++) {
|
||||
if (tasks_.at(i).task == task_sender) {
|
||||
was_deleted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TaskManager::AddTaskCommand::AddTaskCommand(TaskPtr t, QUndoCommand *parent) :
|
||||
QUndoCommand(parent),
|
||||
task_(t)
|
||||
{
|
||||
}
|
||||
|
||||
void TaskManager::AddTaskCommand::redo()
|
||||
{
|
||||
TaskManager::instance()->AddTask(task_);
|
||||
}
|
||||
|
||||
void TaskManager::AddTaskCommand::undo()
|
||||
{
|
||||
TaskManager::instance()->DeleteTask(task_.get());
|
||||
|
||||
task_->ResetState();
|
||||
if (was_deleted) {
|
||||
delete task_sender;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user