Les time-out permettent de ne pas laisser un process bloqué en attente indéfiniment. Pour que le système fonctionne, il faut que le serveur déclare un handler pour le signal d'alarme. Par exemple:
#include <stdio.h> #include <signal.h> ... void my_handler(sig) int sig; { fprintf(stderr,"timeout\n"); } main() { int timeout = 4; signal(SIGALRM, my_handler); ... dec_sem_zero(semid, block, timeout); ... }
Les fonctions suivantes gèrent les time-out, ce sont:
wait_for_sem()
, dec_sem_zero()
, dec_sem()
, get_server_value()
, ask_and_init_shm()
, et
send_command()
.
Ces fonctions possèdent un ou deux arguments indiquant la ou les valeurs de time-out. Un time-out indique, en secondes (entières), le temps maximum que passe un client à attendre soit que le client est prêt, soit que le client finisse d'exécuter sa commande.
Par exemple, "dec_sem_zero()
" et "dec_sem()
" ont un time-out qui indique le temps d'attente pour qu'une ressource soit accessible, le time-out de "wait_for_sem()
" indique le temps d'attente pour la fin d'une exécution, et pour "get_server_value()
", "ask_and_init_shm()
" et "send_command()
" le premier time-out indique l'attente maximum pour la ressource et le deuxième le temps d'attente pour l'exécution.
Dans le dernier cas et en cas d'erreur, on ne sait pas si c'est le premier time-out qui a fonctionné ou le second.
Le problème est délicat lorsqu'un client est en attente de fin d'exécution et que survient un time-out, il ne sait pas si le serveur est mort ou ralenti (stoppé par exemple). Puisque dans un cas d'attente, c'est le client qui doit libérer le serveur (incrémentation du SEM0), il faut que cette opération soit exécutée seulement sous certaines conditions pour ne pas générer de situations illégales (SEMn>1).
S'il est mort, il n'y a pas de problème, on ne libère pas le serveur, car le serveur se réinitialisera correctement lors de sa remise en marche.
S'il n'est pas mort, il faut faire terminer le serveur pour qu'il se retrouve dans un état stable, prêt à accepter une nouvelle commande. Le meilleur moyen à disposition pour réaliser ceci est de lui envoyer un signal d'interruption (dans ce cas le serveur doit être capable de gérer ce signal) puis s'assurer que le client soit bien le client qui avait envoyé la commande (test de concordance des PIDs) et enfin, libérer le serveur uniquement si le SEM0 est bien à zéro. Cet ensemble de tests permet de gérer les interactions extérieures que pourrait avoir effectué l'utilisateur sur les sémaphores (reset par exemple).
Le code correspondant à l'interruption du client par un time-out est par exemple:
timeout=30.; if((stat=wait_for_sem(semid, 2, timeout))==-2){ if(kill(block->pid_server, 0)==0){ kill(block->pid_server, SIGINT); if(getpid()==block->pid_client){ if(get_cmd_sem(semid, 0, GETVAL)==0)inc_sem(semid, 0); } } break; }