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/. */
8 /**********************************************************************/
9 /******************************* PRALARM ******************************/
10 /**********************************************************************/
12 #include "obsolete/pralarm.h"
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 */
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 */
28 typedef enum { alarm_active
, alarm_inactive
} _AlarmState
;
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 */
39 static PRAlarmID
* pr_getNextAlarm(PRAlarm
* alarm
, PRAlarmID
* id
) {
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.
45 * NB: Caller is providing locking
48 PRAlarmID
* result
= id
;
49 PRIntervalTime now
= PR_IntervalNow();
51 if (!PR_CLIST_IS_EMPTY(&alarm
->timers
)) {
52 if (id
!= NULL
) /* have to put this id back in */
54 PRIntervalTime idDelta
= now
- id
->nextNotify
;
55 timer
= alarm
->timers
.next
;
57 result
= (PRAlarmID
*)timer
;
58 if ((PRIntervalTime
)(now
- result
->nextNotify
) > idDelta
) {
59 PR_INSERT_BEFORE(&id
->list
, &alarm
->timers
);
63 } while (timer
!= &alarm
->timers
);
65 result
= (PRAlarmID
*)(timer
= PR_LIST_HEAD(&alarm
->timers
));
66 PR_REMOVE_LINK(timer
); /* remove it from the list */
70 } /* pr_getNextAlarm */
72 static PRIntervalTime
pr_PredictNextNotifyTime(PRAlarmID
* id
) {
74 PRFloat64 baseRate
= (PRFloat64
)id
->period
/ (PRFloat64
)id
->rate
;
75 PRFloat64 offsetFromEpoch
= (PRFloat64
)id
->accumulator
* baseRate
;
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);
81 delta
= id
->nextNotify
- id
->lastNotify
;
83 } /* pr_PredictNextNotifyTime */
85 static void PR_CALLBACK
pr_alarmNotifier(void* arg
) {
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.
93 PRAlarm
* alarm
= (PRAlarm
*)arg
;
94 enum { notify
, abort
, scan
} why
= scan
;
96 while (why
!= abort
) {
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 */
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
);
110 pause
= id
->nextNotify
- (PR_IntervalNow() - id
->epoch
);
111 if ((PRInt32
)pause
<= 0) /* is this one's time up? */
113 why
= notify
; /* set up to do our thing */
114 alarm
->current
= id
; /* id we're about to schedule */
116 (void)PR_WaitCondVar(alarm
->cond
, pause
); /* dally */
121 PR_Unlock(alarm
->lock
);
124 (void)pr_PredictNextNotifyTime(id
);
125 if (!id
->function(id
, id
->clientData
, ~pause
)) {
127 * Notified function decided not to continue. Free
128 * the alarm id to make sure it doesn't get back on
131 PR_DELETE(id
); /* free notifier object */
132 id
= NULL
; /* so it doesn't get back into the list */
134 why
= scan
; /* so we can cycle through the loop again */
138 } /* pr_alarm_notifier */
140 PR_IMPLEMENT(PRAlarm
*) PR_CreateAlarm(void) {
141 PRAlarm
* alarm
= PR_NEWZAP(PRAlarm
);
143 if ((alarm
->lock
= PR_NewLock()) == NULL
) {
146 if ((alarm
->cond
= PR_NewCondVar(alarm
->lock
)) == NULL
) {
149 alarm
->state
= alarm_active
;
150 PR_INIT_CLIST(&alarm
->timers
);
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
) {
162 if (alarm
->cond
!= NULL
) {
163 PR_DestroyCondVar(alarm
->cond
);
165 if (alarm
->lock
!= NULL
) {
166 PR_DestroyLock(alarm
->lock
);
172 PR_IMPLEMENT(PRStatus
) PR_DestroyAlarm(PRAlarm
* alarm
) {
175 PR_Lock(alarm
->lock
);
176 alarm
->state
= alarm_inactive
;
177 rv
= PR_NotifyCondVar(alarm
->cond
);
178 PR_Unlock(alarm
->lock
);
180 if (rv
== PR_SUCCESS
) {
181 rv
= PR_JoinThread(alarm
->notifier
);
183 if (rv
== PR_SUCCESS
) {
184 PR_DestroyCondVar(alarm
->cond
);
185 PR_DestroyLock(alarm
->lock
);
189 } /* PR_DestroyAlarm */
191 PR_IMPLEMENT(PRAlarmID
*)
192 PR_SetAlarm(PRAlarm
* alarm
, PRIntervalTime period
, PRUint32 rate
,
193 PRPeriodicAlarmFn function
, void* clientData
) {
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
201 PRAlarmID
* id
= PR_NEWZAP(PRAlarmID
);
208 PR_INIT_CLIST(&id
->list
);
209 id
->function
= function
;
210 id
->clientData
= clientData
;
213 id
->epoch
= id
->nextNotify
= PR_IntervalNow();
214 (void)pr_PredictNextNotifyTime(id
);
216 PR_Lock(alarm
->lock
);
217 PR_INSERT_BEFORE(&id
->list
, &alarm
->timers
);
218 PR_NotifyCondVar(alarm
->cond
);
219 PR_Unlock(alarm
->lock
);
224 PR_IMPLEMENT(PRStatus
)
225 PR_ResetAlarm(PRAlarmID
* id
, PRIntervalTime period
, PRUint32 rate
) {
227 * Can only be called from within the notify routine. Doesn't
228 * need locking because it can only be called from within the
231 if (id
!= id
->alarm
->current
) {
237 id
->epoch
= PR_IntervalNow();
238 (void)pr_PredictNextNotifyTime(id
);
240 } /* PR_ResetAlarm */