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:
@@ -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();
|
||||
|
||||
@@ -18,6 +18,9 @@ public:
|
||||
);
|
||||
~FileManager();
|
||||
|
||||
const std::vector<std::string>& getSourcePaths() const;
|
||||
const std::vector<std::string>& getIncludePaths() const;
|
||||
|
||||
void reset();
|
||||
void fetchFilePaths();
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user