1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
---|
2 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
---|
3 | *
|
---|
4 | * ***** BEGIN LICENSE BLOCK *****
|
---|
5 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
---|
6 | *
|
---|
7 | * The contents of this file are subject to the Mozilla Public License Version
|
---|
8 | * 1.1 (the "License"); you may not use this file except in compliance with
|
---|
9 | * the License. You may obtain a copy of the License at
|
---|
10 | * http://www.mozilla.org/MPL/
|
---|
11 | *
|
---|
12 | * Software distributed under the License is distributed on an "AS IS" basis,
|
---|
13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
---|
14 | * for the specific language governing rights and limitations under the
|
---|
15 | * License.
|
---|
16 | *
|
---|
17 | * The Original Code is mozilla.org code.
|
---|
18 | *
|
---|
19 | * The Initial Developer of the Original Code is
|
---|
20 | * Netscape Communications Corporation.
|
---|
21 | * Portions created by the Initial Developer are Copyright (C) 2001
|
---|
22 | * the Initial Developer. All Rights Reserved.
|
---|
23 | *
|
---|
24 | * Contributor(s):
|
---|
25 | * Stuart Parmenter <[email protected]>
|
---|
26 | *
|
---|
27 | * Alternatively, the contents of this file may be used under the terms of
|
---|
28 | * either of the GNU General Public License Version 2 or later (the "GPL"),
|
---|
29 | * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
---|
30 | * in which case the provisions of the GPL or the LGPL are applicable instead
|
---|
31 | * of those above. If you wish to allow use of your version of this file only
|
---|
32 | * under the terms of either the GPL or the LGPL, and not to allow others to
|
---|
33 | * use your version of this file under the terms of the MPL, indicate your
|
---|
34 | * decision by deleting the provisions above and replace them with the notice
|
---|
35 | * and other provisions required by the GPL or the LGPL. If you do not delete
|
---|
36 | * the provisions above, a recipient may use your version of this file under
|
---|
37 | * the terms of any one of the MPL, the GPL or the LGPL.
|
---|
38 | *
|
---|
39 | * ***** END LICENSE BLOCK ***** */
|
---|
40 |
|
---|
41 | #include "nsTimerImpl.h"
|
---|
42 | #include "TimerThread.h"
|
---|
43 | #include "nsAutoLock.h"
|
---|
44 |
|
---|
45 | #include "nsVoidArray.h"
|
---|
46 |
|
---|
47 | #include "nsIEventQueue.h"
|
---|
48 |
|
---|
49 | #include "prmem.h"
|
---|
50 |
|
---|
51 | static PRInt32 gGenerator = 0;
|
---|
52 | static TimerThread* gThread = nsnull;
|
---|
53 | static PRBool gFireOnIdle = PR_FALSE;
|
---|
54 | static nsTimerManager* gManager = nsnull;
|
---|
55 |
|
---|
56 | #ifdef DEBUG_TIMERS
|
---|
57 | #include <math.h>
|
---|
58 |
|
---|
59 | double nsTimerImpl::sDeltaSumSquared = 0;
|
---|
60 | double nsTimerImpl::sDeltaSum = 0;
|
---|
61 | double nsTimerImpl::sDeltaNum = 0;
|
---|
62 |
|
---|
63 | static void
|
---|
64 | myNS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
|
---|
65 | double *meanResult, double *stdDevResult)
|
---|
66 | {
|
---|
67 | double mean = 0.0, var = 0.0, stdDev = 0.0;
|
---|
68 | if (n > 0.0 && sumOfValues >= 0) {
|
---|
69 | mean = sumOfValues / n;
|
---|
70 | double temp = (n * sumOfSquaredValues) - (sumOfValues * sumOfValues);
|
---|
71 | if (temp < 0.0 || n <= 1)
|
---|
72 | var = 0.0;
|
---|
73 | else
|
---|
74 | var = temp / (n * (n - 1));
|
---|
75 | // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
|
---|
76 | stdDev = var != 0.0 ? sqrt(var) : 0.0;
|
---|
77 | }
|
---|
78 | *meanResult = mean;
|
---|
79 | *stdDevResult = stdDev;
|
---|
80 | }
|
---|
81 | #endif
|
---|
82 |
|
---|
83 | NS_IMPL_THREADSAFE_QUERY_INTERFACE2(nsTimerImpl, nsITimer, nsITimerInternal)
|
---|
84 | NS_IMPL_THREADSAFE_ADDREF(nsTimerImpl)
|
---|
85 |
|
---|
86 | NS_IMETHODIMP_(nsrefcnt) nsTimerImpl::Release(void)
|
---|
87 | {
|
---|
88 | nsrefcnt count;
|
---|
89 |
|
---|
90 | NS_PRECONDITION(0 != mRefCnt, "dup release");
|
---|
91 | count = PR_AtomicDecrement((PRInt32 *)&mRefCnt);
|
---|
92 | NS_LOG_RELEASE(this, count, "nsTimerImpl");
|
---|
93 | if (count == 0) {
|
---|
94 | mRefCnt = 1; /* stabilize */
|
---|
95 |
|
---|
96 | /* enable this to find non-threadsafe destructors: */
|
---|
97 | /* NS_ASSERT_OWNINGTHREAD(nsTimerImpl); */
|
---|
98 | NS_DELETEXPCOM(this);
|
---|
99 | return 0;
|
---|
100 | }
|
---|
101 |
|
---|
102 | // If only one reference remains, and mArmed is set, then the ref must be
|
---|
103 | // from the TimerThread::mTimers array, so we Cancel this timer to remove
|
---|
104 | // the mTimers element, and return 0 if Cancel in fact disarmed the timer.
|
---|
105 | //
|
---|
106 | // We use an inlined version of nsTimerImpl::Cancel here to check for the
|
---|
107 | // NS_ERROR_NOT_AVAILABLE code returned by gThread->RemoveTimer when this
|
---|
108 | // timer is not found in the mTimers array -- i.e., when the timer was not
|
---|
109 | // in fact armed once we acquired TimerThread::mLock, in spite of mArmed
|
---|
110 | // being true here. That can happen if the armed timer is being fired by
|
---|
111 | // TimerThread::Run as we race and test mArmed just before it is cleared by
|
---|
112 | // the timer thread. If the RemoveTimer call below doesn't find this timer
|
---|
113 | // in the mTimers array, then the last ref to this timer is held manually
|
---|
114 | // and temporarily by the TimerThread, so we should fall through to the
|
---|
115 | // final return and return 1, not 0.
|
---|
116 | //
|
---|
117 | // The original version of this thread-based timer code kept weak refs from
|
---|
118 | // TimerThread::mTimers, removing this timer's weak ref in the destructor,
|
---|
119 | // but that leads to double-destructions in the race described above, and
|
---|
120 | // adding mArmed doesn't help, because destructors can't be deferred, once
|
---|
121 | // begun. But by combining reference-counting and a specialized Release
|
---|
122 | // method with "is this timer still in the mTimers array once we acquire
|
---|
123 | // the TimerThread's lock" testing, we defer destruction until we're sure
|
---|
124 | // that only one thread has its hot little hands on this timer.
|
---|
125 | //
|
---|
126 | // Note that both approaches preclude a timer creator, and everyone else
|
---|
127 | // except the TimerThread who might have a strong ref, from dropping all
|
---|
128 | // their strong refs without implicitly canceling the timer. Timers need
|
---|
129 | // non-mTimers-element strong refs to stay alive.
|
---|
130 |
|
---|
131 | if (count == 1 && mArmed) {
|
---|
132 | mCanceled = PR_TRUE;
|
---|
133 |
|
---|
134 | if (NS_SUCCEEDED(gThread->RemoveTimer(this)))
|
---|
135 | return 0;
|
---|
136 | }
|
---|
137 |
|
---|
138 | return count;
|
---|
139 | }
|
---|
140 |
|
---|
141 | nsTimerImpl::nsTimerImpl() :
|
---|
142 | mClosure(nsnull),
|
---|
143 | mCallbackType(CALLBACK_TYPE_UNKNOWN),
|
---|
144 | mIdle(PR_TRUE),
|
---|
145 | mFiring(PR_FALSE),
|
---|
146 | mArmed(PR_FALSE),
|
---|
147 | mCanceled(PR_FALSE),
|
---|
148 | mGeneration(0),
|
---|
149 | mDelay(0),
|
---|
150 | mTimeout(0)
|
---|
151 | {
|
---|
152 | // XXXbsmedberg: shouldn't this be in Init()?
|
---|
153 | nsIThread::GetCurrent(getter_AddRefs(mCallingThread));
|
---|
154 |
|
---|
155 | mCallback.c = nsnull;
|
---|
156 |
|
---|
157 | #ifdef DEBUG_TIMERS
|
---|
158 | mStart = 0;
|
---|
159 | mStart2 = 0;
|
---|
160 | #endif
|
---|
161 | }
|
---|
162 |
|
---|
163 | nsTimerImpl::~nsTimerImpl()
|
---|
164 | {
|
---|
165 | ReleaseCallback();
|
---|
166 | }
|
---|
167 |
|
---|
168 | //static
|
---|
169 | nsresult
|
---|
170 | nsTimerImpl::Startup()
|
---|
171 | {
|
---|
172 | nsresult rv;
|
---|
173 |
|
---|
174 | gThread = new TimerThread();
|
---|
175 | if (!gThread) return NS_ERROR_OUT_OF_MEMORY;
|
---|
176 |
|
---|
177 | NS_ADDREF(gThread);
|
---|
178 | rv = gThread->InitLocks();
|
---|
179 |
|
---|
180 | if (NS_FAILED(rv)) {
|
---|
181 | NS_RELEASE(gThread);
|
---|
182 | }
|
---|
183 |
|
---|
184 | return rv;
|
---|
185 | }
|
---|
186 |
|
---|
187 | void nsTimerImpl::Shutdown()
|
---|
188 | {
|
---|
189 | #ifdef DEBUG_TIMERS
|
---|
190 | if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
|
---|
191 | double mean = 0, stddev = 0;
|
---|
192 | myNS_MeanAndStdDev(sDeltaNum, sDeltaSum, sDeltaSumSquared, &mean, &stddev);
|
---|
193 |
|
---|
194 | PR_LOG(gTimerLog, PR_LOG_DEBUG, ("sDeltaNum = %f, sDeltaSum = %f, sDeltaSumSquared = %f\n", sDeltaNum, sDeltaSum, sDeltaSumSquared));
|
---|
195 | PR_LOG(gTimerLog, PR_LOG_DEBUG, ("mean: %fms, stddev: %fms\n", mean, stddev));
|
---|
196 | }
|
---|
197 | #endif
|
---|
198 |
|
---|
199 | if (!gThread)
|
---|
200 | return;
|
---|
201 |
|
---|
202 | gThread->Shutdown();
|
---|
203 | NS_RELEASE(gThread);
|
---|
204 |
|
---|
205 | gFireOnIdle = PR_FALSE;
|
---|
206 | }
|
---|
207 |
|
---|
208 |
|
---|
209 | nsresult nsTimerImpl::InitCommon(PRUint32 aType, PRUint32 aDelay)
|
---|
210 | {
|
---|
211 | nsresult rv;
|
---|
212 |
|
---|
213 | NS_ENSURE_TRUE(gThread, NS_ERROR_NOT_INITIALIZED);
|
---|
214 |
|
---|
215 | rv = gThread->Init();
|
---|
216 | NS_ENSURE_SUCCESS(rv, rv);
|
---|
217 |
|
---|
218 | /**
|
---|
219 | * In case of re-Init, both with and without a preceding Cancel, clear the
|
---|
220 | * mCanceled flag and assign a new mGeneration. But first, remove any armed
|
---|
221 | * timer from the timer thread's list.
|
---|
222 | *
|
---|
223 | * If we are racing with the timer thread to remove this timer and we lose,
|
---|
224 | * the RemoveTimer call made here will fail to find this timer in the timer
|
---|
225 | * thread's list, and will return false harmlessly. We test mArmed here to
|
---|
226 | * avoid the small overhead in RemoveTimer of locking the timer thread and
|
---|
227 | * checking its list for this timer. It's safe to test mArmed even though
|
---|
228 | * it might be cleared on another thread in the next cycle (or even already
|
---|
229 | * be cleared by another CPU whose store hasn't reached our CPU's cache),
|
---|
230 | * because RemoveTimer is idempotent.
|
---|
231 | */
|
---|
232 | if (mArmed)
|
---|
233 | gThread->RemoveTimer(this);
|
---|
234 | mCanceled = PR_FALSE;
|
---|
235 | mGeneration = PR_AtomicIncrement(&gGenerator);
|
---|
236 |
|
---|
237 | mType = (PRUint8)aType;
|
---|
238 | SetDelayInternal(aDelay);
|
---|
239 |
|
---|
240 | return gThread->AddTimer(this);
|
---|
241 | }
|
---|
242 |
|
---|
243 | NS_IMETHODIMP nsTimerImpl::InitWithFuncCallback(nsTimerCallbackFunc aFunc,
|
---|
244 | void *aClosure,
|
---|
245 | PRUint32 aDelay,
|
---|
246 | PRUint32 aType)
|
---|
247 | {
|
---|
248 | ReleaseCallback();
|
---|
249 | mCallbackType = CALLBACK_TYPE_FUNC;
|
---|
250 | mCallback.c = aFunc;
|
---|
251 | mClosure = aClosure;
|
---|
252 |
|
---|
253 | return InitCommon(aType, aDelay);
|
---|
254 | }
|
---|
255 |
|
---|
256 | NS_IMETHODIMP nsTimerImpl::InitWithCallback(nsITimerCallback *aCallback,
|
---|
257 | PRUint32 aDelay,
|
---|
258 | PRUint32 aType)
|
---|
259 | {
|
---|
260 | ReleaseCallback();
|
---|
261 | mCallbackType = CALLBACK_TYPE_INTERFACE;
|
---|
262 | mCallback.i = aCallback;
|
---|
263 | NS_ADDREF(mCallback.i);
|
---|
264 |
|
---|
265 | return InitCommon(aType, aDelay);
|
---|
266 | }
|
---|
267 |
|
---|
268 | NS_IMETHODIMP nsTimerImpl::Init(nsIObserver *aObserver,
|
---|
269 | PRUint32 aDelay,
|
---|
270 | PRUint32 aType)
|
---|
271 | {
|
---|
272 | ReleaseCallback();
|
---|
273 | mCallbackType = CALLBACK_TYPE_OBSERVER;
|
---|
274 | mCallback.o = aObserver;
|
---|
275 | NS_ADDREF(mCallback.o);
|
---|
276 |
|
---|
277 | return InitCommon(aType, aDelay);
|
---|
278 | }
|
---|
279 |
|
---|
280 | NS_IMETHODIMP nsTimerImpl::Cancel()
|
---|
281 | {
|
---|
282 | mCanceled = PR_TRUE;
|
---|
283 |
|
---|
284 | if (gThread)
|
---|
285 | gThread->RemoveTimer(this);
|
---|
286 |
|
---|
287 | return NS_OK;
|
---|
288 | }
|
---|
289 |
|
---|
290 | NS_IMETHODIMP nsTimerImpl::SetDelay(PRUint32 aDelay)
|
---|
291 | {
|
---|
292 | // If we're already repeating precisely, update mTimeout now so that the
|
---|
293 | // new delay takes effect in the future.
|
---|
294 | if (mTimeout != 0 && mType == TYPE_REPEATING_PRECISE)
|
---|
295 | mTimeout = PR_IntervalNow();
|
---|
296 |
|
---|
297 | SetDelayInternal(aDelay);
|
---|
298 |
|
---|
299 | if (!mFiring && gThread)
|
---|
300 | gThread->TimerDelayChanged(this);
|
---|
301 |
|
---|
302 | return NS_OK;
|
---|
303 | }
|
---|
304 |
|
---|
305 | NS_IMETHODIMP nsTimerImpl::GetDelay(PRUint32* aDelay)
|
---|
306 | {
|
---|
307 | *aDelay = mDelay;
|
---|
308 | return NS_OK;
|
---|
309 | }
|
---|
310 |
|
---|
311 | NS_IMETHODIMP nsTimerImpl::SetType(PRUint32 aType)
|
---|
312 | {
|
---|
313 | mType = (PRUint8)aType;
|
---|
314 | // XXX if this is called, we should change the actual type.. this could effect
|
---|
315 | // repeating timers. we need to ensure in Fire() that if mType has changed
|
---|
316 | // during the callback that we don't end up with the timer in the queue twice.
|
---|
317 | return NS_OK;
|
---|
318 | }
|
---|
319 |
|
---|
320 | NS_IMETHODIMP nsTimerImpl::GetType(PRUint32* aType)
|
---|
321 | {
|
---|
322 | *aType = mType;
|
---|
323 | return NS_OK;
|
---|
324 | }
|
---|
325 |
|
---|
326 |
|
---|
327 | NS_IMETHODIMP nsTimerImpl::GetClosure(void** aClosure)
|
---|
328 | {
|
---|
329 | *aClosure = mClosure;
|
---|
330 | return NS_OK;
|
---|
331 | }
|
---|
332 |
|
---|
333 |
|
---|
334 | NS_IMETHODIMP nsTimerImpl::GetIdle(PRBool *aIdle)
|
---|
335 | {
|
---|
336 | *aIdle = mIdle;
|
---|
337 | return NS_OK;
|
---|
338 | }
|
---|
339 |
|
---|
340 | NS_IMETHODIMP nsTimerImpl::SetIdle(PRBool aIdle)
|
---|
341 | {
|
---|
342 | mIdle = aIdle;
|
---|
343 | return NS_OK;
|
---|
344 | }
|
---|
345 |
|
---|
346 | void nsTimerImpl::Fire()
|
---|
347 | {
|
---|
348 | if (mCanceled)
|
---|
349 | return;
|
---|
350 |
|
---|
351 | PRIntervalTime now = PR_IntervalNow();
|
---|
352 | #ifdef DEBUG_TIMERS
|
---|
353 | if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
|
---|
354 | PRIntervalTime a = now - mStart; // actual delay in intervals
|
---|
355 | PRUint32 b = PR_MillisecondsToInterval(mDelay); // expected delay in intervals
|
---|
356 | PRUint32 d = PR_IntervalToMilliseconds((a > b) ? a - b : b - a); // delta in ms
|
---|
357 | sDeltaSum += d;
|
---|
358 | sDeltaSumSquared += double(d) * double(d);
|
---|
359 | sDeltaNum++;
|
---|
360 |
|
---|
361 | PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] expected delay time %4dms\n", this, mDelay));
|
---|
362 | PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] actual delay time %4dms\n", this, PR_IntervalToMilliseconds(a)));
|
---|
363 | PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] (mType is %d) -------\n", this, mType));
|
---|
364 | PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] delta %4dms\n", this, (a > b) ? (PRInt32)d : -(PRInt32)d));
|
---|
365 |
|
---|
366 | mStart = mStart2;
|
---|
367 | mStart2 = 0;
|
---|
368 | }
|
---|
369 | #endif
|
---|
370 |
|
---|
371 | PRIntervalTime timeout = mTimeout;
|
---|
372 | if (mType == TYPE_REPEATING_PRECISE) {
|
---|
373 | // Precise repeating timers advance mTimeout by mDelay without fail before
|
---|
374 | // calling Fire().
|
---|
375 | timeout -= PR_MillisecondsToInterval(mDelay);
|
---|
376 | }
|
---|
377 | gThread->UpdateFilter(mDelay, timeout, now);
|
---|
378 |
|
---|
379 | mFiring = PR_TRUE;
|
---|
380 |
|
---|
381 | switch (mCallbackType) {
|
---|
382 | case CALLBACK_TYPE_FUNC:
|
---|
383 | mCallback.c(this, mClosure);
|
---|
384 | break;
|
---|
385 | case CALLBACK_TYPE_INTERFACE:
|
---|
386 | mCallback.i->Notify(this);
|
---|
387 | break;
|
---|
388 | case CALLBACK_TYPE_OBSERVER:
|
---|
389 | mCallback.o->Observe(NS_STATIC_CAST(nsITimer*,this),
|
---|
390 | NS_TIMER_CALLBACK_TOPIC,
|
---|
391 | nsnull);
|
---|
392 | break;
|
---|
393 | default:;
|
---|
394 | }
|
---|
395 |
|
---|
396 | mFiring = PR_FALSE;
|
---|
397 |
|
---|
398 | #ifdef DEBUG_TIMERS
|
---|
399 | if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
|
---|
400 | PR_LOG(gTimerLog, PR_LOG_DEBUG,
|
---|
401 | ("[this=%p] Took %dms to fire timer callback\n",
|
---|
402 | this, PR_IntervalToMilliseconds(PR_IntervalNow() - now)));
|
---|
403 | }
|
---|
404 | #endif
|
---|
405 |
|
---|
406 | if (mType == TYPE_REPEATING_SLACK) {
|
---|
407 | SetDelayInternal(mDelay); // force mTimeout to be recomputed.
|
---|
408 | if (gThread)
|
---|
409 | gThread->AddTimer(this);
|
---|
410 | }
|
---|
411 | }
|
---|
412 |
|
---|
413 |
|
---|
414 | struct TimerEventType : public PLEvent {
|
---|
415 | PRInt32 mGeneration;
|
---|
416 | #ifdef DEBUG_TIMERS
|
---|
417 | PRIntervalTime mInitTime;
|
---|
418 | #endif
|
---|
419 | };
|
---|
420 |
|
---|
421 |
|
---|
422 | void* handleTimerEvent(TimerEventType* event)
|
---|
423 | {
|
---|
424 | nsTimerImpl* timer = NS_STATIC_CAST(nsTimerImpl*, event->owner);
|
---|
425 | if (event->mGeneration != timer->GetGeneration())
|
---|
426 | return nsnull;
|
---|
427 |
|
---|
428 | #ifdef DEBUG_TIMERS
|
---|
429 | if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
|
---|
430 | PRIntervalTime now = PR_IntervalNow();
|
---|
431 | PR_LOG(gTimerLog, PR_LOG_DEBUG,
|
---|
432 | ("[this=%p] time between PostTimerEvent() and Fire(): %dms\n",
|
---|
433 | event->owner, PR_IntervalToMilliseconds(now - event->mInitTime)));
|
---|
434 | }
|
---|
435 | #endif
|
---|
436 |
|
---|
437 | if (gFireOnIdle) {
|
---|
438 | PRBool idle = PR_FALSE;
|
---|
439 | timer->GetIdle(&idle);
|
---|
440 | if (idle) {
|
---|
441 | NS_ASSERTION(gManager, "Global Thread Manager is null!");
|
---|
442 | if (gManager)
|
---|
443 | gManager->AddIdleTimer(timer);
|
---|
444 | return nsnull;
|
---|
445 | }
|
---|
446 | }
|
---|
447 |
|
---|
448 | timer->Fire();
|
---|
449 |
|
---|
450 | return nsnull;
|
---|
451 | }
|
---|
452 |
|
---|
453 | void destroyTimerEvent(TimerEventType* event)
|
---|
454 | {
|
---|
455 | nsTimerImpl *timer = NS_STATIC_CAST(nsTimerImpl*, event->owner);
|
---|
456 | NS_RELEASE(timer);
|
---|
457 | PR_DELETE(event);
|
---|
458 | }
|
---|
459 |
|
---|
460 |
|
---|
461 | void nsTimerImpl::PostTimerEvent()
|
---|
462 | {
|
---|
463 | // XXX we may want to reuse the PLEvent in the case of repeating timers.
|
---|
464 | TimerEventType* event;
|
---|
465 |
|
---|
466 | // construct
|
---|
467 | event = PR_NEW(TimerEventType);
|
---|
468 | if (!event)
|
---|
469 | return;
|
---|
470 |
|
---|
471 | // initialize
|
---|
472 | PL_InitEvent((PLEvent*)event, this,
|
---|
473 | (PLHandleEventProc)handleTimerEvent,
|
---|
474 | (PLDestroyEventProc)destroyTimerEvent);
|
---|
475 |
|
---|
476 | // Since TimerThread addref'd 'this' for us, we don't need to addref here.
|
---|
477 | // We will release in destroyMyEvent. We do need to copy the generation
|
---|
478 | // number from this timer into the event, so we can avoid firing a timer
|
---|
479 | // that was re-initialized after being canceled.
|
---|
480 | event->mGeneration = mGeneration;
|
---|
481 |
|
---|
482 | #ifdef DEBUG_TIMERS
|
---|
483 | if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
|
---|
484 | event->mInitTime = PR_IntervalNow();
|
---|
485 | }
|
---|
486 | #endif
|
---|
487 |
|
---|
488 | // If this is a repeating precise timer, we need to calculate the time for
|
---|
489 | // the next timer to fire before we make the callback.
|
---|
490 | if (mType == TYPE_REPEATING_PRECISE) {
|
---|
491 | SetDelayInternal(mDelay);
|
---|
492 | if (gThread)
|
---|
493 | gThread->AddTimer(this);
|
---|
494 | }
|
---|
495 |
|
---|
496 | PRThread *thread;
|
---|
497 | nsresult rv = mCallingThread->GetPRThread(&thread);
|
---|
498 | if (NS_FAILED(rv)) {
|
---|
499 | NS_WARNING("Dropping timer event because thread is dead");
|
---|
500 | return;
|
---|
501 | }
|
---|
502 |
|
---|
503 | nsCOMPtr<nsIEventQueue> queue;
|
---|
504 | if (gThread)
|
---|
505 | gThread->mEventQueueService->GetThreadEventQueue(thread, getter_AddRefs(queue));
|
---|
506 | if (queue)
|
---|
507 | queue->PostEvent(event);
|
---|
508 | }
|
---|
509 |
|
---|
510 | void nsTimerImpl::SetDelayInternal(PRUint32 aDelay)
|
---|
511 | {
|
---|
512 | PRIntervalTime delayInterval = PR_MillisecondsToInterval(aDelay);
|
---|
513 | if (delayInterval > DELAY_INTERVAL_MAX) {
|
---|
514 | delayInterval = DELAY_INTERVAL_MAX;
|
---|
515 | aDelay = PR_IntervalToMilliseconds(delayInterval);
|
---|
516 | }
|
---|
517 |
|
---|
518 | mDelay = aDelay;
|
---|
519 |
|
---|
520 | PRIntervalTime now = PR_IntervalNow();
|
---|
521 | if (mTimeout == 0 || mType != TYPE_REPEATING_PRECISE)
|
---|
522 | mTimeout = now;
|
---|
523 |
|
---|
524 | mTimeout += delayInterval;
|
---|
525 |
|
---|
526 | #ifdef DEBUG_TIMERS
|
---|
527 | if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
|
---|
528 | if (mStart == 0)
|
---|
529 | mStart = now;
|
---|
530 | else
|
---|
531 | mStart2 = now;
|
---|
532 | }
|
---|
533 | #endif
|
---|
534 | }
|
---|
535 |
|
---|
536 | /**
|
---|
537 | * Timer Manager code
|
---|
538 | */
|
---|
539 |
|
---|
540 | NS_IMPL_THREADSAFE_ISUPPORTS1(nsTimerManager, nsITimerManager)
|
---|
541 |
|
---|
542 | nsTimerManager::nsTimerManager()
|
---|
543 | {
|
---|
544 | mLock = PR_NewLock();
|
---|
545 | gManager = this;
|
---|
546 | }
|
---|
547 |
|
---|
548 | nsTimerManager::~nsTimerManager()
|
---|
549 | {
|
---|
550 | gManager = nsnull;
|
---|
551 | PR_DestroyLock(mLock);
|
---|
552 |
|
---|
553 | nsTimerImpl *theTimer;
|
---|
554 | PRInt32 count = mIdleTimers.Count();
|
---|
555 |
|
---|
556 | for (PRInt32 i = 0; i < count; i++) {
|
---|
557 | theTimer = NS_STATIC_CAST(nsTimerImpl*, mIdleTimers[i]);
|
---|
558 | NS_IF_RELEASE(theTimer);
|
---|
559 | }
|
---|
560 | }
|
---|
561 |
|
---|
562 | NS_IMETHODIMP nsTimerManager::SetUseIdleTimers(PRBool aUseIdleTimers)
|
---|
563 | {
|
---|
564 | if (aUseIdleTimers == PR_FALSE && gFireOnIdle == PR_TRUE)
|
---|
565 | return NS_ERROR_FAILURE;
|
---|
566 |
|
---|
567 | gFireOnIdle = aUseIdleTimers;
|
---|
568 |
|
---|
569 | return NS_OK;
|
---|
570 | }
|
---|
571 |
|
---|
572 | NS_IMETHODIMP nsTimerManager::GetUseIdleTimers(PRBool *aUseIdleTimers)
|
---|
573 | {
|
---|
574 | *aUseIdleTimers = gFireOnIdle;
|
---|
575 | return NS_OK;
|
---|
576 | }
|
---|
577 |
|
---|
578 | NS_IMETHODIMP nsTimerManager::HasIdleTimers(PRBool *aHasTimers)
|
---|
579 | {
|
---|
580 | nsAutoLock lock (mLock);
|
---|
581 | PRUint32 count = mIdleTimers.Count();
|
---|
582 | *aHasTimers = (count != 0);
|
---|
583 | return NS_OK;
|
---|
584 | }
|
---|
585 |
|
---|
586 | nsresult nsTimerManager::AddIdleTimer(nsITimer* timer)
|
---|
587 | {
|
---|
588 | if (!timer)
|
---|
589 | return NS_ERROR_FAILURE;
|
---|
590 | nsAutoLock lock(mLock);
|
---|
591 | mIdleTimers.AppendElement(timer);
|
---|
592 | NS_ADDREF(timer);
|
---|
593 | return NS_OK;
|
---|
594 | }
|
---|
595 |
|
---|
596 | NS_IMETHODIMP nsTimerManager::FireNextIdleTimer()
|
---|
597 | {
|
---|
598 | if (!gFireOnIdle || !nsIThread::IsMainThread()) {
|
---|
599 | return NS_OK;
|
---|
600 | }
|
---|
601 |
|
---|
602 | nsTimerImpl *theTimer = nsnull;
|
---|
603 |
|
---|
604 | {
|
---|
605 | nsAutoLock lock (mLock);
|
---|
606 | PRUint32 count = mIdleTimers.Count();
|
---|
607 |
|
---|
608 | if (count == 0)
|
---|
609 | return NS_OK;
|
---|
610 |
|
---|
611 | theTimer = NS_STATIC_CAST(nsTimerImpl*, mIdleTimers[0]);
|
---|
612 | mIdleTimers.RemoveElement(theTimer);
|
---|
613 | }
|
---|
614 |
|
---|
615 | theTimer->Fire();
|
---|
616 |
|
---|
617 | NS_RELEASE(theTimer);
|
---|
618 |
|
---|
619 | return NS_OK;
|
---|
620 | }
|
---|
621 |
|
---|
622 |
|
---|
623 | // NOT FOR PUBLIC CONSUMPTION!
|
---|
624 | nsresult
|
---|
625 | NS_NewTimer(nsITimer* *aResult, nsTimerCallbackFunc aCallback, void *aClosure,
|
---|
626 | PRUint32 aDelay, PRUint32 aType)
|
---|
627 | {
|
---|
628 | nsTimerImpl* timer = new nsTimerImpl();
|
---|
629 | if (timer == nsnull)
|
---|
630 | return NS_ERROR_OUT_OF_MEMORY;
|
---|
631 | NS_ADDREF(timer);
|
---|
632 |
|
---|
633 | nsresult rv = timer->InitWithFuncCallback(aCallback, aClosure,
|
---|
634 | aDelay, aType);
|
---|
635 | if (NS_FAILED(rv)) {
|
---|
636 | NS_RELEASE(timer);
|
---|
637 | return rv;
|
---|
638 | }
|
---|
639 |
|
---|
640 | *aResult = timer;
|
---|
641 | return NS_OK;
|
---|
642 | }
|
---|