1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
3
* The contents of this file are subject to the Mozilla Public
4
* License Version 1.1 (the "License"); you may not use this file
5
* except in compliance with the License. You may obtain a copy of
6
* the License at http://www.mozilla.org/MPL/
8
* Software distributed under the License is distributed on an "AS
9
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
10
* implied. See the License for the specific language governing
11
* rights and limitations under the License.
13
* The Original Code is the Netscape Portable Runtime (NSPR).
15
* The Initial Developer of the Original Code is Netscape
16
* Communications Corporation. Portions created by Netscape are
17
* Copyright (C) 1998-2000 Netscape Communications Corporation. All
22
* Alternatively, the contents of this file may be used under the
23
* terms of the GNU General Public License Version 2 or later (the
24
* "GPL"), in which case the provisions of the GPL are applicable
25
* instead of those above. If you wish to allow use of your
26
* version of this file only under the terms of the GPL and not to
27
* allow others to use your version of this file under the MPL,
28
* indicate your decision by deleting the provisions above and
29
* replace them with the notice and other provisions required by
30
* the GPL. If you do not delete the provisions above, a recipient
31
* may use your version of this file under either the MPL or the
37
/**********************************************************************/
38
/******************************* PRALARM ******************************/
39
/**********************************************************************/
44
#include "obsolete/pralarm.h"
47
struct PRAlarmID { /* typedef'd in pralarm.h */
48
PRCList list; /* circular list linkage */
49
PRAlarm *alarm; /* back pointer to owning alarm */
50
PRPeriodicAlarmFn function; /* function to call for notify */
51
void *clientData; /* opaque client context */
52
PRIntervalTime period; /* the client defined period */
53
PRUint32 rate; /* rate of notification */
55
PRUint32 accumulator; /* keeps track of # notifies */
56
PRIntervalTime epoch; /* when timer was started */
57
PRIntervalTime nextNotify; /* when we'll next do our thing */
58
PRIntervalTime lastNotify; /* when we last did our thing */
61
typedef enum {alarm_active, alarm_inactive} _AlarmState;
63
struct PRAlarm { /* typedef'd in pralarm.h */
64
PRCList timers; /* base of alarm ids list */
65
PRLock *lock; /* lock used to protect data */
66
PRCondVar *cond; /* condition that used to wait */
67
PRThread *notifier; /* thread to deliver notifies */
68
PRAlarmID *current; /* current alarm being served */
69
_AlarmState state; /* used to delete the alarm */
72
static PRAlarmID *pr_getNextAlarm(PRAlarm *alarm, PRAlarmID *id)
75
* Puts 'id' back into the sorted list iff it's not NULL.
76
* Removes the first element from the list and returns it (or NULL).
77
* List is "assumed" to be short.
79
* NB: Caller is providing locking
82
PRAlarmID *result = id;
83
PRIntervalTime now = PR_IntervalNow();
85
if (!PR_CLIST_IS_EMPTY(&alarm->timers))
87
if (id != NULL) /* have to put this id back in */
89
PRIntervalTime idDelta = now - id->nextNotify;
90
timer = alarm->timers.next;
93
result = (PRAlarmID*)timer;
94
if ((PRIntervalTime)(now - result->nextNotify) > idDelta)
96
PR_INSERT_BEFORE(&id->list, &alarm->timers);
100
} while (timer != &alarm->timers);
102
result = (PRAlarmID*)(timer = PR_LIST_HEAD(&alarm->timers));
103
PR_REMOVE_LINK(timer); /* remove it from the list */
107
} /* pr_getNextAlarm */
109
static PRIntervalTime pr_PredictNextNotifyTime(PRAlarmID *id)
111
PRIntervalTime delta;
112
PRFloat64 baseRate = (PRFloat64)id->period / (PRFloat64)id->rate;
113
PRFloat64 offsetFromEpoch = (PRFloat64)id->accumulator * baseRate;
115
id->accumulator += 1; /* every call advances to next period */
116
id->lastNotify = id->nextNotify; /* just keeping track of things */
117
id->nextNotify = (PRIntervalTime)(offsetFromEpoch + 0.5);
119
delta = id->nextNotify - id->lastNotify;
121
} /* pr_PredictNextNotifyTime */
123
static void PR_CALLBACK pr_alarmNotifier(void *arg)
126
* This is the root of the notifier thread. There is one such thread
127
* for each PRAlarm. It may service an arbitrary (though assumed to be
128
* small) number of alarms using the same thread and structure. It
129
* continues to run until the alarm is destroyed.
131
PRAlarmID *id = NULL;
132
PRAlarm *alarm = (PRAlarm*)arg;
133
enum {notify, abort, scan} why = scan;
137
PRIntervalTime pause;
139
PR_Lock(alarm->lock);
142
alarm->current = NULL; /* reset current id */
143
if (alarm->state == alarm_inactive) why = abort; /* we're toast */
144
else if (why == scan) /* the dominant case */
146
id = pr_getNextAlarm(alarm, id); /* even if it's the same */
147
if (id == NULL) /* there are no alarms set */
148
(void)PR_WaitCondVar(alarm->cond, PR_INTERVAL_NO_TIMEOUT);
151
pause = id->nextNotify - (PR_IntervalNow() - id->epoch);
152
if ((PRInt32)pause <= 0) /* is this one's time up? */
154
why = notify; /* set up to do our thing */
155
alarm->current = id; /* id we're about to schedule */
158
(void)PR_WaitCondVar(alarm->cond, pause); /* dally */
162
PR_Unlock(alarm->lock);
166
(void)pr_PredictNextNotifyTime(id);
167
if (!id->function(id, id->clientData, ~pause))
170
* Notified function decided not to continue. Free
171
* the alarm id to make sure it doesn't get back on
174
PR_DELETE(id); /* free notifier object */
175
id = NULL; /* so it doesn't get back into the list */
177
why = scan; /* so we can cycle through the loop again */
181
} /* pr_alarm_notifier */
183
PR_IMPLEMENT(PRAlarm*) PR_CreateAlarm(void)
185
PRAlarm *alarm = PR_NEWZAP(PRAlarm);
188
if ((alarm->lock = PR_NewLock()) == NULL) goto done;
189
if ((alarm->cond = PR_NewCondVar(alarm->lock)) == NULL) goto done;
190
alarm->state = alarm_active;
191
PR_INIT_CLIST(&alarm->timers);
192
alarm->notifier = PR_CreateThread(
193
PR_USER_THREAD, pr_alarmNotifier, alarm,
194
PR_GetThreadPriority(PR_GetCurrentThread()),
195
PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
196
if (alarm->notifier == NULL) goto done;
201
if (alarm->cond != NULL) PR_DestroyCondVar(alarm->cond);
202
if (alarm->lock != NULL) PR_DestroyLock(alarm->lock);
207
PR_IMPLEMENT(PRStatus) PR_DestroyAlarm(PRAlarm *alarm)
211
PR_Lock(alarm->lock);
212
alarm->state = alarm_inactive;
213
rv = PR_NotifyCondVar(alarm->cond);
214
PR_Unlock(alarm->lock);
216
if (rv == PR_SUCCESS)
217
rv = PR_JoinThread(alarm->notifier);
218
if (rv == PR_SUCCESS)
220
PR_DestroyCondVar(alarm->cond);
221
PR_DestroyLock(alarm->lock);
225
} /* PR_DestroyAlarm */
227
PR_IMPLEMENT(PRAlarmID*) PR_SetAlarm(
228
PRAlarm *alarm, PRIntervalTime period, PRUint32 rate,
229
PRPeriodicAlarmFn function, void *clientData)
232
* Create a new periodic alarm an existing current structure.
233
* Set up the context and compute the first notify time (immediate).
234
* Link the new ID into the head of the list (since it's notifying
238
PRAlarmID *id = PR_NEWZAP(PRAlarmID);
244
PR_INIT_CLIST(&id->list);
245
id->function = function;
246
id->clientData = clientData;
249
id->epoch = id->nextNotify = PR_IntervalNow();
250
(void)pr_PredictNextNotifyTime(id);
252
PR_Lock(alarm->lock);
253
PR_INSERT_BEFORE(&id->list, &alarm->timers);
254
PR_NotifyCondVar(alarm->cond);
255
PR_Unlock(alarm->lock);
260
PR_IMPLEMENT(PRStatus) PR_ResetAlarm(
261
PRAlarmID *id, PRIntervalTime period, PRUint32 rate)
264
* Can only be called from within the notify routine. Doesn't
265
* need locking because it can only be called from within the
268
if (id != id->alarm->current)
273
id->epoch = PR_IntervalNow();
274
(void)pr_PredictNextNotifyTime(id);
276
} /* PR_ResetAlarm */