1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/common/worker_thread_ticker.h"
6
7#include <algorithm>
8
9#include "base/logging.h"
10#include "base/message_loop.h"
11#include "base/task.h"
12#include "base/threading/thread.h"
13
14class WorkerThreadTicker::TimerTask : public Task {
15 public:
16  explicit TimerTask(WorkerThreadTicker* ticker) : ticker_(ticker) {
17  }
18
19  virtual void Run() {
20    // When the ticker is running, the handler list CANNOT be modified.
21    // So we can do the enumeration safely without a lock
22    TickHandlerListType* handlers = &ticker_->tick_handler_list_;
23    for (TickHandlerListType::const_iterator i = handlers->begin();
24         i != handlers->end(); ++i) {
25      (*i)->OnTick();
26    }
27
28    ticker_->ScheduleTimerTask();
29  }
30
31 private:
32  WorkerThreadTicker* ticker_;
33};
34
35WorkerThreadTicker::WorkerThreadTicker(int tick_interval)
36    : timer_thread_("worker_thread_ticker"),
37      is_running_(false),
38      tick_interval_(tick_interval) {
39}
40
41WorkerThreadTicker::~WorkerThreadTicker() {
42  Stop();
43}
44
45bool WorkerThreadTicker::RegisterTickHandler(Callback *tick_handler) {
46  DCHECK(tick_handler);
47  base::AutoLock lock(lock_);
48  // You cannot change the list of handlers when the timer is running.
49  // You need to call Stop first.
50  if (IsRunning())
51    return false;
52  tick_handler_list_.push_back(tick_handler);
53  return true;
54}
55
56bool WorkerThreadTicker::UnregisterTickHandler(Callback *tick_handler) {
57  DCHECK(tick_handler);
58  base::AutoLock lock(lock_);
59  // You cannot change the list of handlers when the timer is running.
60  // You need to call Stop first.
61  if (IsRunning()) {
62    return false;
63  }
64  TickHandlerListType::iterator index = std::remove(tick_handler_list_.begin(),
65                                                    tick_handler_list_.end(),
66                                                    tick_handler);
67  if (index == tick_handler_list_.end()) {
68    return false;
69  }
70  tick_handler_list_.erase(index, tick_handler_list_.end());
71  return true;
72}
73
74bool WorkerThreadTicker::Start() {
75  // Do this in a lock because we don't want 2 threads to
76  // call Start at the same time
77  base::AutoLock lock(lock_);
78  if (IsRunning())
79    return false;
80  if (!timer_thread_.Start())
81    return false;
82  is_running_ = true;
83  ScheduleTimerTask();
84  return true;
85}
86
87bool WorkerThreadTicker::Stop() {
88  // Do this in a lock because we don't want 2 threads to
89  // call Stop at the same time
90  base::AutoLock lock(lock_);
91  if (!IsRunning())
92    return false;
93  is_running_ = false;
94  timer_thread_.Stop();
95  return true;
96}
97
98void WorkerThreadTicker::ScheduleTimerTask() {
99  timer_thread_.message_loop()->PostDelayedTask(FROM_HERE, new TimerTask(this),
100                                                tick_interval_);
101}
102