
/**************************************************************************/
/**************************************************************************/
/*                                                                        */
/*  Nathan Balon                                                          */
/*  SN# 3210 1717                                                         */
/*  CIS 450                                                               */
/*  Programming Assignment 1                                              */
/*  program balon_prog1.c                                                 */
/*                                                                        */
/*  The program uses four function to execute seperate programs           */
/*  which are all called from within main.  The purpose of                */
/*  the program is to show the advantages and disadvantages               */
/*  of using threads has on the performance of a program                  */
/*  under certain conditions. There will be 2 identical programs          */
/*  one that uses threads and another one that does not.                  */
/*  in one case their will be a performance gain from using               */
/*  threads and in the other case their will not.                         */
/*                                                                        */
/*                                                                        */
/**************************************************************************/
/**************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <semaphore.h>

#define NUM_SETS 3
#define SET_SIZE 100000

/* functions used in assignment */
int print_prog_time(int st_sec, int st_nsec, int end_sec, int end_nsec, char *prog_name);
int set_total();
int set_total_th();
void * th_work_total(void * arg);
void * compute_lines(void * arg);
int line_count_th(char *direc);
int line_count(char *direc);

/* Global data */
int total_lines_th = 0;      /* total line read from line count program */
sem_t bin_sem;               /* semaphore for sync of line_total_th */                                                                                                                                              
typedef struct{int set_num; int *set_ptr;}set_info;



/*****************************************************************************/
/*                                                                           */
/*  the function print_prog_time takes five arguments the start time in      */
/*  seconds in microseconds, the end time in both seconds and microseconds   */
/*  and the name of the program of the runtime.  These arguements are used   */
/*  to compute the total runtime of the program and to display the results.  */
/*  This function will be used in all four programs to print the runtime.    */
/*                                                                           */
/*****************************************************************************/

int print_prog_time(int st_sec, int st_nsec, int end_sec, int end_nsec, char *prog_name){
    int total_sec = 0;         // total runtime in seconds
    int total_nsec = 0;        // total runtime in microsec
                                                                                                                                                             
    // compute the total time the program took to run 
    total_sec = end_sec - st_sec;
    if(total_sec >= 1){
        total_nsec = 1000000000 - st_nsec;
        total_nsec += end_nsec;
    }
    else{
        total_nsec = end_nsec - st_nsec;
    }
    if((st_nsec > end_nsec) && (total_sec >= 1)){
        total_sec--;
    }

    // display the time of the program execution 
    printf("---------------------------------------------------------------------\n");
    printf("start time sec  = %ld  nsec = %10ld\n", st_sec, st_nsec);
    printf("  end time sec  = %ld  nsec = %10ld\n", end_sec, end_nsec);
    printf("Total program runtime of program %s is sec = %d.%09dus\n", prog_name, total_sec, total_nsec);
}

/*****************************************************************************/
/*                                                                           */
/*   The first of the two program to compare is a program that computes the  */
/*   total of a number of sets using a two dimensional array. Numbers are    */
/*   first randomly inserted into the arrays.  The the total from each       */
/*   array is computed.  One version of the program uses a single thread     */
/*   of execution and the other uses multiple threads to compute the total.  */
/*                                                                           */
/*****************************************************************************/
                                                                                                                                                             
int set_total(){
    int i, j;                                        // arary indexs
    int number[NUM_SETS][SET_SIZE];                  // sets used 
    long int total_in_set[NUM_SETS];                 // total for each set
    struct timespec start, end, rand_time;           // start and end time 
                                                                                                                                                        
    printf("\nProgram Set Total\n");
    printf("***Initializing data***\n");
    printf("Randomly filling %d sets, with %d items\n", NUM_SETS, SET_SIZE);

    // initialize the data of each set
    for(i = 0; i < NUM_SETS; i++){
	for(j = 0; j < SET_SIZE; j++){
            clock_gettime(CLOCK_REALTIME, &rand_time);
            srand(rand_time.tv_nsec);
            number[i][j] = rand() % 27;
        }
    }

    // get the start time of the program
    if (clock_gettime(CLOCK_REALTIME, &start) == -1){
        fprintf(stderr, "Error initializing the start time\n");
    }

    // compute the total for each set      
    for(i = 0; i < NUM_SETS; i++){
        for(j = 0; j < SET_SIZE; j++){
            total_in_set[i] += number[i][j];
        }
    }

    //display the results of the sets
    for(i = 0; i < NUM_SETS; i++){
        printf("Set %d's total is: %d\n", i, total_in_set[i]);
    }

    // get the end time of the program and print time results 
    if (clock_gettime(CLOCK_REALTIME, &end) == -1){
        fprintf(stderr, "Error unable to intitalize end time\n");
    }
    print_prog_time(start.tv_sec, start.tv_nsec, end.tv_sec, end.tv_nsec, "set_total");
    return(0);
}

/*****************************************************************************/
/*                                                                           */
/*  set_total_th() is the program that uses multiple threads to compute the  */
/*  the total of the integers in an array.  The runtime performance          */
/*  of the multi-threaded program will exceed that of  the single threaded   */
/*  version.  This program shows the results of computation performed by     */
/*  in parrell with threads will surpass that of the performance of a        */
/*  single threaded program.                                                 */
/*                                                                           */
/*****************************************************************************/

int set_total_th(){
    int i, j, res;
    pthread_t work_th[NUM_SETS];                  // thread to compute total
    set_info s_info[NUM_SETS];                    // set into passed to thread
    int number[NUM_SETS][SET_SIZE];               // the sets to total 
    struct timespec start, end, rand_time;        // start and end time
                                                                                                                                                             
    printf("\nProgram Threaded Set Total\n");
    printf("***Initializing data***\n");
    printf("Randomly filling %d sets, with %d items\n", NUM_SETS, SET_SIZE);

    // intitalize the set with data
    for(i = 0; i < NUM_SETS; i++){
        for(j = 0; j < SET_SIZE; j++){
            clock_gettime(CLOCK_REALTIME, &rand_time);
            srand(rand_time.tv_nsec);
            number[i][j] = rand() % 27;
        }
    }

    // get the start time of the program
    if (clock_gettime(CLOCK_REALTIME, &start) == -1){
        fprintf(stderr, "Error initializing the start time\n");
    }

    // create a thread for each set to compute the total 
    for (i = 0; i < NUM_SETS; i++){
        s_info[i].set_num = i;            
        s_info[i].set_ptr = number[i];
        res = pthread_create(&work_th[i], NULL, th_work_total, (void *)&s_info[i]);
        if(res != 0){
            fprintf(stderr, "Error creating thread %d\n", i);
            exit(-1);
        }
    }

    // wait till all threads are done
    for(i = 0; i < NUM_SETS; i ++){
        pthread_join(work_th[i], NULL);
    }                                                                                                                                                      

    // get the end time of the program
    if (clock_gettime(CLOCK_REALTIME, &end) == -1){
        fprintf(stderr, "Error getting clock time\n");
    }
    // print the end time
    print_prog_time(start.tv_sec, start.tv_nsec, end.tv_sec, end.tv_nsec, "set_total_th");
    return(0);
}

/**********************************************************************************/
/*                                                                                */
/*   th_work_total() function is used by each thread in the set_total_th()        */
/*                                                                                */
/**********************************************************************************/

void *th_work_total(void *arg){
    set_info *info;
    int i;
    int total = 0;
    int * num;    
    info = (void *)arg;
    num = info->set_ptr;

    // Compute the total for the set
    for(i = 0; i < SET_SIZE; i++){
         total += *num++;     
    } 
    printf("Set %d's total is: %d\n", info->set_num, total);
    pthread_exit(NULL);
}


/* global variables */

typedef struct {FILE *out_file; char *in_name;} th_file; 

/*********************************************************************/
/*                                                                   */
/*  The function line count takes a directory name as a parameter.   */
/*  line count then opens that directory and reads the number of     */
/*  lines in each file in that directory.  Keeping a running total   */
/*  of the number of lines read.  The results of the number of       */
/*  line in that file is then written to a file.  To simulate more   */
/*  i/o activity the records are written multiple times to the file  */
/*                                                                   */
/*********************************************************************/

int line_count(char *direc){

    int i;                             /* array index */
    char ch;                           /* char from read from file */  
    DIR *dp;                           /* pointer to the directory */
    struct dirent *entry;              /* directory */
    FILE *in_file;                     /* files to be read from */
    int line_count;                    /* line counts for files */
    int total_lines = 0;               /* total lines in all files */
    struct timespec start, end;        /* start and end time */
    int total_time = 0;                /* total program runtime */
    int total_sec, total_nsec;

    /* get the start time of the program */
    if (clock_gettime(CLOCK_REALTIME, &start) == -1){
        fprintf(stderr, "Error initializing the start time\n");
    }
   
    /* print program heading */    
    printf("\nProgram Line Count\n");
    printf("--------------------------------------------------------------------\n");

    /* open the directory that was specified as  */
    /* an argument to the line_count function    */	
    if((dp = opendir(direc)) == NULL){
	fprintf(stderr, "Error cannot open the directory\n");
	exit(-1);
    }
    
    /* read files from the directory */
    while((entry = readdir(dp)) != NULL){
        /* open the files to read from */
        in_file = fopen(entry->d_name, "r");
        if(in_file == NULL){
            fprintf(stderr, "Cannot open %s\n", entry->d_name);
          //  exit(8);
        }
        /* read each char from the file and test if it is a new line */
        line_count = 0;
        while(1){
            ch = fgetc(in_file);
            if (ch == EOF)
                break;
            if (ch == '\n')
                line_count++;
        }
        /* add the lines in the file to the total */
        total_lines += line_count;
        printf("File:   %-35s Lines: %6d\n", entry->d_name, line_count);
    }
    /* print the results of the number of lines per file */
    printf("---------------------------------------------------------------------\n");
    printf("Total lines read from directory %s = %6d\n", direc, total_lines);

    /* get the end time and display the difference between start and end */
    if (clock_gettime(CLOCK_REALTIME, &end) == -1){
        fprintf(stderr, "Error unable to intitalize end time\n");
    }
    print_prog_time(start.tv_sec, start.tv_nsec, end.tv_sec, end.tv_nsec, "set_total_th");
                                                                                                                                                           
    return(0);                                                                                                                                                            
}

/*********************************************************************/
/*                                                                   */
/*  The function line_count_th is almost identical to line_count     */
/*  except that this function uses threads for execution.            */
/*  The program takes a directory name as a parameter.               */
/*  line count then opens that directory and reads the number of     */
/*  lines in each file in that directory.  Keeping a running total   */
/*  of the number of lines read.  The results of the number of       */
/*  line in that file is then written to a file.  To simulate more   */
/*  i/o activity the records are written multiple times to the file  */
/*                                                                   */
/*********************************************************************/

int line_count_th(char *direc){
    int thread_count = 0;
    const int max_threads = 500;
    int i = 0;
    int rc;                              /* return condition */
    DIR *dp;                             /* directory to read files from */
    char *filename;                      /* name of file to open */
    struct dirent *entry;                /* struc to hold dir info */
    th_file *file;                       /* struct of file name to read and file to write to */
    pthread_t user_thread[max_threads];  /* threads to be used in the program */
    pthread_attr_t attr;                 /* pthread attribute */
    int filecount = 0;                   /* number of files */
    struct timespec start, end;          /* start and end time */
    int total_sec, total_nsec;           /* runtime in sec and usec */
    total_lines_th = 0;
	
    /* get the start time */
    if (clock_gettime(CLOCK_REALTIME, &start) == -1){
        fprintf(stderr, "Error initializing the start time\n");
    }
    
    if(sem_init(&bin_sem, 0, 1) != 0 ){
        fprintf(stderr, "Semaphore initialization failed\n");
        exit(-1);
    }
    /* open the directory to read from */                                                                                                                                                        
    if((dp = opendir(direc)) == NULL){
	fprintf(stderr, "Error cannot open the direstory\n");
	exit(-1);
    }
    /* count the number of files in the directory */
    while(readdir(dp) != NULL){
        filecount++;	
    }

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    	   
    if((dp = opendir(direc))== NULL){
        fprintf(stderr, "Error can't open the directory");
        exit(-1);
    }
 
    printf("\nProgram Threaded Line Count\n");
    printf("---------------------------------------------------------------------\n");
    while(((entry = readdir(dp)) != NULL) &&(thread_count < 100)){
        /* open the files to read from */
        filename = entry->d_name;
        rc = pthread_create(&user_thread[thread_count], &attr, compute_lines, (void *)filename);
        thread_count++;
        if(rc){
            fprintf(stderr, "Error in creating a thread for %s\n", entry->d_name);
            exit(-1);
        }
    }
    for (i = 0; i < thread_count; i++){
        pthread_join(user_thread[i], NULL);
    }
    /* print the results of the number of lines per file */
    printf("---------------------------------------------------------------------\n");
    printf("Total lines read from directory %s = %d\n", direc, total_lines_th);

    /* get the end time of the program*/
    if (clock_gettime(CLOCK_REALTIME, &end) == -1){
        fprintf(stderr, "Error unable to intitalize end time\n");
    }
    print_prog_time(start.tv_sec, start.tv_nsec, end.tv_sec, end.tv_nsec, "set_total_th");
    pthread_exit(NULL);
    return(0);                                           	
}

/*******************************************************************/
/*                                                                 */
/*   Function for the treaded line count program. to compute  the  */
/*   number of lines contained in a file.                          */
/*                                                                 */
/*******************************************************************/
void *compute_lines(void *arg){
    int i;
    FILE *in_file;               /* file to read from */
    char *filename;
    char ch;                     /* character read from file */
    int line_count = 0;          /* number of lines */
    filename = (void *)arg;
                                                                                                                                                             
    /* open the file */
    in_file = fopen(filename, "r");
    if (in_file == NULL){
        fprintf(stderr, "ERROR unable to open file %s\n", filename);
        exit(-1);
    }
                                                                                                                                                             
    /* read the number of lines from the file,
       quit when EOF is encountered */
    while(1){
        ch = fgetc(in_file);
        if(ch == EOF)
            break;
        if(ch == '\n')
            line_count ++;
    }
    /* add the number of line to the total count */
    sem_wait(&bin_sem);
    total_lines_th += line_count;
    sem_post(&bin_sem);

    /* print the results of the file */
    printf("File:   %-35s Lines: %6d\n",filename, line_count);                                                                                                                                                             
    pthread_exit("Read the the lines from a file");
}
                                                                                                                                                                                                                                             
/*********************************************************************/
/*                                                                   */
/* The main function will call the programs to be run.               */
/* For each program there will be an identical program that          */
/* produces the same results.  The programs vary in that on of       */ 
/* the programs uses pthreads and the other on does not.  The        */
/* purpose of the test it to show when using a pthread will inprove  */
/* the program performance and when it will not.                     */
/*                                                                   */
/*********************************************************************/

int main(int argc, char *argv[]){
    set_total();
    set_total_th();
    
    /* Call the threaded line count program  */
    /* The arguements of the function supply the directory to read from */
    line_count("/students/grad/n/nbalon/cis450");
    line_count_th("/students/grad/n/nbalon/cis450");
    return(0);
}





/************************************************************************/
/**********************  Sample program output **************************/
/************************************************************************/

/*

Program Set Total
***Initializing data***
Randomly filling 3 sets, with 100000 items
Set 0's total is: 1299542
Set 1's total is: 1299265
Set 2's total is: 1298347
----------------------------------------------------------------------
start time sec  = 1076941945  nsec =  704118882
  end time sec  = 1076941945  nsec =  777830701
Total program runtime of program set_total is sec = 0.073711819us

Program Threaded Set Total
***Initializing data***
Randomly filling 3 sets, with 100000 items
Set 0's total is: 1303308
Set 1's total is: 1299728
Set 2's total is: 1302209
----------------------------------------------------------------------
start time sec  = 1076941946  nsec =  204001294
  end time sec  = 1076941946  nsec =  239672165
Total program runtime of program set_total_th is sec = 0.035670871us

Program Line Count
---------------------------------------------------------------------
File:   .                                   Lines:      0
File:   ..                                  Lines:      0
File:   time_test.c                         Lines:      8
File:   line_count.c                        Lines:     70
File:   a.out                               Lines:      3
File:   lines.c                             Lines:    434
File:   lines                               Lines:      7
File:   th_line_count_result                Lines:      0
File:   line_count_result                   Lines:   9002
File:   lines.c.bak                         Lines:    331
File:   lines.c.bak.save                    Lines:    277
File:   proc_test.c                         Lines:     25
File:   prog1                               Lines:      0
File:   proc_test                           Lines:      4
File:   new_lines.c                         Lines:    297
File:   almost.c                            Lines:    393
File:   lines2.c                            Lines:    221
File:   lines2                              Lines:      6
File:   prog                                Lines:      6
File:   balon_prog1.c                       Lines:    445
File:   balon_prog1                         Lines:      6
File:   prog1.output                        Lines:      0
----------------------------------------------------------------------
Total lines read from directory /students/grad/n/nbalon/cis450 =  11535
----------------------------------------------------------------------
start time sec  = 1076941946  nsec =  239740839
  end time sec  = 1076941946  nsec =  897479175
Total program runtime of program set_total_th is sec = 0.657738336us

Program Threaded Line Count
----------------------------------------------------------------------
File:   .                                   Lines:      0
File:   ..                                  Lines:      0
File:   time_test.c                         Lines:      8
File:   th_line_count_result                Lines:      0
File:   proc_test.c                         Lines:     25
File:   lines                               Lines:      7
File:   balon_prog1                         Lines:      6
File:   prog1                               Lines:      0
File:   lines2                              Lines:      6
File:   prog1.output                        Lines:      0
File:   proc_test                           Lines:      4
File:   prog                                Lines:      6
File:   a.out                               Lines:      3
File:   line_count.c                        Lines:     70
File:   lines.c.bak                         Lines:    331
File:   new_lines.c                         Lines:    297
File:   lines.c.bak.save                    Lines:    277
File:   lines2.c                            Lines:    221
File:   balon_prog1.c                       Lines:    445
File:   lines.c                             Lines:    434
File:   almost.c                            Lines:    393
File:   line_count_result                   Lines:   9002
----------------------------------------------------------------------
Total lines read from directory /students/grad/n/nbalon/cis450 = 11535
----------------------------------------------------------------------
start time sec  = 1076941946  nsec =  897529328
  end time sec  = 1076941948  nsec =   95121867
Total program runtime of program set_total_th is sec = 1.197592539us

*/
