* @next: pointer to the next node in the list, NULL if last node_t
* @value: value stored in the node
*/
typedefstructnode{
structnode*next;
intvalue;
}node_t;
/**
* Structure list
*
* @first: first node of the list, NULL if list is empty
* @size: number of nodes in the list
*/
typedefstructlist{
structnode*first;
structnode*last;
intsize;
}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
*/
typedefstructargs_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(intvalue){
node_t*new=(node_t*)malloc(sizeof(node_t));
if(new==NULL){
returnNULL;
}
new->value=value;
new->next=NULL;
returnnew;
}
//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
*/
intadd_node(list_t*list,intvalue){
if(list==NULL){
return1;
}
node_t*new=init_node(value);
if(new==NULL){
return1;
}
if(list->size==0){
list->first=new;
list->last=new;
list->size++;
}else{
(list->last)->next=new;
list->last=new;
list->size++;
}
return0;
}
/*
* 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(intnum){
list_t*prime_dividers;
prime_dividers=(list_t*)malloc(sizeof(list_t));
if(prime_dividers==NULL){
returnNULL;
}
prime_dividers->first=NULL;
prime_dividers->last=NULL;
prime_dividers->size=0;
intmax=1;
intn=num;
add_node(prime_dividers,num);
while(num%2==0){
if(max!=2){
max=2;
add_node(prime_dividers,2);
}
num=num/2;
}
inti;
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);
}
}
returnprime_dividers;
}
/*
* Clear the given linked list @list
*
* @list: list to be cleared
*/
voidclear_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
*/
inti;
intj;
intk;
intl;
intN;
FILE*fin;
FILE*fout;
sem_tempty;
sem_tfull;
sem_tempty2;
sem_tfull2;
pthread_mutex_tmutex;
pthread_mutex_tmutex2;
inttaille=0;
intcompteurE=0;
intcompteurC=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
*/
voidlecture(int*buffer){
intitem;
char*line2=NULL;
size_tlen2=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
*/
voidcalcul(args_thr_t*input){
list_t*item;
intelem;
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
*/
voidecriture(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
*/
intmain(intargc,char*argv[]){
clock_tstart;
clock_tend;
start=clock();
//si on ne donne pas le nombre de threads, on prends 3 threads par défaut
constchar*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é)
constchar*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 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_tlen=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);
return1;}
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);
return1;}
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);
return1;}
bufs->arg1=buffer;
bufs->arg2=buffer2;
for(x=0;x<(N/2)&&x<taille;x++){//on lance les threads de calcul