1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is the Netscape Portable Runtime (NSPR).
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
40 /**********************************************************************/
41 /******************************* PRALARM ******************************/
42 /**********************************************************************/
47 #include "obsolete/pralarm.h"
50 struct PRAlarmID
{ /* typedef'd in pralarm.h */
51 PRCList list
; /* circular list linkage */
52 PRAlarm
*alarm
; /* back pointer to owning alarm */
53 PRPeriodicAlarmFn function
; /* function to call for notify */
54 void *clientData
; /* opaque client context */
55 PRIntervalTime period
; /* the client defined period */
56 PRUint32 rate
; /* rate of notification */
58 PRUint32 accumulator
; /* keeps track of # notifies */
59 PRIntervalTime epoch
; /* when timer was started */
60 PRIntervalTime nextNotify
; /* when we'll next do our thing */
61 PRIntervalTime lastNotify
; /* when we last did our thing */
64 typedef enum {alarm_active
, alarm_inactive
} _AlarmState
;
66 struct PRAlarm
{ /* typedef'd in pralarm.h */
67 PRCList timers
; /* base of alarm ids list */
68 PRLock
*lock
; /* lock used to protect data */
69 PRCondVar
*cond
; /* condition that used to wait */
70 PRThread
*notifier
; /* thread to deliver notifies */
71 PRAlarmID
*current
; /* current alarm being served */
72 _AlarmState state
; /* used to delete the alarm */
75 static PRAlarmID
*pr_getNextAlarm(PRAlarm
*alarm
, PRAlarmID
*id
)
78 * Puts 'id' back into the sorted list iff it's not NULL.
79 * Removes the first element from the list and returns it (or NULL).
80 * List is "assumed" to be short.
82 * NB: Caller is providing locking
85 PRAlarmID
*result
= id
;
86 PRIntervalTime now
= PR_IntervalNow();
88 if (!PR_CLIST_IS_EMPTY(&alarm
->timers
))
90 if (id
!= NULL
) /* have to put this id back in */
92 PRIntervalTime idDelta
= now
- id
->nextNotify
;
93 timer
= alarm
->timers
.next
;
96 result
= (PRAlarmID
*)timer
;
97 if ((PRIntervalTime
)(now
- result
->nextNotify
) > idDelta
)
99 PR_INSERT_BEFORE(&id
->list
, &alarm
->timers
);
103 } while (timer
!= &alarm
->timers
);
105 result
= (PRAlarmID
*)(timer
= PR_LIST_HEAD(&alarm
->timers
));
106 PR_REMOVE_LINK(timer
); /* remove it from the list */
110 } /* pr_getNextAlarm */
112 static PRIntervalTime
pr_PredictNextNotifyTime(PRAlarmID
*id
)
114 PRIntervalTime delta
;
115 PRFloat64 baseRate
= (PRFloat64
)id
->period
/ (PRFloat64
)id
->rate
;
116 PRFloat64 offsetFromEpoch
= (PRFloat64
)id
->accumulator
* baseRate
;
118 id
->accumulator
+= 1; /* every call advances to next period */
119 id
->lastNotify
= id
->nextNotify
; /* just keeping track of things */
120 id
->nextNotify
= (PRIntervalTime
)(offsetFromEpoch
+ 0.5);
122 delta
= id
->nextNotify
- id
->lastNotify
;
124 } /* pr_PredictNextNotifyTime */
126 static void PR_CALLBACK
pr_alarmNotifier(void *arg
)
129 * This is the root of the notifier thread. There is one such thread
130 * for each PRAlarm. It may service an arbitrary (though assumed to be
131 * small) number of alarms using the same thread and structure. It
132 * continues to run until the alarm is destroyed.
134 PRAlarmID
*id
= NULL
;
135 PRAlarm
*alarm
= (PRAlarm
*)arg
;
136 enum {notify
, abort
, scan
} why
= scan
;
140 PRIntervalTime pause
;
142 PR_Lock(alarm
->lock
);
145 alarm
->current
= NULL
; /* reset current id */
146 if (alarm
->state
== alarm_inactive
) why
= abort
; /* we're toast */
147 else if (why
== scan
) /* the dominant case */
149 id
= pr_getNextAlarm(alarm
, id
); /* even if it's the same */
150 if (id
== NULL
) /* there are no alarms set */
151 (void)PR_WaitCondVar(alarm
->cond
, PR_INTERVAL_NO_TIMEOUT
);
154 pause
= id
->nextNotify
- (PR_IntervalNow() - id
->epoch
);
155 if ((PRInt32
)pause
<= 0) /* is this one's time up? */
157 why
= notify
; /* set up to do our thing */
158 alarm
->current
= id
; /* id we're about to schedule */
161 (void)PR_WaitCondVar(alarm
->cond
, pause
); /* dally */
165 PR_Unlock(alarm
->lock
);
169 (void)pr_PredictNextNotifyTime(id
);
170 if (!id
->function(id
, id
->clientData
, ~pause
))
173 * Notified function decided not to continue. Free
174 * the alarm id to make sure it doesn't get back on
177 PR_DELETE(id
); /* free notifier object */
178 id
= NULL
; /* so it doesn't get back into the list */
180 why
= scan
; /* so we can cycle through the loop again */
184 } /* pr_alarm_notifier */
186 PR_IMPLEMENT(PRAlarm
*) PR_CreateAlarm(void)
188 PRAlarm
*alarm
= PR_NEWZAP(PRAlarm
);
191 if ((alarm
->lock
= PR_NewLock()) == NULL
) goto done
;
192 if ((alarm
->cond
= PR_NewCondVar(alarm
->lock
)) == NULL
) goto done
;
193 alarm
->state
= alarm_active
;
194 PR_INIT_CLIST(&alarm
->timers
);
195 alarm
->notifier
= PR_CreateThread(
196 PR_USER_THREAD
, pr_alarmNotifier
, alarm
,
197 PR_GetThreadPriority(PR_GetCurrentThread()),
198 PR_LOCAL_THREAD
, PR_JOINABLE_THREAD
, 0);
199 if (alarm
->notifier
== NULL
) goto done
;
204 if (alarm
->cond
!= NULL
) PR_DestroyCondVar(alarm
->cond
);
205 if (alarm
->lock
!= NULL
) PR_DestroyLock(alarm
->lock
);
210 PR_IMPLEMENT(PRStatus
) PR_DestroyAlarm(PRAlarm
*alarm
)
214 PR_Lock(alarm
->lock
);
215 alarm
->state
= alarm_inactive
;
216 rv
= PR_NotifyCondVar(alarm
->cond
);
217 PR_Unlock(alarm
->lock
);
219 if (rv
== PR_SUCCESS
)
220 rv
= PR_JoinThread(alarm
->notifier
);
221 if (rv
== PR_SUCCESS
)
223 PR_DestroyCondVar(alarm
->cond
);
224 PR_DestroyLock(alarm
->lock
);
228 } /* PR_DestroyAlarm */
230 PR_IMPLEMENT(PRAlarmID
*) PR_SetAlarm(
231 PRAlarm
*alarm
, PRIntervalTime period
, PRUint32 rate
,
232 PRPeriodicAlarmFn function
, void *clientData
)
235 * Create a new periodic alarm an existing current structure.
236 * Set up the context and compute the first notify time (immediate).
237 * Link the new ID into the head of the list (since it's notifying
241 PRAlarmID
*id
= PR_NEWZAP(PRAlarmID
);
247 PR_INIT_CLIST(&id
->list
);
248 id
->function
= function
;
249 id
->clientData
= clientData
;
252 id
->epoch
= id
->nextNotify
= PR_IntervalNow();
253 (void)pr_PredictNextNotifyTime(id
);
255 PR_Lock(alarm
->lock
);
256 PR_INSERT_BEFORE(&id
->list
, &alarm
->timers
);
257 PR_NotifyCondVar(alarm
->cond
);
258 PR_Unlock(alarm
->lock
);
263 PR_IMPLEMENT(PRStatus
) PR_ResetAlarm(
264 PRAlarmID
*id
, PRIntervalTime period
, PRUint32 rate
)
267 * Can only be called from within the notify routine. Doesn't
268 * need locking because it can only be called from within the
271 if (id
!= id
->alarm
->current
)
276 id
->epoch
= PR_IntervalNow();
277 (void)pr_PredictNextNotifyTime(id
);
279 } /* PR_ResetAlarm */