diff options
-rw-r--r-- | base/timer.h | 41 | ||||
-rw-r--r-- | base/timer_unittest.cc | 21 |
2 files changed, 56 insertions, 6 deletions
diff --git a/base/timer.h b/base/timer.h index 5361130..9aa084b 100644 --- a/base/timer.h +++ b/base/timer.h @@ -82,6 +82,7 @@ class BaseTimer_Helper { TimerTask(TimeDelta delay) : delay_(delay) { // timer_ is set in InitiateDelayedTask. } + virtual ~TimerTask() {} BaseTimer_Helper* timer_; TimeDelta delay_; }; @@ -135,21 +136,49 @@ class BaseTimer : public BaseTimer_Helper { receiver_(receiver), method_(method) { } + + virtual ~TimerTask() { + // This task may be getting cleared because the MessageLoop has been + // destructed. If so, don't leave the Timer with a dangling pointer + // to this now-defunct task. + ClearBaseTimer(); + } + virtual void Run() { if (!timer_) // timer_ is null if we were orphaned. return; - SelfType* self = static_cast<SelfType*>(timer_); - if (kIsRepeating) { - self->Reset(); - } else { - self->delayed_task_ = NULL; - } + if (kIsRepeating) + ResetBaseTimer(); + else + ClearBaseTimer(); DispatchToMethod(receiver_, method_, Tuple0()); } + TimerTask* Clone() const { return new TimerTask(delay_, receiver_, method_); } + private: + // Inform the Base that the timer is no longer active. + void ClearBaseTimer() { + if (timer_) { + SelfType* self = static_cast<SelfType*>(timer_); + // It is possible that the Timer has already been reset, and that this + // Task is old. So, if the Timer points to a different task, assume + // that the Timer has already taken care of properly setting the task. + if (self->delayed_task_ == this) + self->delayed_task_ = NULL; + } + } + + // Inform the Base that we're resetting the timer. + void ResetBaseTimer() { + DCHECK(timer_); + DCHECK(kIsRepeating); + SelfType* self = static_cast<SelfType*>(timer_); + self->Reset(); + } + Receiver* receiver_; ReceiverMethod method_; }; diff --git a/base/timer_unittest.cc b/base/timer_unittest.cc index face9c8..1fb44a3 100644 --- a/base/timer_unittest.cc +++ b/base/timer_unittest.cc @@ -145,3 +145,24 @@ TEST(TimerTest, RepeatingTimer_Cancel) { RunTest_RepeatingTimer_Cancel(MessageLoop::TYPE_UI); RunTest_RepeatingTimer_Cancel(MessageLoop::TYPE_IO); } + +TEST(TimerTest, MessageLoopShutdown) { + // This test is designed to verify that shutdown of the + // message loop does not cause crashes if there were pending + // timers not yet fired. It may only trigger exceptions + // if debug heap checking (or purify) is enabled. + bool did_run = false; + { + OneShotTimerTester a(&did_run); + OneShotTimerTester b(&did_run); + OneShotTimerTester c(&did_run); + OneShotTimerTester d(&did_run); + { + MessageLoop loop(MessageLoop::TYPE_DEFAULT); + a.Start(); + b.Start(); + } // MessageLoop destructs by falling out of scope. + } // OneShotTimers destruct. SHOULD NOT CRASH, of course. + + EXPECT_EQ(false, did_run); +} |