Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

[C/Unix] sem_wait()

13 réponses
Avatar
JKB
Bonjour à tous,

Je suis en train de chercher un bug aléatoire dans un code
multithreadé. Je suis convaincu qu'il s'agit d'un problème de timing.
En débugant, je viens de tomber sur un truc un peu bizarre dans le code
suivant. J'avoue ne pas trop comprendre.

Variables :

- semaphore est un entier;
- semaphore_liste_threads est un semaphore (variable globale);
- semaphore_fork_processus_courant est une clef renvoyant un semaphore
local au thread;
- BUG est une macro dont le but est d'envoyer au processus un SIGBUS
si le premier argument est non nul (au passage, avant le SIGBUS, elle
exécute le second argument).

if (semaphore == 1)
{
while(sem_wait(&semaphore_liste_threads) == -1)
{
if (errno != EINTR)
{
pthread_sigmask(SIG_SETMASK, &oldset, NULL);

while(sem_wait((sem_t *) pthread_getspecific(
semaphore_fork_processus_courant)) == -1)
{
if (errno != EINTR)
{
BUG(1, printf("Lock error !\n")); // (*)
return;
}
}

BUG(1, printf("Lock error !\n"));
return;
}
}
}

Mon programme vient de planter sur un SIGBUS à la ligne (*).
Admettons. Ce qui me pose problème, c'est la valeur de errno :
4063235, qui ne correspond à aucune erreur renvoyée normalement par
sem_wait(). Si je suis l'exécution du code, pour planter là où il
plante, il a déjà fallu que la valeur de errno ne soit pas EINTR après
le premier sem_wait(). Or ce sémaphore est valide au moment où j'appelle
sem_wait().

Je n'ai _jamais_ vu le problème avec l'option -O2 de gcc. Cela
apparaît avec -O3, mais je ne sais pas si le fait de ne pas le voir
signifie qu'il s'agit d'un bug de gcc. Personnellement, je préférerais
pouvoir garder l'option de compilation -O3 car le programme en question
effectue 2E6 cycles par seconde avec -O3 contre 1.7E6 avec -O2.

J'essaye actuellement de reproduire le même fonctionnement sur une
sparc (Linux, même compilo) et jusqu'à présent, je n'y suis pas arrivé.
J'ai essayé sous Solaris 10/Sparc sans voir ce problème.

Le gcc en question est

cauchy:[~] > gcc -v
Using built-in specs.
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.3.3-14'
--with-bugurl=file:///usr/share/doc/gcc-4.3/README.Bugs
--enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr
--enable-shared --enable-multiarch --enable-linker-build-id
--with-system-zlib --libexecdir=/usr/lib --without-included-gettext
--enable-threads=posix --enable-nls
--with-gxx-include-dir=/usr/include/c++/4.3 --program-suffix=-4.3
--enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc
--enable-mpfr --with-tune=generic --enable-checking=release
--build=x86_64-linux-gnu --host=x86_64-linux-gnu
--target=x86_64-linux-gnu
Thread model: posix
gcc version 4.3.3 (Debian 4.3.3-14)
cauchy:[~] >

Une idée ?

Cordialement,

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.

10 réponses

1 2
Avatar
Richard Delorme
Le 05/08/2009 17:45, JKB a écrit :

[...]
while(sem_wait((sem_t *) pthread_getspecific(
semaphore_fork_processus_courant)) == -1)



pthread_specific peut retourner NULL en cas d'erreur. Peut-être
serait-il bon de séparer cette ligne en deux et de tester la validité de
la valeur retourner par pthread_getspecific ?

--
Richard
Avatar
JKB
Le 05-08-2009, ? propos de
Re: [C/Unix] sem_wait(),
Richard Delorme ?crivait dans fr.comp.lang.c :
Le 05/08/2009 17:45, JKB a écrit :

[...]
while(sem_wait((sem_t *) pthread_getspecific(
semaphore_fork_processus_courant)) == -1)



pthread_specific peut retourner NULL en cas d'erreur. Peut-être
serait-il bon de séparer cette ligne en deux et de tester la validité de
la valeur retourner par pthread_getspecific ?



Bien vu, je l'avais raté, celle-là. Mais ça n'explique pas pourquoi
le premier errno a une valeur différente de EINTR sur un argument
valide. Je suis en train de tester avec gcc-4.4.0 le même code.

Cordialement,

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
Avatar
Richard Delorme
Le 05/08/2009 18:02, JKB a écrit :
Le 05-08-2009, ? propos de
Re: [C/Unix] sem_wait(),
Richard Delorme ?crivait dans fr.comp.lang.c :
Le 05/08/2009 17:45, JKB a écrit :

[...]
while(sem_wait((sem_t *) pthread_getspecific(
semaphore_fork_processus_courant)) == -1)


pthread_specific peut retourner NULL en cas d'erreur. Peut-être
serait-il bon de séparer cette ligne en deux et de tester la validité de
la valeur retourner par pthread_getspecific ?



Bien vu, je l'avais raté, celle-là. Mais ça n'explique pas pourquoi
le premier errno a une valeur différente de EINTR sur un argument
valide.



Oui, mais la valeur que tu indiques me semble bizarre. Les valeurs de
errno sont habituellement petites (chez moi EINTR == 4 et EINVAL == 22).
On dirait que la mémoire (propre au thread) qui contient errno a été
corrompue.

> Je suis en train de tester avec gcc-4.4.0 le même code.

Ce n'est pas plutôt un problème de glibc ?

--
Richard
Avatar
JKB
Le 05-08-2009, ? propos de
Re: [C/Unix] sem_wait(),
Richard Delorme ?crivait dans fr.comp.lang.c :
Le 05/08/2009 18:02, JKB a écrit :
Le 05-08-2009, ? propos de
Re: [C/Unix] sem_wait(),
Richard Delorme ?crivait dans fr.comp.lang.c :
Le 05/08/2009 17:45, JKB a écrit :

[...]
while(sem_wait((sem_t *) pthread_getspecific(
semaphore_fork_processus_courant)) == -1)


pthread_specific peut retourner NULL en cas d'erreur. Peut-être
serait-il bon de séparer cette ligne en deux et de tester la validité de
la valeur retourner par pthread_getspecific ?



Bien vu, je l'avais raté, celle-là. Mais ça n'explique pas pourquoi
le premier errno a une valeur différente de EINTR sur un argument
valide.



Oui, mais la valeur que tu indiques me semble bizarre. Les valeurs de
errno sont habituellement petites (chez moi EINTR == 4 et EINVAL == 22).
On dirait que la mémoire (propre au thread) qui contient errno a été
corrompue.



J'ai essayé avec un MALLOC_CHECK_=3 et avec valgrind. Ça n'a
strictement rien levé comme problème. Tout ce que je verrais encore
comme explication, c'est un problème de concurrence de threads.

> Je suis en train de tester avec gcc-4.4.0 le même code.

Ce n'est pas plutôt un problème de glibc ?



Je n'en sais rien. Tout ce dont je suis sûr, c'est qu'avec -O2, je
n'ai jamais vu le problème. Avec gcc-4.4.0 et l'option -O3, mon processus
tourne depuis 275 minutes sans observer ce comportement (alors
qu'auparavant, il plantait très rapidement, soit dans un mutex interne à
un free(), soit dans le cas rapporté dans le message initial). J'ai vu un
certain nombre de rapports de bugs sur -O3 et volatile pour gcc-4.3,
mais rien touchant spécialement mon observation.

J'ai réussi à faire un exemple minimal que je viens de tester sur
sparc/linux avec le même compilateur (gcc-4.3.1) et ça merdoie aussi. Ce
n'est donc pas spécifique à l'architecture.

Il y a autre chose qui me chagrine : dans la macro BUG(), j'effectue
un raise() du signal SIGBUS. Celui-ci n'est pas traité ! Lorsque le
processus s'arrête, un gdb -d pid me montre que le processus est bloqué
sur la macro BUG définie comme :

#define BUG(b, ...)
do { if (b) { printf("[%d] BUG! <%s()> at line #%d of %sn",
(int) getpid(), __FUNCTION__, __LINE__, __FILE__);
__VA_ARGS__; fflush(stdout);
pthread_kill(pthread_self(), SIGBUS); }} while(0)

C'est d'autant plus bizarre que rien n'est affiché. Soit le truc bloque
dans le printf(), soit il est bufferisé et le fflush() ne donne rien.

Cordialement,

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
Avatar
JKB
Le 05-08-2009, ? propos de
Re: [C/Unix] sem_wait(),
JKB ?crivait dans fr.comp.lang.c :
Le 05-08-2009, ? propos de
Re: [C/Unix] sem_wait(),
Richard Delorme ?crivait dans fr.comp.lang.c :
Ce n'est pas plutôt un problème de glibc ?



Je n'en sais rien. Tout ce dont je suis sûr, c'est qu'avec -O2, je
n'ai jamais vu le problème. Avec gcc-4.4.0 et l'option -O3, mon processus
tourne depuis 275 minutes sans observer ce comportement (alors
qu'auparavant, il plantait très rapidement, soit dans un mutex interne à
un free(), soit dans le cas rapporté dans le message initial). J'ai vu un
certain nombre de rapports de bugs sur -O3 et volatile pour gcc-4.3,
mais rien touchant spécialement mon observation.

J'ai réussi à faire un exemple minimal que je viens de tester sur
sparc/linux avec le même compilateur (gcc-4.3.1) et ça merdoie aussi. Ce
n'est donc pas spécifique à l'architecture.

Il y a autre chose qui me chagrine : dans la macro BUG(), j'effectue
un raise() du signal SIGBUS. Celui-ci n'est pas traité ! Lorsque le
processus s'arrête, un gdb -d pid me montre que le processus est bloqué
sur la macro BUG définie comme :

#define BUG(b, ...)
do { if (b) { printf("[%d] BUG! <%s()> at line #%d of %sn",
(int) getpid(), __FUNCTION__, __LINE__, __FILE__);
__VA_ARGS__; fflush(stdout);
pthread_kill(pthread_self(), SIGBUS); }} while(0)

C'est d'autant plus bizarre que rien n'est affiché. Soit le truc bloque
dans le printf(), soit il est bufferisé et le fflush() ne donne rien.



Quelques nouvelles.

Mon exemple minimal a lancé 150 000 threads sur linux/sparc hier
soir sans montrer de dysfonctionnement avec gcc-4.4.1 -O3. Avec
gcc-4.3.3 -O3, ça plantait assez vite.

Le programme complet fonctionne bien sur linux/amd64 avec gcc-4.4.0
-O3. Il plante aussi assez vite avec gcc-4.3.3 -O3. La libc étant la
même sur les deux systèmes, la seule différence est la version mineure
du compilo. Je pense donc pouvoir dire que le problème vient d'une
option d'optimisation qui fait des choses 'bizarres' dans le dos de
l'utilisateur...

Cordialement,

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
Avatar
JKB
Le 06-08-2009, ? propos de
Re: [C/Unix] sem_wait(),
JKB ?crivait dans fr.comp.lang.c :
Le 05-08-2009, ? propos de
Re: [C/Unix] sem_wait(),
JKB ?crivait dans fr.comp.lang.c :
Le 05-08-2009, ? propos de
Re: [C/Unix] sem_wait(),
Richard Delorme ?crivait dans fr.comp.lang.c :
Ce n'est pas plutôt un problème de glibc ?



Je n'en sais rien. Tout ce dont je suis sûr, c'est qu'avec -O2, je
n'ai jamais vu le problème. Avec gcc-4.4.0 et l'option -O3, mon processus
tourne depuis 275 minutes sans observer ce comportement (alors
qu'auparavant, il plantait très rapidement, soit dans un mutex interne à
un free(), soit dans le cas rapporté dans le message initial). J'ai vu un
certain nombre de rapports de bugs sur -O3 et volatile pour gcc-4.3,
mais rien touchant spécialement mon observation.

J'ai réussi à faire un exemple minimal que je viens de tester sur
sparc/linux avec le même compilateur (gcc-4.3.1) et ça merdoie aussi. Ce
n'est donc pas spécifique à l'architecture.

Il y a autre chose qui me chagrine : dans la macro BUG(), j'effectue
un raise() du signal SIGBUS. Celui-ci n'est pas traité ! Lorsque le
processus s'arrête, un gdb -d pid me montre que le processus est bloqué
sur la macro BUG définie comme :

#define BUG(b, ...)
do { if (b) { printf("[%d] BUG! <%s()> at line #%d of %sn",
(int) getpid(), __FUNCTION__, __LINE__, __FILE__);
__VA_ARGS__; fflush(stdout);
pthread_kill(pthread_self(), SIGBUS); }} while(0)

C'est d'autant plus bizarre que rien n'est affiché. Soit le truc bloque
dans le printf(), soit il est bufferisé et le fflush() ne donne rien.



Quelques nouvelles.

Mon exemple minimal a lancé 150 000 threads sur linux/sparc hier
soir sans montrer de dysfonctionnement avec gcc-4.4.1 -O3. Avec
gcc-4.3.3 -O3, ça plantait assez vite.

Le programme complet fonctionne bien sur linux/amd64 avec gcc-4.4.0
-O3. Il plante aussi assez vite avec gcc-4.3.3 -O3. La libc étant la
même sur les deux systèmes, la seule différence est la version mineure
du compilo. Je pense donc pouvoir dire que le problème vient d'une
option d'optimisation qui fait des choses 'bizarres' dans le dos de
l'utilisateur...



Mauvaise nouvelle : le problème survient aussi avec gcc-4.4.1 mais
beaucoup plus rarement... Je continue d'investiguer.

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
Avatar
Mickaël Wolff
JKB wrote:

Le programme complet fonctionne bien sur linux/amd64 avec gcc-4.4.0
-O3. Il plante aussi assez vite avec gcc-4.3.3 -O3. La libc étant la
même sur les deux systèmes, la seule différence est la version mineure
du compilo. Je pense donc pouvoir dire que le problème vient d'une
option d'optimisation qui fait des choses 'bizarres' dans le dos de
l'utilisateur...



Pour printf, je parie sur ça :
http://www.gnu.org/software/libc/manual/html_node/Streams-and-Threads.html#Streams-and-Threads

--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
Avatar
JKB
Le 07-08-2009, ? propos de
Re: [C/Unix] sem_wait(),
Mickaël Wolff ?crivait dans fr.comp.lang.c :
JKB wrote:

Le programme complet fonctionne bien sur linux/amd64 avec gcc-4.4.0
-O3. Il plante aussi assez vite avec gcc-4.3.3 -O3. La libc étant la
même sur les deux systèmes, la seule différence est la version mineure
du compilo. Je pense donc pouvoir dire que le problème vient d'une
option d'optimisation qui fait des choses 'bizarres' dans le dos de
l'utilisateur...



Pour printf, je parie sur ça :
http://www.gnu.org/software/libc/manual/html_node/Streams-and-Threads.html#Streams-and-Threads



Raté ;-) Les printf() sont dans le code originel protégés par des
sémaphores. Pour faire simple, le programme crée un sémaphore nommé
utilisé par tous les processus et threads de mon programme de calcul. Ça
me permet de gérer de façon transparente les problèmes d'écriture et de
lecture sur les terminaux.

Cordialement,

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
Avatar
Mickaël Wolff
JKB wrote:

Raté ;-) Les printf() sont dans le code originel protégés par des
sémaphores. Pour faire simple, le programme crée un sémaphore nommé
utilisé par tous les processus et threads de mon programme de calcul. Ça
me permet de gérer de façon transparente les problèmes d'écriture et de
lecture sur les terminaux.



Essayes quand meme d'utiliser les semaphores fournis par la
bibliothèque pour vérifier que ce ne sont pas les tiens qui déconnent.

Au fait, est-ce que tu link avec pthread ?
--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
Avatar
JKB
Le 07-08-2009, ? propos de
Re: [C/Unix] sem_wait(),
Mickaël Wolff ?crivait dans fr.comp.lang.c :
JKB wrote:

Raté ;-) Les printf() sont dans le code originel protégés par des
sémaphores. Pour faire simple, le programme crée un sémaphore nommé
utilisé par tous les processus et threads de mon programme de calcul. Ça
me permet de gérer de façon transparente les problèmes d'écriture et de
lecture sur les terminaux.



Essayes quand meme d'utiliser les semaphores fournis par la
bibliothèque pour vérifier que ce ne sont pas les tiens qui déconnent.

Au fait, est-ce que tu link avec pthread ?



Naturellement.

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
1 2