I'm attempting to create a program which employs the use of multiple threads to find how many diagonal sums of the grid created from an input text file match a given number passes to the program.
I compile proj4.c and main.c like this in order to access the main
function in main.c using the provided Makefile in a unix environment:
compile:
gcc -Wall -pedantic-errors proj4.c -g -c -pthread
gcc -Wall -pedantic-errors main.c -g -c
gcc -Wall -pedantic-errors main.o proj4.o -g -o proj4.out -pthread
run:
./proj4.out in1.txt out1.txt 10 1
clean:
rm *.out rm *.o
I keep getting a segmentation error on line 18 of the following code:
proj4.c:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include "proj4.h"
// Function to read the grid from the input file
void initializeGrid(grid *g, char *fileName) {
FILE *file = fopen(fileName, "r");
if (!file) {
perror("Error opening file");
exit(EXIT_FAILURE);
}
fscanf(file, "%u", &g->n);
g->p = (unsigned char **)malloc(g->n * sizeof(unsigned char *));
for (unsigned int i = 0; i < g->n; i++) {
// vvv (line 18) vvv
g->p[i] = (unsigned char *)malloc(g->n * sizeof(unsigned char));
for (unsigned int j = 0; j < g->n; j++) {
fscanf(file, " %c", &g->p[i][j]);
}
}
fclose(file);
}
// Define a struct to pass data to the worker function
typedef struct {
grid *input;
unsigned long s;
grid *output;
int thread_id;
int total_threads;
} worker_args;
// Worker thread function
void *worker(void *args) {
worker_args *w_args = (worker_args *)args;
grid *input = w_args->input;
unsigned long s = w_args->s;
grid *output = w_args->output;
int thread_id = w_args->thread_id;
int total_threads = w_args->total_threads;
unsigned int n = input->n;
// Calculate the range of elements each thread should work on
unsigned int work_size = (n * (n - 1)) / (2 * total_threads);
unsigned int start = thread_id * work_size;
unsigned int end = (thread_id + 1) * work_size;
if (thread_id == total_threads - 1) {
end = n * (n - 1) / 2;
}
for (unsigned int idx = start; idx < end; idx++) {
int row, col;
// Find row value without using sqrt
row = n - 2;
while (idx <= (row * (row + 1)) / 2) {
row--;
}
col = idx - (row * (row + 1)) / 2;
output->p[row][col] = (input->p[row][col] + input->p[col][row + 1]) % s;
}
return NULL;
}
// Function to find the diagonal sums
void diagonalSums(grid *input, unsigned long s, grid *output, int t) {
pthread_t *threads = (pthread_t *)malloc(t * sizeof(pthread_t));
worker_args *thread_args = (worker_args *)malloc(t * sizeof(worker_args));
for (int i = 0; i < t; i++) {
thread_args[i].input = input;
thread_args[i].s = s;
thread_args[i].output = output;
thread_args[i].thread_id = i;
thread_args[i].total_threads = t;
if (pthread_create(&threads[i], NULL, worker, (void *)&thread_args[i]) != 0) {
perror("Error creating thread");
exit(EXIT_FAILURE);
}
}
for (int i = 0; i < t; i++) {
if (pthread_join(threads[i], NULL) != 0) {
perror("Error joining thread");
exit(EXIT_FAILURE);
}
}
free(threads);
free(thread_args);
}
// Function to write the grid to the output file
void writeGrid(grid *g, char *fileName) {
FILE *file = fopen(fileName, "w");
if (!file) {
perror("Error opening file");
exit(EXIT_FAILURE);
}
for (unsigned int i = 0; i < g->n; i++) {
for (unsigned int j = 0; j < g->n; j++) {
fprintf(file, "%c ", g->p[i][j]);
}
fprintf(file, "\n");
}
fclose(file);
}
// Function to free the grid
void freeGrid(grid *g) {
for (unsigned int i = 0; i < g->n; i++) {
free(g->p[i]);
}
free(g->p);
}
main.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/time.h>
#include "proj4.h"
/*
* Do not modify anything in this file.
*/
/*
* Helper function for main.
* Check for some errors and print error messages
* for command line arguments passed to main.
* If an error is found, the program terminates.
*/
void errorCheck(int argc, char **argv) {
bool errorFound = false;
if (argc != 5) {
printf("Usage error: ./proj4.out inputFile outputFile s t\n");
errorFound = true;
}
else if (access(argv[1], F_OK) != 0) {
printf("Error accessing %s in the present working directory\n", argv[1]);
errorFound = true;
} else {
int t = atoi(argv[4]);
if (t < 1 || 3 < t) {
printf("Error: t must be between 1 and 3 (inclusive)\n");
errorFound = true;
}
}
if (errorFound)
exit(0);
}
/*
* This program should be compiled to ./proj4.out using the provided
* Makefile, and it will process five command line arguments.
* ./proj.out input.txt output.txt s t
* input.txt is the name of a file in the present working directory that
* contains a n-by-n grid of digits (1 through 9),
* where n >= 1.
* output.txt is the name of a file in the present working directory to save
* the output of all of the diagonal sums. If the file does not exist,
* then this file will be created in the present working directory.
* s is the sum for the diagonal sums.
* t is the number of threads (1 <= t <= 3) to use
* to compute the diagonal sums. If t is 1, then only the
* main thread will be used. If 2 <= t <= 3, then the main
* thread and (t - 1) POSIX thread(s) will be used to compute
* the diagonal sums.
* This program will only time the call to diagonalSums.
*
*/
int main(int argc, char **argv) {
errorCheck(argc, argv);
char *inputFile = argv[1];
char *outputFile = argv[2];
unsigned long sum = (unsigned long)atol(argv[3]);
int t = atoi(argv[4]);
grid g, diagonalSumsOutput;
initializeGrid(&g, inputFile);
printf("Computing the diagonal sums equal to %ld in a %d-by-%d grid using %d thread(s).\n",
sum, g.n, g.n, t);
struct timeval start, end; // start and end time
unsigned long e_usec; // elapsed microseconds
gettimeofday(&start, 0); // mark the start time
diagonalSums(&g, sum, &diagonalSumsOutput, t);
gettimeofday(&end, 0); // mark the end time
e_usec = ((end.tv_sec * 1000000) + end.tv_usec) -
((start.tv_sec * 1000000) + start.tv_usec);
printf("Elapsed time for computing the diagonal sums using %d thread(s): %lf seconds.\n",
t, e_usec / 1000000.0);
printf("Writing the diagonal sums equal to %ld to the file %s.\n",
sum, outputFile);
writeGrid(&diagonalSumsOutput, outputFile);
freeGrid(&g);
freeGrid(&diagonalSumsOutput);
printf("Program is complete. Goodbye!\n");
return 0;
}
proj4.h:
#ifndef PROJ4_H
#define PROJ4_H
/*
* The struct grid_t contains a pointer p to a 2D array of
* unsigned chars with n rows and n columns stored on
* the heap of this process. Once this is initialized properly,
* p[i][j] should be a valid unsigned char for all i = 0..(n-1)
* and j = 0..(n-1).
* Do not alter this typedef or struct in any way.
*/
typedef struct grid_t {
unsigned int n;
unsigned char **p;
} grid;
/*
* Initialize g based on fileName, where fileName
* is a name of file in the present working directory
* that contains a valid n-by-n grid of digits, where each
* digit in the grid is between 1 and 9 (inclusive).
* Do not alter this function prototype in any way.
*/
void initializeGrid(grid *g, char *fileName);
/*
* This function will compute all diagonal sums in input that equal s using
* t threads, where 1 <= t <= 3, and store all of the resulting
* diagonal sums in output. Each thread should do
* roughly (doesn't have to be exactly) (100 / t) percent of the
* computations involved in calculating the diagonal sums.
* This function should call (or call another one of your functions that calls)
* pthread_create and pthread_join when 2 <= t <= 3 to create additional POSIX
* thread(s) to compute all diagonal sums.
* Do not alter this function prototype in any way.
*/
void diagonalSums(grid *input, unsigned long s, grid *output, int t);
/*
* Write the contents of g to fileName in the present
* working directory. If fileName exists in the present working directory,
* then this function should overwrite the contents in fileName.
* If fileName does not exist in the present working directory,
* then this function should create a new file named fileName
* and assign read and write permissions to the owner.
* Do not alter this function prototype in any way.
*/
void writeGrid(grid *g, char *fileName);
/*
* Free up all dynamically allocated memory used by g.
* This function should be called when the program is finished using g.
* Do not alter this function prototype in any way.
*/
void freeGrid(grid *g);
/*
* You may add any additional function prototypes and any additional
* things you need to complete this project below this comment.
* Anything you add to this file should be commented.
*/
#endif
I tried these commands:
./proj4.out in5.txt out5.txt 1222 3; diff out5.txt correctOut5.txt | wc -c;
I get this error:
Segmentation fault (core dumped) diff: out5.txt: No such file or directory
I expected roughly this as an output (Time usage will vary):
Computing the diagonal sums equal to 1222 in a 3567-by-3567 grid using 3 thread(s).
Elapsed time for computing the diagonal sums using 3 thread(s): 61.737591 seconds.
Writing the diagonal sums equal to 1222 to the file out5.txt.
Program is complete. Goodbye!
I need help understanding why the Segmentation fault is occurring and what I can do to fix it.
Edit: Since in5.txt is extremely large because it is part of a school project, I shall provide in1.txt which also results in the same error:
56535
82355
15459
69251
61338