xpcom/threads/nsTimerImpl.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:d39f136eb2bd
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "nsTimerImpl.h"
8 #include "TimerThread.h"
9 #include "nsAutoPtr.h"
10 #include "nsThreadManager.h"
11 #include "nsThreadUtils.h"
12 #include "plarena.h"
13 #include "pratom.h"
14 #include "GeckoProfiler.h"
15 #include "mozilla/Atomics.h"
16
17 using mozilla::Atomic;
18 using mozilla::TimeDuration;
19 using mozilla::TimeStamp;
20
21 static Atomic<int32_t> gGenerator;
22 static TimerThread* gThread = nullptr;
23
24 #ifdef DEBUG_TIMERS
25
26 PRLogModuleInfo*
27 GetTimerLog()
28 {
29 static PRLogModuleInfo *sLog;
30 if (!sLog)
31 sLog = PR_NewLogModule("nsTimerImpl");
32 return sLog;
33 }
34
35 #include <math.h>
36
37 double nsTimerImpl::sDeltaSumSquared = 0;
38 double nsTimerImpl::sDeltaSum = 0;
39 double nsTimerImpl::sDeltaNum = 0;
40
41 static void
42 myNS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
43 double *meanResult, double *stdDevResult)
44 {
45 double mean = 0.0, var = 0.0, stdDev = 0.0;
46 if (n > 0.0 && sumOfValues >= 0) {
47 mean = sumOfValues / n;
48 double temp = (n * sumOfSquaredValues) - (sumOfValues * sumOfValues);
49 if (temp < 0.0 || n <= 1)
50 var = 0.0;
51 else
52 var = temp / (n * (n - 1));
53 // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
54 stdDev = var != 0.0 ? sqrt(var) : 0.0;
55 }
56 *meanResult = mean;
57 *stdDevResult = stdDev;
58 }
59 #endif
60
61 namespace {
62
63 // TimerEventAllocator is a thread-safe allocator used only for nsTimerEvents.
64 // It's needed to avoid contention over the default allocator lock when
65 // firing timer events (see bug 733277). The thread-safety is required because
66 // nsTimerEvent objects are allocated on the timer thread, and freed on another
67 // thread. Because TimerEventAllocator has its own lock, contention over that
68 // lock is limited to the allocation and deallocation of nsTimerEvent objects.
69 //
70 // Because this allocator is layered over PLArenaPool, it never shrinks -- even
71 // "freed" nsTimerEvents aren't truly freed, they're just put onto a free-list
72 // for later recycling. So the amount of memory consumed will always be equal
73 // to the high-water mark consumption. But nsTimerEvents are small and it's
74 // unusual to have more than a few hundred of them, so this shouldn't be a
75 // problem in practice.
76
77 class TimerEventAllocator
78 {
79 private:
80 struct FreeEntry {
81 FreeEntry* mNext;
82 };
83
84 PLArenaPool mPool;
85 FreeEntry* mFirstFree;
86 mozilla::Monitor mMonitor;
87
88 public:
89 TimerEventAllocator()
90 : mFirstFree(nullptr),
91 mMonitor("TimerEventAllocator")
92 {
93 PL_InitArenaPool(&mPool, "TimerEventPool", 4096, /* align = */ 0);
94 }
95
96 ~TimerEventAllocator()
97 {
98 PL_FinishArenaPool(&mPool);
99 }
100
101 void* Alloc(size_t aSize);
102 void Free(void* aPtr);
103 };
104
105 } // anonymous namespace
106
107 class nsTimerEvent : public nsRunnable {
108 public:
109 NS_IMETHOD Run();
110
111 nsTimerEvent()
112 : mTimer()
113 , mGeneration(0)
114 {
115 MOZ_COUNT_CTOR(nsTimerEvent);
116
117 MOZ_ASSERT(gThread->IsOnTimerThread(),
118 "nsTimer must always be allocated on the timer thread");
119
120 sAllocatorUsers++;
121 }
122
123 #ifdef DEBUG_TIMERS
124 TimeStamp mInitTime;
125 #endif
126
127 static void Init();
128 static void Shutdown();
129 static void DeleteAllocatorIfNeeded();
130
131 static void* operator new(size_t size) CPP_THROW_NEW {
132 return sAllocator->Alloc(size);
133 }
134 void operator delete(void* p) {
135 sAllocator->Free(p);
136 DeleteAllocatorIfNeeded();
137 }
138
139 already_AddRefed<nsTimerImpl> ForgetTimer()
140 {
141 return mTimer.forget();
142 }
143
144 void SetTimer(already_AddRefed<nsTimerImpl> aTimer)
145 {
146 mTimer = aTimer;
147 mGeneration = mTimer->GetGeneration();
148 }
149
150 private:
151 ~nsTimerEvent() {
152 MOZ_COUNT_DTOR(nsTimerEvent);
153
154 MOZ_ASSERT(!sCanDeleteAllocator || sAllocatorUsers > 0,
155 "This will result in us attempting to deallocate the nsTimerEvent allocator twice");
156 sAllocatorUsers--;
157 }
158
159 nsRefPtr<nsTimerImpl> mTimer;
160 int32_t mGeneration;
161
162 static TimerEventAllocator* sAllocator;
163 static Atomic<int32_t> sAllocatorUsers;
164 static bool sCanDeleteAllocator;
165 };
166
167 TimerEventAllocator* nsTimerEvent::sAllocator = nullptr;
168 Atomic<int32_t> nsTimerEvent::sAllocatorUsers;
169 bool nsTimerEvent::sCanDeleteAllocator = false;
170
171 namespace {
172
173 void* TimerEventAllocator::Alloc(size_t aSize)
174 {
175 MOZ_ASSERT(aSize == sizeof(nsTimerEvent));
176
177 mozilla::MonitorAutoLock lock(mMonitor);
178
179 void* p;
180 if (mFirstFree) {
181 p = mFirstFree;
182 mFirstFree = mFirstFree->mNext;
183 }
184 else {
185 PL_ARENA_ALLOCATE(p, &mPool, aSize);
186 if (!p)
187 return nullptr;
188 }
189
190 return p;
191 }
192
193 void TimerEventAllocator::Free(void* aPtr)
194 {
195 mozilla::MonitorAutoLock lock(mMonitor);
196
197 FreeEntry* entry = reinterpret_cast<FreeEntry*>(aPtr);
198
199 entry->mNext = mFirstFree;
200 mFirstFree = entry;
201 }
202
203 } // anonymous namespace
204
205 NS_IMPL_QUERY_INTERFACE(nsTimerImpl, nsITimer)
206 NS_IMPL_ADDREF(nsTimerImpl)
207
208 NS_IMETHODIMP_(MozExternalRefCountType) nsTimerImpl::Release(void)
209 {
210 nsrefcnt count;
211
212 MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
213 count = --mRefCnt;
214 NS_LOG_RELEASE(this, count, "nsTimerImpl");
215 if (count == 0) {
216 mRefCnt = 1; /* stabilize */
217
218 /* enable this to find non-threadsafe destructors: */
219 /* NS_ASSERT_OWNINGTHREAD(nsTimerImpl); */
220 delete this;
221 return 0;
222 }
223
224 // If only one reference remains, and mArmed is set, then the ref must be
225 // from the TimerThread::mTimers array, so we Cancel this timer to remove
226 // the mTimers element, and return 0 if Cancel in fact disarmed the timer.
227 //
228 // We use an inlined version of nsTimerImpl::Cancel here to check for the
229 // NS_ERROR_NOT_AVAILABLE code returned by gThread->RemoveTimer when this
230 // timer is not found in the mTimers array -- i.e., when the timer was not
231 // in fact armed once we acquired TimerThread::mLock, in spite of mArmed
232 // being true here. That can happen if the armed timer is being fired by
233 // TimerThread::Run as we race and test mArmed just before it is cleared by
234 // the timer thread. If the RemoveTimer call below doesn't find this timer
235 // in the mTimers array, then the last ref to this timer is held manually
236 // and temporarily by the TimerThread, so we should fall through to the
237 // final return and return 1, not 0.
238 //
239 // The original version of this thread-based timer code kept weak refs from
240 // TimerThread::mTimers, removing this timer's weak ref in the destructor,
241 // but that leads to double-destructions in the race described above, and
242 // adding mArmed doesn't help, because destructors can't be deferred, once
243 // begun. But by combining reference-counting and a specialized Release
244 // method with "is this timer still in the mTimers array once we acquire
245 // the TimerThread's lock" testing, we defer destruction until we're sure
246 // that only one thread has its hot little hands on this timer.
247 //
248 // Note that both approaches preclude a timer creator, and everyone else
249 // except the TimerThread who might have a strong ref, from dropping all
250 // their strong refs without implicitly canceling the timer. Timers need
251 // non-mTimers-element strong refs to stay alive.
252
253 if (count == 1 && mArmed) {
254 mCanceled = true;
255
256 MOZ_ASSERT(gThread, "Armed timer exists after the thread timer stopped.");
257 if (NS_SUCCEEDED(gThread->RemoveTimer(this)))
258 return 0;
259 }
260
261 return count;
262 }
263
264 nsTimerImpl::nsTimerImpl() :
265 mClosure(nullptr),
266 mCallbackType(CALLBACK_TYPE_UNKNOWN),
267 mFiring(false),
268 mArmed(false),
269 mCanceled(false),
270 mGeneration(0),
271 mDelay(0)
272 {
273 // XXXbsmedberg: shouldn't this be in Init()?
274 mEventTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
275
276 mCallback.c = nullptr;
277 }
278
279 nsTimerImpl::~nsTimerImpl()
280 {
281 ReleaseCallback();
282 }
283
284 //static
285 nsresult
286 nsTimerImpl::Startup()
287 {
288 nsresult rv;
289
290 nsTimerEvent::Init();
291
292 gThread = new TimerThread();
293 if (!gThread) return NS_ERROR_OUT_OF_MEMORY;
294
295 NS_ADDREF(gThread);
296 rv = gThread->InitLocks();
297
298 if (NS_FAILED(rv)) {
299 NS_RELEASE(gThread);
300 }
301
302 return rv;
303 }
304
305 void nsTimerImpl::Shutdown()
306 {
307 #ifdef DEBUG_TIMERS
308 if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
309 double mean = 0, stddev = 0;
310 myNS_MeanAndStdDev(sDeltaNum, sDeltaSum, sDeltaSumSquared, &mean, &stddev);
311
312 PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("sDeltaNum = %f, sDeltaSum = %f, sDeltaSumSquared = %f\n", sDeltaNum, sDeltaSum, sDeltaSumSquared));
313 PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("mean: %fms, stddev: %fms\n", mean, stddev));
314 }
315 #endif
316
317 if (!gThread)
318 return;
319
320 gThread->Shutdown();
321 NS_RELEASE(gThread);
322
323 nsTimerEvent::Shutdown();
324 }
325
326
327 nsresult nsTimerImpl::InitCommon(uint32_t aType, uint32_t aDelay)
328 {
329 nsresult rv;
330
331 if (NS_WARN_IF(!gThread))
332 return NS_ERROR_NOT_INITIALIZED;
333 if (!mEventTarget) {
334 NS_ERROR("mEventTarget is NULL");
335 return NS_ERROR_NOT_INITIALIZED;
336 }
337
338 rv = gThread->Init();
339 if (NS_WARN_IF(NS_FAILED(rv)))
340 return rv;
341
342 /**
343 * In case of re-Init, both with and without a preceding Cancel, clear the
344 * mCanceled flag and assign a new mGeneration. But first, remove any armed
345 * timer from the timer thread's list.
346 *
347 * If we are racing with the timer thread to remove this timer and we lose,
348 * the RemoveTimer call made here will fail to find this timer in the timer
349 * thread's list, and will return false harmlessly. We test mArmed here to
350 * avoid the small overhead in RemoveTimer of locking the timer thread and
351 * checking its list for this timer. It's safe to test mArmed even though
352 * it might be cleared on another thread in the next cycle (or even already
353 * be cleared by another CPU whose store hasn't reached our CPU's cache),
354 * because RemoveTimer is idempotent.
355 */
356 if (mArmed)
357 gThread->RemoveTimer(this);
358 mCanceled = false;
359 mTimeout = TimeStamp();
360 mGeneration = gGenerator++;
361
362 mType = (uint8_t)aType;
363 SetDelayInternal(aDelay);
364
365 return gThread->AddTimer(this);
366 }
367
368 NS_IMETHODIMP nsTimerImpl::InitWithFuncCallback(nsTimerCallbackFunc aFunc,
369 void *aClosure,
370 uint32_t aDelay,
371 uint32_t aType)
372 {
373 if (NS_WARN_IF(!aFunc))
374 return NS_ERROR_INVALID_ARG;
375
376 ReleaseCallback();
377 mCallbackType = CALLBACK_TYPE_FUNC;
378 mCallback.c = aFunc;
379 mClosure = aClosure;
380
381 return InitCommon(aType, aDelay);
382 }
383
384 NS_IMETHODIMP nsTimerImpl::InitWithCallback(nsITimerCallback *aCallback,
385 uint32_t aDelay,
386 uint32_t aType)
387 {
388 if (NS_WARN_IF(!aCallback))
389 return NS_ERROR_INVALID_ARG;
390
391 ReleaseCallback();
392 mCallbackType = CALLBACK_TYPE_INTERFACE;
393 mCallback.i = aCallback;
394 NS_ADDREF(mCallback.i);
395
396 return InitCommon(aType, aDelay);
397 }
398
399 NS_IMETHODIMP nsTimerImpl::Init(nsIObserver *aObserver,
400 uint32_t aDelay,
401 uint32_t aType)
402 {
403 if (NS_WARN_IF(!aObserver))
404 return NS_ERROR_INVALID_ARG;
405
406 ReleaseCallback();
407 mCallbackType = CALLBACK_TYPE_OBSERVER;
408 mCallback.o = aObserver;
409 NS_ADDREF(mCallback.o);
410
411 return InitCommon(aType, aDelay);
412 }
413
414 NS_IMETHODIMP nsTimerImpl::Cancel()
415 {
416 mCanceled = true;
417
418 if (gThread)
419 gThread->RemoveTimer(this);
420
421 ReleaseCallback();
422
423 return NS_OK;
424 }
425
426 NS_IMETHODIMP nsTimerImpl::SetDelay(uint32_t aDelay)
427 {
428 if (mCallbackType == CALLBACK_TYPE_UNKNOWN && mType == TYPE_ONE_SHOT) {
429 // This may happen if someone tries to re-use a one-shot timer
430 // by re-setting delay instead of reinitializing the timer.
431 NS_ERROR("nsITimer->SetDelay() called when the "
432 "one-shot timer is not set up.");
433 return NS_ERROR_NOT_INITIALIZED;
434 }
435
436 // If we're already repeating precisely, update mTimeout now so that the
437 // new delay takes effect in the future.
438 if (!mTimeout.IsNull() && mType == TYPE_REPEATING_PRECISE)
439 mTimeout = TimeStamp::Now();
440
441 SetDelayInternal(aDelay);
442
443 if (!mFiring && gThread)
444 gThread->TimerDelayChanged(this);
445
446 return NS_OK;
447 }
448
449 NS_IMETHODIMP nsTimerImpl::GetDelay(uint32_t* aDelay)
450 {
451 *aDelay = mDelay;
452 return NS_OK;
453 }
454
455 NS_IMETHODIMP nsTimerImpl::SetType(uint32_t aType)
456 {
457 mType = (uint8_t)aType;
458 // XXX if this is called, we should change the actual type.. this could effect
459 // repeating timers. we need to ensure in Fire() that if mType has changed
460 // during the callback that we don't end up with the timer in the queue twice.
461 return NS_OK;
462 }
463
464 NS_IMETHODIMP nsTimerImpl::GetType(uint32_t* aType)
465 {
466 *aType = mType;
467 return NS_OK;
468 }
469
470
471 NS_IMETHODIMP nsTimerImpl::GetClosure(void** aClosure)
472 {
473 *aClosure = mClosure;
474 return NS_OK;
475 }
476
477
478 NS_IMETHODIMP nsTimerImpl::GetCallback(nsITimerCallback **aCallback)
479 {
480 if (mCallbackType == CALLBACK_TYPE_INTERFACE)
481 NS_IF_ADDREF(*aCallback = mCallback.i);
482 else if (mTimerCallbackWhileFiring)
483 NS_ADDREF(*aCallback = mTimerCallbackWhileFiring);
484 else
485 *aCallback = nullptr;
486
487 return NS_OK;
488 }
489
490
491 NS_IMETHODIMP nsTimerImpl::GetTarget(nsIEventTarget** aTarget)
492 {
493 NS_IF_ADDREF(*aTarget = mEventTarget);
494 return NS_OK;
495 }
496
497
498 NS_IMETHODIMP nsTimerImpl::SetTarget(nsIEventTarget* aTarget)
499 {
500 if (NS_WARN_IF(mCallbackType != CALLBACK_TYPE_UNKNOWN))
501 return NS_ERROR_ALREADY_INITIALIZED;
502
503 if (aTarget)
504 mEventTarget = aTarget;
505 else
506 mEventTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
507 return NS_OK;
508 }
509
510
511 void nsTimerImpl::Fire()
512 {
513 if (mCanceled)
514 return;
515
516 PROFILER_LABEL("Timer", "Fire");
517
518 #ifdef MOZ_TASK_TRACER
519 mozilla::tasktracer::AutoRunFakeTracedTask runTracedTask(mTracedTask);
520 #endif
521
522 #ifdef DEBUG_TIMERS
523 TimeStamp now = TimeStamp::Now();
524 if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
525 TimeDuration a = now - mStart; // actual delay in intervals
526 TimeDuration b = TimeDuration::FromMilliseconds(mDelay); // expected delay in intervals
527 TimeDuration delta = (a > b) ? a - b : b - a;
528 uint32_t d = delta.ToMilliseconds(); // delta in ms
529 sDeltaSum += d;
530 sDeltaSumSquared += double(d) * double(d);
531 sDeltaNum++;
532
533 PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("[this=%p] expected delay time %4ums\n", this, mDelay));
534 PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("[this=%p] actual delay time %fms\n", this, a.ToMilliseconds()));
535 PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("[this=%p] (mType is %d) -------\n", this, mType));
536 PR_LOG(GetTimerLog(), PR_LOG_DEBUG, ("[this=%p] delta %4dms\n", this, (a > b) ? (int32_t)d : -(int32_t)d));
537
538 mStart = mStart2;
539 mStart2 = TimeStamp();
540 }
541 #endif
542
543 TimeStamp timeout = mTimeout;
544 if (IsRepeatingPrecisely()) {
545 // Precise repeating timers advance mTimeout by mDelay without fail before
546 // calling Fire().
547 timeout -= TimeDuration::FromMilliseconds(mDelay);
548 }
549
550 if (mCallbackType == CALLBACK_TYPE_INTERFACE)
551 mTimerCallbackWhileFiring = mCallback.i;
552 mFiring = true;
553
554 // Handle callbacks that re-init the timer, but avoid leaking.
555 // See bug 330128.
556 CallbackUnion callback = mCallback;
557 unsigned callbackType = mCallbackType;
558 if (callbackType == CALLBACK_TYPE_INTERFACE)
559 NS_ADDREF(callback.i);
560 else if (callbackType == CALLBACK_TYPE_OBSERVER)
561 NS_ADDREF(callback.o);
562 ReleaseCallback();
563
564 switch (callbackType) {
565 case CALLBACK_TYPE_FUNC:
566 callback.c(this, mClosure);
567 break;
568 case CALLBACK_TYPE_INTERFACE:
569 callback.i->Notify(this);
570 break;
571 case CALLBACK_TYPE_OBSERVER:
572 callback.o->Observe(static_cast<nsITimer*>(this),
573 NS_TIMER_CALLBACK_TOPIC,
574 nullptr);
575 break;
576 default:;
577 }
578
579 // If the callback didn't re-init the timer, and it's not a one-shot timer,
580 // restore the callback state.
581 if (mCallbackType == CALLBACK_TYPE_UNKNOWN &&
582 mType != TYPE_ONE_SHOT && !mCanceled) {
583 mCallback = callback;
584 mCallbackType = callbackType;
585 } else {
586 // The timer was a one-shot, or the callback was reinitialized.
587 if (callbackType == CALLBACK_TYPE_INTERFACE)
588 NS_RELEASE(callback.i);
589 else if (callbackType == CALLBACK_TYPE_OBSERVER)
590 NS_RELEASE(callback.o);
591 }
592
593 mFiring = false;
594 mTimerCallbackWhileFiring = nullptr;
595
596 #ifdef DEBUG_TIMERS
597 if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
598 PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
599 ("[this=%p] Took %fms to fire timer callback\n",
600 this, (TimeStamp::Now() - now).ToMilliseconds()));
601 }
602 #endif
603
604 // Reschedule repeating timers, except REPEATING_PRECISE which already did
605 // that in PostTimerEvent, but make sure that we aren't armed already (which
606 // can happen if the callback reinitialized the timer).
607 if (IsRepeating() && mType != TYPE_REPEATING_PRECISE && !mArmed) {
608 if (mType == TYPE_REPEATING_SLACK)
609 SetDelayInternal(mDelay); // force mTimeout to be recomputed. For
610 // REPEATING_PRECISE_CAN_SKIP timers this has
611 // already happened.
612 if (gThread)
613 gThread->AddTimer(this);
614 }
615 }
616
617 void nsTimerEvent::Init()
618 {
619 sAllocator = new TimerEventAllocator();
620 }
621
622 void nsTimerEvent::Shutdown()
623 {
624 sCanDeleteAllocator = true;
625 DeleteAllocatorIfNeeded();
626 }
627
628 void nsTimerEvent::DeleteAllocatorIfNeeded()
629 {
630 if (sCanDeleteAllocator && sAllocatorUsers == 0) {
631 delete sAllocator;
632 sAllocator = nullptr;
633 }
634 }
635
636 NS_IMETHODIMP nsTimerEvent::Run()
637 {
638 if (mGeneration != mTimer->GetGeneration())
639 return NS_OK;
640
641 #ifdef DEBUG_TIMERS
642 if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
643 TimeStamp now = TimeStamp::Now();
644 PR_LOG(GetTimerLog(), PR_LOG_DEBUG,
645 ("[this=%p] time between PostTimerEvent() and Fire(): %fms\n",
646 this, (now - mInitTime).ToMilliseconds()));
647 }
648 #endif
649
650 mTimer->Fire();
651 // Since nsTimerImpl is not thread-safe, we should release |mTimer|
652 // here in the target thread to avoid race condition. Otherwise,
653 // ~nsTimerEvent() which calls nsTimerImpl::Release() could run in the
654 // timer thread and result in race condition.
655 mTimer = nullptr;
656
657 return NS_OK;
658 }
659
660 already_AddRefed<nsTimerImpl>
661 nsTimerImpl::PostTimerEvent(already_AddRefed<nsTimerImpl> aTimerRef)
662 {
663 nsRefPtr<nsTimerImpl> timer(aTimerRef);
664 if (!timer->mEventTarget) {
665 NS_ERROR("Attempt to post timer event to NULL event target");
666 return timer.forget();
667 }
668
669 // XXX we may want to reuse this nsTimerEvent in the case of repeating timers.
670
671 // Since TimerThread addref'd 'timer' for us, we don't need to addref here.
672 // We will release either in ~nsTimerEvent(), or pass the reference back to
673 // the caller. We need to copy the generation number from this timer into the
674 // event, so we can avoid firing a timer that was re-initialized after being
675 // canceled.
676
677 // Note: We override operator new for this class, and the override is
678 // fallible!
679 nsRefPtr<nsTimerEvent> event = new nsTimerEvent;
680 if (!event)
681 return timer.forget();
682
683 #ifdef DEBUG_TIMERS
684 if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
685 event->mInitTime = TimeStamp::Now();
686 }
687 #endif
688
689 // If this is a repeating precise timer, we need to calculate the time for
690 // the next timer to fire before we make the callback.
691 if (timer->IsRepeatingPrecisely()) {
692 timer->SetDelayInternal(timer->mDelay);
693
694 // But only re-arm REPEATING_PRECISE timers.
695 if (gThread && timer->mType == TYPE_REPEATING_PRECISE) {
696 nsresult rv = gThread->AddTimer(timer);
697 if (NS_FAILED(rv)) {
698 return timer.forget();
699 }
700 }
701 }
702
703 nsIEventTarget* target = timer->mEventTarget;
704 event->SetTimer(timer.forget());
705
706 nsresult rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
707 if (NS_FAILED(rv)) {
708 timer = event->ForgetTimer();
709 if (gThread) {
710 gThread->RemoveTimer(timer);
711 }
712 return timer.forget();
713 }
714
715 return nullptr;
716 }
717
718 void nsTimerImpl::SetDelayInternal(uint32_t aDelay)
719 {
720 TimeDuration delayInterval = TimeDuration::FromMilliseconds(aDelay);
721
722 mDelay = aDelay;
723
724 TimeStamp now = TimeStamp::Now();
725 if (mTimeout.IsNull() || mType != TYPE_REPEATING_PRECISE)
726 mTimeout = now;
727
728 mTimeout += delayInterval;
729
730 #ifdef DEBUG_TIMERS
731 if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) {
732 if (mStart.IsNull())
733 mStart = now;
734 else
735 mStart2 = now;
736 }
737 #endif
738 }
739
740 // NOT FOR PUBLIC CONSUMPTION!
741 nsresult
742 NS_NewTimer(nsITimer* *aResult, nsTimerCallbackFunc aCallback, void *aClosure,
743 uint32_t aDelay, uint32_t aType)
744 {
745 nsTimerImpl* timer = new nsTimerImpl();
746 if (timer == nullptr)
747 return NS_ERROR_OUT_OF_MEMORY;
748 NS_ADDREF(timer);
749
750 nsresult rv = timer->InitWithFuncCallback(aCallback, aClosure,
751 aDelay, aType);
752 if (NS_FAILED(rv)) {
753 NS_RELEASE(timer);
754 return rv;
755 }
756
757 *aResult = timer;
758 return NS_OK;
759 }

mercurial