From 3c7c3da5273e83f4c102d821dc322cce44dda521 Mon Sep 17 00:00:00 2001 From: Eberhard Graether Date: Thu, 23 Apr 2015 18:57:38 +0200 Subject: [PATCH] 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. --- src/app/qt/element/QtMainWindow.cpp | 192 ++++++++++-------- src/app/qt/element/QtMainWindow.h | 7 + src/lib/Application.cpp | 3 + src/lib/CMakeLists.txt | 13 ++ src/lib/Project.cpp | 106 ++++++---- src/lib/Project.h | 5 + .../controller/StatusBarController.cpp | 2 +- src/lib/data/Storage.cpp | 10 +- src/lib/data/Storage.h | 3 +- src/lib/data/parser/Parser.h | 3 +- src/lib/data/parser/ParserClient.h | 1 + src/lib/data/parser/cxx/ASTAction.cpp | 2 - src/lib/data/parser/cxx/CxxParser.cpp | 122 ++++++----- src/lib/data/parser/cxx/CxxParser.h | 32 ++- .../data/parser/cxx/PreprocessorCallbacks.cpp | 1 + src/lib/data/parser/cxx/TaskParseCxx.cpp | 97 +++++++++ src/lib/data/parser/cxx/TaskParseCxx.h | 39 ++++ src/lib/utility/file/FileManager.cpp | 10 + src/lib/utility/file/FileManager.h | 3 + src/lib/utility/file/FileRegister.cpp | 73 +++++-- src/lib/utility/file/FileRegister.h | 14 +- .../messaging/type/MessageFinishedParsing.h | 4 +- .../messaging/type/MessageInterruptTasks.h | 20 ++ src/lib/utility/scheduling/Task.cpp | 89 ++++++++ src/lib/utility/scheduling/Task.h | 41 ++++ src/lib/utility/scheduling/TaskGroup.cpp | 14 ++ src/lib/utility/scheduling/TaskGroup.h | 22 ++ .../scheduling/TaskGroupSequential.cpp | 61 ++++++ .../utility/scheduling/TaskGroupSequential.h | 24 +++ src/lib/utility/scheduling/TaskScheduler.cpp | 132 ++++++++++++ src/lib/utility/scheduling/TaskScheduler.h | 47 +++++ src/lib/utility/utility.cpp | 18 +- src/lib/utility/utility.h | 4 + src/test/CxxParserTestSuite.h | 7 +- 34 files changed, 1000 insertions(+), 221 deletions(-) create mode 100644 src/lib/data/parser/cxx/TaskParseCxx.cpp create mode 100644 src/lib/data/parser/cxx/TaskParseCxx.h create mode 100644 src/lib/utility/messaging/type/MessageInterruptTasks.h create mode 100644 src/lib/utility/scheduling/Task.cpp create mode 100644 src/lib/utility/scheduling/Task.h create mode 100644 src/lib/utility/scheduling/TaskGroup.cpp create mode 100644 src/lib/utility/scheduling/TaskGroup.h create mode 100644 src/lib/utility/scheduling/TaskGroupSequential.cpp create mode 100644 src/lib/utility/scheduling/TaskGroupSequential.h create mode 100644 src/lib/utility/scheduling/TaskScheduler.cpp create mode 100644 src/lib/utility/scheduling/TaskScheduler.h diff --git a/src/app/qt/element/QtMainWindow.cpp b/src/app/qt/element/QtMainWindow.cpp index 7ffb09b1..6022aa20 100644 --- a/src/app/qt/element/QtMainWindow.cpp +++ b/src/app/qt/element/QtMainWindow.cpp @@ -12,6 +12,7 @@ #include "qt/view/QtViewWidgetWrapper.h" #include "utility/logging/logging.h" #include "utility/messaging/type/MessageFind.h" +#include "utility/messaging/type/MessageInterruptTasks.h" #include "utility/messaging/type/MessageLoadProject.h" #include "utility/messaging/type/MessageLoadSource.h" #include "utility/messaging/type/MessageRedo.h" @@ -36,7 +37,9 @@ QtMainWindow::QtMainWindow() setupFindMenu(); setupHelpMenu(); - // Need to call loadLayout here for right DockWidgetsize on Linux + setupShortcuts(); + + // Need to call loadLayout here for right DockWidget size on Linux // Seconde call is in Application.cpp loadLayout(); } @@ -45,94 +48,6 @@ QtMainWindow::~QtMainWindow() { } -void QtMainWindow::about() -{ - QMessageBox::about( - this, - tr("About"), - tr( - "Developed by:\n\n" - "Manuel Dobusch\n" - "Eberhard Gräther\n" - "Malte Langkabel\n" - "Victoria Pfausler\n" - "Andreas Stallinger\n" - ) - ); -} - -void QtMainWindow::newProject() -{ - QString sourceDir = QFileDialog::getExistingDirectory(this, tr("Open Directory")); - - if (!sourceDir.isEmpty()) - { - MessageLoadSource(sourceDir.toStdString()).dispatch(); - } -} - -void QtMainWindow::openProject(const QString &path) -{ - QString fileName = path; - - if (fileName.isNull()) - { - fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "", "XML Files (*.xml)"); - } - - if (!fileName.isEmpty()) - { - MessageLoadProject(fileName.toStdString()).dispatch(); - } -} - -void QtMainWindow::undo() -{ - MessageUndo().dispatch(); -} - -void QtMainWindow::redo() -{ - MessageRedo().dispatch(); -} - -void QtMainWindow::saveProject() -{ - MessageSaveProject("").dispatch(); -} - -void QtMainWindow::saveAsProject() -{ - QString filename = ""; - filename = QFileDialog::getSaveFileName(this, "Save File as", "", "XML Files(*.xml)"); - - if(!filename.isEmpty()) - { - MessageSaveProject(filename.toStdString()).dispatch(); - } -} - -void QtMainWindow::find() -{ - MessageFind().dispatch(); -} - -void QtMainWindow::closeWindow() -{ - QApplication* app = dynamic_cast(QCoreApplication::instance()); - - QWidget* activeWindow = app->activeWindow(); - if (activeWindow) - { - activeWindow->close(); - } -} - -void QtMainWindow::refresh() -{ - MessageRefresh().dispatch(); -} - void QtMainWindow::addView(View* view) { QDockWidget* dock = new QDockWidget(tr(view->getName().c_str()), this); @@ -207,6 +122,99 @@ bool QtMainWindow::event(QEvent* event) return QMainWindow::event(event); } +void QtMainWindow::about() +{ + QMessageBox::about( + this, + tr("About"), + tr( + "Developed by:\n\n" + "Manuel Dobusch\n" + "Eberhard Gräther\n" + "Malte Langkabel\n" + "Victoria Pfausler\n" + "Andreas Stallinger\n" + ) + ); +} + +void QtMainWindow::newProject() +{ + QString sourceDir = QFileDialog::getExistingDirectory(this, tr("Open Directory")); + + if (!sourceDir.isEmpty()) + { + MessageLoadSource(sourceDir.toStdString()).dispatch(); + } +} + +void QtMainWindow::openProject(const QString &path) +{ + QString fileName = path; + + if (fileName.isNull()) + { + fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "", "XML Files (*.xml)"); + } + + if (!fileName.isEmpty()) + { + MessageLoadProject(fileName.toStdString()).dispatch(); + } +} + +void QtMainWindow::find() +{ + MessageFind().dispatch(); +} + +void QtMainWindow::closeWindow() +{ + QApplication* app = dynamic_cast(QCoreApplication::instance()); + + QWidget* activeWindow = app->activeWindow(); + if (activeWindow) + { + activeWindow->close(); + } +} + +void QtMainWindow::refresh() +{ + MessageRefresh().dispatch(); +} + +void QtMainWindow::saveProject() +{ + MessageSaveProject("").dispatch(); +} + +void QtMainWindow::saveAsProject() +{ + QString filename = ""; + filename = QFileDialog::getSaveFileName(this, "Save File as", "", "XML Files(*.xml)"); + + if(!filename.isEmpty()) + { + MessageSaveProject(filename.toStdString()).dispatch(); + } +} + +void QtMainWindow::undo() +{ + MessageUndo().dispatch(); +} + +void QtMainWindow::redo() +{ + MessageRedo().dispatch(); +} + +void QtMainWindow::handleEscapeShortcut() +{ + MessageInterruptTasks().dispatch(); +} + void QtMainWindow::setupProjectMenu() { QMenu *menu = new QMenu(tr("&Project"), this); @@ -254,6 +262,12 @@ void QtMainWindow::setupHelpMenu() menu->addAction(tr("About &Qt"), QCoreApplication::instance(), SLOT(aboutQt())); } +void QtMainWindow::setupShortcuts() +{ + m_escapeShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); + connect(m_escapeShortcut, SIGNAL(activated()), SLOT(handleEscapeShortcut())); +} + QDockWidget* QtMainWindow::getDockWidgetForView(View* view) const { for (size_t i = 0; i < m_dockWidgets.size(); i++) diff --git a/src/app/qt/element/QtMainWindow.h b/src/app/qt/element/QtMainWindow.h index c5ea0c64..3fb0c54c 100644 --- a/src/app/qt/element/QtMainWindow.h +++ b/src/app/qt/element/QtMainWindow.h @@ -4,6 +4,7 @@ #include #include +#include #include class QDockWidget; @@ -41,6 +42,8 @@ public slots: void undo(); void redo(); + void handleEscapeShortcut(); + private: void setupEditMenu(); void setupProjectMenu(); @@ -48,9 +51,13 @@ private: void setupFindMenu(); void setupHelpMenu(); + void setupShortcuts(); + QDockWidget* getDockWidgetForView(View* view) const; std::vector> m_dockWidgets; + + QShortcut* m_escapeShortcut; }; #endif // QT_MAIN_WINDOW_H diff --git a/src/lib/Application.cpp b/src/lib/Application.cpp index 5f8ac3c4..01de79c4 100644 --- a/src/lib/Application.cpp +++ b/src/lib/Application.cpp @@ -3,6 +3,7 @@ #include "utility/logging/logging.h" #include "utility/messaging/MessageQueue.h" #include "utility/messaging/type/MessageActivateTokens.h" +#include "utility/scheduling/TaskScheduler.h" #include "component/view/MainView.h" #include "component/view/ViewFactory.h" @@ -40,10 +41,12 @@ std::shared_ptr Application::create(ViewFactory* viewFactory) Application::Application() { MessageQueue::getInstance()->startMessageLoopThreaded(); + TaskScheduler::getInstance()->startSchedulerLoopThreaded(); } Application::~Application() { + TaskScheduler::getInstance()->stopSchedulerLoop(); MessageQueue::getInstance()->stopMessageLoop(); m_mainView->saveLayout(); } diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 53c9896e..37da7fe7 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -142,6 +142,9 @@ add_files( data/name/NameHierarchy.cpp data/name/NameHierarchy.h + data/parser/cxx/TaskParseCxx.cpp + data/parser/cxx/TaskParseCxx.h + data/parser/ParseFunction.cpp data/parser/ParseFunction.h data/parser/ParseLocation.cpp @@ -241,6 +244,7 @@ add_files( utility/messaging/type/MessageFinishedParsing.h utility/messaging/type/MessageGraphNodeExpand.h utility/messaging/type/MessageGraphNodeMove.h + utility/messaging/type/MessageInterruptTasks.h utility/messaging/type/MessageLoadProject.h utility/messaging/type/MessageLoadSource.h utility/messaging/type/MessageRefresh.h @@ -260,6 +264,15 @@ add_files( utility/messaging/MessageQueue.cpp utility/messaging/MessageQueue.h + utility/scheduling/Task.cpp + utility/scheduling/Task.h + utility/scheduling/TaskGroup.cpp + utility/scheduling/TaskGroup.h + utility/scheduling/TaskGroupSequential.cpp + utility/scheduling/TaskGroupSequential.h + utility/scheduling/TaskScheduler.cpp + utility/scheduling/TaskScheduler.h + utility/text/Dictionary.cpp utility/text/Dictionary.h utility/text/TextAccess.cpp diff --git a/src/lib/Project.cpp b/src/lib/Project.cpp index 2a6753f8..0ae78875 100644 --- a/src/lib/Project.cpp +++ b/src/lib/Project.cpp @@ -6,7 +6,7 @@ #include "data/access/StorageAccessProxy.h" #include "data/graph/Token.h" -#include "data/parser/cxx/CxxParser.h" +#include "data/parser/cxx/TaskParseCxx.h" #include "settings/ApplicationSettings.h" #include "settings/ProjectSettings.h" @@ -24,29 +24,31 @@ Project::~Project() bool Project::loadProjectSettings(const std::string& projectSettingsFile) { bool success = ProjectSettings::getInstance()->load(projectSettingsFile); - if(success) + if (success) { m_projectSettingsFilepath = projectSettingsFile; + + createFileManager(); } return success; } -bool Project::saveProjectSettings( const std::string& projectSettingsFile ) +bool Project::saveProjectSettings(const std::string& projectSettingsFile) { - if(!projectSettingsFile.empty()) + if (projectSettingsFile.size()) { m_projectSettingsFilepath = projectSettingsFile; - ProjectSettings::getInstance()->save(projectSettingsFile); } - else if (!m_projectSettingsFilepath.empty()) - { - ProjectSettings::getInstance()->save(m_projectSettingsFilepath); - } - else + + if (!m_projectSettingsFilepath.size()) { return false; } - LOG_INFO_STREAM(<< "Projectsettings saved in File: " << m_projectSettingsFilepath); + + ProjectSettings::getInstance()->save(m_projectSettingsFilepath); + + LOG_INFO_STREAM(<< "ProjectSettings saved to file: " << m_projectSettingsFilepath); + return true; } @@ -54,12 +56,21 @@ void Project::clearProjectSettings() { m_projectSettingsFilepath.clear(); ProjectSettings::getInstance()->clear(); + + m_fileManager.reset(); } bool Project::setSourceDirectoryPath(const std::string& sourceDirectoryPath) { m_projectSettingsFilepath = sourceDirectoryPath + "/ProjectSettings.xml"; - return ProjectSettings::getInstance()->setSourcePaths(std::vector(1, sourceDirectoryPath)); + bool success = ProjectSettings::getInstance()->setSourcePaths(std::vector(1, sourceDirectoryPath)); + + if (success) + { + createFileManager(); + } + + return success; } void Project::clearStorage() @@ -72,25 +83,13 @@ void Project::clearStorage() void Project::parseCode() { - std::shared_ptr projSettings = ProjectSettings::getInstance(); - std::shared_ptr appSettings = ApplicationSettings::getInstance(); - - std::vector sourcePaths = projSettings->getSourcePaths(); - if (!sourcePaths.size()) + if (!m_fileManager) { + LOG_ERROR("No FileManger was created."); return; } - std::vector includePaths(sourcePaths); - - // TODO: move this creation to another place (after projectsettings have been loaded) - if (!m_fileManager) - { - std::vector sourceExtensions = ProjectSettings::getInstance()->getSourceExtensions(); - std::vector includeExtensions = ProjectSettings::getInstance()->getHeaderExtensions(); - - m_fileManager = std::make_shared(sourcePaths, includePaths, sourceExtensions, includeExtensions); - } + std::shared_ptr projSettings = ProjectSettings::getInstance(); m_fileManager->fetchFilePaths(); std::set addedFilePaths = m_fileManager->getAddedFilePaths(); @@ -109,36 +108,59 @@ void Project::parseCode() if (filesToParse.size() == 0) { - MessageFinishedParsing(0, 0, m_storage->getErrorCount()).dispatch(); + MessageFinishedParsing(0, 0, 0, m_storage->getErrorCount()).dispatch(); return; } + Task::dispatch(std::make_shared( + m_storage.get(), + m_fileManager.get(), + getParserArguments(), + filesToParse + )); +} + +void Project::createFileManager() +{ + std::shared_ptr projSettings = ProjectSettings::getInstance(); + + std::vector sourcePaths(projSettings->getSourcePaths()); + std::vector includePaths(sourcePaths); + + std::vector sourceExtensions = projSettings->getSourceExtensions(); + std::vector includeExtensions = projSettings->getHeaderExtensions(); + + if (sourcePaths.size()) + { + m_fileManager = std::make_shared(sourcePaths, includePaths, sourceExtensions, includeExtensions); + } +} + +Parser::Arguments Project::getParserArguments() const +{ + std::shared_ptr projSettings = ProjectSettings::getInstance(); + std::shared_ptr appSettings = ApplicationSettings::getInstance(); + Parser::Arguments args; + if (!m_fileManager) + { + LOG_ERROR("No FileManger was created."); + return args; + } + utility::append(args.compilerFlags, projSettings->getCompilerFlags()); utility::append(args.compilerFlags, appSettings->getCompilerFlags()); // Add the include paths as HeaderSearchPaths as well, so clang will also look here when searching include files. - utility::append(args.systemHeaderSearchPaths, includePaths); + utility::append(args.systemHeaderSearchPaths, m_fileManager->getIncludePaths()); utility::append(args.systemHeaderSearchPaths, projSettings->getHeaderSearchPaths()); utility::append(args.systemHeaderSearchPaths, appSettings->getHeaderSearchPaths()); utility::append(args.frameworkSearchPaths, projSettings->getFrameworkSearchPaths()); utility::append(args.frameworkSearchPaths, appSettings->getFrameworkSearchPaths()); - CxxParser parser(m_storage.get(), m_fileManager.get()); - - float duration = utility::duration( - [&]() - { - parser.parseFiles(filesToParse, args); - } - ); - - // m_storage->logGraph(); - // m_storage->logLocations(); - - MessageFinishedParsing(filesToParse.size(), duration, m_storage->getErrorCount()).dispatch(); + return args; } Project::Project(StorageAccessProxy* storageAccessProxy) diff --git a/src/lib/Project.h b/src/lib/Project.h index 6ac97830..b9a3b9b5 100644 --- a/src/lib/Project.h +++ b/src/lib/Project.h @@ -5,6 +5,7 @@ #include "utility/file/FileManager.h" +#include "data/parser/Parser.h" #include "data/Storage.h" class StorageAccessProxy; @@ -30,6 +31,10 @@ private: Project(const Project&); Project operator=(const Project&); + void createFileManager(); + + Parser::Arguments getParserArguments() const; + std::string m_projectSettingsFilepath; StorageAccessProxy* const m_storageAccessProxy; diff --git a/src/lib/component/controller/StatusBarController.cpp b/src/lib/component/controller/StatusBarController.cpp index 8c8492f4..823af47d 100644 --- a/src/lib/component/controller/StatusBarController.cpp +++ b/src/lib/component/controller/StatusBarController.cpp @@ -30,7 +30,7 @@ void StatusBarController::handleMessage(MessageFinishedParsing* message) { std::stringstream ss; ss << "Parsing Finished: "; - ss << message->fileCount << " files, "; + ss << message->fileCount << "/" << message->totalFileCount << " files, "; ss << std::setprecision(2) << std::fixed << message->parseTime << " seconds, "; ss << message->errorCount << " error(s)"; diff --git a/src/lib/data/Storage.cpp b/src/lib/data/Storage.cpp index 09351955..0413f4b4 100644 --- a/src/lib/data/Storage.cpp +++ b/src/lib/data/Storage.cpp @@ -150,11 +150,6 @@ void Storage::logLocations() const LOG_INFO_STREAM(<< '\n' << m_locationCollection); } -size_t Storage::getErrorCount() const -{ - return m_errorLocationCollection.getTokenLocationCount(); -} - void Storage::onError(const ParseLocation& location, const std::string& message) { log("ERROR", message, location); @@ -198,6 +193,11 @@ void Storage::onError(const ParseLocation& location, const std::string& message) } } +size_t Storage::getErrorCount() const +{ + return m_errorLocationCollection.getTokenLocationCount(); +} + Id Storage::onTypedefParsed( const ParseLocation& location, const NameHierarchy& nameHierarchy, const ParseTypeUsage& underlyingType, AccessType access diff --git a/src/lib/data/Storage.h b/src/lib/data/Storage.h index 92f65efc..d7489075 100644 --- a/src/lib/data/Storage.h +++ b/src/lib/data/Storage.h @@ -29,10 +29,9 @@ public: void logGraph() const; void logLocations() const; - size_t getErrorCount() const; - // ParserClient implementation virtual void onError(const ParseLocation& location, const std::string& message); + virtual size_t getErrorCount() const; virtual Id onTypedefParsed( const ParseLocation& location, const NameHierarchy& nameHierarchy, diff --git a/src/lib/data/parser/Parser.h b/src/lib/data/parser/Parser.h index e87cc9ae..ff34659e 100644 --- a/src/lib/data/parser/Parser.h +++ b/src/lib/data/parser/Parser.h @@ -5,9 +5,8 @@ #include #include -#include "data/parser/ParserClient.h" - class FilePath; +class ParserClient; class TextAccess; class Parser diff --git a/src/lib/data/parser/ParserClient.h b/src/lib/data/parser/ParserClient.h index 2c961c17..dbbfc550 100644 --- a/src/lib/data/parser/ParserClient.h +++ b/src/lib/data/parser/ParserClient.h @@ -51,6 +51,7 @@ public: virtual ~ParserClient(); virtual void onError(const ParseLocation& location, const std::string& message) = 0; + virtual size_t getErrorCount() const = 0; virtual Id onTypedefParsed( const ParseLocation& location, const NameHierarchy& nameHierarchy, diff --git a/src/lib/data/parser/cxx/ASTAction.cpp b/src/lib/data/parser/cxx/ASTAction.cpp index f87f4dd9..f9efabab 100644 --- a/src/lib/data/parser/cxx/ASTAction.cpp +++ b/src/lib/data/parser/cxx/ASTAction.cpp @@ -21,8 +21,6 @@ std::unique_ptr ASTAction::CreateASTConsumer(clang::Compiler bool ASTAction::BeginSourceFileAction(clang::CompilerInstance& compiler, llvm::StringRef filePath) { - m_client->onFileParsed(filePath.str()); - clang::Preprocessor& preprocessor = compiler.getPreprocessor(); preprocessor.addPPCallbacks( llvm::make_unique(compiler.getSourceManager(), m_client, m_fileRegister)); diff --git a/src/lib/data/parser/cxx/CxxParser.cpp b/src/lib/data/parser/cxx/CxxParser.cpp index 371d6596..6dbcef85 100644 --- a/src/lib/data/parser/cxx/CxxParser.cpp +++ b/src/lib/data/parser/cxx/CxxParser.cpp @@ -1,5 +1,8 @@ #include "data/parser/cxx/CxxParser.h" +#include "clang/Tooling/Tooling.h" + +#include "utility/file/FileManager.h" #include "utility/file/FileRegister.h" #include "utility/logging/logging.h" #include "utility/text/TextAccess.h" @@ -50,7 +53,7 @@ namespace CxxParser::CxxParser(ParserClient* client, const FileManager* fileManager) : Parser(client) - , m_fileManager(fileManager) + , m_fileRegister(std::make_shared(fileManager)) { } @@ -60,70 +63,33 @@ CxxParser::~CxxParser() void CxxParser::parseFiles(const std::vector& filePaths, const Arguments& arguments) { - // Commandline flags passed to the programm. Everything after '--' will be interpreted by the ClangTool. - std::vector args = getCommandlineArguments(arguments); - args.insert(args.begin(), "app"); - args.insert(args.begin() + 1, "--"); - - int argc = args.size(); - const char** argv = new const char*[argc]; - for (size_t i = 0; i < args.size(); i++) - { - argv[i] = args[i].c_str(); - } - - std::shared_ptr compilationDatabase( - clang::tooling::FixedCompilationDatabase::loadFromCommandLine(argc, argv) - ); - - if (!compilationDatabase) - { - LOG_ERROR("Failed to load compilation database"); - return; - } - - FileRegister fileRegister(m_fileManager, filePaths); + setupParsing(filePaths, arguments); std::vector sourcePaths; - for (const FilePath& path : fileRegister.getSourceFilePaths()) + for (const FilePath& path : m_fileRegister->getUnparsedSourceFilePaths()) { sourcePaths.push_back(path.absoluteStr()); } - llvm::IntrusiveRefCntPtr options = new clang::DiagnosticOptions(); - CxxDiagnosticConsumer reporter(llvm::errs(), &*options, m_client, arguments.logErrors); + runTool(sourcePaths); - ASTActionFactory actionFactory(m_client, &fileRegister); - - clang::tooling::ClangTool tool(*compilationDatabase, sourcePaths); - tool.setDiagnosticConsumer(&reporter); - tool.run(&actionFactory); - - std::vector unparsedHeaders = fileRegister.getUnparsedIncludeFilePaths(); + std::vector unparsedHeaders = m_fileRegister->getUnparsedIncludeFilePaths(); for (const FilePath& path : unparsedHeaders) { - if (!fileRegister.includeFileIsParsed(path)) + if (!m_fileRegister->includeFileIsParsed(path)) { - clang::tooling::ClangTool tool(*compilationDatabase, std::vector(1, path.str())); - tool.setDiagnosticConsumer(&reporter); - tool.run(&actionFactory); + runTool(std::vector(1, path.str())); } } - - delete argv; } void CxxParser::parseFile(std::shared_ptr textAccess, const Arguments& arguments) { std::vector args = getCommandlineArguments(arguments); + std::shared_ptr diagnostics = getDiagnostics(arguments); - llvm::IntrusiveRefCntPtr options = new clang::DiagnosticOptions(); - CxxDiagnosticConsumer reporter(llvm::errs(), &*options, m_client, arguments.logErrors); - - FileRegister fileRegister(m_fileManager, std::vector()); - - ASTActionFactory actionFactory(m_client, &fileRegister); - runToolOnCodeWithArgs(&reporter, actionFactory.create(), textAccess->getText(), args); + ASTActionFactory actionFactory(m_client, m_fileRegister.get()); + runToolOnCodeWithArgs(diagnostics.get(), actionFactory.create(), textAccess->getText(), args); } std::vector CxxParser::getCommandlineArguments(const Arguments& arguments) const @@ -165,3 +131,65 @@ std::vector CxxParser::getCommandlineArguments(const Arguments& arg return args; } + +std::shared_ptr CxxParser::getCompilationDatabase( + const Arguments& arguments +) const { + // Commandline flags passed to the programm. Everything after '--' will be interpreted by the ClangTool. + std::vector args = getCommandlineArguments(arguments); + args.insert(args.begin(), "app"); + args.insert(args.begin() + 1, "--"); + + int argc = args.size(); + const char** argv = new const char*[argc]; + for (size_t i = 0; i < args.size(); i++) + { + argv[i] = args[i].c_str(); + } + + std::shared_ptr compilationDatabase( + clang::tooling::FixedCompilationDatabase::loadFromCommandLine(argc, argv) + ); + + delete argv; + + if (!compilationDatabase) + { + LOG_ERROR("Failed to load compilation database"); + return nullptr; + } + + return compilationDatabase; +} + +std::shared_ptr CxxParser::getDiagnostics(const Arguments& arguments) const +{ + llvm::IntrusiveRefCntPtr options = new clang::DiagnosticOptions(); + return std::make_shared(llvm::errs(), &*options, m_client, arguments.logErrors); +} + +void CxxParser::setupParsing(const std::vector& filePaths, const Arguments& arguments) +{ + m_fileRegister->setFilePaths(filePaths); + m_compilationDatabase = getCompilationDatabase(arguments); + m_diagnostics = getDiagnostics(arguments); +} + +void CxxParser::runTool(const std::vector& files) +{ + clang::tooling::ClangTool tool(*m_compilationDatabase, files); + tool.setDiagnosticConsumer(m_diagnostics.get()); + + ASTActionFactory actionFactory(m_client, m_fileRegister.get()); + tool.run(&actionFactory); +} + +FileRegister* CxxParser::getFileRegister() +{ + return m_fileRegister.get(); +} + +ParserClient* CxxParser::getParserClient() +{ + return m_client; +} diff --git a/src/lib/data/parser/cxx/CxxParser.h b/src/lib/data/parser/cxx/CxxParser.h index 10e05886..cc393952 100644 --- a/src/lib/data/parser/cxx/CxxParser.h +++ b/src/lib/data/parser/cxx/CxxParser.h @@ -2,7 +2,19 @@ #define CXX_PARSER_H #include "data/parser/Parser.h" -#include "utility/file/FileManager.h" + +namespace clang +{ + namespace tooling + { + class FixedCompilationDatabase; + } +} + +class CxxDiagnosticConsumer; +class FileManager; +class FileRegister; +class TaskParseCxx; class CxxParser: public Parser { @@ -10,13 +22,29 @@ public: CxxParser(ParserClient* client, const FileManager* fileManager); ~CxxParser(); + // ParserClient implementation virtual void parseFiles(const std::vector& filePaths, const Arguments& arguments); virtual void parseFile(std::shared_ptr textAccess, const Arguments& arguments); private: std::vector getCommandlineArguments(const Arguments& arguments) const; + std::shared_ptr getCompilationDatabase(const Arguments& arguments) const; - const FileManager* m_fileManager; + std::shared_ptr getDiagnostics(const Arguments& arguments) const; + + // Accessed by TaskParseCxx + void setupParsing(const std::vector& filePaths, const Arguments& arguments); + void runTool(const std::vector& files); + + FileRegister* getFileRegister(); + ParserClient* getParserClient(); + + friend class TaskParseCxx; + + std::shared_ptr m_fileRegister; + + std::shared_ptr m_compilationDatabase; + std::shared_ptr m_diagnostics; }; #endif // CXX_PARSER_H diff --git a/src/lib/data/parser/cxx/PreprocessorCallbacks.cpp b/src/lib/data/parser/cxx/PreprocessorCallbacks.cpp index 481831bc..e8074303 100644 --- a/src/lib/data/parser/cxx/PreprocessorCallbacks.cpp +++ b/src/lib/data/parser/cxx/PreprocessorCallbacks.cpp @@ -26,6 +26,7 @@ void PreprocessorCallbacks::FileChanged( const clang::FileEntry *fileEntry = m_sourceManager.getFileEntryForID(m_sourceManager.getFileID(location)); if (fileEntry && m_fileRegister->getFileManager()->hasFilePath(fileEntry->getName())) { + m_client->onFileParsed(fileEntry->getName()); m_fileRegister->markIncludeFileParsing(fileEntry->getName()); } } diff --git a/src/lib/data/parser/cxx/TaskParseCxx.cpp b/src/lib/data/parser/cxx/TaskParseCxx.cpp new file mode 100644 index 00000000..7bcde31c --- /dev/null +++ b/src/lib/data/parser/cxx/TaskParseCxx.cpp @@ -0,0 +1,97 @@ +#include "data/parser/cxx/TaskParseCxx.h" + +#include + +#include "utility/file/FileRegister.h" +#include "utility/messaging/type/MessageFinishedParsing.h" +#include "utility/messaging/type/MessageStatus.h" + +#include "data/parser/ParserClient.h" + +TaskParseCxx::TaskParseCxx( + ParserClient* client, + const FileManager* fileManager, + const Parser::Arguments& arguments, + const std::vector& files +) + : m_parser(client, fileManager) + , m_arguments(arguments) + , m_files(files) +{ +} + +void TaskParseCxx::enter() +{ + m_start = utility::durationStart(); + + m_parser.setupParsing(m_files, m_arguments); + + for (const FilePath& path : m_parser.getFileRegister()->getUnparsedSourceFilePaths()) + { + m_sourcePaths.push(path.absoluteStr()); + } +} + +Task::TaskState TaskParseCxx::update() +{ + std::string sourcePath; + bool isSource = false; + + FileRegister* fileRegister = m_parser.getFileRegister(); + + if (m_sourcePaths.size()) + { + sourcePath = m_sourcePaths.front(); + m_sourcePaths.pop(); + isSource = true; + } + else + { + std::vector unparsedHeaders = fileRegister->getUnparsedIncludeFilePaths(); + if (unparsedHeaders.size()) + { + sourcePath = unparsedHeaders[0].str(); + } + } + + if (!sourcePath.size()) + { + return Task::STATE_FINISHED; + } + + std::stringstream ss; + ss << "parsing (ESC to quit): ["; + ss << fileRegister->getParsedFilesCount() << "/" << fileRegister->getFilesCount() << "] "; + ss << sourcePath; + + MessageStatus(ss.str()).dispatch(); + + m_parser.runTool(std::vector(1, sourcePath)); + + if (isSource) + { + fileRegister->markSourceFileParsed(sourcePath); + } + + return Task::STATE_RUNNING; +} + +void TaskParseCxx::exit() +{ + FileRegister* fileRegister = m_parser.getFileRegister(); + + MessageFinishedParsing( + fileRegister->getParsedFilesCount(), + fileRegister->getFilesCount(), + utility::duration(m_start), + m_parser.getParserClient()->getErrorCount() + ).dispatch(); +} + +void TaskParseCxx::interrupt() +{ +} + +void TaskParseCxx::revert() +{ +} diff --git a/src/lib/data/parser/cxx/TaskParseCxx.h b/src/lib/data/parser/cxx/TaskParseCxx.h new file mode 100644 index 00000000..99a3d658 --- /dev/null +++ b/src/lib/data/parser/cxx/TaskParseCxx.h @@ -0,0 +1,39 @@ +#ifndef TASK_PARSE_CXX_H +#define TASK_PARSE_CXX_H + +#include + +#include "utility/scheduling/Task.h" +#include "utility/utility.h" + +#include "data/parser/cxx/CxxParser.h" + +class TaskParseCxx + : public Task +{ +public: + TaskParseCxx( + ParserClient* client, + const FileManager* fileManager, + const Parser::Arguments& arguments, + const std::vector& files + ); + + virtual void enter(); + virtual TaskState update(); + virtual void exit(); + + virtual void interrupt(); + virtual void revert(); + +private: + CxxParser m_parser; + const Parser::Arguments m_arguments; + const std::vector m_files; + + std::queue m_sourcePaths; + + utility::TimePoint m_start; +}; + +#endif // TASK_PARSE_CXX_H diff --git a/src/lib/utility/file/FileManager.cpp b/src/lib/utility/file/FileManager.cpp index 401f575c..0f4e8c87 100644 --- a/src/lib/utility/file/FileManager.cpp +++ b/src/lib/utility/file/FileManager.cpp @@ -22,6 +22,16 @@ FileManager::~FileManager() { } +const std::vector& FileManager::getSourcePaths() const +{ + return m_sourcePaths; +} + +const std::vector& FileManager::getIncludePaths() const +{ + return m_includePaths; +} + void FileManager::reset() { m_files.clear(); diff --git a/src/lib/utility/file/FileManager.h b/src/lib/utility/file/FileManager.h index e9615b60..8aac62b5 100644 --- a/src/lib/utility/file/FileManager.h +++ b/src/lib/utility/file/FileManager.h @@ -18,6 +18,9 @@ public: ); ~FileManager(); + const std::vector& getSourcePaths() const; + const std::vector& getIncludePaths() const; + void reset(); void fetchFilePaths(); diff --git a/src/lib/utility/file/FileRegister.cpp b/src/lib/utility/file/FileRegister.cpp index c76c1ea1..1a321004 100644 --- a/src/lib/utility/file/FileRegister.cpp +++ b/src/lib/utility/file/FileRegister.cpp @@ -3,14 +3,26 @@ #include "utility/file/FileManager.h" #include "utility/file/FileSystem.h" -FileRegister::FileRegister(const FileManager* fileManager, const std::vector& filePaths) +FileRegister::FileRegister(const FileManager* fileManager) : m_fileManager(fileManager) { +} + +const FileManager* FileRegister::getFileManager() const +{ + return m_fileManager; +} + +void FileRegister::setFilePaths(const std::vector& 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 FileRegister::getUnparsedSourceFilePaths() const { - return m_fileManager; -} - -const std::vector& FileRegister::getSourceFilePaths() const -{ - return m_sourceFilePaths; + return getUnparsedFilePaths(m_sourceFilePaths); } std::vector FileRegister::getUnparsedIncludeFilePaths() const { - std::vector filePaths; - - for (std::pair&& 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::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::iterator it = m_includeFilePaths.find(FilePath(filePath)); @@ -90,3 +98,28 @@ void FileRegister::markParsingIncludeFilesParsed() } } } + +std::vector FileRegister::getUnparsedFilePaths(const std::map filePaths) const +{ + std::vector files; + + for (std::pair&& 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(); +} diff --git a/src/lib/utility/file/FileRegister.h b/src/lib/utility/file/FileRegister.h index b5aa4865..01038b1c 100644 --- a/src/lib/utility/file/FileRegister.h +++ b/src/lib/utility/file/FileRegister.h @@ -12,19 +12,25 @@ class FileManager; class FileRegister { public: - FileRegister(const FileManager* fileManager, const std::vector& filePaths); + explicit FileRegister(const FileManager* fileManager); const FileManager* getFileManager() const; - const std::vector& getSourceFilePaths() const; + void setFilePaths(const std::vector& filePaths); + + std::vector getUnparsedSourceFilePaths() const; std::vector 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 getUnparsedFilePaths(const std::map filePaths) const; + const FileManager* m_fileManager; - std::vector m_sourceFilePaths; + std::map m_sourceFilePaths; std::map m_includeFilePaths; }; diff --git a/src/lib/utility/messaging/type/MessageFinishedParsing.h b/src/lib/utility/messaging/type/MessageFinishedParsing.h index a8f80c6d..1050868c 100644 --- a/src/lib/utility/messaging/type/MessageFinishedParsing.h +++ b/src/lib/utility/messaging/type/MessageFinishedParsing.h @@ -6,8 +6,9 @@ class MessageFinishedParsing: public Message { 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; }; diff --git a/src/lib/utility/messaging/type/MessageInterruptTasks.h b/src/lib/utility/messaging/type/MessageInterruptTasks.h new file mode 100644 index 00000000..40c72414 --- /dev/null +++ b/src/lib/utility/messaging/type/MessageInterruptTasks.h @@ -0,0 +1,20 @@ +#ifndef MESSAGE_INTERRUPT_TASKS_H +#define MESSAGE_INTERRUPT_TASKS_H + +#include "utility/messaging/Message.h" + +class MessageInterruptTasks: + public Message +{ +public: + MessageInterruptTasks() + { + } + + static const std::string getStaticType() + { + return "MessageInterruptTasks"; + } +}; + +#endif // MESSAGE_INTERRUPT_TASKS_H diff --git a/src/lib/utility/scheduling/Task.cpp b/src/lib/utility/scheduling/Task.cpp new file mode 100644 index 00000000..37c6230c --- /dev/null +++ b/src/lib/utility/scheduling/Task.cpp @@ -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) +{ + 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; +} diff --git a/src/lib/utility/scheduling/Task.h b/src/lib/utility/scheduling/Task.h new file mode 100644 index 00000000..1fca06ae --- /dev/null +++ b/src/lib/utility/scheduling/Task.h @@ -0,0 +1,41 @@ +#ifndef TASK_H +#define TASK_H + +#include + +class Task +{ +public: + enum TaskState + { + STATE_NEW, + STATE_RUNNING, + STATE_FINISHED, + STATE_CANCELED + }; + + static void dispatch(std::shared_ptr 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 diff --git a/src/lib/utility/scheduling/TaskGroup.cpp b/src/lib/utility/scheduling/TaskGroup.cpp new file mode 100644 index 00000000..54d1db5c --- /dev/null +++ b/src/lib/utility/scheduling/TaskGroup.cpp @@ -0,0 +1,14 @@ +#include "utility/scheduling/TaskGroup.h" + +TaskGroup::TaskGroup() +{ +} + +TaskGroup::~TaskGroup() +{ +} + +void TaskGroup::addTask(std::shared_ptr task) +{ + m_tasks.push_back(task); +} diff --git a/src/lib/utility/scheduling/TaskGroup.h b/src/lib/utility/scheduling/TaskGroup.h new file mode 100644 index 00000000..cd355f94 --- /dev/null +++ b/src/lib/utility/scheduling/TaskGroup.h @@ -0,0 +1,22 @@ +#ifndef TASK_GROUP_H +#define TASK_GROUP_H + +#include +#include + +#include "utility/scheduling/Task.h" + +class TaskGroup + : public Task +{ +public: + TaskGroup(); + virtual ~TaskGroup(); + + void addTask(std::shared_ptr task); + +protected: + std::vector> m_tasks; +}; + +#endif // TASK_GROUP_H diff --git a/src/lib/utility/scheduling/TaskGroupSequential.cpp b/src/lib/utility/scheduling/TaskGroupSequential.cpp new file mode 100644 index 00000000..34a8a3f3 --- /dev/null +++ b/src/lib/utility/scheduling/TaskGroupSequential.cpp @@ -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 = 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); + } +} diff --git a/src/lib/utility/scheduling/TaskGroupSequential.h b/src/lib/utility/scheduling/TaskGroupSequential.h new file mode 100644 index 00000000..f71461ed --- /dev/null +++ b/src/lib/utility/scheduling/TaskGroupSequential.h @@ -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 diff --git a/src/lib/utility/scheduling/TaskScheduler.cpp b/src/lib/utility/scheduling/TaskScheduler.cpp new file mode 100644 index 00000000..3269d0bb --- /dev/null +++ b/src/lib/utility/scheduling/TaskScheduler.cpp @@ -0,0 +1,132 @@ +#include "utility/scheduling/TaskScheduler.h" + +#include +#include + +#include "utility/logging/logging.h" +#include "utility/messaging/type/MessageStatus.h" + +std::shared_ptr TaskScheduler::getInstance() +{ + if (!s_instance) + { + s_instance = std::shared_ptr(new TaskScheduler()); + } + + return s_instance; +} + +void TaskScheduler::pushTask(std::shared_ptr task) +{ + std::lock_guard lock(m_tasksMutex); + m_tasks.push(task); +} + +void TaskScheduler::interruptCurrentTask() +{ + std::lock_guard lock(m_tasksMutex); + m_interruptTask = true; +} + +void TaskScheduler::startSchedulerLoopThreaded() +{ + std::thread(&TaskScheduler::startSchedulerLoop, this).detach(); +} + +void TaskScheduler::startSchedulerLoop() +{ + { + std::lock_guard lock(m_loopMutex); + + if (m_loopIsRunning) + { + LOG_ERROR("Loop is already running"); + return; + } + + m_loopIsRunning = true; + } + + while (true) + { + { + std::lock_guard 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 lock(m_loopMutex); + + if (!m_loopIsRunning) + { + LOG_WARNING("Loop is not running"); + } + + m_loopIsRunning = false; + } + + interruptCurrentTask(); +} + +bool TaskScheduler::loopIsRunning() const +{ + std::lock_guard lock(m_loopMutex); + return m_loopIsRunning; +} + +std::shared_ptr TaskScheduler::s_instance; + +TaskScheduler::TaskScheduler() + : m_loopIsRunning(false) + , m_interruptTask(false) +{ +} + +void TaskScheduler::updateTasks() +{ + std::lock_guard lock(m_tasksMutex); + + bool interrupt = m_interruptTask; + + while (m_tasks.size()) + { + std::shared_ptr 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 lock(m_tasksMutex); + if (m_tasks.size()) + { + MessageStatus("Stop running tasks...").dispatch(); + } +} diff --git a/src/lib/utility/scheduling/TaskScheduler.h b/src/lib/utility/scheduling/TaskScheduler.h new file mode 100644 index 00000000..8957fb11 --- /dev/null +++ b/src/lib/utility/scheduling/TaskScheduler.h @@ -0,0 +1,47 @@ +#ifndef TASK_SCHEDULER_H +#define TASK_SCHEDULER_H + +#include +#include +#include + +#include "utility/messaging/MessageListener.h" +#include "utility/messaging/type/MessageInterruptTasks.h" +#include "utility/scheduling/Task.h" + +class TaskScheduler + : public MessageListener +{ +public: + static std::shared_ptr getInstance(); + + void pushTask(std::shared_ptr task); + void interruptCurrentTask(); + + void startSchedulerLoopThreaded(); + void startSchedulerLoop(); + void stopSchedulerLoop(); + + bool loopIsRunning() const; + +private: + static std::shared_ptr s_instance; + + TaskScheduler(); + TaskScheduler(const TaskScheduler&); + void operator=(const TaskScheduler&); + + void updateTasks(); + + virtual void handleMessage(MessageInterruptTasks* message); + + bool m_loopIsRunning; + + std::queue> m_tasks; + bool m_interruptTask; + + mutable std::mutex m_tasksMutex; + mutable std::mutex m_loopMutex; +}; + +#endif // TASK_SCHEDULER_H diff --git a/src/lib/utility/utility.cpp b/src/lib/utility/utility.cpp index 18c9eb47..3078f296 100644 --- a/src/lib/utility/utility.cpp +++ b/src/lib/utility/utility.cpp @@ -1,16 +1,26 @@ #include "utility/utility.h" -float utility::duration(std::function func) +utility::TimePoint utility::durationStart() { - std::chrono::time_point start = std::chrono::system_clock::now(); - - func(); + return std::chrono::system_clock::now(); +} +float utility::duration(const TimePoint& start) +{ std::chrono::duration duration = std::chrono::duration_cast(std::chrono::system_clock::now() - start); return duration.count(); } +float utility::duration(std::function 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; diff --git a/src/lib/utility/utility.h b/src/lib/utility/utility.h index 785b7b7a..263d16ce 100644 --- a/src/lib/utility/utility.h +++ b/src/lib/utility/utility.h @@ -8,6 +8,10 @@ namespace utility { + typedef std::chrono::time_point TimePoint; + + TimePoint durationStart(); + float duration(const TimePoint& start); float duration(std::function func); template diff --git a/src/test/CxxParserTestSuite.h b/src/test/CxxParserTestSuite.h index a757a990..8d3ed80d 100644 --- a/src/test/CxxParserTestSuite.h +++ b/src/test/CxxParserTestSuite.h @@ -2405,7 +2405,7 @@ public: TS_ASSERT_EQUALS(client.usages.size(), 3); TS_ASSERT_EQUALS(client.typeUses.size(), 8); - TS_ASSERT_EQUALS(client.files.size(), 2); + TS_ASSERT_EQUALS(client.files.size(), 3); TS_ASSERT_EQUALS(client.includes.size(), 1); } @@ -2429,6 +2429,11 @@ private: errors.push_back(addLocationSuffix(message, location)); } + virtual size_t getErrorCount() const + { + return 0; + } + virtual Id onTypedefParsed( const ParseLocation& location, const NameHierarchy& nameHierarchy, const ParseTypeUsage& underlyingType, AccessType access