From 1d7b6db0a3b54498a532e28cce9903bf33f5e224 Mon Sep 17 00:00:00 2001 From: John Breaux Date: Sat, 23 Apr 2022 17:56:41 -0500 Subject: [PATCH] Code cleanup and stylization --- inc/graph.hpp | 29 +++++++---- inc/read.hpp | 28 ++++++++++ src/graph.cpp | 2 +- src/main.cpp | 12 ++--- src/read.cpp | 127 +++++++++++++++++++++++++++++++++++++++++++++ src/read_input.cpp | 106 ------------------------------------- src/reducible.cpp | 47 +++++++++++------ 7 files changed, 210 insertions(+), 141 deletions(-) create mode 100644 inc/read.hpp create mode 100644 src/read.cpp delete mode 100644 src/read_input.cpp diff --git a/inc/graph.hpp b/inc/graph.hpp index 8fd14b7..48b97f8 100644 --- a/inc/graph.hpp +++ b/inc/graph.hpp @@ -1,34 +1,41 @@ -#ifndef PROJECT2_GRAPH_HPP_INCLUDED -#define PROJECT2_GRAPH_HPP_INCLUDED +#ifndef PROJECT2_INC_GRAPH_HPP +#define PROJECT2_INC_GRAPH_HPP #include // string #include // vector, vector +typedef std::vector> matrix; + class graph { -public: + public: // Constructors - graph() {} + graph () {} // Initializers: // Read a graph from a file - int read(std::string filename); + int read (std::string filename); + // Error codes given off by read () + enum ERROR { INVALID_PROCESSES = 0b000001, + INVALID_RESOURCES = 0b000010, + INVALID_COUNTS = 0b000100, + INVALID_ROWS = 0b001000, + INVALID_COLUMNS = 0b010000, + INVALID_FILENAME = 0b100000 }; // check functions: // is the graph... - bool reducible();// ? - bool knotted();// ? + bool reducible (); // ? + bool knotted (); // ? // miscellaneous functions: // print the graph void print (); - typedef std::vector> matrix; - -private: + private: int num_processes = 0; int num_resources = 0; std::vector resource_counts; matrix m; // Tell me, Mr. Anderson, what good is a phone call if you are unable to speak? }; -#endif \ No newline at end of file +#endif // PROJECT2_INC_GRAPH_HPP \ No newline at end of file diff --git a/inc/read.hpp b/inc/read.hpp new file mode 100644 index 0000000..71f8a97 --- /dev/null +++ b/inc/read.hpp @@ -0,0 +1,28 @@ +#ifndef PROJECT2_INC_READ_HPP +#define PROJECT2_INC_READ_HPP + +#include +#include + +/* stovi: +* convert string s to vector no matter the cost + + @param &s const reference to a string + + @returns vector: signed integer representations + of the groups of contiguous digits in the string +*/ +std::vector stovi (const std::string &s); + +/* print_errpr_message: +* Print the error message associated with the given error number + + @param error int representing the error states of graph::read() + @param filename string containing the name of a file + + @returns void + +*/ +void print_error_message (int error, std::string filename); + +#endif // PROJECT2_INC_READ_HPP \ No newline at end of file diff --git a/src/graph.cpp b/src/graph.cpp index 2886333..b795d5f 100644 --- a/src/graph.cpp +++ b/src/graph.cpp @@ -4,7 +4,7 @@ | Created 2022-04-17 | +-----------------------------------------------+ */ #include "graph.hpp" - +// clang-format off void graph::print () { printf("num_processes: %d\nnum_resources: %d\n", num_processes, num_resources); printf("resource_counts: ["); for (auto e: resource_counts) printf("%d, ", e); printf("\b\b]\n"); diff --git a/src/main.cpp b/src/main.cpp index 46f913f..44ae169 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,9 +4,9 @@ +-------------+---------+-----------------------+ | Created 2022-04-16 | +-----------------------------------------------+ */ -#include "graph.hpp" // Graph reduction/Knot detection, struct adjmatrix +#include "graph.hpp" // Graph reduction/Knot detection, struct adjmatrix -int main(int argc, char** argv) { +int main (int argc, char **argv) { // TODO: Grab file name from args //? Command line argument structure? //? Other flags? What other features should this have? @@ -14,15 +14,15 @@ int main(int argc, char** argv) { std::string filename = argv[1]; graph g; - //TODO: Implement reading from a file - int err = g.read(filename); + // Read from a file, and print the result + int err = g.read (filename); if (err) { return err; } - g.print(); + g.print (); // TODO: Implement graph reduction and/or knot detection - printf("Graph is %s\n", g.reducible()?"not reducible! Deadlock!":"reducible! No deadlock!");//? + printf ("Graph is %s\n", g.reducible () ? "not reducible! Deadlock!" : "reducible! No deadlock!"); //? // TODO: Destroy the graph created by read_file return 0; diff --git a/src/read.cpp b/src/read.cpp new file mode 100644 index 0000000..d684498 --- /dev/null +++ b/src/read.cpp @@ -0,0 +1,127 @@ +/* +-------------+---------+-----------------------+ + | John Breaux | jab0910 | JohnBreaux@my.unt.edu | + +-------------+---------+-----------------------+ + | Created 2022-04-17 | + +-----------------------------------------------+ */ +#include "read.hpp" + +#include +#include + +#include "graph.hpp" + +//* Vector of regex rules, in order of highest to lowest precedence +std::vector patterns = { + //* Rule 1: A line containing % is a comment, and should not be parsed + std::regex ("(%)"), + //* Rule 2: A line containing `num_processes` contains the number of processes + std::regex ("(num_processes)\\D*(\\d+)"), + //* Rule 3: A line containing `num_resources` contains the number of resources + std::regex ("(num_resources)\\D*(\\d+)"), + //* Rule 4: A line containing integers should be saved somewhere + std::regex ("(-?[0-9]+.*-?[0-9]+)") // Magical! +}; + +/* read: +* Read from a file of name `filename` + params: + @param filename Name of the file to read graph from + returns: + @returns int: error code representing the list of errors in the lowest 6 bits +*/ +int graph::read (std::string filename) { + // Open file with name filename as input fstream + std::ifstream f (filename); + if (!f) { + print_error_message (INVALID_FILENAME, filename); + return INVALID_FILENAME; + } // File bad! + // Read the entire file + while (f && !f.eof ()) { + // acquire a line + std::string line; + std::getline (f, line); + // Iterate over each pattern, and grab the associated data + for (const auto &pattern: patterns) { + std::smatch res; + // Capture data with a regex search, using the pattern + if (std::regex_search (line, res, pattern)) { + // get the pattern type, value + std::string type = res.format ("$1"), value = res.format ("$2"); + // Handle the pattern + //* Rule 1: If line is a comment, ignore it and move on + if (type == "%") break; + + //* Rule 2: If num_processes= matched, assign value to num_processes + else if (type == "num_processes") num_processes = std::stoi (value); + + //* Rule 3: If num_resources= matched, assign value to num_resources + else if (type == "num_resources") num_resources = std::stoi (value); + + //* Rule 4: If this line is a comma-separated list of numbers: + //* If resource_counts is empty, this is the resource counts, + //* else it's part of the Matrix Reloaded + else { + if (resource_counts.size ()) m.push_back (stovi (type)); + else resource_counts = stovi (type); + } + break; + } + } + } + + //* Test validity of the input file + // Check if all sections are properly defined: + int ret = 0; + if (!num_processes) + ret |= INVALID_PROCESSES; + if (!num_resources) + ret |= INVALID_RESOURCES; + if (resource_counts.size () != num_resources) + ret |= INVALID_COUNTS; + if (m.size () != num_processes + num_resources) + ret |= INVALID_ROWS; + for (auto i: m) { + if (i.size () != num_processes + num_resources) ret |= INVALID_COLUMNS; + } + // Print the appropriate error message + if (ret) print_error_message (ret, filename); + + // fstream destructor closes file when going out of scope + // in this house we RAII sometimes. + return ret; +} + +std::vector stovi (const std::string &s) { + // Create the number classification rule + std::regex rule ("-?[0-9]+"); + // Make a new tokenizing iterator using the rule + std::sregex_token_iterator start {s.begin (), s.end (), rule, 0}, end; + // Match the numbers + std::vector res; + for (auto i = start; i != end; ++i) res.push_back (atoi (i->str ().c_str ())); + // give the numbers back + return res; +} + +void print_error_message (int error, std::string filename) { + // Error message strings + const char err_please[] = "Check to ensure your input file is correct and follows the Rules.\n"; + const char err_undefn[] = "%s: Definition failed for %s\n"; + const char err_nofile[] = "%s: File not found.\n"; + const char *fn = filename.c_str (); + // Test for each kind of error message, and print the appropriate messages + if (error & graph::INVALID_PROCESSES) + fprintf (stderr, err_undefn, fn, "num_processes"); + if (error & graph::INVALID_RESOURCES) + fprintf (stderr, err_undefn, fn, "num_resources"); + if (error & graph::INVALID_COUNTS) + fprintf (stderr, err_undefn, fn, "resource counts"); + if (error & graph::INVALID_ROWS) + fprintf (stderr, err_undefn, fn, "adjacency matrix rows"); + if (error & graph::INVALID_COLUMNS) + fprintf (stderr, err_undefn, fn, "adjacency matrix columns"); + if (error & graph::INVALID_FILENAME) + fprintf (stderr, err_nofile, fn); // file not found + if (error) printf (err_please); +} \ No newline at end of file diff --git a/src/read_input.cpp b/src/read_input.cpp deleted file mode 100644 index a612d41..0000000 --- a/src/read_input.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* +-------------+---------+-----------------------+ - | John Breaux | jab0910 | JohnBreaux@my.unt.edu | - +-------------+---------+-----------------------+ - | Created 2022-04-17 | - +-----------------------------------------------+ */ -#include -#include -#include "graph.hpp" - -//* Vector of regex rules, in order of precedence. Earlier entries will be checked before later entries. -std::vector patterns = { - //* Rule 1: A line containing % is a comment, and should not be parsed - std::regex("(%)"), - //* Rule 2: A line containing `num_processes` contains the number of processes - std::regex("(num_processes)\\D*(\\d+)"), - //* Rule 3: A line containing `num_resources` contains the number of resources - std::regex("(num_resources)\\D*(\\d+)"), - //* Rule 4: A line containing integers should be saved somewhere - std::regex("(-?[0-9]+.*-?[0-9]+)") // Magical! -}; - -// Say hi to Stovey, here to bake your cakes. -//* convert string s to vector no matter the cost -std::vector stovi (const std::string &s) { - // Create the number classification rule - std::regex rule("-?[0-9]+"); - // Make a new tokenizing iterator using the rule - std::sregex_token_iterator start{s.begin(), s.end(), rule, 0}, end; - // Match the numbers - std::vector res; - for (auto i = start; i != end; ++i) res.push_back(atoi(i->str().c_str())); - // give the numbers back - return res; -} - -//* Read from a file of name `filename` -int graph::read(std::string filename) { - // Open file with name filename as input fstream - std::ifstream f(filename); - // Read the entire file - while (!f.eof()) { - // acquire a line - std::string line; - std::getline (f, line); - // Iterate over each pattern, and grab the associated data - for (const auto &pattern: patterns) { - std::smatch res; - // Capture data with a regex search, using the pattern - if (std::regex_search (line, res, pattern)) { - // get the pattern type, value - std::string type = res.format("$1"), value = res.format("$2"); - // Handle the pattern - // Rule 1: If line is a comment, ignore it and move on - if (type == "%") break; - - // Rule 2: If num_processes= matched, assign value to num_processes - if (type == "num_processes") { num_processes = std::stoi(value); } else - - // Rule 3: If num_resources= matched, assign value to num_resources - if (type == "num_resources") { num_resources = std::stoi(value); } else - - // Rule 4: If this line is a comma-separated list of numbers: - // If resource_counts is empty, assume this is the resource counts, - // else it's part of the matrix being reloaded - if (!resource_counts.size()) { resource_counts = stovi(type); } else - { m.push_back(stovi(type)); } - break; - } - } - } - - //* Test validity of the input file - // Check if all sections are properly defined: - const char errmsg[] = "%s: Definition failed for %s\n"; - int ret = 0; - if (!num_processes){ - printf(errmsg, filename.c_str(), "num_processes"); - ret |= 0b00001; - } - if (!num_resources){ - printf(errmsg, filename.c_str(), "num_resources"); - ret |= 0b00010; - } - if (resource_counts.size() != num_resources){ - printf(errmsg, filename.c_str(), "resource counts"); - ret |= 0b00100; - } - if (m.size() != num_processes + num_resources){ - printf(errmsg, filename.c_str(), "adjacency matrix rows"); - ret |= 0b01000; - } - for (auto i: m) { - if (i.size() != num_processes + num_resources) - ret |= 0b10000; - } - if (ret & 0b10000) { - printf(errmsg, filename.c_str(), "adjacency matrix columns"); - } - if (ret) { - printf("Check to ensure your input file is correct and follows the Rules.\n"); - } - - // fstream destructor closes file when going out of scope - // in this house we RAII sometimes. - return ret; -} \ No newline at end of file diff --git a/src/reducible.cpp b/src/reducible.cpp index af23576..84eb36b 100644 --- a/src/reducible.cpp +++ b/src/reducible.cpp @@ -7,55 +7,68 @@ using std::vector; -bool blocked(const int num_processes, const int num_resources, const vector resource_counts, const graph::matrix &matrix, int process_id); +bool blocked (const int num_processes, const int num_resources, const vector resource_counts, const matrix &matrix, int process_id); /* reducible: - Perform the graph reduction algorithm on the adjacency matrix to detect deadlocks +* Perform the graph reduction algorithm on the adjacency matrix to detect deadlocks This algorithm is described in section 5.2 of the Zybook The graph reduction algorithm is implemented here with a few obvious optimizations: The algorithm should clear at least one blocked process per iteration; This leads to an easy failure condition, since fewer processes should be blocked than there are remaining iterations. - By extension, - @params: none, uses class-internal data - @returns: - bool: - true if graph is deadlocked (not reducible) - false if graph is not deadlocked (reducible) + params: + none + returns: + @returns bool: true if graph is deadlocked (not reducible), else false */ -bool graph::reducible() { +bool graph::reducible () { // Make a copy of the matrix, so we don't overwrite it matrix m_copy = m; // run for num_processes iterations; there's no point to doing more, since at least one process should clear each time for (int num_iterations = 0; num_iterations < num_processes; num_iterations++) { int num_blocked = 0; // calculate whether every process is blocked or not - for(int p = 0; p < num_processes; p++) { + for (int p = 0; p < num_processes; p++) { // check if process is blocked - if (blocked(num_processes,num_resources,resource_counts,m_copy, p)) { - //indicate process is blocked + if (blocked (num_processes, num_resources, resource_counts, m_copy, p)) { + // indicate process is blocked num_blocked++; } else { // erase unblocked process from the graph m_copy[p][p] = 0; for (int r = num_processes; r < num_processes + num_resources; r++) { - m_copy[r][p] = 0; m_copy[p][r] = 0; + m_copy[r][p] = 0; + m_copy[p][r] = 0; } } } // print the number of blocked processes - printf("\nIteration %d:\nnum_blocked: %d\n", num_iterations+1, num_blocked); + printf ("\nIteration %d:\nnum_blocked: %d\n", num_iterations + 1, num_blocked); // If more processes are blocked than can be cleared before the algorithm finishes, give up if (num_blocked == num_processes - num_iterations) return true; // Print the new matrix + // clang-format off printf("| ");for(auto x:m_copy){for(auto y:x)printf("%d, ",y);printf("\b\b |\n| ");}printf("\b\b \b\b"); + // clang-format on // If no processes are blocked, we won! if (num_blocked == 0) return false; } return true; } -bool blocked(const int np, const int nr, const vector rc, const graph::matrix &m, int process_id) { +/* blocked: + Checks if a process is blocked for a given state of the graph + params: + @param np num_processes + @param nr num_resources + @param rc resource_counts + @param m adjacency matrix + @param id process ID (0-indexed, ascending) + returns: + @returns bool: true when blocked, else false + +*/ +bool blocked (const int np, const int nr, const vector rc, const matrix &m, int id) { //* Calculate free resources, and compare free units to requests vector resources = rc; for (int r = 0; r < nr; r++) { @@ -63,10 +76,10 @@ bool blocked(const int np, const int nr, const vector rc, const graph::matr for (int p = 0; p < np; p++) { // subtract units currently in use // a resource is in use when >0 in the allocation quadrant of the table, i.e. [(np,0), (nr+np, np)) - resources[r] -= m[r+np][p]; + resources[r] -= m[r + np][p]; } // If the program requests more resources than are available, it's blocked - if (m[process_id][r+np] > resources[r]) { + if (m[id][r + np] > resources[r]) { return true; } }