tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

pralarm.c (7777B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "primpl.h"
      7 
      8 /**********************************************************************/
      9 /******************************* PRALARM ******************************/
     10 /**********************************************************************/
     11 
     12 #include "obsolete/pralarm.h"
     13 
     14 struct PRAlarmID {            /* typedef'd in pralarm.h       */
     15  PRCList list;               /* circular list linkage        */
     16  PRAlarm* alarm;             /* back pointer to owning alarm */
     17  PRPeriodicAlarmFn function; /* function to call for notify  */
     18  void* clientData;           /* opaque client context        */
     19  PRIntervalTime period;      /* the client defined period    */
     20  PRUint32 rate;              /* rate of notification         */
     21 
     22  PRUint32 accumulator;      /* keeps track of # notifies    */
     23  PRIntervalTime epoch;      /* when timer was started       */
     24  PRIntervalTime nextNotify; /* when we'll next do our thing */
     25  PRIntervalTime lastNotify; /* when we last did our thing   */
     26 };
     27 
     28 typedef enum { alarm_active, alarm_inactive } _AlarmState;
     29 
     30 struct PRAlarm {      /* typedef'd in pralarm.h       */
     31  PRCList timers;     /* base of alarm ids list       */
     32  PRLock* lock;       /* lock used to protect data    */
     33  PRCondVar* cond;    /* condition that used to wait  */
     34  PRThread* notifier; /* thread to deliver notifies   */
     35  PRAlarmID* current; /* current alarm being served   */
     36  _AlarmState state;  /* used to delete the alarm     */
     37 };
     38 
     39 static PRAlarmID* pr_getNextAlarm(PRAlarm* alarm, PRAlarmID* id) {
     40  /*
     41   * Puts 'id' back into the sorted list iff it's not NULL.
     42   * Removes the first element from the list and returns it (or NULL).
     43   * List is "assumed" to be short.
     44   *
     45   * NB: Caller is providing locking
     46   */
     47  PRCList* timer;
     48  PRAlarmID* result = id;
     49  PRIntervalTime now = PR_IntervalNow();
     50 
     51  if (!PR_CLIST_IS_EMPTY(&alarm->timers)) {
     52    if (id != NULL) /* have to put this id back in */
     53    {
     54      PRIntervalTime idDelta = now - id->nextNotify;
     55      timer = alarm->timers.next;
     56      do {
     57        result = (PRAlarmID*)timer;
     58        if ((PRIntervalTime)(now - result->nextNotify) > idDelta) {
     59          PR_INSERT_BEFORE(&id->list, &alarm->timers);
     60          break;
     61        }
     62        timer = timer->next;
     63      } while (timer != &alarm->timers);
     64    }
     65    result = (PRAlarmID*)(timer = PR_LIST_HEAD(&alarm->timers));
     66    PR_REMOVE_LINK(timer); /* remove it from the list */
     67  }
     68 
     69  return result;
     70 } /* pr_getNextAlarm */
     71 
     72 static PRIntervalTime pr_PredictNextNotifyTime(PRAlarmID* id) {
     73  PRIntervalTime delta;
     74  PRFloat64 baseRate = (PRFloat64)id->period / (PRFloat64)id->rate;
     75  PRFloat64 offsetFromEpoch = (PRFloat64)id->accumulator * baseRate;
     76 
     77  id->accumulator += 1;            /* every call advances to next period */
     78  id->lastNotify = id->nextNotify; /* just keeping track of things */
     79  id->nextNotify = (PRIntervalTime)(offsetFromEpoch + 0.5);
     80 
     81  delta = id->nextNotify - id->lastNotify;
     82  return delta;
     83 } /* pr_PredictNextNotifyTime */
     84 
     85 static void PR_CALLBACK pr_alarmNotifier(void* arg) {
     86  /*
     87   * This is the root of the notifier thread. There is one such thread
     88   * for each PRAlarm. It may service an arbitrary (though assumed to be
     89   * small) number of alarms using the same thread and structure. It
     90   * continues to run until the alarm is destroyed.
     91   */
     92  PRAlarmID* id = NULL;
     93  PRAlarm* alarm = (PRAlarm*)arg;
     94  enum { notify, abort, scan } why = scan;
     95 
     96  while (why != abort) {
     97    PRIntervalTime pause;
     98 
     99    PR_Lock(alarm->lock);
    100    while (why == scan) {
    101      alarm->current = NULL; /* reset current id */
    102      if (alarm->state == alarm_inactive) {
    103        why = abort;          /* we're toast */
    104      } else if (why == scan) /* the dominant case */
    105      {
    106        id = pr_getNextAlarm(alarm, id); /* even if it's the same */
    107        if (id == NULL) {                /* there are no alarms set */
    108          (void)PR_WaitCondVar(alarm->cond, PR_INTERVAL_NO_TIMEOUT);
    109        } else {
    110          pause = id->nextNotify - (PR_IntervalNow() - id->epoch);
    111          if ((PRInt32)pause <= 0) /* is this one's time up? */
    112          {
    113            why = notify;        /* set up to do our thing */
    114            alarm->current = id; /* id we're about to schedule */
    115          } else {
    116            (void)PR_WaitCondVar(alarm->cond, pause); /* dally */
    117          }
    118        }
    119      }
    120    }
    121    PR_Unlock(alarm->lock);
    122 
    123    if (why == notify) {
    124      (void)pr_PredictNextNotifyTime(id);
    125      if (!id->function(id, id->clientData, ~pause)) {
    126        /*
    127         * Notified function decided not to continue. Free
    128         * the alarm id to make sure it doesn't get back on
    129         * the list.
    130         */
    131        PR_DELETE(id); /* free notifier object */
    132        id = NULL;     /* so it doesn't get back into the list */
    133      }
    134      why = scan; /* so we can cycle through the loop again */
    135    }
    136  }
    137 
    138 } /* pr_alarm_notifier */
    139 
    140 PR_IMPLEMENT(PRAlarm*) PR_CreateAlarm(void) {
    141  PRAlarm* alarm = PR_NEWZAP(PRAlarm);
    142  if (alarm != NULL) {
    143    if ((alarm->lock = PR_NewLock()) == NULL) {
    144      goto done;
    145    }
    146    if ((alarm->cond = PR_NewCondVar(alarm->lock)) == NULL) {
    147      goto done;
    148    }
    149    alarm->state = alarm_active;
    150    PR_INIT_CLIST(&alarm->timers);
    151    alarm->notifier =
    152        PR_CreateThread(PR_USER_THREAD, pr_alarmNotifier, alarm,
    153                        PR_GetThreadPriority(PR_GetCurrentThread()),
    154                        PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
    155    if (alarm->notifier == NULL) {
    156      goto done;
    157    }
    158  }
    159  return alarm;
    160 
    161 done:
    162  if (alarm->cond != NULL) {
    163    PR_DestroyCondVar(alarm->cond);
    164  }
    165  if (alarm->lock != NULL) {
    166    PR_DestroyLock(alarm->lock);
    167  }
    168  PR_DELETE(alarm);
    169  return NULL;
    170 } /* CreateAlarm */
    171 
    172 PR_IMPLEMENT(PRStatus) PR_DestroyAlarm(PRAlarm* alarm) {
    173  PRStatus rv;
    174 
    175  PR_Lock(alarm->lock);
    176  alarm->state = alarm_inactive;
    177  rv = PR_NotifyCondVar(alarm->cond);
    178  PR_Unlock(alarm->lock);
    179 
    180  if (rv == PR_SUCCESS) {
    181    rv = PR_JoinThread(alarm->notifier);
    182  }
    183  if (rv == PR_SUCCESS) {
    184    PR_DestroyCondVar(alarm->cond);
    185    PR_DestroyLock(alarm->lock);
    186    PR_DELETE(alarm);
    187  }
    188  return rv;
    189 } /* PR_DestroyAlarm */
    190 
    191 PR_IMPLEMENT(PRAlarmID*)
    192 PR_SetAlarm(PRAlarm* alarm, PRIntervalTime period, PRUint32 rate,
    193            PRPeriodicAlarmFn function, void* clientData) {
    194  /*
    195   * Create a new periodic alarm an existing current structure.
    196   * Set up the context and compute the first notify time (immediate).
    197   * Link the new ID into the head of the list (since it's notifying
    198   * immediately).
    199   */
    200 
    201  PRAlarmID* id = PR_NEWZAP(PRAlarmID);
    202 
    203  if (!id) {
    204    return NULL;
    205  }
    206 
    207  id->alarm = alarm;
    208  PR_INIT_CLIST(&id->list);
    209  id->function = function;
    210  id->clientData = clientData;
    211  id->period = period;
    212  id->rate = rate;
    213  id->epoch = id->nextNotify = PR_IntervalNow();
    214  (void)pr_PredictNextNotifyTime(id);
    215 
    216  PR_Lock(alarm->lock);
    217  PR_INSERT_BEFORE(&id->list, &alarm->timers);
    218  PR_NotifyCondVar(alarm->cond);
    219  PR_Unlock(alarm->lock);
    220 
    221  return id;
    222 } /* PR_SetAlarm */
    223 
    224 PR_IMPLEMENT(PRStatus)
    225 PR_ResetAlarm(PRAlarmID* id, PRIntervalTime period, PRUint32 rate) {
    226  /*
    227   * Can only be called from within the notify routine. Doesn't
    228   * need locking because it can only be called from within the
    229   * notify routine.
    230   */
    231  if (id != id->alarm->current) {
    232    return PR_FAILURE;
    233  }
    234  id->period = period;
    235  id->rate = rate;
    236  id->accumulator = 1;
    237  id->epoch = PR_IntervalNow();
    238  (void)pr_PredictNextNotifyTime(id);
    239  return PR_SUCCESS;
    240 } /* PR_ResetAlarm */