From becc5a9dd968b0ed970d10cefcafb9cbf0af77a2 Mon Sep 17 00:00:00 2001 From: Brandon Rodriguez <brodriguez8774@gmail.com> Date: Wed, 15 Nov 2017 13:26:26 -0500 Subject: [PATCH] Implement handling of multiple threads to read multiple files --- .../MyExamples/PassFirstLineInEachDirFile.c | 274 ++++++++++++++++++ Main.c | 202 ++++++++++++- 2 files changed, 465 insertions(+), 11 deletions(-) create mode 100644 Examples/MyExamples/PassFirstLineInEachDirFile.c diff --git a/Examples/MyExamples/PassFirstLineInEachDirFile.c b/Examples/MyExamples/PassFirstLineInEachDirFile.c new file mode 100644 index 0000000..85d68f7 --- /dev/null +++ b/Examples/MyExamples/PassFirstLineInEachDirFile.c @@ -0,0 +1,274 @@ +/** + * Brandon Rodriguez + * CS 3240 + * 11-15-17 + * a4 (Assignment 5) + */ + + +/** + * Description: + * Testing of thread implementation to solve a problem. + * Slightly more advanced example of joining with data passing. + * + * This reads in a provided directory and opens up threads equivalent to the number of + * files within. Each process handles a diffierent file, grabbing the first line, + * reading it, then returning the line to main which reads line again. + */ + + +/** + * Known Issues: + * No valgrind issues in small-data folder but valgrind finds four issues + * in large-data folder. Not sure where the errors are comming from, as valgrind + * (seemingly?) doesn't even point to any lines of code for this issue. Baffling. + */ + + +#define _BSD_SOURCE + +// Import headers. +#include <ctype.h> +#include <dirent.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/stat.h> +#include "apue.h" +#include "HelperHeader.h" + + +// Define Vars. +#define BUFFER_SIZE 4096 + + +// Variables. +typedef struct{ + char* user_name; + char* password; + char* blood_type; + char* domain_name; + int db_index; +} data_struct; +int dir_file_counter; +char* absolute_path; +pthread_t* thread_array; + + +// Method Declaration. +int change_directory(); +void open_folder(); +void* thread_read_file(); // Reads given file and reorganizes data. + + +/** + * Program's main. + * Initializes and runs program. + */ +int main(int argc, char* argv[]) { + int index; + int return_int; + char* return_string; + + // Check for valid args. + if (argc < 2) { + err_msg("Must enter a directory path."); + return -1; + } else if (argc > 2) { + err_msg("Must enter exacly one directory path."); + return -1; + } else { + + return_int = change_directory(argv[1]); + if (return_int == 0) { + // Get absolute path. + absolute_path = calloc(BUFFER_SIZE, sizeof(char*)); + if (getcwd(absolute_path, BUFFER_SIZE) == NULL) { + err_sys("Failed to get absolute path."); + } + + // Open folder and create appropriate number of threads. + open_folder(); + + // Iterate through all threads and grab returned value. + for (index = 0; index < dir_file_counter; index++) { + pthread_join(thread_array[index], (void**) &return_string); + printf("Thread returned: %s", return_string); + free(return_string); + } + + free(absolute_path); + free(thread_array); + } + } + + return 0; +} + + +/** + * Safely changes directory by first checking path value and permissions. + * + * Returns 0 on success or -1 on failure. + */ +int change_directory(char* folder_location) { + int return_int; + struct stat stat_buffer; + + return_int =lstat(folder_location, &stat_buffer); + if (return_int <0) { + err_sys("Failed to stat file with err %d", return_int); + return -1; + } + + // First, ensure that it is, infact, a directory. + if (S_ISDIR(stat_buffer.st_mode)) { + // Next, check permissions. + if (access(folder_location, X_OK) == 0) { + // Change into directory. + return_int = chdir(folder_location); + if (return_int < 0) { + err_sys("Failed to change directory with err %d", return_int); + return -1; + } + } else { // No execute permission. + err_msg("No execute permission. Cannot change into directory.\n"); + return -1; + } + } else { // Not a dir. + err_msg("Provided path is not a directory.\n"); + return -1; + } + + return 0; +} + + +/** + * Opens indicated folder and hands files off to threads. + */ +void open_folder() { + int index; + int return_int; + char* temp_string; + struct dirent* dir_struct; + struct stat stat_buffer; + DIR* dir_pointer; + + // Iterate through directory first time, to count number of files to open/threads to make. + dir_file_counter = 0; + dir_pointer = opendir(absolute_path); + + // Check that directory has read permissions. + if (access(absolute_path, R_OK) == 0) { + + // Loop until no more files in directory. + while (dir_pointer != NULL) { + if ((dir_struct = readdir(dir_pointer)) != NULL) { + + // New file found. Check if standard file type. + return_int = lstat(dir_struct->d_name, &stat_buffer); + if (return_int < 0) { + err_sys("Failed to stat file with err %d", return_int); + } + if (S_ISREG(stat_buffer.st_mode)) { + dir_file_counter++; + } + } else { + // End of files in directory. Closing stream. + return_int = closedir(dir_pointer); + if (return_int < 0) { + err_msg("Failed to properly close directory."); + } + dir_pointer = NULL; + } + } + } else { + err_msg("Directory does not have read access. Cannot view files."); + } + printf("Directory File Counter: %d\n", dir_file_counter); + + + // Prepare to set up threads. + thread_array = calloc(dir_file_counter, sizeof(pthread_t)); + index = 0; + pthread_attr_t attr; + size_t stacksize; + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &stacksize); + if (stacksize < 8388608) { + stacksize = 8388608; + } + pthread_attr_setstacksize(&attr, (stacksize * 2)); + + + // Iterate through directory again. This time, actually create threads and hand off files. + dir_pointer = opendir(absolute_path); + + // Check that directory has read permissions. + if (access(absolute_path, R_OK) == 0) { + + // Loop until no more files in directory. + while (dir_pointer != NULL) { + if ((dir_struct = readdir(dir_pointer)) != NULL) { + + // New file found. Check if standard file type. + return_int = lstat(dir_struct->d_name, &stat_buffer); + if (return_int < 0) { + err_sys("Failed to stat file with err %d", return_int); + } + if (S_ISREG(stat_buffer.st_mode)) { + + // Get absolute file path. + temp_string = copy_string_with_buffer(absolute_path, BUFFER_SIZE); + strcat(temp_string, "/"); + strcat(temp_string, dir_struct->d_name); + printf("Path: %s\n", temp_string); + + // Actually create threads. + pthread_create(&thread_array[index], &attr, thread_read_file, (void*) copy_string(temp_string)); + index++; + free(temp_string); + } + } else { + // End of files in directory. Closing stream. + return_int = closedir(dir_pointer); + if (return_int < 0) { + err_msg("Failed to properly close directory."); + } + dir_pointer = NULL; + } + } + } else { + err_msg("Directory does not have read access. Cannot view files."); + } + +} + + +/** + * Uses thread to read file value. + */ +void* thread_read_file(void* file_location) { + FILE* read_file; + char* line_buffer = calloc(BUFFER_SIZE, sizeof(char*)); + int result_int; + + // printf("I'ma child and I was given dis: %s\n", file_location); + // pthread_exit(file_location); + + read_file = fopen(file_location, "r"); + fgets(line_buffer, BUFFER_SIZE, read_file); + free(file_location); + result_int = fclose(read_file); + if (result_int != 0) { + err_msg("Failed to close file properly."); + } + + printf("I'ma child and I found dis: %s", line_buffer); + + pthread_exit(line_buffer); +} diff --git a/Main.c b/Main.c index ca6b80f..132e9b8 100644 --- a/Main.c +++ b/Main.c @@ -27,14 +27,19 @@ */ +#define _BSD_SOURCE + // Import headers. #include <ctype.h> +#include <dirent.h> #include <fcntl.h> #include <pthread.h> #include <stdio.h> #include <string.h> #include <stdlib.h> +#include <sys/stat.h> #include "apue.h" +#include "HelperHeader.h" // Define Vars. @@ -49,10 +54,15 @@ typedef struct{ char* domain_name; int db_index; } data_struct; +int dir_file_counter; +char* absolute_path; +pthread_t* thread_array; // Method Declaration. -void* ThreadReadFile(); // Reads given file and reorganizes data. +int change_directory(); +void open_folder(); +void* thread_read_file(); // Reads given file and reorganizes data. /** @@ -60,34 +70,204 @@ void* ThreadReadFile(); // Reads given file and reorganizes data. * Initializes and runs program. */ int main(int argc, char* argv[]) { - pthread_t thread; - - char* file_location = "Data/small-data/0"; + int index; + int return_int; char* return_string; - pthread_create(&thread, NULL, ThreadReadFile, (void*) file_location); - pthread_join(thread, (void**) &return_string); - printf("Thread returned:\n%s", return_string); - free(return_string); + // Check for valid args. + if (argc < 2) { + err_msg("Must enter a directory path."); + return -1; + } else if (argc > 2) { + err_msg("Must enter exacly one directory path."); + return -1; + } else { + + return_int = change_directory(argv[1]); + if (return_int == 0) { + // Get absolute path. + absolute_path = calloc(BUFFER_SIZE, sizeof(char*)); + if (getcwd(absolute_path, BUFFER_SIZE) == NULL) { + err_sys("Failed to get absolute path."); + } + + // Open folder and create appropriate number of threads. + open_folder(); + + // Iterate through all threads and grab returned value. + for (index = 0; index < dir_file_counter; index++) { + pthread_join(thread_array[index], (void**) &return_string); + printf("Thread returned: %s", return_string); + free(return_string); + } + + free(absolute_path); + free(thread_array); + } + } + + return 0; +} + + +/** + * Safely changes directory by first checking path value and permissions. + * + * Returns 0 on success or -1 on failure. + */ +int change_directory(char* folder_location) { + int return_int; + struct stat stat_buffer; + + return_int =lstat(folder_location, &stat_buffer); + if (return_int <0) { + err_sys("Failed to stat file with err %d", return_int); + return -1; + } + + // First, ensure that it is, infact, a directory. + if (S_ISDIR(stat_buffer.st_mode)) { + // Next, check permissions. + if (access(folder_location, X_OK) == 0) { + // Change into directory. + return_int = chdir(folder_location); + if (return_int < 0) { + err_sys("Failed to change directory with err %d", return_int); + return -1; + } + } else { // No execute permission. + err_msg("No execute permission. Cannot change into directory.\n"); + return -1; + } + } else { // Not a dir. + err_msg("Provided path is not a directory.\n"); + return -1; + } + + return 0; +} + + +/** + * Opens indicated folder and hands files off to threads. + */ +void open_folder() { + int index; + int return_int; + char* temp_string; + struct dirent* dir_struct; + struct stat stat_buffer; + DIR* dir_pointer; + + // Iterate through directory first time, to count number of files to open/threads to make. + dir_file_counter = 0; + dir_pointer = opendir(absolute_path); + + // Check that directory has read permissions. + if (access(absolute_path, R_OK) == 0) { + + // Loop until no more files in directory. + while (dir_pointer != NULL) { + if ((dir_struct = readdir(dir_pointer)) != NULL) { + + // New file found. Check if standard file type. + return_int = lstat(dir_struct->d_name, &stat_buffer); + if (return_int < 0) { + err_sys("Failed to stat file with err %d", return_int); + } + if (S_ISREG(stat_buffer.st_mode)) { + dir_file_counter++; + } + } else { + // End of files in directory. Closing stream. + return_int = closedir(dir_pointer); + if (return_int < 0) { + err_msg("Failed to properly close directory."); + } + dir_pointer = NULL; + } + } + } else { + err_msg("Directory does not have read access. Cannot view files."); + } + printf("Directory File Counter: %d\n", dir_file_counter); + + + // Prepare to set up threads. + thread_array = calloc(dir_file_counter, sizeof(pthread_t)); + index = 0; + pthread_attr_t attr; + size_t stacksize; + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &stacksize); + if (stacksize < 8388608) { + stacksize = 8388608; + } + pthread_attr_setstacksize(&attr, (stacksize * 2)); + + + // Iterate through directory again. This time, actually create threads and hand off files. + dir_pointer = opendir(absolute_path); + + // Check that directory has read permissions. + if (access(absolute_path, R_OK) == 0) { + + // Loop until no more files in directory. + while (dir_pointer != NULL) { + if ((dir_struct = readdir(dir_pointer)) != NULL) { + + // New file found. Check if standard file type. + return_int = lstat(dir_struct->d_name, &stat_buffer); + if (return_int < 0) { + err_sys("Failed to stat file with err %d", return_int); + } + if (S_ISREG(stat_buffer.st_mode)) { + + // Get absolute file path. + temp_string = copy_string_with_buffer(absolute_path, BUFFER_SIZE); + strcat(temp_string, "/"); + strcat(temp_string, dir_struct->d_name); + printf("Path: %s\n", temp_string); + + // Actually create threads. + pthread_create(&thread_array[index], &attr, thread_read_file, (void*) copy_string(temp_string)); + index++; + free(temp_string); + } + } else { + // End of files in directory. Closing stream. + return_int = closedir(dir_pointer); + if (return_int < 0) { + err_msg("Failed to properly close directory."); + } + dir_pointer = NULL; + } + } + } else { + err_msg("Directory does not have read access. Cannot view files."); + } + } /** * Uses thread to read file value. */ -void* ThreadReadFile(void* file_location) { +void* thread_read_file(void* file_location) { FILE* read_file; char* line_buffer = calloc(BUFFER_SIZE, sizeof(char*)); int result_int; read_file = fopen(file_location, "r"); fgets(line_buffer, BUFFER_SIZE, read_file); + free(file_location); result_int = fclose(read_file); if (result_int != 0) { - err_sys("Failed to close file properly."); + err_msg("Failed to close file properly."); } - printf("I'ma child and I found dis: \n%s\n", line_buffer); + printf("I'ma child and I found dis: %s", line_buffer); pthread_exit(line_buffer); } -- GitLab