Extension d'un noyau de processus légers
Etape 3 - Echéancier
1. Que veut-on fournir
Une gestion d'échéancier devient nécessaire des que
l'on souhaite décider dynamiquement d'actions à faire à
des dates quelquonques dans le futur. On peut définir une échéance
comme un triplet : <date, action, parametres d'action>. On souhaite
ici réaliser un module Echéancier, qui permet d'enregistrer
un ensemble d'actions, à exécuter à des dates données.
Ce module assure que deux actions associées à des dates identiques
seront exécutées dans l'ordre dans lequel elles ont été
enregistrées.
2. Interface à réaliser
Les interfaces qui devront être fournies sont les suivantes.
Interface du module Echéancier
/* Create a new event*/
Event * newEvent (
int date,
void (*action) (void *),
void (* param)); /* the effective parameter
for the action */
/* Delete an event */
void killEvent (Event * evt);
/* Initialize the echeancier */
void initEcheancier(..);
/* Free the echeancier */
void freeEcheancier();
3. Principes de mise en oeuvre
On souhaite démultiplexer les ressources de gestion du temps fournies
par le noyau pour pouvoir gérer N échéanciers
avec un seul compte à rebours. On utilisera donc un compteur
à rebours particulier pour décompter le délai entre
le moment ou une échéance a été créée
et le moment ou se termine l'échéance. Ce délai est
la différence entre la date courante au moment de la définition
de l'échéance, et la date d'échéance.
Dans tous les cas, il faudra assurer que ce compteur sonnera
lorsque l'échéance la plus proche arrive. Lorsqu'une échéance
arrive, alorsi toutes les actions associées à l'échéance
seront exécutées.
On prendra soin de bien gérer l'ajout d'un événement
dont la date d'occurrence est antérieure à la date de l'échéance
la plus proche. De même que pour la suppression de l'échéance
la plus proche, il faudra :
-
arrêter le compte à rebours de l'échéancier
-
recalculer le nouveau délai d'attente
-
relancer le compte à rebours de l'échéancier
Contraintes de réalisation
On fixe un certain nombre de contraintes sur la réalisation du module
Echéancier. La structure de données principale gérée
par l'échéancier est une liste doublement chainée
d'échéances, une échéance étant elle-même
composée d'une date et d'une liste d'actions à exécuter.
La liste doublement chainée d'échéances doit en outre
être ordonnée par date croissante des échéances.
<figure>
Les types des principales structures de données sont les suivants.
/* Echeancier's cellule */
typedef struct Dt_st {
int date;
Dt_st *next, *pred;
Event *event;
/* queue of actions */
} DueTime;
/* List of actions */
typedef struct Evt_st {
Evt_st *next, *pred;
DueTime *dt;
void (*action) (void *);
void *param;
} Event;
Contraintes relatives à la synchronisation
On prendra soin de rendre les fonctions atomiques des lors que celles-ci
manipulent des structures de données critiques du noyau. Etant donné
que l'échéancier fait appel aux fonctions du module TimeService,
il faudra faire attention au démasquage systématique arrivant
en fin d'exécution des fonctions du TimeService. On rappelle en
effet que les primitives Mask et UnMask, telles qu'implémentées
dans la version courante du noyau, ne permettent pas de réaliser
des Mask et UnMask imbriqués.
4. Suspension temporisée
Afin de valider en partie le travail réalisé, on demande
d'utiliser l'échéancier pour offrir deux primitives de suspension
temporisée, utilisant les sémaphores fournies dans la version
courante du noyau. Ces primitives sont :
-
ThreadDelay (int delay), qui bloque le processus courant pendant un certain
délai
-
ThreadWait (int date), qui bloque le processus courant jusqu'à une
certaine date.
5. Travail demandé
Il est demandé de :
-
Réaliser le module Echéancier
-
Tester le module, en réalisant des jeux de tests mettant en jeu
des exécutions concurrentes de toutes les fonctions réalisées.
Ces jeux de tests pourront être demandés lors de la démonstration
finale.
-
Réaliser les primitives de suspension temporisées, et les
tester.