[C] Core dumped avec pthread

Core dumped avec pthread [C] - C - Programmation

Marsh Posté le 25-02-2023 à 15:39:25    

Salut à tous :hello:
 
J'essaie de comprendre ma connerie que je n'arrive pas à identifier :pt1cable:
Je 'reprends' le C (niveau découverte) mais j'y rajoute le Multithreading, si certaines convention de forme ne sont pas respectées et que ça vous pique les yeux n'hésitez pas à m'engueuler :D
Au lancement du prog après un temps très court, j'ai quelque fois un core dump dans les 2-3 sec.
Si aucun problème déclaré vers le lancement, alors le programme se déroule jusqu'à la fin (testé avec de grandes limites).
 

Code :
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4. #include <unistd.h>
  5. #include <pthread.h>
  6. #include <string.h>
  7. typedef struct int_protege{
  8.     pthread_mutex_t intex;
  9.     int entier;
  10. }int_p;
  11. int_p incremented = {PTHREAD_MUTEX_INITIALIZER,0};
  12. void* routine_adding(){
  13.     int statut, compte;
  14.     while(incremented.entier < 10000){ //limite
  15.         pthread_mutex_unlock(&incremented.intex);
  16.         statut = pthread_mutex_trylock(&incremented.intex);
  17.         if (statut == 0){
  18.             incremented.entier += compte;
  19.             printf("%ld a augmenté de %d : %d\n", pthread_self(),compte,incremented.entier);
  20.             fflush(stdout);
  21.             compte = 0;
  22.             pthread_mutex_unlock(&incremented.intex);
  23.         }
  24.         compte++;
  25.         pthread_mutex_lock(&incremented.intex);
  26.     }
  27.     pthread_mutex_unlock(&incremented.intex);
  28. }
  29. int main(int argc, char** argv){
  30.     pthread_t thread1, thread2;
  31.     pthread_create(&thread1,NULL,&routine_adding,NULL);
  32.     pthread_create(&thread2,NULL,&routine_adding,NULL);
  33.     pthread_join(thread1,NULL);
  34.     pthread_join(thread2,NULL);
  35.     return EXIT_SUCCESS;
  36. }


 
Je suis sur VM linux mint xfce, ce pourrait-il que ça soit en lien avec mon PB ?
Merci pour la lecture !
 
EDIT du lendemain (Merci kajoux !) :
 

Code :
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4. #include <unistd.h>
  5. #include <pthread.h>
  6. #include <string.h>
  7. typedef struct int_protege{
  8.     pthread_mutex_t intex;
  9.     int entier;
  10. }int_p;
  11. int_p incremented = {PTHREAD_MUTEX_INITIALIZER,0};
  12. void* routine_adding(){
  13.     int statut, compte;
  14.     pthread_mutex_lock(&incremented.intex);
  15.     while(incremented.entier < 10000){ //limite
  16.         pthread_mutex_unlock(&incremented.intex);
  17.         statut = pthread_mutex_trylock(&incremented.intex);
  18.         if (statut == 0){
  19.             incremented.entier += compte;
  20.             printf("%ld a augmenté de %d : %d\n", pthread_self(),compte,incremented.entier);
  21.             fflush(stdout);
  22.             compte = 0;
  23.             pthread_mutex_unlock(&incremented.intex);
  24.         }
  25.         compte++;
  26.         pthread_mutex_lock(&incremented.intex);
  27.     }
  28.     pthread_mutex_unlock(&incremented.intex);
  29. }
  30. int main(int argc, char** argv){
  31.     pthread_t thread1, thread2;
  32.     pthread_create(&thread1,NULL,&routine_adding,NULL);
  33.     pthread_create(&thread2,NULL,&routine_adding,NULL);
  34.     pthread_join(thread1,NULL);
  35.     pthread_join(thread2,NULL);
  36.     return EXIT_SUCCESS;
  37. }


 
Ne pas laisser le mutex pouvant être unlock sans lock préalable ... !


Message édité par Shadoko le 26-02-2023 à 10:08:22

---------------
Pourquoi faire simple quand on peut faire compliqué ?
Reply

Marsh Posté le 25-02-2023 à 15:39:25   

Reply

Marsh Posté le 25-02-2023 à 23:29:03    

Je pense que c'est parce que tu commences par unlocker un thread qui n'a pas été locké (comportement indéfini).
Comme ça ça a l'air de passer mieux :

Code :
  1. while(incremented.entier < 10000){ //limite
  2.         //pthread_mutex_unlock(&incremented.intex);
  3.         statut = pthread_mutex_trylock(&incremented.intex);
  4.         if (statut == 0){
  5.             incremented.entier += compte;
  6.             printf("%ld a augmenté de %d : %d\n", pthread_self(),compte,incremented.entier);
  7.             fflush(stdout);
  8.             compte = 0;
  9.             pthread_mutex_unlock(&incremented.intex);
  10.         }
  11.         compte++;
  12.         pthread_mutex_lock(&incremented.intex);
  13.         pthread_mutex_unlock(&incremented.intex);
  14.     }
  15.     //pthread_mutex_unlock(&incremented.intex);


Accessoirement la bonne signature pour le callback c'est

static void *routine_adding(void *data)


et il devrait donc renvoyer NULL (ou autre chose…).
Compile avec les warnings -Wall -Wextra.


Message édité par kajoux le 25-02-2023 à 23:38:03
Reply

Marsh Posté le 26-02-2023 à 10:05:16    

Salut kajoux :hello:
 
Merci beaucoup pour ton intervention !
J'essaie de garder le fait de lock le mutex avant de checker le int dans l'assertion du while, car pendant que l'un vérifie, l'autre pourrait faire changer le True/False de (incremented.entier < 10000) sans que le thread vérifiant n'ait la bonne visibilité mémoire.
 
Mais tu as réglé mon problème cela dit car en rajoutant un petit lock au dessus du while dans mon code initial, tout va mieux !
 
Ce qui est curieux c'est que dans les cas de crash, les deux threads avaient réussi à incrémenter à plusieurs répétitions le compteur sans bug jusqu'au crash.
Le premier unlock était donc passé, saurais-tu (ou à bon entendeur, salut) la raison de ce phénomène désormais réglé ?
 
Je n'ai vu nulle part le static dans les tutos qui présentent des routines, est-ce qu'on en a besoin pour autre chose que pour la rendre file scope, ce qu'elle est déjà à mon sens ?  
Merci et à plus !


---------------
Pourquoi faire simple quand on peut faire compliqué ?
Reply

Marsh Posté le 26-02-2023 à 10:27:31    

Shadoko a écrit :

Ce qui est curieux c'est que dans les cas de crash, les deux threads avaient réussi à incrémenter à plusieurs répétitions le compteur sans bug jusqu'au crash.
Le premier unlock était donc passé, saurais-tu (ou à bon entendeur, salut) la raison de ce phénomène désormais réglé ?


La raison est la manière d'utiliser la mémoire en informatique d'une manière générale, et c'est aussi la raison pour laquelle le comportement est dit "indéfini" (undefined behavior dans les manuels) dans ce cas.
Ça passe un certain nombre de fois, tant qu'il est possible de lire/écrire quelque chose en mémoire sans planter, puis ça finit par planter éventuellement.
C'est aussi la raison pour laquelle les bugs de mauvaise utilisation mémoire peuvent être difficiles à débuguer.
Je n'ai pas essayé de faire tourner ton programme dans Valgrind, mais il dit probablement quelque chose dans ce cas.

Shadoko a écrit :

Je n'ai vu nulle part le static dans les tutos qui présentent des routines, est-ce qu'on en a besoin pour autre chose que pour la rendre file scope, ce qu'elle est déjà à mon sens ?


Il permet en fait d'éviter un warning de déclaration manquante à la compilation, et c'est vrai que dire qu'il fait partie de la signature du callback est un abus de langage.

Reply

Marsh Posté le 26-02-2023 à 14:44:22    

Il faudrait que je me mette au profiling et tout l'outillage pour constater mes premières erreurs dans ma reprise du C, tu fais bien de me le rappeler, au lieu d'encrer des erreurs dans ma tête.
D'accord, je suppose que c'est un warning qui vient avec le -Wall car je ne l'avais pas encore constaté jusque-là.
Merci encore :jap:


---------------
Pourquoi faire simple quand on peut faire compliqué ?
Reply

Marsh Posté le 26-02-2023 à 15:19:38    

obligatoire, surtout mais pas que pour les débutants: -Wall -Wextra
conseillé pour son propre code à mon avis: -Werror
 
Sinon tu peux aussi regarder GDB et Valgrind, très utiles si on les maîtrise (un minimum).

Reply

Marsh Posté le 26-02-2023 à 15:28:27    

Shadoko a écrit :

D'accord, je suppose que c'est un warning qui vient avec le -Wall car je ne l'avais pas encore constaté jusque-là.


En fait c'est -Wmissing-declarations et il n'est pas dans -Wall ni dans -Wextra, c'est moi qui l'ai ajouté à mes flags  ;)

Reply

Marsh Posté le 26-02-2023 à 15:59:45    

Merci messieurs je me plonge là dedans actuellement :jap:


---------------
Pourquoi faire simple quand on peut faire compliqué ?
Reply

Marsh Posté le 28-02-2023 à 18:27:39    

Shadoko a écrit :

Ce qui est curieux c'est que dans les cas de crash, les deux threads avaient réussi à incrémenter à plusieurs répétitions le compteur sans bug jusqu'au crash.
Le premier unlock était donc passé, saurais-tu (ou à bon entendeur, salut) la raison de ce phénomène désormais réglé ?


Citation :

If a thread attempts to unlock a mutex that it has not locked or a mutex which is unlocked, undefined behavior results.


UB => opération illégale => aucune raison d'en attendre un comportement logique ou cohérent, ça va dépendre des détails d'implémentation du mutex et de ce que le compilo fait avec.

kajoux a écrit :

La raison est la manière d'utiliser la mémoire en informatique d'une manière générale, et c'est aussi la raison pour laquelle le comportement est dit "indéfini" (undefined behavior dans les manuels) dans ce cas.


Les UB c'est beaucoup plus brutal que ça, la présence d'un UB peut même casser le programme pendant sa compilation. En fait si UB il y a, le programme est considéré comme cassé par définition.


Message édité par masklinn le 28-02-2023 à 18:31:34

---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed