diff --git a/projet j3/facto.c b/projet j3/facto.c new file mode 100644 index 0000000000000000000000000000000000000000..6057212b5e825fa1c0c67de0b498e51f56a077e2 --- /dev/null +++ b/projet j3/facto.c @@ -0,0 +1,474 @@ +#include <CUnit/CUnit.h> +#include <CUnit/Basic.h> +#include <CUnit/Automated.h> +#include <stdio.h> +#include <math.h> +#include <malloc.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/mman.h> +#include <string.h> +#include <stdlib.h> +#include <semaphore.h> +#include <pthread.h> +#include <unistd.h> +#include <time.h> + + +/** +* Structure node +* +* @next: pointer to the next node in the list, NULL if last node_t +* @value: value stored in the node +*/ +typedef struct node { + struct node *next; + int value; +} node_t; + + +/** +* Structure list +* +* @first: first node of the list, NULL if list is empty +* @size: number of nodes in the list +*/ +typedef struct list { + struct node *first; + struct node *last; + int size; +} list_t; + + +/** + * Structure args_thr + * + * Cette structure a été crée afin de pouvoir donner plusieurs arguments aux fonctions exécutées par les threads + */ +typedef struct args_thr { + int *arg1; + list_t *arg2; +} args_thr_t; + +/* +* Create a new node containing the value @value. +* +* @value: value stored in the node +* @next : pointer to next node, initialised to NULL. +* @return: returns the pointer to the newly created node, or NULL if an error occured. +*/ +node_t *init_node(int value) { + node_t* new = (node_t*) malloc(sizeof(node_t)); + + if(new == NULL){ + return NULL; + } + + new -> value = value; + new -> next = NULL; + + return new; +} + + +//tg fdp +/* +* Add a node at the head of the list @list and increment the number of nodes in the list (member `size`) +* +* @l: linked list +* @value: value to add to the list +* +* @return: 0 if success, 1 otherwise +*/ +int add_node(list_t *list, int value) { + if(list == NULL){ + return 1; + } + + node_t* new = init_node(value); + if(new == NULL){ + return 1; + } + + if(list->size == 0){ + list->first = new; + list->last = new; + list->size++; + } else{ + (list->last)->next = new; + list->last = new; + list->size++; + } + return 0; +} + + + +/* +* Factorize the number @num into prime numbers and put them into a linked list +* +* @num: number to factorize in prime numbers +* +* @return: a linked list with all prime nubers wich factorize @num +*/ +list_t* prime_divs(int num) { + list_t* prime_dividers; + prime_dividers = (list_t*) malloc(sizeof(list_t)); + if (prime_dividers == NULL){ + return NULL; + } + prime_dividers -> first = NULL; + prime_dividers -> last = NULL; + prime_dividers -> size = 0; + + int max = 1; + int n = num; + add_node(prime_dividers,num); + while(num % 2 == 0){ + if (max != 2){ + max = 2; + add_node(prime_dividers, 2); + } + num = num / 2; + } + + int i; + for (i = 3; i <= sqrt(num); i = i+2){ + + while(num % i == 0){ + if(i != max){ + max = i; + add_node(prime_dividers,i); + } + num = num/i; + } + + } + if((num > 2) && (num != max)){ + if(num != n){ + add_node(prime_dividers,num); + } + } + return prime_dividers; +} + + + +/* +* Clear the given linked list @list +* +* @list: list to be cleared +*/ +void clear_ls(list_t *list){ + if (list -> first == NULL){ + free(list); + return; + } + if (list -> first != NULL){ + node_t *head = list->first; + while (head->next != NULL){ + node_t *current = head ->next; + free(head); + head = current; + + } + free(head); + + free(list); + return; + } +} + +/** + * Déclaration des variables utilisées dans les fonctions lecture, calcul et ecriture + */ +int i; +int j; +int k; +int l; +int N; +FILE* fin; +FILE* fout; +sem_t empty; +sem_t full; +sem_t empty2; +sem_t full2; +pthread_mutex_t mutex; +pthread_mutex_t mutex2; +int taille = 0; +int compteurE = 0; +int compteurC = 0; + +/** + * Fonction de type producteur (cfr syllabus : Les sémaphores : Problème des producteurs-consommateurs) + * Lis le fichier FILE* fin et place les nombres lus dans un buffer. + * Le buffer est parcouru grâce à l'indice i et à la variable N, qui est la taille du buffer. + * Le nombre de lignes du fichier est aussi récupéré via la variable taille. + * On s'arrête quand tout le fichier a été parcouru + * + * @buffer : buffer dans lequelles nombres lus seront placés + */ +void lecture(int* buffer) { + int item; + char *line2 = NULL; + size_t len2 = 0; + while(getline(&line2, &len2, fin) != -1){ //boucle pour parcourir le fichier, getline renvoie -1 quand on arrive à la fin du fichier, lin et len s'incrémentent tout seuls + item = atoi(line2); //on stocke le nombre lu dans la variable item + sem_wait(&empty); + pthread_mutex_lock(&mutex); + buffer[i] = item; //on place item (le nombre lu) dans le buffer (safe car mutex) + i = ((i+1)%N); //on incrémente l'index à chaque fois qu'on mets un nombre dans le buffer. Quand i (l'indice) est égal à N (taille du buffer) i retombe à 0 grâce au modulo (safe car mutex) + pthread_mutex_unlock(&mutex); + sem_post(&full); + } +} + +/** + * Fonction de type consommateur (vis-à -vis de la fonction lecture) et producteur (vis-à -vis de la fonction écriture) (cfr syllabus : Les sémaphores : Problème des producteurs-consommateurs) + * Calcule les diviseurs premiers des nombres contenus dans buffer, puis les place dans buffer2 + * Les buffers sont parcourus par grâce aux indices j, k et à la variable N, qui correspond à la taille des buffers + * On s'arrête lorsque tous les nombres à traiter sont traités, donc lorsque la varianble compteurC est égale à la variable taille + * + * @buffer: buffer duquel proviennent les nombres à traiter + * @buffer2: buffer dans lequel les nombres traités sont stockés + */ +void calcul(args_thr_t *input) { + list_t *item; + int elem; + while(compteurC < taille) { //qd le compteur (! différent de compteurE !) est égal à la taille du fichier (donc quand toutes les lignes on été traitées), on s'arrête + //partie consomateur + sem_wait(&full); + pthread_mutex_lock(&mutex); + compteurC +=1; //on incrémente un compteur à chaque fois qu'un nombre est traité (variable globale, safe car mutex) + int * temp_buf = input->arg1; + elem = temp_buf[j]; //on récupère un nombre à traiter dans le buffer et on le place dans la variable elem (safe car mutex) + j = ((j+1)%N); //on incrémente l'index à chaque fois qu'on extrait un nombre du buffer. Qd j (l'index) est égal à la taille du buffer (N), j retombe à 0 grâce au modulo (safe car mutex) + pthread_mutex_unlock(&mutex); + sem_post(&empty); + + item = prime_divs(elem); //on calcule les diviseurs premiers de elem, et on place la liste obtenue dans item + //partie producteur + sem_wait(&empty2); + pthread_mutex_lock(&mutex2); + input ->arg2[k] = *item; //on place un pointeur vers la liste des diviseurs premier dans le deuxième buffer (safe car mutex) + k = ((k+1)%N); //vous avez compris comment on incrémente maintenant + pthread_mutex_unlock(&mutex2); + sem_post(&full2); + } +} + +/** + * Fonction de type consommateur (cfr syllabus : Les sémaphores : Problème des producteurs-consommateurs) + * Place dans le fichier FILE* fout les nombres (et leurs diviseurs premiers, le tout sous forme de liste) contenus dans buffer2 + * Le buffer est parcouru grâce à l'indice l et à la variable N, qui correspond à la taille du buffer + * On s'arrête lorsque toutes les listes on été traitées, donc lorsque la variable compteurE est égale à la variable taille + * + * @buffer2: buffer dans lequel les listes de nombres à écrire dans le fichier sont stockés + */ +void ecriture(list_t *buffer2) { + list_t *item; + + while(compteurE < taille) { // qd le compteur (! différent de compteurC !) est égal à la taille du fichier (quand on a réécrit toutes les lignes), on s'arrête + sem_wait(&full2); + pthread_mutex_lock(&mutex2); + compteurE += 1; //on incrémente le compteur à chaque fois qu'on écrit un nombre (variable globale, safe car mutex) + item = &buffer2[l]; //on récupère la liste pointée par le pointeur à l'index l (safe car mutex) + l = ((l+1)%N); //on incrémente comme d'hab, allez voir dans lecture et calcul si vous avez oublié + if(item -> size != 0){ //on écrit dans le fichier fout les diviseurs contenus dans la liste, cette partie de code ne vient pas de moi, mais de fact.c (le programme sans les threads) + node_t* toprint = (item->first); + while(toprint != NULL){ + fprintf(fout, "%d ", toprint->value); + toprint = toprint->next; + } + } + fprintf(fout, "\n");//on va à la ligne dans le fichier fout + pthread_mutex_unlock(&mutex2); + sem_post(&empty2); + } + return; +} + +/* +Algorithme de type producteurs-consommateurs +* Read de input file @argv[1] wich contain an integer on every single line and write in the output file @argv[2] + the factorization of every integers in the corresponding line + Le nombre de threads à utiliser est en option + En premier lieu on vérifie si le nombre de threads est spécifié + Puis, les variables, indices, sémaphores, buffers, fichiers et threads sont initialisés + Ensuite, on lis dans le fichier d'input les nombres à traiter, puis on calcule leurs diviseurs premiers + (on passe par une structure pour pouvoir donner plusieurs arguments à la fonction utilisée par les threads) + et on écrit le résultat dans le fichier d'ouput +* +* @argc: number of arguments +* @argv[]: array of arguments +* +* @return: 0 if no problem occured +*/ +int main(int argc, char *argv[]){ + clock_t start; + clock_t end; + start=clock(); + //si on ne donne pas le nombre de threads, on prends 3 threads par défaut + const char* in = argv[1]; //par défaut, path's vers les fichiers d'entrée et de sortie (si le nombre de threads n'est pas spécifié) + const char* out = argv[2]; + N = 6; //deux fois le nombre de threads, plus facile de mettre qq fois N/2 pour le nombre de threads que de chercher tous les modulos et les tailles de buffer dans le programme pour les multiplier par 2 + + if (getopt(argc,argv, "N:") != -1) { //getopt regarde si l'option N est donnée, si non getopt renvoie -1, si oui elle crée optarg et optind + N = 2*atoi(optarg); //optarg = nombre de threads. La raison pour laquelle on le multiplie par deux est qq lignes au-dessus + in = argv[optind];// optind = index du prochain argument non optionel. + out = argv[optind + 1];// on stocke les path's des fichiers d'entrée et de sortie dans in et out (leur index n'est pas le même que si le nombre de threads n'est pas spécifié) + } + //on initialise tout ce dont on a besoin + pthread_t *threads = (pthread_t *) malloc(sizeof(pthread_t)*(N/2)); + if (threads == NULL) { + fclose(fin); + fclose(fout); + return 1;} + pthread_t lect; + pthread_t ecr; + int *buffer = (int*)malloc(N*sizeof(int)); + if (buffer == NULL) { + fclose(fin); + fclose(fout); + free(threads); + return 1;} + list_t *buffer2 = (list_t*)malloc(N*sizeof(list_t)); + if (buffer2 == NULL) { + fclose(fin); + fclose(fout); + free(buffer); + free(threads); + return 1;} + pthread_mutex_init(&mutex,NULL); + pthread_mutex_init(&mutex2,NULL); + sem_init(&empty,0,N); + sem_init(&full,0,0); + sem_init(&empty2,0,N); + sem_init(&full2,0,0); + i=0;j=0;k=0;l=0; + int x; + int err; + fin = fopen(in,"r"); + fout = fopen(out,"w"); + //on récupère la taille du fichier avant de commencer à travailler si jamais on demande plus de threads qu'il n'y a de lignes dans le fichier + char* line = NULL; + size_t len = 0; + while (getline(&line,&len,fin) != -1) {taille += 1;} + free(line); + rewind(fin); + + err = pthread_create(&lect,NULL, (void *) lecture, (void *) buffer);//on lance la lecture du fichier d'entrée + if (err != 0) { + fclose(fin); + fclose(fout); + sem_destroy(&empty); + sem_destroy(&full); + sem_destroy(&empty2); + sem_destroy(&full2); + pthread_mutex_destroy(&mutex); + pthread_mutex_destroy(&mutex2); + free(buffer); + free(buffer2); + free(threads); + return 1;} + + args_thr_t* bufs = (args_thr_t *) malloc(sizeof(args_thr_t));//on crée une structure pour stocker les arguments de la fonction calcul, car la fonction pthread_create ne prends qu'un seul pointeur vers les arguments de la fonction utilisée + if (bufs == NULL) { + fclose(fin); + fclose(fout); + sem_destroy(&empty); + sem_destroy(&full); + sem_destroy(&empty2); + sem_destroy(&full2); + pthread_mutex_destroy(&mutex); + pthread_mutex_destroy(&mutex2); + free(buffer); + free(buffer2); + free(threads); + return 1;} + + if (bufs == NULL) { + fclose(fin); + fclose(fout); + sem_destroy(&empty); + sem_destroy(&full); + sem_destroy(&empty2); + sem_destroy(&full2); + pthread_mutex_destroy(&mutex); + pthread_mutex_destroy(&mutex2); + free(buffer); + free(buffer2); + free(threads); + return 1;} + bufs -> arg1 = buffer; + bufs -> arg2 = buffer2; + + for(x=0;x<(N/2) && x < taille ;x++) { //on lance les threads de calcul + err = pthread_create(&threads[x],NULL,(void *) calcul,(void *) bufs); + if (err != 0) { + fclose(fin); + fclose(fout); + sem_destroy(&empty); + sem_destroy(&full); + sem_destroy(&empty2); + sem_destroy(&full2); + pthread_mutex_destroy(&mutex); + pthread_mutex_destroy(&mutex2); + free(buffer); + free(buffer2); + free(threads); + return 1;}// crash test pour le lancement des threads + } + + err = pthread_create(&ecr,NULL, (void *) ecriture, (void *) buffer2);//on lance l'écriture des diviseurs premiers dans le fichier de sortie + if (err != 0) { + fclose(fin); + fclose(fout); + sem_destroy(&empty); + sem_destroy(&full); + sem_destroy(&empty2); + sem_destroy(&full2); + pthread_mutex_destroy(&mutex); + pthread_mutex_destroy(&mutex2); + free(buffer); + free(buffer2); + free(threads); + return 1;} + + err = pthread_join(ecr,NULL); + if (err != 0) { + fclose(fin); + fclose(fout); + sem_destroy(&empty); + sem_destroy(&full); + sem_destroy(&empty2); + sem_destroy(&full2); + pthread_mutex_destroy(&mutex); + pthread_mutex_destroy(&mutex2); + free(buffer); + free(buffer2); + free(threads); + return 1;} + //on ferme les fichiers et on libère la mémoire. + fclose(fin); + fclose(fout); + sem_destroy(&empty); + sem_destroy(&full); + sem_destroy(&empty2); + sem_destroy(&full2); + pthread_mutex_destroy(&mutex); + pthread_mutex_destroy(&mutex2); + free(buffer); + free(buffer2); + free(threads); + printf("%s\n","End of process." ); + + end=clock(); + double procTime=(double)(end-start)/CLOCKS_PER_SEC; + printf("Processing time = %f ms\n", procTime*pow(10,3)); + + + return 0; +}