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

Threads POSIX et free()

2 réponses
Avatar
JKB
Bonjour à tous,

J'ai un petit problème avec un code écrit en C et utilisant les
threads POSIX. Je poste ici parce que j'ai déjà vu le problème sous
Solaris (résolu il me semble avec l'utilisation de la libmtmalloc en
lieu et place des malloc() et free() de la libc). J'observe maintenant
quelque chose de semblable sous Linux et ça m'inquiète.

De temps en temps, j'ai un free() tout bête qui bloque dans une
section protégée par un mutex. J'avoue ne vois pas trop savoir pourquoi.
Comme le mutex est verrouillé, les autres threads attendent sa
libération qui n'intervient jamais puisque le programme reste bloqué
dans ce free().

Là, ça vient de bloquer dans le free(l_element_courant), juste avant
la libération du mutex. Celui-ci est déclaré de manière statique avec
la valeur PTHREAD_MUTEX_INITIALIZER. Les listes chaînées sont des
structures contenant deux champs, un void *donnee et un pointeur suivant
sur le maillon suivant.

void
retrait_thread_surveillance(struct_processus *s_etat_processus,
struct_argument_thread *s_argument_thread)
{
struct_liste_chainee *l_element_precedent;
struct_liste_chainee *l_element_courant;

if (pthread_mutex_lock(&mutex_liste_threads) != 0)
{
(*s_etat_processus).erreur_systeme = d_es_processus;
return;
}

l_element_precedent = NULL;
l_element_courant = liste_threads_surveillance;

while(l_element_courant != NULL)
{
if ((*l_element_courant).donnee == (void *) s_argument_thread)
{
break;
}

l_element_precedent = l_element_courant;
l_element_courant = (*l_element_courant).suivant;
}

if (l_element_courant == NULL)
{
pthread_mutex_unlock(&mutex_liste_threads);
(*s_etat_processus).erreur_systeme = d_es_processus;
return;
}

if (l_element_precedent == NULL)
{
liste_threads_surveillance = (*l_element_courant).suivant;
}
else
{
(*l_element_precedent).suivant = (*l_element_courant).suivant;
}

free(l_element_courant);

if (pthread_mutex_unlock(&mutex_liste_threads) != 0)
{
(*s_etat_processus).erreur_systeme = d_es_processus;
return;
}

return;
}

Quelques sorties de gdb :

(gdb) backtrace
#0 0x00007fff39384cfe in ?? () from /lib/libc.so.6
#1 0x00007fff3932264e in ?? () from /lib/libc.so.6
#2 0x00007fff3931eceb in free () from /lib/libc.so.6
#3 0x000000000056096a in retrait_thread_surveillance (
s_etat_processus=0x7fff43909b20, s_argument_thread=0x7fff2c007cb0)
at interruptions.conv.c:248
#4 0x00000000004630ed in surveillance_processus (argument=0x7fff2c007cb0)
at gestion_processus.conv.c:1664
#5 0x00007fff3a185faa in start_thread () from /lib/libpthread.so.0
#6 0x00007fff393792cd in clone () from /lib/libc.so.6

Il va sans dire que j'ai vérifié la validité du pointeur (qui ne
peut être libéré que dans cette routine, c'est facile à vérifier,
d'autant plus que la racine de la liste est déclarée statique à ce
fichier). Rien n'est relevé par valgrind. Les valeurs des pointeurs
sont :

(gdb) print l_element_courant
$1 = (struct_liste_chainee *) 0x7fff2405cf80
(gdb) print *l_element_courant
$2 = {suivant = 0xecf210, donnee = 0x7fff2c007cb0}
(gdb) print s_argument_thread
$3 = (struct_argument_thread *) 0x7fff2c007cb0
(gdb)

ce qui n'est pas aberrant. La question de fond est donc : qu'est-ce qui
pourrait bien faire qu'un free() bloque ? Si le pointeur passé en
argument était invalide, à la rigueur, je comprendrais. Mais là, le
free() bloque sur un pointeur parfaitement valide !

Je prends tout début d'explication. Est-ce que la définition de
_REENTRANT pourrait corriger le problème ? Je n'ai rien trouvé dans les
fichiers d'en-tête qui pourrait me le faire croire et l'erreur en
question est _vraiment_ difficile à reproduire...

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.

2 réponses

Avatar
Xavier Roche
JKB a écrit :
Je prends tout début d'explication. Est-ce que la définition de
_REENTRANT pourrait corriger le problème ? Je n'ai rien trouvé dans les



Toujours utiliser -D_REENTRANT en multithreadé ; cela impacte un certain
nombre de fonctions qui sinon ne sont pas thread-safe (tout ce qui
nécessite des variables de thread, comme gmtime() par exemple, ou errno)

<http://pauillac.inria.fr/~xleroy/linuxthreads/faq.html#H>
Avatar
JKB
Le 27-03-2009, ? propos de
Re: Threads POSIX et free(),
Xavier Roche ?crivait dans fr.comp.lang.c :
JKB a écrit :
Je prends tout début d'explication. Est-ce que la définition de
_REENTRANT pourrait corriger le problème ? Je n'ai rien trouvé dans les



Toujours utiliser -D_REENTRANT en multithreadé ; cela impacte un certain
nombre de fonctions qui sinon ne sont pas thread-safe (tout ce qui
nécessite des variables de thread, comme gmtime() par exemple, ou errno)

<http://pauillac.inria.fr/~xleroy/linuxthreads/faq.html#H>



Merci pour l'explication, mais il me semblait que c'était
automatique avec les dernières version de gcc. Je vais donc essayer avec
ça. Néanmoins, je viens de passer une bonne partie de la journée à
regarder ce que fait free(). Le truc bloque toujours aléatoirement sur
le pthread_mutex_lock() interne à la fonction free() de la glibc.
Pourtant, je fais tourne en parallèle ce code depuis quelques heures
sous valgrind sans que celui-ci ne râle sur une corruption mémoire (je
n'en ai pas trouvé non plus à la main...). Je continue la recherche et
si je trouve, je vous tiendrai au courant ;-)

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.