Comprendre la classe DelayQueue dans Live555

Cet article explore la classe DelayQueue de la bibliothèque Live555, un composant essentiel pour la gestion des événements retardés. Il s'agit du premier d'une série d'naalyses du code source de Live555, se concentrant sur les classes de composants fondamentales.

Vue d'ensemble de la classe DelayQueue

La DelayQueue implémente une file d'attente d'événements basée sur une liste chaînée doublement liée. Bien que la structure interne ne soit pas excessivement complexe, la compréhension de son approche de gestion du temps est cruciale. La classe DelayQueue hérite de DelayQueueEntry. Sa conception est telle que l'instance de DelayQueue elle-même agit comme le nœud sentinelle (ou tête de liste) de la file d'attente. Les autres nœuds de la liste sont des instances de DelayQueueEntry (ou de ses sous-classes, comme AlarmHandler).

Un membre important est fLastSyncTime, qui enregistre le moment de la dernière synchronisation de la file d'attente. La manière dont les délais sont calculés est cumulative : le temps de délai réel d'un nœud est la somme de son propre champ fDeltaTimeRemaining et des champs fDeltaTimeRemaining de tous les nœuds précédents dans la chaîne.

Définition de la classe DelayQueue


 class DelayQueue: public DelayQueueEntry {
 public:
   DelayQueue();
   virtual ~DelayQueue();

   void addEntry(DelayQueueEntry* newEntry);
   void updateEntry(DelayQueueEntry* entry, DelayInterval newDelay);
   void updateEntry(intptr_t tokenToFind, DelayInterval newDelay);
   void removeEntry(DelayQueueEntry* entry);
   DelayQueueEntry* removeEntry(intptr_t tokenToFind);

   DelayInterval const& timeToNextAlarm();
   void handleAlarm();

 private:
   DelayQueueEntry* head() { return fNext; }
   DelayQueueEntry* findEntryByToken(intptr_t token);
   void synchronize();
   EventTime fLastSyncTime;
 };
 

Constructeur et Destructeur

Le constructeur initialise le nœud sentinelle avec un délai infini (ETERNITY) et enregistre l'heure actuelle comme fLastSyncTime. ```

DelayQueue::DelayQueue() : DelayQueueEntry(ETERNITY) { fLastSyncTime = TimeNow(); }


 Le destructeur est responsable de la libération de tous les nœuds de la file d'attente, en parcourant la liste et en supprimant chaque entrée. ```

  DelayQueue::~DelayQueue() {
    while (fNext != this) {
      DelayQueueEntry* entryToRemove = fNext;
      removeEntry(entryToRemove);
      delete entryToRemove;
    }
  }
  

Méthode removeEntry

Cette méthode retire un nœud de la liste chaînée. Une caractéristique clé est la mise à jour du champ fDeltaTimeRemaining du nœud suivant : entry->fNext->fDeltaTimeRemaining += entry->fDeltaTimeRemaining;. Cela maintient la nature cumulative des délais. ```

void DelayQueue::removeEntry(DelayQueueEntry* entry) { if (entry == NULL || entry->fNext == NULL) return;

entry->fNext->fDeltaTimeRemaining += entry->fDeltaTimeRemaining;
entry->fPrev->fNext = entry->fNext;
entry->fNext->fPrev = entry->fPrev;
entry->fNext = entry->fPrev = NULL;

}


 Une version surchargée existe pour supprimer un nœud par son token. ### Méthode findEntryByToken

 Parcourt la liste pour trouver un `DelayQueueEntry` correspondant à un token donné. Renvoie un pointeur vers le nœud trouvé ou `NULL` s'il n'est pas trouvé. ```

  DelayQueueEntry* DelayQueue::findEntryByToken(intptr_t tokenToFind) {
    DelayQueueEntry* cur = head();
    while (cur != this) {
      if (cur->token() == tokenToFind) return cur;
      cur = cur->fNext;
    }
    return NULL;
  }
  

Méthode synchronize

C'est une méthode interne essentielle pour mettre à jour les délais restants de chaque nœud en fonction du temps écoulé depuis la dernière synchronisation. 1. Elle calcule le temps écoulé depuis fLastSyncTime. 2. Elle parcourt la liste à partir du nœud suivant le sentinelle. 3. Pour chaque nœud, elle vérifie si son temps de délai restant est inférieur au temps écoulé. Si c'est le cas, le délai du nœud est mis à zéro et le temps écoulé est réduit de ce délai. 4. Le délai restant du nœud actuel est ensuite ajusté en soustrayant le temps écoulé restant. 5. fLastSyncTime est mis à jour avec l'heure actuelle.


 void DelayQueue::synchronize() {
   EventTime timeNow = TimeNow();
   if (timeNow < fLastSyncTime) {
     fLastSyncTime = timeNow;
     return;
   }
   DelayInterval timeSinceLastSync = timeNow - fLastSyncTime;
   fLastSyncTime = timeNow;

   DelayQueueEntry* curEntry = head();
   while (timeSinceLastSync >= curEntry->fDeltaTimeRemaining) {
     timeSinceLastSync -= curEntry->fDeltaTimeRemaining;
     curEntry->fDeltaTimeRemaining = DELAY_ZERO;
     curEntry = curEntry->fNext;
   }
   curEntry->fDeltaTimeRemaining -= timeSinceLastSync;
 }
 

Méthode addEntry

Cette méthode insère un nouveau nœud DelayQueueEntry dans la file d'attente. Avant l'insertion, elle appelle synchronize() pour mettre à jour les délais. Elle parcourt ensuite la liste pour trouver la position corrcete où le nouveau délai du nœud doit être inséré, en ajustant les champs fDeltaTimeRemaining des nœuds existants pour maintenir la chaîne de délais. ```

void DelayQueue::addEntry(DelayQueueEntry* newEntry) { synchronize();

DelayQueueEntry* cur = head();
while (newEntry->fDeltaTimeRemaining >= cur->fDeltaTimeRemaining) {
  newEntry->fDeltaTimeRemaining -= cur->fDeltaTimeRemaining;
  cur = cur->fNext;
}

cur->fDeltaTimeRemaining -= newEntry->fDeltaTimeRemaining;

newEntry->fNext = cur;
newEntry->fPrev = cur->fPrev;
cur->fPrev = newEntry->fPrev->fNext = newEntry;

}


### Méthode updateEntry

 Permet de modifier le délai restant d'un nœud existant. Elle retire le nœud, met à jour son délai, puis le réinsère dans la file d'attente à la nouvelle position calculée. ```

  void DelayQueue::updateEntry(DelayQueueEntry* entry, DelayInterval newDelay) {
    if (entry == NULL) return;

    removeEntry(entry);
    entry->fDeltaTimeRemaining = newDelay;
    addEntry(entry);
  }
  

Une version surchargée permet la mise à jour par token. ### Méthode timeToNextAlarm

Retourne le délai restant jusqu'au prochain événement (c'est-à-dire le fDeltaTimeRemaining du premier nœud réel dans la file d'attente). Elle appelle synchronize() si nécessaire pour garantir que le délai retourné est à jour. ```

DelayInterval const& DelayQueue::timeToNextAlarm() { if (head()->fDeltaTimeRemaining == DELAY_ZERO) return DELAY_ZERO;

synchronize();
return head()->fDeltaTimeRemaining;

}


### Méthode handleAlarm

 Cette méthode traite le prochain événement en attente. Si le délai restant du premier nœud est zéro (ou après synchronisation s'il ne l'est pas), le nœud est retiré de la file d'attente et sa méthode `handleTimeout()` est appelée. C'est ainsi que les actions associées aux événements retardés sont déclenchées. ```

  void DelayQueue::handleAlarm() {
    if (head()->fDeltaTimeRemaining != DELAY_ZERO) synchronize();

    if (head()->fDeltaTimeRemaining == DELAY_ZERO) {
      DelayQueueEntry* toRemove = head();
      removeEntry(toRemove);

      toRemove->handleTimeout();
    }
  }
  

Étiquettes: Live555 C++ file d'attente gestion du temps Programmation concurrente

Publié le 17 août à 20h27