#pragma once ///////////////////////////////////////////////////////////////////// // MockDepAnal.h - This package builds a mock of all of pass 1 // // // // Jim Fawcett, CSE687 - Object Oriented Design, Spring 2016 // ///////////////////////////////////////////////////////////////////// /* * No real dependency analysis is done here, but this mock sets up * all the event handling and data flow needed, using a ThreadPool * and Task class to manage a lot of concurrency in a relatively * easy way. * * This solution uses packages: * - MockDepAnal - AnalysisExecutive, TypeAnal, TableMerge, * and EventHandler classes * - IFileMgr - IFileMgr interface * - FileMgr - FileMgr, AnalFileEventHandler, AnalDirEventHandler, * and AnalDoneEventHandler classes * - Task - Task class * - ThreadPool - ThreadPool class * - Logger - Logger and StaticLogger * - BlockingQueue - BlockingQueue used by the three previous packages * - Utilities - Converter has toString and toValue methods, * and several other helper classes */ #include #include #include #include "../Cpp11-BlockingQueue/Cpp11-BlockingQueue.h" #include "../FileMgr/IFileMgr.h" #include "../FileMgr/FileMgr.h" #include "../Logger/Logger.h" #include "../Utilities/Utilities.h" #include "../Tasks/Task.h" using Show = StaticLogger<0>; using DebugLog = StaticLogger<1>; class TypeAnal { public: using File = std::string; using Table = std::string; // simple for demo TypeAnal(BlockingQueue& tableQ) : tableQ_(tableQ) {} void doTypeAnal(const File& filename) { static size_t count = 0; Show::write( "\n analyzing file \"" + filename + "\" on thread " + Utilities::Converter::toString(std::this_thread::get_id()) ); // mock table tableQ_.enQ("Table" + Utilities::Converter::toString(++count)); } void done(size_t numFilesProcessed) { tableQ_.enQ("Done" + Utilities::Converter::toString(numFilesProcessed)); } private: BlockingQueue
& tableQ_; }; class TableMerge { public: TableMerge(BlockingQueue& tableQ) : tableQ_(tableQ) {} TypeAnal::Table merge() { while (true) { TypeAnal::Table table = tableQ_.deQ(); if (table.find("Done") > -1) { std::string count = table.substr(4); size_t numFilesProcessed = Utilities::Converter::toValue(count); /* * Note that when this "done" mock table is received there still could * be analysis threads working on the last few files. * * To handle that case you need to keep track of the active analysis * threads, and not break out of this loop until the last active thread * completes and their tables have been merged. * * This is left as an exercise for you. */ break; } else { merged_ += table; merged_ += " "; Show::write("\n -----------------\n mock " + table + "\n -----------------"); } } return merged_; } private: BlockingQueue& tableQ_; TypeAnal::Table merged_; size_t numTablesCreated = 0; }; ///////////////////////////////////////////////////////////////////// // Event Handlers class AnalysisExecutive; class AnalFileEventHandler : public FileManager::IFileEventHandler { public: AnalFileEventHandler(AnalysisExecutive* pExec) : pExec_(pExec) {} void execute(const std::string& file); private: AnalysisExecutive* pExec_; }; class AnalDirEventHandler : public FileManager::IDirEventHandler { public: AnalDirEventHandler(AnalysisExecutive* pExec) : pExec_(pExec) {} void execute(const std::string& dir); private: AnalysisExecutive* pExec_; }; class AnalDoneEventHandler : public FileManager::IDoneEventHandler { public: AnalDoneEventHandler(AnalysisExecutive* pExec) : pExec_(pExec) {} void execute(size_t numFilesProcessed); private: AnalysisExecutive* pExec_; }; ///////////////////////////////////////////////////////////////////// // class AnalysisExecutive { public: using File = std::string; using Path = std::string; using CallObj = std::function; AnalysisExecutive(const Path& path) : ta_(tableQueue_), tm_(tableQueue_), fevt_(this), devt_(this), dnEvt_(this) { FileManager::FileMgrFactory factory; pFileMgr_ = factory.create(path); pFileMgr_->regForFiles(&fevt_); pFileMgr_->regForDirs(&devt_); pFileMgr_->regForDone(&dnEvt_); } void start() { CallObj co = [this]() { Show::write("\n starting file search"); pFileMgr_->search(); }; task_.workItem(&co); CallObj to = [this]() { Show::write("\n starting table merge"); tm_.merge(); }; task_.workItem(&to); } void stop() { task_.workItem(nullptr); } void wait() { task_.wait(); } void file(const File& file) { DebugLog::write("\n analyzing file \"" + file + "\""); File fileSpec = path_ + "\\" + file; CallObj co = [this, fileSpec]() { ta_.doTypeAnal(fileSpec); }; task_.workItem(&co); // threadpool will make copy } void dir(const Path& path) { path_ = path; } void done(size_t numFilesProcessed) { Show::write("\n Done Event: " + Utilities::Converter::toString(numFilesProcessed) + " files processed"); ta_.done(numFilesProcessed); } private: TypeAnal ta_; TableMerge tm_; BlockingQueue tableQueue_; Path path_; Task task_; FileManager::IFileMgr* pFileMgr_; AnalFileEventHandler fevt_; AnalDirEventHandler devt_; AnalDoneEventHandler dnEvt_; };