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 <pavlov@netscape.com>
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 "nsAutoPtr.h"
44 : #include "nsThreadManager.h"
45 : #include "nsThreadUtils.h"
46 : #include "prmem.h"
47 : #include "sampler.h"
48 : #include NEW_H
49 : #include "nsFixedSizeAllocator.h"
50 :
51 : using mozilla::TimeDuration;
52 : using mozilla::TimeStamp;
53 :
54 : static PRInt32 gGenerator = 0;
55 : static TimerThread* gThread = nsnull;
56 :
57 : #ifdef DEBUG_TIMERS
58 : #include <math.h>
59 :
60 : double nsTimerImpl::sDeltaSumSquared = 0;
61 : double nsTimerImpl::sDeltaSum = 0;
62 : double nsTimerImpl::sDeltaNum = 0;
63 :
64 : static void
65 0 : myNS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
66 : double *meanResult, double *stdDevResult)
67 : {
68 0 : double mean = 0.0, var = 0.0, stdDev = 0.0;
69 0 : if (n > 0.0 && sumOfValues >= 0) {
70 0 : mean = sumOfValues / n;
71 0 : double temp = (n * sumOfSquaredValues) - (sumOfValues * sumOfValues);
72 0 : if (temp < 0.0 || n <= 1)
73 0 : var = 0.0;
74 : else
75 0 : var = temp / (n * (n - 1));
76 : // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
77 0 : stdDev = var != 0.0 ? sqrt(var) : 0.0;
78 : }
79 0 : *meanResult = mean;
80 0 : *stdDevResult = stdDev;
81 0 : }
82 : #endif
83 :
84 : namespace {
85 :
86 : // TimerEventAllocator is a fixed size allocator class which is used in order
87 : // to avoid the default allocator lock contention when firing timer events.
88 : // It is a thread-safe wrapper around nsFixedSizeAllocator. The thread-safety
89 : // is required because nsTimerEvent objects are allocated on the timer thread,
90 : // and freed on the main thread. Since this is a TimerEventAllocator specific
91 : // lock, the lock contention issue is only limited to the allocation and
92 : // deallocation of nsTimerEvent objects.
93 1419 : class TimerEventAllocator : public nsFixedSizeAllocator {
94 : public:
95 1419 : TimerEventAllocator() :
96 1419 : mMonitor("TimerEventAllocator")
97 : {
98 1419 : }
99 :
100 4298 : void* Alloc(size_t aSize)
101 : {
102 8596 : mozilla::MonitorAutoLock lock(mMonitor);
103 4298 : return nsFixedSizeAllocator::Alloc(aSize);
104 : }
105 4298 : void Free(void* aPtr, size_t aSize)
106 : {
107 8596 : mozilla::MonitorAutoLock lock(mMonitor);
108 4298 : nsFixedSizeAllocator::Free(aPtr, aSize);
109 4298 : }
110 :
111 : private:
112 : mozilla::Monitor mMonitor;
113 : };
114 :
115 : }
116 :
117 : class nsTimerEvent : public nsRunnable {
118 : public:
119 : NS_IMETHOD Run();
120 :
121 4298 : nsTimerEvent(nsTimerImpl *timer, PRInt32 generation)
122 4298 : : mTimer(timer), mGeneration(generation) {
123 : // timer is already addref'd for us
124 4298 : MOZ_COUNT_CTOR(nsTimerEvent);
125 4298 : }
126 :
127 : #ifdef DEBUG_TIMERS
128 : TimeStamp mInitTime;
129 : #endif
130 :
131 : static void Init();
132 : static void Shutdown();
133 :
134 4298 : static void* operator new(size_t size) CPP_THROW_NEW {
135 4298 : return sAllocator->Alloc(size);
136 : }
137 4298 : void operator delete(void* p) {
138 4298 : sAllocator->Free(p, sizeof(nsTimerEvent));
139 4298 : }
140 :
141 : private:
142 12894 : ~nsTimerEvent() {
143 : #ifdef DEBUG
144 4298 : if (mTimer)
145 0 : NS_WARNING("leaking reference to nsTimerImpl");
146 : #endif
147 4298 : MOZ_COUNT_DTOR(nsTimerEvent);
148 8596 : }
149 :
150 : nsTimerImpl *mTimer;
151 : PRInt32 mGeneration;
152 :
153 : static TimerEventAllocator* sAllocator;
154 : };
155 :
156 : TimerEventAllocator* nsTimerEvent::sAllocator = nsnull;
157 :
158 57582 : NS_IMPL_THREADSAFE_QUERY_INTERFACE1(nsTimerImpl, nsITimer)
159 74346 : NS_IMPL_THREADSAFE_ADDREF(nsTimerImpl)
160 :
161 74343 : NS_IMETHODIMP_(nsrefcnt) nsTimerImpl::Release(void)
162 : {
163 : nsrefcnt count;
164 :
165 74343 : NS_PRECONDITION(0 != mRefCnt, "dup release");
166 74343 : count = NS_AtomicDecrementRefcnt(mRefCnt);
167 74343 : NS_LOG_RELEASE(this, count, "nsTimerImpl");
168 74343 : if (count == 0) {
169 12301 : mRefCnt = 1; /* stabilize */
170 :
171 : /* enable this to find non-threadsafe destructors: */
172 : /* NS_ASSERT_OWNINGTHREAD(nsTimerImpl); */
173 12301 : delete this;
174 12301 : return 0;
175 : }
176 :
177 : // If only one reference remains, and mArmed is set, then the ref must be
178 : // from the TimerThread::mTimers array, so we Cancel this timer to remove
179 : // the mTimers element, and return 0 if Cancel in fact disarmed the timer.
180 : //
181 : // We use an inlined version of nsTimerImpl::Cancel here to check for the
182 : // NS_ERROR_NOT_AVAILABLE code returned by gThread->RemoveTimer when this
183 : // timer is not found in the mTimers array -- i.e., when the timer was not
184 : // in fact armed once we acquired TimerThread::mLock, in spite of mArmed
185 : // being true here. That can happen if the armed timer is being fired by
186 : // TimerThread::Run as we race and test mArmed just before it is cleared by
187 : // the timer thread. If the RemoveTimer call below doesn't find this timer
188 : // in the mTimers array, then the last ref to this timer is held manually
189 : // and temporarily by the TimerThread, so we should fall through to the
190 : // final return and return 1, not 0.
191 : //
192 : // The original version of this thread-based timer code kept weak refs from
193 : // TimerThread::mTimers, removing this timer's weak ref in the destructor,
194 : // but that leads to double-destructions in the race described above, and
195 : // adding mArmed doesn't help, because destructors can't be deferred, once
196 : // begun. But by combining reference-counting and a specialized Release
197 : // method with "is this timer still in the mTimers array once we acquire
198 : // the TimerThread's lock" testing, we defer destruction until we're sure
199 : // that only one thread has its hot little hands on this timer.
200 : //
201 : // Note that both approaches preclude a timer creator, and everyone else
202 : // except the TimerThread who might have a strong ref, from dropping all
203 : // their strong refs without implicitly canceling the timer. Timers need
204 : // non-mTimers-element strong refs to stay alive.
205 :
206 62042 : if (count == 1 && mArmed) {
207 51 : mCanceled = true;
208 :
209 51 : NS_ASSERTION(gThread, "An armed timer exists after the thread timer stopped.");
210 51 : if (NS_SUCCEEDED(gThread->RemoveTimer(this)))
211 51 : return 0;
212 : }
213 :
214 61991 : return count;
215 : }
216 :
217 12304 : nsTimerImpl::nsTimerImpl() :
218 : mClosure(nsnull),
219 : mCallbackType(CALLBACK_TYPE_UNKNOWN),
220 : mFiring(false),
221 : mArmed(false),
222 : mCanceled(false),
223 : mGeneration(0),
224 12304 : mDelay(0)
225 : {
226 : // XXXbsmedberg: shouldn't this be in Init()?
227 12304 : mEventTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
228 :
229 12304 : mCallback.c = nsnull;
230 12304 : }
231 :
232 24602 : nsTimerImpl::~nsTimerImpl()
233 : {
234 12301 : ReleaseCallback();
235 12301 : }
236 :
237 : //static
238 : nsresult
239 1419 : nsTimerImpl::Startup()
240 : {
241 : nsresult rv;
242 :
243 1419 : nsTimerEvent::Init();
244 :
245 1419 : gThread = new TimerThread();
246 1419 : if (!gThread) return NS_ERROR_OUT_OF_MEMORY;
247 :
248 1419 : NS_ADDREF(gThread);
249 1419 : rv = gThread->InitLocks();
250 :
251 1419 : if (NS_FAILED(rv)) {
252 0 : NS_RELEASE(gThread);
253 : }
254 :
255 1419 : return rv;
256 : }
257 :
258 1419 : void nsTimerImpl::Shutdown()
259 : {
260 : #ifdef DEBUG_TIMERS
261 1419 : if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
262 0 : double mean = 0, stddev = 0;
263 0 : myNS_MeanAndStdDev(sDeltaNum, sDeltaSum, sDeltaSumSquared, &mean, &stddev);
264 :
265 0 : PR_LOG(gTimerLog, PR_LOG_DEBUG, ("sDeltaNum = %f, sDeltaSum = %f, sDeltaSumSquared = %f\n", sDeltaNum, sDeltaSum, sDeltaSumSquared));
266 0 : PR_LOG(gTimerLog, PR_LOG_DEBUG, ("mean: %fms, stddev: %fms\n", mean, stddev));
267 : }
268 : #endif
269 :
270 1419 : if (!gThread)
271 0 : return;
272 :
273 1419 : gThread->Shutdown();
274 1419 : NS_RELEASE(gThread);
275 :
276 1419 : nsTimerEvent::Shutdown();
277 : }
278 :
279 :
280 21292 : nsresult nsTimerImpl::InitCommon(PRUint32 aType, PRUint32 aDelay)
281 : {
282 : nsresult rv;
283 :
284 21292 : NS_ENSURE_TRUE(gThread, NS_ERROR_NOT_INITIALIZED);
285 :
286 21292 : rv = gThread->Init();
287 21292 : NS_ENSURE_SUCCESS(rv, rv);
288 :
289 : /**
290 : * In case of re-Init, both with and without a preceding Cancel, clear the
291 : * mCanceled flag and assign a new mGeneration. But first, remove any armed
292 : * timer from the timer thread's list.
293 : *
294 : * If we are racing with the timer thread to remove this timer and we lose,
295 : * the RemoveTimer call made here will fail to find this timer in the timer
296 : * thread's list, and will return false harmlessly. We test mArmed here to
297 : * avoid the small overhead in RemoveTimer of locking the timer thread and
298 : * checking its list for this timer. It's safe to test mArmed even though
299 : * it might be cleared on another thread in the next cycle (or even already
300 : * be cleared by another CPU whose store hasn't reached our CPU's cache),
301 : * because RemoveTimer is idempotent.
302 : */
303 21292 : if (mArmed)
304 0 : gThread->RemoveTimer(this);
305 21292 : mCanceled = false;
306 21292 : mTimeout = TimeStamp();
307 21292 : mGeneration = PR_ATOMIC_INCREMENT(&gGenerator);
308 :
309 21292 : mType = (PRUint8)aType;
310 21292 : SetDelayInternal(aDelay);
311 :
312 21292 : return gThread->AddTimer(this);
313 : }
314 :
315 8659 : NS_IMETHODIMP nsTimerImpl::InitWithFuncCallback(nsTimerCallbackFunc aFunc,
316 : void *aClosure,
317 : PRUint32 aDelay,
318 : PRUint32 aType)
319 : {
320 8659 : NS_ENSURE_ARG_POINTER(aFunc);
321 :
322 8659 : ReleaseCallback();
323 8659 : mCallbackType = CALLBACK_TYPE_FUNC;
324 8659 : mCallback.c = aFunc;
325 8659 : mClosure = aClosure;
326 :
327 8659 : return InitCommon(aType, aDelay);
328 : }
329 :
330 9808 : NS_IMETHODIMP nsTimerImpl::InitWithCallback(nsITimerCallback *aCallback,
331 : PRUint32 aDelay,
332 : PRUint32 aType)
333 : {
334 9808 : NS_ENSURE_ARG_POINTER(aCallback);
335 :
336 9808 : ReleaseCallback();
337 9808 : mCallbackType = CALLBACK_TYPE_INTERFACE;
338 9808 : mCallback.i = aCallback;
339 9808 : NS_ADDREF(mCallback.i);
340 :
341 9808 : return InitCommon(aType, aDelay);
342 : }
343 :
344 2825 : NS_IMETHODIMP nsTimerImpl::Init(nsIObserver *aObserver,
345 : PRUint32 aDelay,
346 : PRUint32 aType)
347 : {
348 2825 : NS_ENSURE_ARG_POINTER(aObserver);
349 :
350 2825 : ReleaseCallback();
351 2825 : mCallbackType = CALLBACK_TYPE_OBSERVER;
352 2825 : mCallback.o = aObserver;
353 2825 : NS_ADDREF(mCallback.o);
354 :
355 2825 : return InitCommon(aType, aDelay);
356 : }
357 :
358 20237 : NS_IMETHODIMP nsTimerImpl::Cancel()
359 : {
360 20237 : mCanceled = true;
361 :
362 20237 : if (gThread)
363 19885 : gThread->RemoveTimer(this);
364 :
365 20237 : ReleaseCallback();
366 :
367 20237 : return NS_OK;
368 : }
369 :
370 3664 : NS_IMETHODIMP nsTimerImpl::SetDelay(PRUint32 aDelay)
371 : {
372 3664 : if (mCallbackType == CALLBACK_TYPE_UNKNOWN && mType == TYPE_ONE_SHOT) {
373 : // This may happen if someone tries to re-use a one-shot timer
374 : // by re-setting delay instead of reinitializing the timer.
375 : NS_ERROR("nsITimer->SetDelay() called when the "
376 0 : "one-shot timer is not set up.");
377 0 : return NS_ERROR_NOT_INITIALIZED;
378 : }
379 :
380 : // If we're already repeating precisely, update mTimeout now so that the
381 : // new delay takes effect in the future.
382 3664 : if (!mTimeout.IsNull() && mType == TYPE_REPEATING_PRECISE)
383 0 : mTimeout = TimeStamp::Now();
384 :
385 3664 : SetDelayInternal(aDelay);
386 :
387 3664 : if (!mFiring && gThread)
388 3658 : gThread->TimerDelayChanged(this);
389 :
390 3664 : return NS_OK;
391 : }
392 :
393 39 : NS_IMETHODIMP nsTimerImpl::GetDelay(PRUint32* aDelay)
394 : {
395 39 : *aDelay = mDelay;
396 39 : return NS_OK;
397 : }
398 :
399 0 : NS_IMETHODIMP nsTimerImpl::SetType(PRUint32 aType)
400 : {
401 0 : mType = (PRUint8)aType;
402 : // XXX if this is called, we should change the actual type.. this could effect
403 : // repeating timers. we need to ensure in Fire() that if mType has changed
404 : // during the callback that we don't end up with the timer in the queue twice.
405 0 : return NS_OK;
406 : }
407 :
408 0 : NS_IMETHODIMP nsTimerImpl::GetType(PRUint32* aType)
409 : {
410 0 : *aType = mType;
411 0 : return NS_OK;
412 : }
413 :
414 :
415 0 : NS_IMETHODIMP nsTimerImpl::GetClosure(void** aClosure)
416 : {
417 0 : *aClosure = mClosure;
418 0 : return NS_OK;
419 : }
420 :
421 :
422 0 : NS_IMETHODIMP nsTimerImpl::GetCallback(nsITimerCallback **aCallback)
423 : {
424 0 : if (mCallbackType == CALLBACK_TYPE_INTERFACE)
425 0 : NS_IF_ADDREF(*aCallback = mCallback.i);
426 0 : else if (mTimerCallbackWhileFiring)
427 0 : NS_ADDREF(*aCallback = mTimerCallbackWhileFiring);
428 : else
429 0 : *aCallback = nsnull;
430 :
431 0 : return NS_OK;
432 : }
433 :
434 :
435 0 : NS_IMETHODIMP nsTimerImpl::GetTarget(nsIEventTarget** aTarget)
436 : {
437 0 : NS_IF_ADDREF(*aTarget = mEventTarget);
438 0 : return NS_OK;
439 : }
440 :
441 :
442 321 : NS_IMETHODIMP nsTimerImpl::SetTarget(nsIEventTarget* aTarget)
443 : {
444 321 : NS_ENSURE_TRUE(mCallbackType == CALLBACK_TYPE_UNKNOWN,
445 : NS_ERROR_ALREADY_INITIALIZED);
446 :
447 321 : if (aTarget)
448 321 : mEventTarget = aTarget;
449 : else
450 0 : mEventTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
451 321 : return NS_OK;
452 : }
453 :
454 :
455 4298 : void nsTimerImpl::Fire()
456 : {
457 4298 : if (mCanceled)
458 55 : return;
459 :
460 8486 : SAMPLE_LABEL("Timer", "Fire");
461 :
462 4243 : TimeStamp now = TimeStamp::Now();
463 : #ifdef DEBUG_TIMERS
464 4243 : if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
465 0 : TimeDuration a = now - mStart; // actual delay in intervals
466 0 : TimeDuration b = TimeDuration::FromMilliseconds(mDelay); // expected delay in intervals
467 0 : TimeDuration delta = (a > b) ? a - b : b - a;
468 0 : PRUint32 d = delta.ToMilliseconds(); // delta in ms
469 0 : sDeltaSum += d;
470 0 : sDeltaSumSquared += double(d) * double(d);
471 0 : sDeltaNum++;
472 :
473 0 : PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] expected delay time %4ums\n", this, mDelay));
474 0 : PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] actual delay time %fms\n", this, a.ToMilliseconds()));
475 0 : PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] (mType is %d) -------\n", this, mType));
476 0 : PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] delta %4dms\n", this, (a > b) ? (PRInt32)d : -(PRInt32)d));
477 :
478 0 : mStart = mStart2;
479 0 : mStart2 = TimeStamp();
480 : }
481 : #endif
482 :
483 4243 : TimeStamp timeout = mTimeout;
484 4243 : if (IsRepeatingPrecisely()) {
485 : // Precise repeating timers advance mTimeout by mDelay without fail before
486 : // calling Fire().
487 1 : timeout -= TimeDuration::FromMilliseconds(mDelay);
488 : }
489 4243 : if (gThread)
490 4243 : gThread->UpdateFilter(mDelay, timeout, now);
491 :
492 4243 : if (mCallbackType == CALLBACK_TYPE_INTERFACE)
493 2415 : mTimerCallbackWhileFiring = mCallback.i;
494 4243 : mFiring = true;
495 :
496 : // Handle callbacks that re-init the timer, but avoid leaking.
497 : // See bug 330128.
498 4243 : CallbackUnion callback = mCallback;
499 4243 : PRUintn callbackType = mCallbackType;
500 4243 : if (callbackType == CALLBACK_TYPE_INTERFACE)
501 2415 : NS_ADDREF(callback.i);
502 1828 : else if (callbackType == CALLBACK_TYPE_OBSERVER)
503 19 : NS_ADDREF(callback.o);
504 4243 : ReleaseCallback();
505 :
506 4243 : switch (callbackType) {
507 : case CALLBACK_TYPE_FUNC:
508 1809 : callback.c(this, mClosure);
509 1809 : break;
510 : case CALLBACK_TYPE_INTERFACE:
511 2415 : callback.i->Notify(this);
512 2415 : break;
513 : case CALLBACK_TYPE_OBSERVER:
514 : callback.o->Observe(static_cast<nsITimer*>(this),
515 : NS_TIMER_CALLBACK_TOPIC,
516 19 : nsnull);
517 19 : break;
518 : default:;
519 : }
520 :
521 : // If the callback didn't re-init the timer, and it's not a one-shot timer,
522 : // restore the callback state.
523 5994 : if (mCallbackType == CALLBACK_TYPE_UNKNOWN &&
524 1751 : mType != TYPE_ONE_SHOT && !mCanceled) {
525 1701 : mCallback = callback;
526 1701 : mCallbackType = callbackType;
527 : } else {
528 : // The timer was a one-shot, or the callback was reinitialized.
529 2542 : if (callbackType == CALLBACK_TYPE_INTERFACE)
530 2406 : NS_RELEASE(callback.i);
531 136 : else if (callbackType == CALLBACK_TYPE_OBSERVER)
532 19 : NS_RELEASE(callback.o);
533 : }
534 :
535 4243 : mFiring = false;
536 4243 : mTimerCallbackWhileFiring = nsnull;
537 :
538 : #ifdef DEBUG_TIMERS
539 4243 : if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
540 0 : PR_LOG(gTimerLog, PR_LOG_DEBUG,
541 : ("[this=%p] Took %fms to fire timer callback\n",
542 : this, (TimeStamp::Now() - now).ToMilliseconds()));
543 : }
544 : #endif
545 :
546 : // Reschedule repeating timers, except REPEATING_PRECISE which already did
547 : // that in PostTimerEvent, but make sure that we aren't armed already (which
548 : // can happen if the callback reinitialized the timer).
549 4243 : if (IsRepeating() && mType != TYPE_REPEATING_PRECISE && !mArmed) {
550 1750 : if (mType == TYPE_REPEATING_SLACK)
551 1750 : SetDelayInternal(mDelay); // force mTimeout to be recomputed. For
552 : // REPEATING_PRECISE_CAN_SKIP timers this has
553 : // already happened.
554 1750 : if (gThread)
555 1750 : gThread->AddTimer(this);
556 : }
557 : }
558 :
559 1419 : void nsTimerEvent::Init()
560 : {
561 1419 : sAllocator = new TimerEventAllocator();
562 : static const size_t kBucketSizes[] = {sizeof(nsTimerEvent)};
563 1419 : static const PRInt32 kNumBuckets = mozilla::ArrayLength(kBucketSizes);
564 : static const PRInt32 kInitialPoolSize = 1024 * NS_SIZE_IN_HEAP(sizeof(nsTimerEvent));
565 1419 : sAllocator->Init("TimerEventPool", kBucketSizes, kNumBuckets, kInitialPoolSize);
566 1419 : }
567 :
568 1419 : void nsTimerEvent::Shutdown()
569 : {
570 1419 : delete sAllocator;
571 1419 : sAllocator = nsnull;
572 1419 : }
573 :
574 4298 : NS_IMETHODIMP nsTimerEvent::Run()
575 : {
576 8596 : nsRefPtr<nsTimerImpl> timer;
577 4298 : timer.swap(mTimer);
578 :
579 4298 : if (mGeneration != timer->GetGeneration())
580 0 : return NS_OK;
581 :
582 : #ifdef DEBUG_TIMERS
583 4298 : if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
584 0 : TimeStamp now = TimeStamp::Now();
585 0 : PR_LOG(gTimerLog, PR_LOG_DEBUG,
586 : ("[this=%p] time between PostTimerEvent() and Fire(): %fms\n",
587 : this, (now - mInitTime).ToMilliseconds()));
588 : }
589 : #endif
590 :
591 4298 : timer->Fire();
592 :
593 4298 : return NS_OK;
594 : }
595 :
596 4298 : nsresult nsTimerImpl::PostTimerEvent()
597 : {
598 : // XXX we may want to reuse this nsTimerEvent in the case of repeating timers.
599 :
600 : // Since TimerThread addref'd 'this' for us, we don't need to addref here.
601 : // We will release in destroyMyEvent. We need to copy the generation number
602 : // from this timer into the event, so we can avoid firing a timer that was
603 : // re-initialized after being canceled.
604 :
605 8596 : nsRefPtr<nsTimerEvent> event = new nsTimerEvent(this, mGeneration);
606 4298 : if (!event)
607 0 : return NS_ERROR_OUT_OF_MEMORY;
608 :
609 : #ifdef DEBUG_TIMERS
610 4298 : if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
611 0 : event->mInitTime = TimeStamp::Now();
612 : }
613 : #endif
614 :
615 : // If this is a repeating precise timer, we need to calculate the time for
616 : // the next timer to fire before we make the callback.
617 4298 : if (IsRepeatingPrecisely()) {
618 1 : SetDelayInternal(mDelay);
619 :
620 : // But only re-arm REPEATING_PRECISE timers.
621 1 : if (gThread && mType == TYPE_REPEATING_PRECISE) {
622 1 : nsresult rv = gThread->AddTimer(this);
623 1 : if (NS_FAILED(rv))
624 0 : return rv;
625 : }
626 : }
627 :
628 4298 : nsresult rv = mEventTarget->Dispatch(event, NS_DISPATCH_NORMAL);
629 4298 : if (NS_FAILED(rv) && gThread)
630 0 : gThread->RemoveTimer(this);
631 4298 : return rv;
632 : }
633 :
634 26707 : void nsTimerImpl::SetDelayInternal(PRUint32 aDelay)
635 : {
636 26707 : TimeDuration delayInterval = TimeDuration::FromMilliseconds(aDelay);
637 :
638 26707 : mDelay = aDelay;
639 :
640 26707 : TimeStamp now = TimeStamp::Now();
641 26707 : if (mTimeout.IsNull() || mType != TYPE_REPEATING_PRECISE)
642 26706 : mTimeout = now;
643 :
644 26707 : mTimeout += delayInterval;
645 :
646 : #ifdef DEBUG_TIMERS
647 26707 : if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) {
648 0 : if (mStart.IsNull())
649 0 : mStart = now;
650 : else
651 0 : mStart2 = now;
652 : }
653 : #endif
654 26707 : }
655 :
656 : // NOT FOR PUBLIC CONSUMPTION!
657 : nsresult
658 0 : NS_NewTimer(nsITimer* *aResult, nsTimerCallbackFunc aCallback, void *aClosure,
659 : PRUint32 aDelay, PRUint32 aType)
660 : {
661 0 : nsTimerImpl* timer = new nsTimerImpl();
662 0 : if (timer == nsnull)
663 0 : return NS_ERROR_OUT_OF_MEMORY;
664 0 : NS_ADDREF(timer);
665 :
666 : nsresult rv = timer->InitWithFuncCallback(aCallback, aClosure,
667 0 : aDelay, aType);
668 0 : if (NS_FAILED(rv)) {
669 0 : NS_RELEASE(timer);
670 0 : return rv;
671 : }
672 :
673 0 : *aResult = timer;
674 0 : return NS_OK;
675 4392 : }
|