summaryrefslogtreecommitdiffstats
path: root/base/timer.cc
blob: 7de55c70cd74b2792a5e96e02c4a42df122a3c55 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/timer.h"

#include <math.h>
#if defined(OS_WIN)
#include <mmsystem.h>
#endif

#include "base/atomic_sequence_num.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/task.h"

namespace base {

// A sequence number for all allocated times (used to break ties when
// comparing times in the TimerManager, and assure FIFO execution sequence).
static AtomicSequenceNumber timer_id_counter_(base::LINKER_INITIALIZED);

//-----------------------------------------------------------------------------
// Timer

Timer::Timer(int delay, Task* task, bool repeating)
    : task_(task),
      delay_(delay),
      repeating_(repeating) {
  timer_id_ = timer_id_counter_.GetNext();
  DCHECK(delay >= 0);
  Reset();
}

Timer::Timer(Time fire_time, Task* task)
    : fire_time_(fire_time),
      task_(task),
      repeating_(false) {
  timer_id_ = timer_id_counter_.GetNext();

  // TODO(darin): kill off this stuff.  because we are forced to compute 'now'
  // in order to determine the delay, it is possible that our fire time could
  // be in the past.  /sigh/
  creation_time_ = Time::Now();
  delay_ = static_cast<int>((fire_time_ - creation_time_).InMilliseconds());
  if (delay_ < 0)
    delay_ = 0;
}

void Timer::Reset() {
  creation_time_ = Time::Now();
  fire_time_ = creation_time_ + TimeDelta::FromMilliseconds(delay_);
  DHISTOGRAM_COUNTS(L"Timer.Durations", delay_);
}

//-----------------------------------------------------------------------------
// TimerPQueue

void TimerPQueue::RemoveTimer(Timer* timer) {
  const std::vector<Timer*>::iterator location =
      find(c.begin(), c.end(), timer);
  if (location != c.end()) {
    c.erase(location);
    make_heap(c.begin(), c.end(), TimerComparison());
  }
}

bool TimerPQueue::ContainsTimer(const Timer* timer) const {
  return find(c.begin(), c.end(), timer) != c.end();
}

//-----------------------------------------------------------------------------
// TimerManager

TimerManager::TimerManager(MessageLoop* message_loop)
    : use_broken_delay_(false),
      message_loop_(message_loop) {
#if defined(OS_WIN)
  // We've experimented with all sorts of timers, and initially tried
  // to avoid using timeBeginPeriod because it does affect the system
  // globally.  However, after much investigation, it turns out that all
  // of the major plugins (flash, windows media 9-11, and quicktime)
  // already use timeBeginPeriod to increase the speed of the clock.
  // Since the browser must work with these plugins, the browser already
  // needs to support a fast clock.  We may as well use this ourselves,
  // as it really is the best timer mechanism for our needs.
  timeBeginPeriod(1);
#endif
}

TimerManager::~TimerManager() {
#if defined(OS_WIN)
  // Match timeBeginPeriod() from construction.
  timeEndPeriod(1);
#endif

  // Be nice to unit tests, and discard and delete all timers along with the
  // embedded task objects by handing off to MessageLoop (which would have Run()
  // and optionally deleted the objects).
  while (timers_.size()) {
    Timer* pending = timers_.top();
    timers_.pop();
    message_loop_->DiscardTimer(pending);
  }
}


Timer* TimerManager::StartTimer(int delay, Task* task, bool repeating) {
  Timer* t = new Timer(delay, task, repeating);
  StartTimer(t);
  return t;
}

void TimerManager::StopTimer(Timer* timer) {
  // Make sure the timer is actually running.
  if (!IsTimerRunning(timer))
    return;
  // Kill the active timer, and remove the pending entry from the queue.
  if (timer != timers_.top()) {
    timers_.RemoveTimer(timer);
  } else {
    timers_.pop();
    DidChangeNextTimer();
  }
}

void TimerManager::ResetTimer(Timer* timer) {
  StopTimer(timer);
  timer->Reset();
  StartTimer(timer);
}

bool TimerManager::IsTimerRunning(const Timer* timer) const {
  return timers_.ContainsTimer(timer);
}

Timer* TimerManager::PeekTopTimer() {
  if (timers_.empty())
    return NULL;
  return timers_.top();
}

bool TimerManager::RunSomePendingTimers() {
  bool did_work = false;
  // Process a small group of timers.  Cap the maximum number of timers we can
  // process so we don't deny cycles to other parts of the process when lots of
  // timers have been set.
  const int kMaxTimersPerCall = 2;
  for (int i = 0; i < kMaxTimersPerCall; ++i) {
    if (timers_.empty() || timers_.top()->fire_time() > Time::Now())
      break;

    // Get a pending timer.  Deal with updating the timers_ queue and setting
    // the TopTimer.  We'll execute the timer task only after the timer queue
    // is back in a consistent state.
    Timer* pending = timers_.top();

    // If pending task isn't invoked_later, then it must be possible to run it
    // now (i.e., current task needs to be reentrant).
    // TODO(jar): We may block tasks that we can queue from being popped.
    if (!message_loop_->NestableTasksAllowed() &&
        !pending->task()->owned_by_message_loop_)
      break;

    timers_.pop();
    did_work = true;

    // If the timer is repeating, add it back to the list of timers to process.
    if (pending->repeating()) {
      pending->Reset();
      timers_.push(pending);
    }

    message_loop_->RunTimerTask(pending);
  }

  // Restart the WM_TIMER (if necessary).
  if (did_work)
    DidChangeNextTimer();

  return did_work;
}

// Note: Caller is required to call timer->Reset() before calling StartTimer().
// TODO(jar): change API so that Reset() is called as part of StartTimer, making
// the API a little less error prone.
void TimerManager::StartTimer(Timer* timer) {
  // Make sure the timer is not running.
  if (IsTimerRunning(timer))
    return;

  timers_.push(timer);  // Priority queue will sort the timer into place.

  if (timers_.top() == timer)  // We are new head of queue.
    DidChangeNextTimer();
}

Time TimerManager::GetNextFireTime() const {
  if (timers_.empty())
    return Time();

  return timers_.top()->fire_time();
}

void TimerManager::DidChangeNextTimer() {
  // Determine if the next timer expiry actually changed...
  if (!timers_.empty()) {
    const Time& expiry = timers_.top()->fire_time();
    if (expiry == next_timer_expiry_)
      return;
    next_timer_expiry_ = expiry;
  } else {
    next_timer_expiry_ = Time();
  }
  message_loop_->DidChangeNextTimerExpiry();
}

//-----------------------------------------------------------------------------
// BaseTimer_Helper 

void BaseTimer_Helper::OrphanDelayedTask() {
  if (delayed_task_) {
    delayed_task_->timer_ = NULL;
    delayed_task_ = NULL;
  }
}

void BaseTimer_Helper::InitiateDelayedTask(TimerTask* timer_task) {
  OrphanDelayedTask();

  delayed_task_ = timer_task;
  delayed_task_->timer_ = this;
  MessageLoop::current()->PostDelayedTask(
      FROM_HERE, timer_task, static_cast<int>(delay_.InMilliseconds()));
}

}  // namespace base