diff --git a/projet E2/main.c b/projet E2/main.c new file mode 100644 index 0000000000000000000000000000000000000000..8a175baad89c6a4dc9f47cfd25f018fa57a25db7 --- /dev/null +++ b/projet E2/main.c @@ -0,0 +1,512 @@ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> +#include <semaphore.h> +#include <pthread.h> +#include <limits.h> +#include <time.h> +#include "main.h" + +#define NTYPE unsigned long long +#define NTYPE_POURCENT "%llu" +#define LIMITE pow(10,19) +/* +long unsigned : "%lu" +long long lli +unsigned long long llu +...*/ +/* +FAIRE : +- faudrait véirifier qu'on a jamais 2 fois le même nombre dans l'algo final (et que tous les nombres y sont) +- combien de threads faut-il prendre par défaut ? +- actuellement si on a un très grands nombres, il va prendre tout le temps et une mémoire énorme pcq on travaille sur un thread par nombre + à mon avis on va devoir faire du multi-thread par nombre + ou au moins réduire sensiblement la quantité de ram qu'on demande pcq elle est beaucoup trop gande il me semble +============================================================================== +QUESTIONS: +- tests pour les autres fonctions, avec le prinf ds l'output +- est-ce que Jenkins doit être bloquant au niveau des push ? => on a le choix +- le README +- fonctionnement en sigle-thread à vérif, faire des tests sur le code avec plusiers ? +- déterminer le nombre de thread à utiliser si on a le choix +- pas nécessaires mais éviter le crash => OK +- enlever les anti-erreurs +- véirif pass test sur ingi +- curseurs de lecture au début des fichiers? +- pourquoi mutex dans la fct_file_out? +*/ + +////////////////////////////////////////////////////////////////////////////////////////////////////// +//STRUCTURES + +//pour un nombre number +typedef struct outputstruct{ + NTYPE number; + int number_div; //le nombre de diviseurs de number + NTYPE *tab_div; //le pointeur vers le tableau qui contiendra les diviseurs premiers de number + } outputstruct_t; +//pour stocker les buffers +typedef struct buffers{ + NTYPE *buf_1; //buffer des nombres de l'input, rempli par la fonction fct_file_in et lu par la fonction fct_calcul + outputstruct_t **buf_2; //buffer des structures de chaque nombre, rempli par fct_calcul et lu par fct_file_out + } buffers_t; + +// FONCTIONS +/* tout ceci est dans le fichier .h +NTYPE *prime_divs( NTYPE*, NTYPE); +void *fct_calcul( void*); +void *fct_file_in( void*); +void *fct_file_out( void*); +*/ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//FILES + +FILE* file_in; +FILE* file_out; + +////////////////////////////////////////////////////////////////////////////////////////////////////// +//SEMAPHORES ET MUTEX + +pthread_mutex_t mutex_buffer_1; // mutex pour les consommateurs de buffer 1 +sem_t empty_1; // sémaphore pour le buffer 1, pour déterminer quand le buffer 1 est vide +sem_t full_1; // idem, pour déterminer quand le buffer 1 est plein +int index_remplissage_1 =0; // pas thread-safe pcq un seul thread qui le modif (producer) +int index_vidage_1 =0; // doit être thread-safe pcq plusieurs threads qui calculent (consumer) + +pthread_mutex_t mutex_buffer_2; // mutex pour les producteurs de buffer 2 +sem_t empty_2; // sémaphore pour le buffer 2, pour déterminer quand le buffer 2 est vide +sem_t full_2; // sémaphore pour le buffer 2, pour déterminer quand le buffer 2 est vide +int index_remplissage_2 =0; // pas thread-safe pcq un seul thread qui le modif (producer) +int index_vidage_2 =0; // doit être thread-safe pcq plusieurs threads qui calculent (consumer) + +int size1; // taille de buffer 1 et de buffer 2 +int n_threads; + + +int main(int argc, char** argv){ + + fprintf(stdout,"types actuels:");fprintf(stdout,"%s",NTYPE_POURCENT); + + //////////////////////////////////////////////////////////////////////////////////////// + //GESTION DES ARGUMENTS DONNÉS À LA MAIN + + if(argc != 3 && argc != 5){ fprintf(stderr,"not right amount of arguments\n"); return -1; } + //CASE [ prog_name file1 file2 ] + if(argc == 3){ + n_threads = 3;// nombre le plus intéressant à déterminer + //open + if( (file_in =fopen(argv[1],"r" )) == NULL ){ fprintf(stderr,"error in open file_in\n"); } + if( (file_out=fopen(argv[2],"w" )) == NULL ){ fprintf(stderr,"error in open file_out\n"); }//écrit dans le file, elle crée si besoin + + } + //CASE [ prog_name -N n_threads file1 file2 ] + else{ + if( (argv[1][0] != '-') || (argv[1][1] != 'N') ){ fprintf(stderr,"not right amount of arguments\n"); return -1;} + n_threads = atoi(argv[2]); // *(((int*)(* argv) + 3)); + //open + if( (file_in =fopen(argv[3],"r" )) == NULL ){ fprintf(stderr,"error in open file_in\n"); } + if( (file_out=fopen(argv[4],"w" )) == NULL ){ fprintf(stderr,"error in open file_out\n"); }//écrit dans le file, elle crée si besoin + } + + // placer les curseurs de lecture des fichiers au début de ces derniers + if( fseek(file_in , 0, SEEK_SET) != 0){ fprintf(stderr,"error dans la fct seek in\n"); } + if( fseek(file_out, 0, SEEK_SET) != 0){ fprintf(stderr,"error dans la fct seek out\n"); } + + ////////////////////////////////////////////////////////////////////////////////////////////// + //INITIALISATIONS + + //mutex + if(pthread_mutex_init( &mutex_buffer_2, NULL) !=0){ fprintf(stderr,"error in m utex init de buffer 2\n"); } + if(pthread_mutex_init( &mutex_buffer_1, NULL) !=0){ fprintf(stderr,"error in m utex init de buffer 1\n"); } + + //buffers 1,2 + size1 = 2*n_threads; + + NTYPE* buffer_1 =(NTYPE*) malloc(sizeof(NTYPE )*size1); + outputstruct_t** buffer_2 =(outputstruct_t**) malloc(sizeof(outputstruct_t* )*size1); + + if(buffer_1 == NULL){ fprintf(stderr,"erreur dans le malloc de buffer_1\n"); } + if(buffer_2 == NULL){ fprintf(stderr,"erreur dans le malloc de buffer_2\n"); } + + //structures outputstruct_t dans le buffer 2 + for (int p=0; p<size1;p++){ + buffer_2[p]= ( outputstruct_t* ) malloc(sizeof(outputstruct_t)); + } + + //structure buffer_t + buffers_t *buffers = (buffers_t*) malloc(sizeof(buffers_t)); + buffers->buf_1 = buffer_1; + buffers->buf_2 = buffer_2; + + //sémaphores + if(sem_init(&empty_1 , 0 , size1) != 0){ fprintf(stderr,"erreur dans le sem_init empty_1\n" );} + if(sem_init(&full_1 , 0 , 0 ) != 0){ fprintf(stderr,"erreur dans le sem_init full_1 \n" );} + if(sem_init(&empty_2 , 0 , size1) != 0){ fprintf(stderr,"erreur dans le sem_init empty_2\n" );} + if(sem_init(&full_2 , 0 , 0 ) != 0){ fprintf(stderr,"erreur dans le sem_init full_2 \n" );} + + //threads + pthread_t threads[n_threads]; + pthread_t thread_ecriture; + pthread_t thread_lecture; + + /////////////////////////////////////////////////////////////////////////////////////////////// + //ON MET LES THREADS AU TRAVAIL + + if (pthread_create(&thread_ecriture,NULL,&fct_file_out ,(void*) buffers) != 0 ){ fprintf(stderr,"erreur create thread ecriture\n"); } + if (pthread_create(&thread_lecture, NULL,&fct_file_in ,(void*) buffers) != 0 ){ fprintf(stderr,"erreur create thread lecture\n"); } + for (int i=0; i<n_threads; i++){ + if (pthread_create(&(threads[i]),NULL, &fct_calcul , (void*)buffers) != 0){ fprintf(stderr,"erreur create_thread[%d]\n",i); } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /* + - JOIN des threads, + - DESTROY des mutex + - DESTROY des sémaphores + - FREE des données + - CLOSE des files + */ + if (pthread_join(thread_lecture, NULL) != 0) { fprintf(stderr,"erreur dans appel à pthread_join_lecture\n"); } + for( int j = 0; j <n_threads ; j++){ + if( pthread_join(threads[j],NULL ) != 0){ fprintf(stderr,"erreur dans appel à pthread_join_calculs\n"); } + } + if (pthread_join(thread_ecriture, NULL) != 0) { fprintf(stderr,"erreur dans appel à pthread_join_ecriture\n"); } + + if(sem_destroy(&empty_1)!=0){ fprintf(stderr,"erreur dans sem destroy empty1 \n"); } + if(sem_destroy(&full_1 )!=0){ fprintf(stderr,"erreur dans sem destroy full1 \n"); } + if(sem_destroy(&empty_2)!=0){ fprintf(stderr,"erreur dans sem destroy empty2 \n"); } + if(sem_destroy(&full_2 )!=0){ fprintf(stderr,"erreur dans sem destroy full2 \n"); } + + if(pthread_mutex_destroy(&mutex_buffer_1)!=0){ fprintf(stderr,"erreur dans sem destroy empty1\n"); } + if(pthread_mutex_destroy(&mutex_buffer_2)!=0){ fprintf(stderr,"erreur dans sem destroy full1\n" ); } + + for (int b=0;b<size1;b++){ free(buffer_2[b]); } + free(buffer_1); free(buffer_2); free(buffers); + + fclose(file_in); fclose(file_out); fprintf(stderr,"fin du programme, exit normal O(∩_∩)O\n"); + +}//fin de la main + + +//la fonction suivante est dans le fichier prime_divs.c +/* + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// FCT DE CALCUL DES N PREMIERS (POUR UN NUMBER DONNE) +NTYPE * prime_divs (NTYPE *diviseur, NTYPE number){ + NTYPE fix_number = number; + NTYPE i, j; + NTYPE a = ceil(sqrt(number)); + *diviseur = 0; + + // on a une légère perte de mémoire vu qu'on utilise pas le 0 et le 1 dans les array on pourrais ganger en taille + + + //NTYPE clk_tck = CLOCKS_PER_SEC; + //clock_t t1, t2, t3; + //t1 = clock(); + + char *bitarray = (char*)malloc(sizeof(char) * a); + //INFO: bitset: array de 0 et 1, + //1 => pas un bon nombre (pas premier et ou pas diviseur) + //0 => ok on le prends (ou pas encore calculé) + //exemple pour 35 => bitarray : [0,1,1,1,0,1] + + + if (bitarray == NULL){ fprintf(stderr, "erreur malloc de prime_divs\n"); } + memset(bitarray, 0, a); + // comment faire pour que le nombre lui-même ne soit pas compté dans les diviseurs + //t2 = clock(); + i = 2; + //NTYPE variable = 1; + //changer le i<a si possible + while (number > 1 && i<a+1) { + + //CASE: pour les nombres qui 0 (sont premiers mais pas forcément diviseurs) + if (!bitarray[i]){ + + //tous les mult de i (sauf i lui-même) à 1 pcq se sont PAS des nombres premiers + for(j = 2*i; j < a; j += i){ if (!bitarray[j]){ bitarray[j] = 1; } } + + //SWITCH on sait que i est nombre premier => est-il un diviseur ? + + //CASE oui => les nombres premiers qu'il reste à identifier sont les diviseurs premiers de number/i + // (divisés par i autant de fois que nécessaire pour + // - éviter les redondances de calcul + // - trouver plus aisément le dernier nombre premier) + + if ((number % i) == 0){ + //printf("%llu ", i); + //variable*=i; + (*diviseur)++; + number /= i; + while(number % i == 0){ number /= i; + //variable*=i; + } + + //CASE non => on met à 1 pour dire pas compté après + }else{ bitarray[i]=1; } + } + i++; + } + + //t3 = clock(); + //printf("tout le code prend : %lf ", (t3-t1)/clk_tck); + //printf("bitarray prend : %lf ", (t2-t1)/clk_tck); + + NTYPE* prime_dividers; + if(( prime_dividers= (NTYPE*) malloc(sizeof(NTYPE)*((*diviseur)+1) ) )==NULL){ fprintf(stderr,"erreur dans le malloc de prime_divs\n"); } + + + //Remplir prime_dividers avec les nombres premiers, diviseurs de number + NTYPE k = 0;// nombre de diviseurs => PQ pas utiliser diviseurs lui-même ? + NTYPE m; + + for(m = 2; m < i; m++) { + // on add à prime_dividers le nombres dont l'indice vaut 0 dans le bitset + if(!bitarray[m] && (fix_number != 2) ) { + //printf(NTYPE_POURCENT, m); + prime_dividers[k] = m; + k++; + } + } + if (fix_number == 2){ + (*diviseur)--; + // ajoute le denier nombre dans la liste de diviseurs dans un cas particulier de l'algo (à détailler) + }if (number != 1 && (number != fix_number)) { + prime_dividers[k] = number; + (*diviseur)++; + //printf("\n"); + } + free(bitarray); + + return prime_dividers; +} +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// FONCTION DE LA THREAD DE LECTURE + +void* fct_file_in(void* buffs){ + //BUT: lecture de l'input et remplissage du buffer_1 de buffs par le thread de lecture + + /////////////////////////////////////////////////////////////////////////////////////////////////// + //PRODUCER BUFFER 1 + + //DONNEES + buffers_t *buffers = (buffers_t *) buffs; + NTYPE* buffer_1 = buffers->buf_1; + + int eof = 0; // eof étant le return de fscan est soit le nombre de int détectés soit EOF + int caractere = 0; //pour gérer un cas particulier + NTYPE input_number; // contiendra le nombre lu + + //CODE + //avec prise en compte des caractères spéciaux dans l'input + while (eof != EOF){ + // RAPPEL: fscan met le curseur de lecture avant le caractère qu'il n'a pas su lire (un string alors qu'il devait lire un int par ex) + // RAPPEL: fgetc lit le caractère et met le curseur de lecture après le caractère + + //gère le cas où on a un nombre négatif + char nana; //contiendra le premier caractere d'une ligne du fichier input + fscanf(file_in,"%c",&nana); + + if(nana == '-'){ while( fgetc(file_in) !='\n'){} + }else{ fseek(file_in,-1,SEEK_CUR);} + + //lit le nombre + eof = fscanf(file_in, NTYPE_POURCENT, &input_number); + + //ne prend pas en compte les nombres plus grand que LIMITE (car code non fonctionnel pour ceux-là ) + if (input_number > LIMITE){ + continue; + } + + //CASE: fin de lecture de l'input => remplissage de buffer 1 avec n_threads "1" pour lui indiquer que fin de lecture + if (eof == EOF){ + for(int i =0; i< n_threads; i++){ + + if(sem_wait(&empty_1)!=0){ fprintf(stderr,"erreur dans le sem wait empty_1 in read fct\n"); } + + buffer_1[index_remplissage_1] = 1; + index_remplissage_1++; + index_remplissage_1 %= size1; + + if(sem_post(&full_1) != 0){ fprintf(stderr,"erreur dans le sem post full_1 in read fct\n" ); } + } + continue; //à la prochaine itération, on est sûr de sortir + } + + //CASE: pas bon format (caractères spéciaux dans la ligne) + if( (eof != 1) ){ while( fgetc(file_in) !='\n'){} } + + //CASE: "int\n" bon format et nombre plus grand que 2 + //met le nombre dans le buffer 1 + else if( (input_number >= 2) && (caractere=fgetc(file_in))=='\n'){ + if(sem_wait(&empty_1)!=0){ fprintf(stderr,"erreur dans le sem wait empty_1 in read fct\n"); } + + buffer_1[index_remplissage_1] = input_number; + index_remplissage_1++; + index_remplissage_1 %= size1; + + if(sem_post(&full_1) != 0){ fprintf(stderr,"erreur dans le sem post full_1 in read fct\n" ); } + + } + + /* + reste encore un cas particulier, si la ligne de l'input est : + 4-2\n + */ + //CASE: on lit le 4 (fscanf), mais le caractère suivant stocké dans + //caractere n'est pas \n, donc on passe la ligne + else if(caractere != '\n'){ + while(fgetc(file_in)!='\n'){} + } + + } + return (void*) NULL; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +// FONCTION DES THREADS DE CALCUL + +void* fct_calcul(void* buffs){ + /* BUT : + 1) prend le nombre dans le buffer 1 de buffs + 2) appelle prime_divs + 3) écrit dans buffer_2 de buffs avec le tableau des nombres premiers remplis + */ + + //DONNEES + buffers_t* buffers = (buffers_t*) buffs; + outputstruct_t** buffer_2 = buffers->buf_2; + NTYPE* buffer_1 = buffers->buf_1; + + NTYPE nombre; + NTYPE diviseurs[1]; //stockera le nombre de diviseurs de nombre + diviseurs[0] = 0; + NTYPE* array; //contiendra les nombres premiers + + //CODE + while(1){ + ////////////////////////////////////////////////////////////////////////////////////////// + //A. CONSUMER BUFFER 1 + + int index; // indice auxquel on peut aller prendre le nombre dans buffer_1 + + //prend le nombre dans le buffer 1 + if(sem_wait(&full_1)!=0){ fprintf(stderr,"erreur dans le sem wait full_1 in fct calcul\n"); } + + pthread_mutex_lock(&mutex_buffer_1); + + index = index_vidage_1; + index_vidage_1++; + index_vidage_1 %= size1; + + pthread_mutex_unlock(&mutex_buffer_1); + + nombre = buffer_1[index]; + + if(sem_post(&empty_1) != 0){fprintf(stderr,"erreur dans le sem _post empty_1\n" );} + + //CASE number=1: signifie que la lecture de l'input est terminée, donc l'indique dans le buffer 2 + if(nombre == 1){ + + if(sem_wait(&empty_2)!=0){ fprintf(stderr,"erreur dans le sem empty_2 in fct calcul\n"); } // attente d’une place libre + + pthread_mutex_lock(&mutex_buffer_2); + + buffer_2[index_remplissage_2]->number = 1; + buffer_2[index_remplissage_2]->number_div = 1; + buffer_2[index_remplissage_2]->tab_div = NULL; + + index_remplissage_2++; + (index_remplissage_2) %= size1; + + pthread_mutex_unlock(&mutex_buffer_2); + + if(sem_post(&full_2) != 0){ fprintf(stderr,"erreur dans le sem _post full_2\n" ); } + + break; + } + + ////////////////////////////////////////////////////////////////////////// + //B. CALCUL + + //appel de prime_divs pour remplir le tableau des diviseurs premiers de nombre + array = prime_divs(diviseurs, nombre); + + ///////////////////////////////////////////////////////////////////////////////////////// + //C. PRODUCER BUFFER 2 + + //remplit le buffer 2 avec la structure complétée pour le nombre + if(sem_wait(&empty_2)!=0){ fprintf(stderr,"erreur dans le sem empty_2 in fct calcul\n"); } // attente d’une place libre + + pthread_mutex_lock(&mutex_buffer_2); + + //(le tableau précédent à déjà été free, PAS la structure,cfr thread fct_file_out) + buffer_2[index_remplissage_2]->number = nombre; + buffer_2[index_remplissage_2]->number_div = *diviseurs; + buffer_2[index_remplissage_2]->tab_div = array; + + index_remplissage_2++; + (index_remplissage_2) %= size1; + + pthread_mutex_unlock(&mutex_buffer_2); + + if(sem_post(&full_2) != 0){ fprintf(stderr,"erreur dans le sem _post full_2\n" ); } + } + return (void*) NULL; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +// FONCTION DE LA THREAD D'ECRITURE + +void* fct_file_out(void* buffs){ + //BUT: prend les structures dans buffer_2 de buffs et écrit dans l'output + + //////////////////////////////////////////////////////////////////////////////////////// + // CONSUMER BUFFER 2 + + //DONNEES + buffers_t* buffers = (buffers_t *) buffs; + outputstruct_t** buffer_2 = buffers->buf_2; + + //CODE + int n_moinsun = 0; //pour gérer la fin de la boucle while + + while (n_moinsun < n_threads ){ + if(sem_wait(&full_2)!=0){ fprintf(stderr,"erreur dans le sem wait in fct calcul\n"); } // attente d’une place remplie + + //pthread_mutex_lock(&mutex_buffer_2); + + //écrit dans l'output si ce n'est pas fini + if(buffer_2[index_vidage_2]->number != 1){ + fprintf(file_out,NTYPE_POURCENT,buffer_2[index_vidage_2]->number); + for( int i=0; i < buffer_2[index_vidage_2]->number_div ; i++){// number->div est utile ici + fprintf(file_out," "); + fprintf(file_out,NTYPE_POURCENT, (buffer_2[index_vidage_2]->tab_div)[i] ); + } + fprintf(file_out,"\n"); + //CASE : il n'y a plus de structures à écrire dans l'output + //pour le signaler, incrémente n_moinsun; lorsque toutes les threads + //sont passées par là , la boucle s'arrête + }else{ n_moinsun++; } + + // pas besoin pcq index_vidage n'est utilisé que par la fct file_out + //pthread_mutex_lock(&mutex_buffer_2); + + free(buffer_2[index_vidage_2]->tab_div);//on free le pointeur de l'ancien tableau de nombres premiers de la struct + + //pthread_mutex_unlock(&mutex_buffer_2); + + index_vidage_2++; + index_vidage_2 %= size1; + + //pthread_mutex_unlock(&mutex_buffer_2); + if(sem_post(&empty_2) != 0){ fprintf(stderr,"erreur dans le sem _post empty_2\n" ); } + } + return (void*) NULL; +}