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,10 +1,12 @@
#ifndef PROJECT2_GRAPH_HPP_INCLUDED
#define PROJECT2_GRAPH_HPP_INCLUDED
#ifndef PROJECT2_INC_GRAPH_HPP
#define PROJECT2_INC_GRAPH_HPP
#include <string> // string
#include <vector> // vector<int>, vector<vector<int>
typedef std::vector<std::vector<int>> matrix;
class graph {
public:
// Constructors
@ -12,6 +14,13 @@ public:
// Initializers:
// Read a graph from a file
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...
@ -22,8 +31,6 @@ public:
// print the graph
void print ();
typedef std::vector<std::vector<int>> matrix;
private:
int num_processes = 0;
int num_resources = 0;
@ -31,4 +38,4 @@ private:
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 |
+-----------------------------------------------+ */
#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");

View File

@ -14,7 +14,7 @@ int main(int argc, char** argv) {
std::string filename = argv[1];
graph g;
//TODO: Implement reading from a file
// Read from a file, and print the result
int err = g.read (filename);
if (err) {
return err;

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,21 +7,19 @@
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:
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 () {
// Make a copy of the matrix, so we don't overwrite it
@ -39,7 +37,8 @@ bool graph::reducible() {
// 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;
}
}
}
@ -48,14 +47,28 @@ bool graph::reducible() {
// 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<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
vector<int> resources = rc;
for (int r = 0; r < nr; r++) {
@ -66,7 +79,7 @@ bool blocked(const int np, const int nr, const vector<int> rc, const graph::matr
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;
}
}