Back to index

lightning-sunbird  0.9+nobinonly
pralarm.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "primpl.h"
00039 
00040 /**********************************************************************/
00041 /******************************* PRALARM ******************************/
00042 /**********************************************************************/
00043 
00044 #ifdef XP_MAC
00045 #include "pralarm.h"
00046 #else
00047 #include "obsolete/pralarm.h"
00048 #endif
00049 
00050 struct PRAlarmID {                       /* typedef'd in pralarm.h       */
00051     PRCList list;                        /* circular list linkage        */
00052     PRAlarm *alarm;                      /* back pointer to owning alarm */
00053     PRPeriodicAlarmFn function;          /* function to call for notify  */
00054     void *clientData;                    /* opaque client context        */
00055     PRIntervalTime period;               /* the client defined period    */
00056     PRUint32 rate;                       /* rate of notification         */
00057 
00058     PRUint32 accumulator;                /* keeps track of # notifies    */
00059     PRIntervalTime epoch;                /* when timer was started       */
00060     PRIntervalTime nextNotify;           /* when we'll next do our thing */
00061     PRIntervalTime lastNotify;           /* when we last did our thing   */
00062 };
00063 
00064 typedef enum {alarm_active, alarm_inactive} _AlarmState;
00065 
00066 struct PRAlarm {                         /* typedef'd in pralarm.h       */
00067     PRCList timers;                      /* base of alarm ids list       */
00068     PRLock *lock;                        /* lock used to protect data    */
00069     PRCondVar *cond;                     /* condition that used to wait  */
00070     PRThread *notifier;                  /* thread to deliver notifies   */
00071     PRAlarmID *current;                  /* current alarm being served   */
00072     _AlarmState state;                   /* used to delete the alarm     */
00073 };
00074 
00075 static PRAlarmID *pr_getNextAlarm(PRAlarm *alarm, PRAlarmID *id)
00076 {
00077 /*
00078  * Puts 'id' back into the sorted list iff it's not NULL.
00079  * Removes the first element from the list and returns it (or NULL).
00080  * List is "assumed" to be short.
00081  *
00082  * NB: Caller is providing locking
00083  */
00084     PRCList *timer;
00085     PRAlarmID *result = id;
00086     PRIntervalTime now = PR_IntervalNow();
00087 
00088     if (!PR_CLIST_IS_EMPTY(&alarm->timers))
00089     {    
00090         if (id != NULL)  /* have to put this id back in */
00091         {        
00092             PRIntervalTime idDelta = now - id->nextNotify;
00093             timer = alarm->timers.next;
00094             do
00095             {
00096                 result = (PRAlarmID*)timer;
00097                 if ((PRIntervalTime)(now - result->nextNotify) > idDelta)
00098                 {
00099                     PR_INSERT_BEFORE(&id->list, &alarm->timers);
00100                     break;
00101                 }
00102                 timer = timer->next;
00103             } while (timer != &alarm->timers);
00104         }
00105         result = (PRAlarmID*)(timer = PR_LIST_HEAD(&alarm->timers));
00106         PR_REMOVE_LINK(timer);  /* remove it from the list */
00107     }
00108 
00109     return result;
00110 }  /* pr_getNextAlarm */
00111 
00112 static PRIntervalTime pr_PredictNextNotifyTime(PRAlarmID *id)
00113 {
00114     PRIntervalTime delta;
00115     PRFloat64 baseRate = (PRFloat64)id->period / (PRFloat64)id->rate;
00116     PRFloat64 offsetFromEpoch = (PRFloat64)id->accumulator * baseRate;
00117 
00118     id->accumulator += 1;  /* every call advances to next period */
00119     id->lastNotify = id->nextNotify;  /* just keeping track of things */
00120     id->nextNotify = (PRIntervalTime)(offsetFromEpoch + 0.5);
00121 
00122     delta = id->nextNotify - id->lastNotify;
00123     return delta;
00124 }  /* pr_PredictNextNotifyTime */
00125 
00126 static void PR_CALLBACK pr_alarmNotifier(void *arg)
00127 {
00128     /*
00129      * This is the root of the notifier thread. There is one such thread
00130      * for each PRAlarm. It may service an arbitrary (though assumed to be
00131      * small) number of alarms using the same thread and structure. It
00132      * continues to run until the alarm is destroyed.
00133      */
00134     PRAlarmID *id = NULL;
00135     PRAlarm *alarm = (PRAlarm*)arg;
00136     enum {notify, abort, scan} why = scan;
00137 
00138     while (why != abort)
00139     {
00140         PRIntervalTime pause;
00141 
00142         PR_Lock(alarm->lock);
00143         while (why == scan)
00144         {
00145             alarm->current = NULL;  /* reset current id */
00146             if (alarm->state == alarm_inactive) why = abort;  /* we're toast */
00147             else if (why == scan)  /* the dominant case */
00148             {
00149                 id = pr_getNextAlarm(alarm, id);  /* even if it's the same */
00150                 if (id == NULL)  /* there are no alarms set */
00151                     (void)PR_WaitCondVar(alarm->cond, PR_INTERVAL_NO_TIMEOUT);
00152                 else
00153                 {
00154                     pause = id->nextNotify - (PR_IntervalNow() - id->epoch);
00155                     if ((PRInt32)pause <= 0)  /* is this one's time up? */
00156                     {
00157                         why = notify;  /* set up to do our thing */
00158                         alarm->current = id;  /* id we're about to schedule */
00159                     }
00160                     else
00161                         (void)PR_WaitCondVar(alarm->cond, pause);  /* dally */
00162                 }
00163             }
00164         }
00165         PR_Unlock(alarm->lock);
00166 
00167         if (why == notify)
00168         {
00169             (void)pr_PredictNextNotifyTime(id);
00170             if (!id->function(id, id->clientData, ~pause))
00171             {
00172                 /*
00173                  * Notified function decided not to continue. Free
00174                  * the alarm id to make sure it doesn't get back on
00175                  * the list.
00176                  */
00177                 PR_DELETE(id);  /* free notifier object */
00178                 id = NULL;  /* so it doesn't get back into the list */
00179             }
00180             why = scan;  /* so we can cycle through the loop again */
00181         }
00182     }
00183 
00184 }  /* pr_alarm_notifier */
00185 
00186 PR_IMPLEMENT(PRAlarm*) PR_CreateAlarm(void)
00187 {
00188     PRAlarm *alarm = PR_NEWZAP(PRAlarm);
00189     if (alarm != NULL)
00190     {
00191         if ((alarm->lock = PR_NewLock()) == NULL) goto done;
00192         if ((alarm->cond = PR_NewCondVar(alarm->lock)) == NULL) goto done;
00193         alarm->state = alarm_active;
00194         PR_INIT_CLIST(&alarm->timers);
00195         alarm->notifier = PR_CreateThread(
00196             PR_USER_THREAD, pr_alarmNotifier, alarm,
00197             PR_GetThreadPriority(PR_GetCurrentThread()),
00198             PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
00199         if (alarm->notifier == NULL) goto done;
00200     }
00201     return alarm;
00202 
00203 done:
00204     if (alarm->cond != NULL) PR_DestroyCondVar(alarm->cond);
00205     if (alarm->lock != NULL) PR_DestroyLock(alarm->lock);
00206     PR_DELETE(alarm);
00207     return NULL;
00208 }  /* CreateAlarm */
00209 
00210 PR_IMPLEMENT(PRStatus) PR_DestroyAlarm(PRAlarm *alarm)
00211 {
00212     PRStatus rv;
00213 
00214     PR_Lock(alarm->lock);
00215     alarm->state = alarm_inactive;
00216     rv = PR_NotifyCondVar(alarm->cond);
00217     PR_Unlock(alarm->lock);
00218 
00219     if (rv == PR_SUCCESS)
00220         rv = PR_JoinThread(alarm->notifier);
00221     if (rv == PR_SUCCESS)
00222     {
00223         PR_DestroyCondVar(alarm->cond);
00224         PR_DestroyLock(alarm->lock);
00225         PR_DELETE(alarm);
00226     }
00227     return rv;
00228 }  /* PR_DestroyAlarm */
00229 
00230 PR_IMPLEMENT(PRAlarmID*) PR_SetAlarm(
00231     PRAlarm *alarm, PRIntervalTime period, PRUint32 rate,
00232     PRPeriodicAlarmFn function, void *clientData)
00233 {
00234     /*
00235      * Create a new periodic alarm an existing current structure.
00236      * Set up the context and compute the first notify time (immediate).
00237      * Link the new ID into the head of the list (since it's notifying
00238      * immediately).
00239      */
00240 
00241     PRAlarmID *id = PR_NEWZAP(PRAlarmID);
00242 
00243     if (!id)
00244         return NULL;
00245 
00246     id->alarm = alarm;
00247     PR_INIT_CLIST(&id->list);
00248     id->function = function;
00249     id->clientData = clientData;
00250     id->period = period;
00251     id->rate = rate;
00252     id->epoch = id->nextNotify = PR_IntervalNow();
00253     (void)pr_PredictNextNotifyTime(id);
00254 
00255     PR_Lock(alarm->lock);
00256     PR_INSERT_BEFORE(&id->list, &alarm->timers);
00257     PR_NotifyCondVar(alarm->cond);
00258     PR_Unlock(alarm->lock);
00259 
00260     return id;
00261 }  /* PR_SetAlarm */
00262 
00263 PR_IMPLEMENT(PRStatus) PR_ResetAlarm(
00264     PRAlarmID *id, PRIntervalTime period, PRUint32 rate)
00265 {
00266     /*
00267      * Can only be called from within the notify routine. Doesn't
00268      * need locking because it can only be called from within the
00269      * notify routine.
00270      */
00271     if (id != id->alarm->current)
00272         return PR_FAILURE;
00273     id->period = period;
00274     id->rate = rate;
00275     id->accumulator = 1;
00276     id->epoch = PR_IntervalNow();
00277     (void)pr_PredictNextNotifyTime(id);
00278     return PR_SUCCESS;
00279 }  /* PR_ResetAlarm */
00280 
00281 
00282