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

Traitement des signaux asynchrones

47 réponses
Avatar
JKB
Bonjour à tous,

J'ai un petit doute sur la gestion des signaux POSIX installés grâce
à sigaction(). J'ai accès à un certain nombre d'Unix dont Linux,
Tru64, Solaris, NetBSD, OpenBSD et FreeBSD. Et NetBSD me pose un
petit problème. Je n'arrive pas à savoir s'il s'agit d'un problème
dans mon code ou d'u truc qui m'aurait échappé.

Considérons le code suivant :

do
{
code_retour = nanosleep(&temporisation, &temporisation);
erreur = errno;

...
...
} while((code_retour == -1) && (erreur == EINTR));

Je simplifie. J'interromps nanosleep() grâce à un SIGINT (ctrl+C) et
je vois le gestionnaire se lancer (j'ai collé un printf() dans le
gestionnaire en question). Sous NetBSD, le gestionnaire de signal
est lancé dans un thread spécifique (et non le thread de
l'appelant). Ça ne semble pas être en contradiction avec les specs
POSIX. Mais nanosleep() sort avec un code d'erreur EINTR _avant_ que
le signal soit effectivement traité. Or dans la suite des
instructions de la boucle se trouve un test sur une variable volatile
et atomique positionnée par ce gestionnaire de signal. La plupart du
temps, ce test échoue car le gestionnaire de signal s'exécutant dans
un autre thread n'a pas encore positionné la variable à la bonne
valeur.

D'où une série de questions ;-)
1/ Est-ce un comportement attendu ou un bug de la gestion des
signaux de NetBSD ? Je n'observe ce comportement que sous ce
système.
2/ Comment s'assurer de façon propre que le gestionnaire de signal
a terminé ses traitement sans y coller un sem_post() (j'aimerais
éviter) ?

Merci de votre attention,

JKB

PS: Nicolas George, pas la peine de répondre, je t'ai plonké depuis
longtemps.

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr

7 réponses

1 2 3 4 5
Avatar
Bruno Ducrot
On 11-10-2012, JKB wrote:
Même pas... Parce que j'ai passé la journée d'hier à mettre sur
papier une machine à état qui fonctionnait partout. Le couple
sem_wait()/sem_post() arrive à des conditions bloquantes dans
certains cas. Je crois avoir trouvé un début de solution en lançant
le traitement du gestionnaire de signal dans un thread dédié (mais
pas le gestionnaire, sinon je ne peux pas interrompre les appels
systèmes lents) et en effectuant un raise() pour réveiller
l'appel système lent en fin de traitement du gestionnaire.

L'intérêt est de garder le même traitement quel que soit l'OS cible.
Plus de nouvelles lorsque j'aurai implanté la chose, donc pas avant
la semaine prochaine.




Voilà. Ca s'est la réponse classique d'un NetBSDiste qui refuse obstinément
les bons vieux gros hacks bien puant d'un FreeBSDiste ;)

--
Bruno Ducrot

A quoi ca sert que Ducrot hisse des carcasses ?
Avatar
JKB
Le 12 Oct 2012 10:52:10 GMT,
Bruno Ducrot écrivait :
On 11-10-2012, JKB wrote:
Même pas... Parce que j'ai passé la journée d'hier à mettre sur
papier une machine à état qui fonctionnait partout. Le couple
sem_wait()/sem_post() arrive à des conditions bloquantes dans
certains cas. Je crois avoir trouvé un début de solution en lançant
le traitement du gestionnaire de signal dans un thread dédié (mais
pas le gestionnaire, sinon je ne peux pas interrompre les appels
systèmes lents) et en effectuant un raise() pour réveiller
l'appel système lent en fin de traitement du gestionnaire.

L'intérêt est de garder le même traitement quel que soit l'OS cible.
Plus de nouvelles lorsque j'aurai implanté la chose, donc pas avant
la semaine prochaine.




Voilà. Ca s'est la réponse classique d'un NetBSDiste qui refuse obstinément
les bons vieux gros hacks bien puant d'un FreeBSDiste ;)



C'est surtout la réponse d'un type qui n'a pour indication de
machine cible que 'POSIX' et qui doit faire avec ça ;-)

J'ai déjà assez de hacks affreux pour MacOS X et d'autres trucs
prétendûment POSIX que si je pouvais éviter de rajouter des #if
__NetBSD__ partout, ça m'arrangerait... D'autant plus que le
programme en question est déjà horriblement complexe et que je ne me
vois pas débugguer plusieurs traitements sensiblement différents
dans le temps imparti :-(

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
Avatar
Lucas Levrel
Le 12 octobre 2012, JKB a écrit :

Ce que je ne vois pas (mais je dois rater un truc dans ton
raisonnement). Dans ton gestionnaire de signal, tu as donc un
tableau comportant une association entre un ID 'maison' et le TID.
Tu ne peux utiliser pthread_self(), mais comment connais-tu dans le
gestionnaire l'ID 'maison' ? Tu ne peux le connaître que grâce à une
variable globale au thread et ces fonctions ne sont pas async sygnal
safe.

Ou je n'ai pas compris...



Je crois voir où est l'incompréhension. J'en suis resté au SIGINT qui agit
de manière globale (indépendante du thread qui l'a capté). Mais il y a le
SIGUSR1 dont le gestionnaire ne doit agir que sur le thread qui l'a reçu.

--
LL
Avatar
Patrick Lamaizière
JKB :

Bonjour à tous,



'soir,

J'ai un petit doute sur la gestion des signaux POSIX installés grâce
à sigaction(). J'ai accès à un certain nombre d'Unix dont Linux,
Tru64, Solaris, NetBSD, OpenBSD et FreeBSD. Et NetBSD me pose un
petit problème. Je n'arrive pas à savoir s'il s'agit d'un problème
dans mon code ou d'u truc qui m'aurait échappé.

Considérons le code suivant :

do
{
code_retour = nanosleep(&temporisation, &temporisation);
erreur = errno;

...
...
} while((code_retour == -1) && (erreur == EINTR));



...

2/ Comment s'assurer de façon propre que le gestionnaire de signal
a terminé ses traitement sans y coller un sem_post() (j'aimerais
éviter) ?



Peut-être le coup du self-pipe ? Après le nanosleep s'il retourne un
-1, tu fais un select() sur un pipe utilisé en interne. À la fin du
gestionnaire de signal, tu écris un caractère dans le pipe et select()
rend alors la main. write est dans les fonctions "signal safe"

C'est juste une idée...
Avatar
JKB
Le Mon, 15 Oct 2012 00:32:50 +0200,
Patrick Lamaizière écrivait :
JKB :

Bonjour à tous,



'soir,

J'ai un petit doute sur la gestion des signaux POSIX installés grâce
à sigaction(). J'ai accès à un certain nombre d'Unix dont Linux,
Tru64, Solaris, NetBSD, OpenBSD et FreeBSD. Et NetBSD me pose un
petit problème. Je n'arrive pas à savoir s'il s'agit d'un problème
dans mon code ou d'u truc qui m'aurait échappé.

Considérons le code suivant :

do
{
code_retour = nanosleep(&temporisation, &temporisation);
erreur = errno;

...
...
} while((code_retour == -1) && (erreur == EINTR));



...

2/ Comment s'assurer de façon propre que le gestionnaire de signal
a terminé ses traitement sans y coller un sem_post() (j'aimerais
éviter) ?



Peut-être le coup du self-pipe ? Après le nanosleep s'il retourne un
-1, tu fais un select() sur un pipe utilisé en interne. À la fin du
gestionnaire de signal, tu écris un caractère dans le pipe et select()
rend alors la main. write est dans les fonctions "signal safe"

C'est juste une idée...



Et c'est un peu l'idée que j'ai utilisée. Reprenons les hypothèses de
départ, à savoir traiter SIGINT sur l'ensemble d'un processus
et SIGUSR1 par thread en s'affranchissant des appels système lents
qui doivent être relancés en cas de SIGUSR1 après un traitement
spécifique de polling (SA_RESTART ne convient pas ici).

Sous NetBSD, lorsque le programme est lié avec la libpthread, le
gestionnaire de signal se lance dans un thread dédié cassant le
synchronisme du code.

Plutôt que d'essayer de rendre l'appel du gestionnaire de signal
synchrone sous NetBSD, j'ai décidé de rendre cet appel asynchrone
sur tous les OS.

Je crée donc un thread de surveillance des signaux contenant le code
antérieurement dans le gestionnaire de signal. Le gestionnaire de
signal envoie de façon atomique le signal reçu (par un write dans
un pipe) à ce thread. Le thread de surveillance attend sur un poll()
puis effectue de façon totalement asynchrone son traitement. À la
fin de ce traitement, il envoie au processus (par un kill(getpid(),
signal) et non un raise()) un signal qui ne fait rien histoire de
débloquer un hypothétique appel système lent.

C'est le principe car dans les faits, il y a un thread spécifique
qui gère le dernier signal pour pouvoir interrompre le bon thread en
cas d'envoi d'un signal à un thread spécifique.

Pour ceux qui seraient intéressés, le code source se trouve ici :
http://www.rpl2.fr/cgi-bin/cvsweb/rpl/src/interruptions.c?annotate=1.106
à partir de la ligne 1624.

Je n'ai pas encore de réponse claire de l'équipe de NetBSD.

Cordialement,

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
Avatar
Lucas Levrel
Le 15 octobre 2012, JKB a écrit :

Pour ceux qui seraient intéressés, le code source se trouve ici :
http://www.rpl2.fr/cgi-bin/cvsweb/rpl/src/interruptions.c?annotate=1.106
à partir de la ligne 1624.



Les lignes 2504 et 2508 ne devraient-elles pas être interverties ?

--
LL
Avatar
JKB
Le Mon, 15 Oct 2012 14:38:32 +0200,
Lucas Levrel écrivait :
Le 15 octobre 2012, JKB a écrit :

Pour ceux qui seraient intéressés, le code source se trouve ici :
http://www.rpl2.fr/cgi-bin/cvsweb/rpl/src/interruptions.c?annotate=1.106
à partir de la ligne 1624.



Les lignes 2504 et 2508 ne devraient-elles pas être interverties ?



Euh... Si. Bien vu (et merci) !

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
1 2 3 4 5