diff --git a/rapport.c b/rapport.c new file mode 100644 index 0000000000000000000000000000000000000000..64172aaf05f621c6f9f366e5bc9433a866eb1b42 --- /dev/null +++ b/rapport.c @@ -0,0 +1,401 @@ +// +// Created by Giovi on 02/04/2020. +// Note: +// PRODUCER-CONSUMER VERSION WITH THREADS, SEMAPHORES AMD MUTEXES +// +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <stdbool.h> +#include <time.h> +#include <pthread.h> +#include <semaphore.h> + +/* STRUCTURES AND VARIABLES */ +#define NT_READ 1 // Number of threads reading numbers: PRODUCER +#define NT_WRITE 1 // Number of threads writing number dividers: CONSUMER + +/* BUFFER containing numbers used + * in read and write operations + * Allow the implementation of + * Producer-Consumer algorithm + */ +#define BUFSIZE 5 /* Max number of elements in the number buffer */ +#define N 5 /* Max number of elements in the semaphore buffer */ + +unsigned long buffer[BUFSIZE] = {0}; +int in = 0; +int out = 0; + +/* "struct factorization" + * "prime_dividers" is an array containing max N dividers + * "cnt" is the number of calculated dividers + * "number" is number to calculate the prime dividers + */ +typedef struct factorization { + unsigned long prime_dividers[N]; + unsigned int cnt; + unsigned long number; +} factor; + +/* struct file_descriptors + * "in" is the inputfile file descriptor + * "out" is the outputfile file descriptor + * "options" "v" (verbose) with debugging information, "q" without debugging information + */ +typedef struct file_descriptors { + FILE *in; + FILE *out; + char options; +} fd; + +/* Additional buffer used to put in memory + * all the numbers stored in the input file. + * "#define MAXELEM 120" can be tuned according + * how many numbers (lines) are in the input file. + * The following bash command can be used: + * wc -l file.txt + */ +#define MAXELEM 120 // +unsigned long Array_Numbers[MAXELEM]; +unsigned int n_arry = 0; + +/* MUTEX */ +pthread_mutex_t mutex_readwrite; +/* This additional Mutex has been eliminated in the PRODUCER-CONSUMER VERSION */ +// pthread_mutex_t mutex_dividers; +/* SEMAPHORE for critical section */ +sem_t empty; +sem_t full; + +/* +* FUNCTIONS used in the programme: +* bool is_div(unsigned long, unsigned long); +* bool is_prime(unsigned long); +* factor *prime_divs(unsigned long); +* void *read_numbers(FILE *fd) - PRODUCER +* void *write_dividers(FILE *fd) - CONSUMER +*/ +bool is_div(unsigned long numbr, unsigned long i){ + if((numbr % i) == 0) { // verify if i is the divider of numbr + return(true); // i is a divider of numbr + } else { + return(false); // i is not a divider of numbr + } +} + +bool is_prime(unsigned long nbr) { + /* + * Optimised solution with less loop, see: https://fr.wikipedia.org/wiki/Nombre_premier + * "Crible d'Ératosthène: + * Les premiers algorithmes pour décider si un nombre est premier + * (appelés tests de primalité) consistent à essayer de le diviser par tous les nombres + * qui n'excèdent pas sa racine carrée" + */ +// for(unsigned long j = 2; j <= nbr; j++) { // OLD SOLUTION LESS EFFICIENT + for (unsigned long j = 2; j <= sqrt(nbr); j++) { + if ((nbr % j) == 0) { // verify if i is the divider of nbr + return (false); // i is a divider of nbr but not prime + } + } + return (true); // "yes": i is a prime number +} + +factor* prime_divs (unsigned long numbr) { +/* Allocation of memory for "struct factor" + * Initialize the structure "dividers" to zero + */ + factor *dividers; + // Intialize dividers struct ("struct factor") + dividers = (factor *) calloc(1, sizeof(factor)); + + unsigned int n = 0; + dividers->number = numbr; + unsigned long tmp_number = dividers->number; + // Old Python code line translated in C + // "for(unsigned long i = 2; i < number; i++) {" + for(unsigned long i = 2; i <= tmp_number ; i++) { /* OPTIMIZED VERSION */ + if(is_div(dividers->number,i) == true){ + if(is_prime(i) == true){ + dividers->prime_dividers[n] = i; + /* The max numbers of loops is reduced + * from "number" to "tmp_number/i" + */ + tmp_number = tmp_number/i; + if(i != dividers->number) { + n++; + } + } + } + } + dividers->cnt = n; + return (dividers); +} + +/* PRODUCER */ +void* producer(fd *f) { + unsigned long nummer; + /* OPTIONS + * for( ; (fscanf(f->in, "%lu", &nummer) && (!feof(f->in))) ; ) { + * while (!feof(f->in)) { + */ + for(int j = 0; j < n_arry; j++) { + nummer = Array_Numbers[j]; + if (f->options == 'v') { + printf("Producer - read number: %lu\n", nummer); + } + /* Critical section */ + sem_wait(&empty); + pthread_mutex_lock(&mutex_readwrite); + + buffer[in] = nummer; + if (f->options == 'v') { + printf("Producer, Number in the buffer: %lu [%d]\n", buffer[in], in); + } + in = (in + 1) % BUFSIZE; + + if (f->options == 'v') { + printf("Producer, New buffer index: [%d]\n", in); + } + + // End critical section + pthread_mutex_unlock(&mutex_readwrite); + sem_post(&full); // End critical section + } + if (f->options == 'v') { + printf("Producer: EOF\n"); + } + pthread_exit((void*)0); // thread exit: normal termination => 0 +} + +/* CONSUMER */ +void *consumer(fd *f) { + factor *dividers; // Structure storing dividers and their number + unsigned long number = 0; + unsigned int elements = 0; + + while(elements < n_arry) { + // read the number from the buffer + sem_wait(&full); // Critical section //////ERROR + pthread_mutex_lock(&mutex_readwrite); + number = buffer[out]; + if (f->options == 'v') { + printf("Consumer - number in the buffer %lu\n", buffer[out]); + printf("Consumer - index of the buffer [%d]\n", out); + } + out = (out + 1) % BUFSIZE; + pthread_mutex_unlock(&mutex_readwrite); + sem_post(&empty); // End critical section +/// + if(f->options == 'v') { + printf("Consumer - new index of the buffer %d\n", out); + } + /* Verify if the number is prime or not, in this case the dividers are calculated */ + dividers = prime_divs(number); // Core function of the app // +/// + if(f->options == 'v') { + printf("\nConsumer - Write number: %lu ", dividers->number); + } + /* This additional Mutex has been eliminated in the PRODUCER-CONSUMER VERSION */ + // MUTEX LOCK + /* + if (pthread_mutex_lock(&mutex_dividers) != 0){ + printf("\nConsumer - lock mutex_dividers ERROR %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + */ + fprintf(f->out, "Number: %lu ", dividers->number); + if(dividers->cnt != 0) { + for(int i = 0; i < dividers->cnt; i++) { + if(f->options == 'v') { + printf("%lu ", dividers->prime_dividers[i]); + } + fprintf(f->out, " %lu", dividers->prime_dividers[i]); + } + } else { + fprintf(f->out, "This is a prime number"); + if(f->options == 'v') { + printf("This is a prime number"); + } + } + /* This additional Mutex has been eliminated in the PRODUCER-CONSUMER VERSION */ + // MUTEX UNLOCK + /* + if (pthread_mutex_unlock(&mutex_dividers) != 0){ + printf("\nConsumer - unlock mutex_readwrite ERROR %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + */ + fprintf(f->out, "\n"); + free(dividers); + elements++; // New loop + } + pthread_exit((void*)0); // thread exit: normal termination => 0;; +} + +/****************** + * MAIN PROGRAMME * + *****************/ +int main (int argc, char *argv[]) { +// Declare f (type fd). Structure containing the file descriptors + fd *f; + // Allocation of memory for "struct file_descriptors" + f = (fd *) calloc(1, sizeof(fd)); + // Inputfile and outputfile arguments + char *inputfile; // Input filemane (string) + char *outputfile; // Output filemane (string) + + pthread_t p_thread[NT_READ]; // thread IDs // PRODUCER THREADS + pthread_t c_thread[NT_WRITE]; // thread IDs // CONSUMER THREADS +/* + * START CLOCK to measure CPU time + * calculate programme performance + */ + clock_t start, end; + double cpu_time_used; + start = clock(); + + /* Check if the number of arguments is correct */ + if (argc == 4) { + inputfile = argv[1]; + outputfile = argv[2]; + char *opt = argv[3]; // v or q + f->options = *opt; + } else { + printf("%s\n", "Many or few arguments"); + exit(EXIT_FAILURE); + } + if (f->options == 'v') { + system("pwd"); // Current directory + printf("\n -- Programme start --\n"); + printf(" nr arguments: %d\n", argc); + printf(" input file: %s\n", inputfile); + printf(" output file: %s\n", outputfile); + printf(" option: %c\n\n", f->options); + } + +// Initialize the buffer containig the numbers + memset(&buffer, 0, BUFSIZE * sizeof(unsigned long)); + + if(f->options == 'v') { + printf("Initialize semaphore\n"); + } + // Initialize semaphores + sem_init(&empty, 0 , N); // buffer vide + sem_init(&full, 0 , 0); // buffer vide + + if(f->options == 'v') { + printf("Initialize mutex\n"); + } + // INITIALIZE MUTEX "mutex_readwrite" + if (pthread_mutex_init(&mutex_readwrite , NULL) != 0){ + printf("\n mutex init failed-mutex_readwrite\n"); + exit(EXIT_FAILURE); + } + /* This additional Mutex has been eliminated in the PRODUCER-CONSUMER VERSION */ + /* + if (pthread_mutex_init(&mutex_dividers , NULL) != 0){ + printf("\n mutex init failed-mutex_readwrite\n"); + exit(EXIT_FAILURE); + } + */ + // Open input and output files + if(f->options == 'v') { + printf("Create input & output file descriptors\n"); + } + f->in = fopen(inputfile, "r"); + f->out = fopen(outputfile, "a+"); + if ((f->in == NULL) || (f->out == NULL)) { + exit(EXIT_FAILURE); + } + /* Read all numbers from the input file and put them in an array + * to speed up the divisors calculation + */ + unsigned long numero; + while(!feof(f->in)) { + fscanf(f->in, "%lu\n", &numero); + if(f->options == 'v') { + printf("%d. Number %lu\n",n_arry, numero); + } + Array_Numbers[n_arry] = numero; + n_arry++; + // Check and message to inform about the maximum number array elements + // tha are allowed. The max can be tuned + if(n_arry > MAXELEM) { + printf("Please increase the maximum number of array elements (size),\n"); + printf("you must increase the MAXELEM constant in the c programme\n"); + printf("In particular increase \"120\" in the following code line => #define MAXELEM 120\n"); + printf("Thanks :)\n"); + exit(EXIT_FAILURE); + } + } +// ACTIVATE THREADS - CONSUMER-PRODUCER + if(f->options == 'v') { + printf("Create Threads\n"); + } + for (int i = 0; i < NT_READ; i++) { + pthread_create(&p_thread[i], NULL, (void*)producer, f); + if (f->options == 'v') { + printf("\nMain: create thread n.%d Producer\n", i+1); + } + } + for (int i = 0; i < NT_WRITE; i++) { + pthread_create(&c_thread[i], NULL, (void*)consumer, f); + if (f->options == 'v') { + printf("\nMain: create thread n.%d Consumer\n", i+1); + } + } + /* JOIN THREADS */ + for (int i = 0; i < NT_READ; i++) { + pthread_join(p_thread[i], NULL); + if (f->options == 'v') { + printf("\nMain: join thread n.%d Producer\n", i+1); + } + } + for (int i = 0; i < NT_WRITE; i++) { + pthread_join(c_thread[i], NULL); + if (f->options == 'v') { + printf("\nMain: join thread n.%d Consumer\n", i+1); + } + } + /* END CLOCK to measure CPU time */ + end = clock(); + cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC; + fprintf(f->out, "\nThe programme execution needed %.3f seconds\n\n", cpu_time_used); + // Print on the "stdout" + printf("\nThe programme execution needed %.3f seconds\n\n", cpu_time_used); + + // Eliminate semaphores + if (sem_destroy(&empty) != 0){ + printf("\nSem destroy Empty %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + if (sem_destroy(&full) != 0){ + printf("\nSem destroy Full %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + // Eliminate mutex "mutex_readwrite" + if (pthread_mutex_destroy(&mutex_readwrite) != 0){ + printf("\nmutex_destroy-mutex_readwrite %s\n", strerror(errno)); + fclose(f->in); + fclose(f->out); + exit(EXIT_FAILURE); + } + /* This additional Mutex has been eliminated in the PRODUCER-CONSUMER VERSION */ + /* + if (pthread_mutex_destroy(&mutex_dividers) != 0){ + printf("\nmutex_destroy-mutex_dividers %s\n", strerror(errno)); + } + */ + fclose(f->in); + fclose(f->out); + free(f); // Free the memory allocated with malloc + + return (EXIT_SUCCESS); +} \ No newline at end of file