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 */