Code cleanup and stylization

This commit is contained in:
John 2022-04-23 17:56:41 -05:00
parent e06a3dcd82
commit 1d7b6db0a3
7 changed files with 210 additions and 141 deletions

View File

@ -1,34 +1,41 @@
#ifndef PROJECT2_GRAPH_HPP_INCLUDED #ifndef PROJECT2_INC_GRAPH_HPP
#define PROJECT2_GRAPH_HPP_INCLUDED #define PROJECT2_INC_GRAPH_HPP
#include <string> // string #include <string> // string
#include <vector> // vector<int>, vector<vector<int> #include <vector> // vector<int>, vector<vector<int>
typedef std::vector<std::vector<int>> matrix;
class graph { class graph {
public: public:
// Constructors // Constructors
graph() {} graph () {}
// Initializers: // Initializers:
// Read a graph from a file // 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: // check functions:
// is the graph... // is the graph...
bool reducible();// ? bool reducible (); // ?
bool knotted();// ? bool knotted (); // ?
// miscellaneous functions: // miscellaneous functions:
// print the graph // print the graph
void print (); void print ();
typedef std::vector<std::vector<int>> matrix; private:
private:
int num_processes = 0; int num_processes = 0;
int num_resources = 0; int num_resources = 0;
std::vector<int> resource_counts; std::vector<int> resource_counts;
matrix m; // Tell me, Mr. Anderson, what good is a phone call if you are unable to speak? matrix m; // Tell me, Mr. Anderson, what good is a phone call if you are unable to speak?
}; };
#endif #endif // PROJECT2_INC_GRAPH_HPP

28
inc/read.hpp Normal file
View File

@ -0,0 +1,28 @@
#ifndef PROJECT2_INC_READ_HPP
#define PROJECT2_INC_READ_HPP
#include <string>
#include <vector>
/* stovi:
* convert string s to vector<int> no matter the cost
@param &s const reference to a string
@returns vector<int>: signed integer representations
of the groups of contiguous digits in the string
*/
std::vector<int> 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

View File

@ -4,7 +4,7 @@
| Created 2022-04-17 | | Created 2022-04-17 |
+-----------------------------------------------+ */ +-----------------------------------------------+ */
#include "graph.hpp" #include "graph.hpp"
// clang-format off
void graph::print () { void graph::print () {
printf("num_processes: %d\nnum_resources: %d\n", num_processes, num_resources); 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"); printf("resource_counts: ["); for (auto e: resource_counts) printf("%d, ", e); printf("\b\b]\n");

View File

@ -4,9 +4,9 @@
+-------------+---------+-----------------------+ +-------------+---------+-----------------------+
| Created 2022-04-16 | | 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 // TODO: Grab file name from args
//? Command line argument structure? //? Command line argument structure?
//? Other flags? What other features should this have? //? Other flags? What other features should this have?
@ -14,15 +14,15 @@ int main(int argc, char** argv) {
std::string filename = argv[1]; std::string filename = argv[1];
graph g; graph g;
//TODO: Implement reading from a file // Read from a file, and print the result
int err = g.read(filename); int err = g.read (filename);
if (err) { if (err) {
return err; return err;
} }
g.print(); g.print ();
// TODO: Implement graph reduction and/or knot detection // 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 // TODO: Destroy the graph created by read_file
return 0; return 0;

127
src/read.cpp Normal file
View File

@ -0,0 +1,127 @@
/* +-------------+---------+-----------------------+
| John Breaux | jab0910 | JohnBreaux@my.unt.edu |
+-------------+---------+-----------------------+
| Created 2022-04-17 |
+-----------------------------------------------+ */
#include "read.hpp"
#include <fstream>
#include <regex>
#include "graph.hpp"
//* Vector of regex rules, in order of highest to lowest precedence
std::vector<std::regex> 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<int> 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<int> 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);
}

View File

@ -1,106 +0,0 @@
/* +-------------+---------+-----------------------+
| John Breaux | jab0910 | JohnBreaux@my.unt.edu |
+-------------+---------+-----------------------+
| Created 2022-04-17 |
+-----------------------------------------------+ */
#include <fstream>
#include <regex>
#include "graph.hpp"
//* Vector of regex rules, in order of precedence. Earlier entries will be checked before later entries.
std::vector<std::regex> 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<int> no matter the cost
std::vector<int> 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<int> 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;
}

View File

@ -7,55 +7,68 @@
using std::vector; using std::vector;
bool blocked(const int num_processes, const int num_resources, const vector<int> resource_counts, const graph::matrix &matrix, int process_id); bool blocked (const int num_processes, const int num_resources, const vector<int> resource_counts, const matrix &matrix, int process_id);
/* reducible: /* 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 This algorithm is described in section 5.2 of the Zybook
The graph reduction algorithm is implemented here with a few obvious optimizations: The graph reduction algorithm is implemented here with a few obvious optimizations:
The algorithm should clear at least one blocked process per iteration; 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. This leads to an easy failure condition, since fewer processes should be blocked than there are remaining iterations.
By extension, params:
@params: none, uses class-internal data none
@returns: returns:
bool: @returns bool: true if graph is deadlocked (not reducible), else false
true if graph is deadlocked (not reducible)
false if graph is not deadlocked (reducible)
*/ */
bool graph::reducible() { bool graph::reducible () {
// Make a copy of the matrix, so we don't overwrite it // Make a copy of the matrix, so we don't overwrite it
matrix m_copy = m; 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 // 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++) { for (int num_iterations = 0; num_iterations < num_processes; num_iterations++) {
int num_blocked = 0; int num_blocked = 0;
// calculate whether every process is blocked or not // 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 // check if process is blocked
if (blocked(num_processes,num_resources,resource_counts,m_copy, p)) { if (blocked (num_processes, num_resources, resource_counts, m_copy, p)) {
//indicate process is blocked // indicate process is blocked
num_blocked++; num_blocked++;
} else { } else {
// erase unblocked process from the graph // erase unblocked process from the graph
m_copy[p][p] = 0; m_copy[p][p] = 0;
for (int r = num_processes; r < num_processes + num_resources; r++) { 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 // 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 more processes are blocked than can be cleared before the algorithm finishes, give up
if (num_blocked == num_processes - num_iterations) return true; if (num_blocked == num_processes - num_iterations) return true;
// Print the new matrix // 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"); 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 no processes are blocked, we won!
if (num_blocked == 0) return false; if (num_blocked == 0) return false;
} }
return true; return true;
} }
bool blocked(const int np, const int nr, const vector<int> 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<int> rc, const matrix &m, int id) {
//* Calculate free resources, and compare free units to requests //* Calculate free resources, and compare free units to requests
vector<int> resources = rc; vector<int> resources = rc;
for (int r = 0; r < nr; r++) { for (int r = 0; r < nr; r++) {
@ -63,10 +76,10 @@ bool blocked(const int np, const int nr, const vector<int> rc, const graph::matr
for (int p = 0; p < np; p++) { for (int p = 0; p < np; p++) {
// subtract units currently in use // 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)) // 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 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; return true;
} }
} }