logic: made parsing interruptable by introducing TaskScheduler

This change introduces the TaskScheduler, which queues and processes Tasks on a separate thread. The Task class provides
a common interface for deriving all specific tasks. A task can split it's processing into multiple update calls. The
TaskScheduler will update a task until it is finished or interrupt it when necessary. TaskGroups can be used to bundle
multiple Tasks together. So far only TaskGroupSequential was implemented which runs the Tasks in the set order.

TaskParseCxx utilizes the CxxParser to parse each source file in a single update call. Parsing can be interrupted using
the ESC key. The Statusbar shows the parsing progress.
This commit is contained in:
Eberhard Graether
2015-04-23 18:57:38 +02:00
parent a18213d172
commit 3c7c3da527
34 changed files with 1000 additions and 221 deletions
+10
View File
@@ -22,6 +22,16 @@ FileManager::~FileManager()
{
}
const std::vector<std::string>& FileManager::getSourcePaths() const
{
return m_sourcePaths;
}
const std::vector<std::string>& FileManager::getIncludePaths() const
{
return m_includePaths;
}
void FileManager::reset()
{
m_files.clear();
+3
View File
@@ -18,6 +18,9 @@ public:
);
~FileManager();
const std::vector<std::string>& getSourcePaths() const;
const std::vector<std::string>& getIncludePaths() const;
void reset();
void fetchFilePaths();
+53 -20
View File
@@ -3,14 +3,26 @@
#include "utility/file/FileManager.h"
#include "utility/file/FileSystem.h"
FileRegister::FileRegister(const FileManager* fileManager, const std::vector<FilePath>& filePaths)
FileRegister::FileRegister(const FileManager* fileManager)
: m_fileManager(fileManager)
{
}
const FileManager* FileRegister::getFileManager() const
{
return m_fileManager;
}
void FileRegister::setFilePaths(const std::vector<FilePath>& filePaths)
{
m_sourceFilePaths.clear();
m_includeFilePaths.clear();
for (const FilePath& path : filePaths)
{
if (m_fileManager->hasSourceExtension(path))
{
m_sourceFilePaths.push_back(path);
m_sourceFilePaths.emplace(path, STATE_UNPARSED);
}
else
{
@@ -19,29 +31,14 @@ FileRegister::FileRegister(const FileManager* fileManager, const std::vector<Fil
}
}
const FileManager* FileRegister::getFileManager() const
std::vector<FilePath> FileRegister::getUnparsedSourceFilePaths() const
{
return m_fileManager;
}
const std::vector<FilePath>& FileRegister::getSourceFilePaths() const
{
return m_sourceFilePaths;
return getUnparsedFilePaths(m_sourceFilePaths);
}
std::vector<FilePath> FileRegister::getUnparsedIncludeFilePaths() const
{
std::vector<FilePath> filePaths;
for (std::pair<FilePath, ParseState>&& p : m_includeFilePaths)
{
if (p.second == STATE_UNPARSED)
{
filePaths.push_back(p.first);
}
}
return filePaths;
return getUnparsedFilePaths(m_includeFilePaths);
}
bool FileRegister::includeFileIsParsing(const FilePath& filePath) const
@@ -66,6 +63,17 @@ bool FileRegister::includeFileIsParsed(const FilePath& filePath) const
return it->second == STATE_PARSED;
}
void FileRegister::markSourceFileParsed(const std::string& filePath)
{
std::map<FilePath, ParseState>::iterator it = m_sourceFilePaths.find(FilePath(filePath));
if (it == m_sourceFilePaths.end())
{
return;
}
it->second = STATE_PARSED;
}
void FileRegister::markIncludeFileParsing(const std::string& filePath)
{
std::map<FilePath, ParseState>::iterator it = m_includeFilePaths.find(FilePath(filePath));
@@ -90,3 +98,28 @@ void FileRegister::markParsingIncludeFilesParsed()
}
}
}
std::vector<FilePath> FileRegister::getUnparsedFilePaths(const std::map<FilePath, ParseState> filePaths) const
{
std::vector<FilePath> files;
for (std::pair<FilePath, ParseState>&& p : filePaths)
{
if (p.second == STATE_UNPARSED)
{
files.push_back(p.first);
}
}
return files;
}
size_t FileRegister::getFilesCount() const
{
return m_sourceFilePaths.size() + m_includeFilePaths.size();
}
size_t FileRegister::getParsedFilesCount() const
{
return getFilesCount() - getUnparsedSourceFilePaths().size() - getUnparsedIncludeFilePaths().size();
}
+11 -3
View File
@@ -12,19 +12,25 @@ class FileManager;
class FileRegister
{
public:
FileRegister(const FileManager* fileManager, const std::vector<FilePath>& filePaths);
explicit FileRegister(const FileManager* fileManager);
const FileManager* getFileManager() const;
const std::vector<FilePath>& getSourceFilePaths() const;
void setFilePaths(const std::vector<FilePath>& filePaths);
std::vector<FilePath> getUnparsedSourceFilePaths() const;
std::vector<FilePath> getUnparsedIncludeFilePaths() const;
bool includeFileIsParsing(const FilePath& filePath) const;
bool includeFileIsParsed(const FilePath& filePath) const;
void markSourceFileParsed(const std::string& filePath);
void markIncludeFileParsing(const std::string& filePath);
void markParsingIncludeFilesParsed();
size_t getFilesCount() const;
size_t getParsedFilesCount() const;
private:
enum ParseState
{
@@ -33,9 +39,11 @@ private:
STATE_PARSED
};
std::vector<FilePath> getUnparsedFilePaths(const std::map<FilePath, ParseState> filePaths) const;
const FileManager* m_fileManager;
std::vector<FilePath> m_sourceFilePaths;
std::map<FilePath, ParseState> m_sourceFilePaths;
std::map<FilePath, ParseState> m_includeFilePaths;
};
@@ -6,8 +6,9 @@
class MessageFinishedParsing: public Message<MessageFinishedParsing>
{
public:
MessageFinishedParsing(size_t fileCount, float parseTime, size_t errorCount)
MessageFinishedParsing(size_t fileCount, size_t totalFileCount, float parseTime, size_t errorCount)
: fileCount(fileCount)
, totalFileCount(totalFileCount)
, parseTime(parseTime)
, errorCount(errorCount)
{
@@ -19,6 +20,7 @@ public:
}
size_t fileCount;
size_t totalFileCount;
float parseTime;
size_t errorCount;
};
@@ -0,0 +1,20 @@
#ifndef MESSAGE_INTERRUPT_TASKS_H
#define MESSAGE_INTERRUPT_TASKS_H
#include "utility/messaging/Message.h"
class MessageInterruptTasks:
public Message<MessageInterruptTasks>
{
public:
MessageInterruptTasks()
{
}
static const std::string getStaticType()
{
return "MessageInterruptTasks";
}
};
#endif // MESSAGE_INTERRUPT_TASKS_H
+89
View File
@@ -0,0 +1,89 @@
#include "utility/scheduling/Task.h"
#include "utility/logging/logging.h"
#include "utility/scheduling/TaskScheduler.h"
void Task::dispatch(std::shared_ptr<Task> task)
{
TaskScheduler::getInstance()->pushTask(task);
}
Task::Task()
: m_state(STATE_NEW)
{
}
Task::~Task()
{
}
Task::TaskState Task::getState() const
{
return m_state;
}
Task::TaskState Task::process(bool interruptTask)
{
if (interruptTask)
{
switch (m_state)
{
case STATE_NEW:
case STATE_CANCELED:
break;
case STATE_RUNNING:
interrupt();
exit();
break;
case STATE_FINISHED:
revert();
break;
}
setState(STATE_CANCELED);
}
else
{
switch (m_state)
{
case STATE_NEW:
case STATE_CANCELED:
enter();
case STATE_RUNNING:
{
TaskState newState = update();
if (newState == STATE_NEW || newState == STATE_CANCELED)
{
LOG_ERROR("Task can't change to state NEW or CANCELLED");
return m_state;
}
setState(newState);
if (m_state == STATE_FINISHED)
{
exit();
}
}
break;
case STATE_FINISHED:
break;
}
}
return m_state;
}
void Task::execute()
{
TaskState state;
do
{
state = process(false);
}
while (state != STATE_FINISHED);
}
void Task::setState(TaskState state)
{
m_state = state;
}
+41
View File
@@ -0,0 +1,41 @@
#ifndef TASK_H
#define TASK_H
#include <memory>
class Task
{
public:
enum TaskState
{
STATE_NEW,
STATE_RUNNING,
STATE_FINISHED,
STATE_CANCELED
};
static void dispatch(std::shared_ptr<Task> task);
Task();
virtual ~Task();
TaskState getState() const;
TaskState process(bool interruptTask);
void execute();
virtual void enter() = 0;
virtual TaskState update() = 0;
virtual void exit() = 0;
virtual void interrupt() = 0;
virtual void revert() = 0;
protected:
void setState(TaskState state);
private:
TaskState m_state;
};
#endif // TASK_H
+14
View File
@@ -0,0 +1,14 @@
#include "utility/scheduling/TaskGroup.h"
TaskGroup::TaskGroup()
{
}
TaskGroup::~TaskGroup()
{
}
void TaskGroup::addTask(std::shared_ptr<Task> task)
{
m_tasks.push_back(task);
}
+22
View File
@@ -0,0 +1,22 @@
#ifndef TASK_GROUP_H
#define TASK_GROUP_H
#include <memory>
#include <vector>
#include "utility/scheduling/Task.h"
class TaskGroup
: public Task
{
public:
TaskGroup();
virtual ~TaskGroup();
void addTask(std::shared_ptr<Task> task);
protected:
std::vector<std::shared_ptr<Task>> m_tasks;
};
#endif // TASK_GROUP_H
@@ -0,0 +1,61 @@
#include "utility/scheduling/TaskGroupSequential.h"
TaskGroupSequential::TaskGroupSequential()
: m_taskIndex(-1)
{
}
TaskGroupSequential::~TaskGroupSequential()
{
}
void TaskGroupSequential::enter()
{
}
Task::TaskState TaskGroupSequential::update()
{
if (!m_tasks.size())
{
return Task::STATE_FINISHED;
}
if (m_taskIndex < 0 || m_tasks[m_taskIndex]->getState() != Task::STATE_RUNNING)
{
m_taskIndex++;
}
std::shared_ptr<Task> task = m_tasks[m_taskIndex];
TaskState state = task->process(false);
if (state == Task::STATE_FINISHED && size_t(m_taskIndex + 1) == m_tasks.size())
{
return Task::STATE_FINISHED;
}
return Task::STATE_RUNNING;
}
void TaskGroupSequential::exit()
{
}
void TaskGroupSequential::interrupt()
{
if (m_taskIndex > 0 && size_t(m_taskIndex) < m_tasks.size())
{
for (int i = m_taskIndex; i >= 0; i--)
{
m_tasks[m_taskIndex]->process(true);
}
}
}
void TaskGroupSequential::revert()
{
for (int i = m_tasks.size() - 1; i >= 0; i--)
{
m_tasks[m_taskIndex]->process(true);
}
}
@@ -0,0 +1,24 @@
#ifndef TASK_GROUP_SEQUENTIAL_H
#define TASK_GROUP_SEQUENTIAL_H
#include "utility/scheduling/TaskGroup.h"
class TaskGroupSequential
: public TaskGroup
{
public:
TaskGroupSequential();
virtual ~TaskGroupSequential();
virtual void enter();
virtual TaskState update();
virtual void exit();
virtual void interrupt();
virtual void revert();
private:
int m_taskIndex;
};
#endif // TASK_GROUP_SEQUENTIAL_H
@@ -0,0 +1,132 @@
#include "utility/scheduling/TaskScheduler.h"
#include <chrono>
#include <thread>
#include "utility/logging/logging.h"
#include "utility/messaging/type/MessageStatus.h"
std::shared_ptr<TaskScheduler> TaskScheduler::getInstance()
{
if (!s_instance)
{
s_instance = std::shared_ptr<TaskScheduler>(new TaskScheduler());
}
return s_instance;
}
void TaskScheduler::pushTask(std::shared_ptr<Task> task)
{
std::lock_guard<std::mutex> lock(m_tasksMutex);
m_tasks.push(task);
}
void TaskScheduler::interruptCurrentTask()
{
std::lock_guard<std::mutex> lock(m_tasksMutex);
m_interruptTask = true;
}
void TaskScheduler::startSchedulerLoopThreaded()
{
std::thread(&TaskScheduler::startSchedulerLoop, this).detach();
}
void TaskScheduler::startSchedulerLoop()
{
{
std::lock_guard<std::mutex> lock(m_loopMutex);
if (m_loopIsRunning)
{
LOG_ERROR("Loop is already running");
return;
}
m_loopIsRunning = true;
}
while (true)
{
{
std::lock_guard<std::mutex> lock(m_loopMutex);
if (!m_loopIsRunning)
{
return;
}
}
updateTasks();
const int SLEEP_TIME_MS = 25;
std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME_MS));
}
}
void TaskScheduler::stopSchedulerLoop()
{
{
std::lock_guard<std::mutex> lock(m_loopMutex);
if (!m_loopIsRunning)
{
LOG_WARNING("Loop is not running");
}
m_loopIsRunning = false;
}
interruptCurrentTask();
}
bool TaskScheduler::loopIsRunning() const
{
std::lock_guard<std::mutex> lock(m_loopMutex);
return m_loopIsRunning;
}
std::shared_ptr<TaskScheduler> TaskScheduler::s_instance;
TaskScheduler::TaskScheduler()
: m_loopIsRunning(false)
, m_interruptTask(false)
{
}
void TaskScheduler::updateTasks()
{
std::lock_guard<std::mutex> lock(m_tasksMutex);
bool interrupt = m_interruptTask;
while (m_tasks.size())
{
std::shared_ptr<Task> task = m_tasks.front();
m_tasksMutex.unlock();
Task::TaskState state = task->process(interrupt);
m_tasksMutex.lock();
if (state == Task::STATE_FINISHED || state == Task::STATE_CANCELED)
{
m_tasks.pop();
}
interrupt = m_interruptTask;
}
m_interruptTask = false;
}
void TaskScheduler::handleMessage(MessageInterruptTasks* message)
{
interruptCurrentTask();
std::lock_guard<std::mutex> lock(m_tasksMutex);
if (m_tasks.size())
{
MessageStatus("Stop running tasks...").dispatch();
}
}
@@ -0,0 +1,47 @@
#ifndef TASK_SCHEDULER_H
#define TASK_SCHEDULER_H
#include <memory>
#include <mutex>
#include <queue>
#include "utility/messaging/MessageListener.h"
#include "utility/messaging/type/MessageInterruptTasks.h"
#include "utility/scheduling/Task.h"
class TaskScheduler
: public MessageListener<MessageInterruptTasks>
{
public:
static std::shared_ptr<TaskScheduler> getInstance();
void pushTask(std::shared_ptr<Task> task);
void interruptCurrentTask();
void startSchedulerLoopThreaded();
void startSchedulerLoop();
void stopSchedulerLoop();
bool loopIsRunning() const;
private:
static std::shared_ptr<TaskScheduler> s_instance;
TaskScheduler();
TaskScheduler(const TaskScheduler&);
void operator=(const TaskScheduler&);
void updateTasks();
virtual void handleMessage(MessageInterruptTasks* message);
bool m_loopIsRunning;
std::queue<std::shared_ptr<Task>> m_tasks;
bool m_interruptTask;
mutable std::mutex m_tasksMutex;
mutable std::mutex m_loopMutex;
};
#endif // TASK_SCHEDULER_H
+14 -4
View File
@@ -1,16 +1,26 @@
#include "utility/utility.h"
float utility::duration(std::function<void()> func)
utility::TimePoint utility::durationStart()
{
std::chrono::time_point<std::chrono::system_clock> start = std::chrono::system_clock::now();
func();
return std::chrono::system_clock::now();
}
float utility::duration(const TimePoint& start)
{
std::chrono::duration<float> duration =
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - start);
return duration.count();
}
float utility::duration(std::function<void()> func)
{
const TimePoint start = durationStart();
func();
return duration(start);
}
bool utility::intersectionPoint(Vec2f a1, Vec2f b1, Vec2f a2, Vec2f b2, Vec2f* i)
{
Vec2f p = a1;
+4
View File
@@ -8,6 +8,10 @@
namespace utility
{
typedef std::chrono::time_point<std::chrono::system_clock> TimePoint;
TimePoint durationStart();
float duration(const TimePoint& start);
float duration(std::function<void()> func);
template<typename T>