1// excerpts from http://code.google.com/p/muduo/
2//
3// Use of this source code is governed by a BSD-style license
4// that can be found in the License file.
5//
6// Author: Shuo Chen (chenshuo at chenshuo dot com)
7
8#define __STDC_LIMIT_MACROS
9#include "TimerQueue.h"
10
11#include "logging/Logging.h"
12#include "EventLoop.h"
13#include "Timer.h"
14#include "TimerId.h"
15
16#include <boost/bind.hpp>
17
18#include <sys/timerfd.h>
19
20namespace muduo
21{
22namespace detail
23{
24
25int createTimerfd()
26{
27  int timerfd = ::timerfd_create(CLOCK_MONOTONIC,
28                                 TFD_NONBLOCK | TFD_CLOEXEC);
29  if (timerfd < 0)
30  {
31    LOG_SYSFATAL << "Failed in timerfd_create";
32  }
33  return timerfd;
34}
35
36struct timespec howMuchTimeFromNow(Timestamp when)
37{
38  int64_t microseconds = when.microSecondsSinceEpoch()
39                         - Timestamp::now().microSecondsSinceEpoch();
40  if (microseconds < 100)
41  {
42    microseconds = 100;
43  }
44  struct timespec ts;
45  ts.tv_sec = static_cast<time_t>(
46      microseconds / Timestamp::kMicroSecondsPerSecond);
47  ts.tv_nsec = static_cast<long>(
48      (microseconds % Timestamp::kMicroSecondsPerSecond) * 1000);
49  return ts;
50}
51
52void readTimerfd(int timerfd, Timestamp now)
53{
54  uint64_t howmany;
55  ssize_t n = ::read(timerfd, &howmany, sizeof howmany);
56  LOG_TRACE << "TimerQueue::handleRead() " << howmany << " at " << now.toString();
57  if (n != sizeof howmany)
58  {
59    LOG_ERROR << "TimerQueue::handleRead() reads " << n << " bytes instead of 8";
60  }
61}
62
63void resetTimerfd(int timerfd, Timestamp expiration)
64{
65  // wake up loop by timerfd_settime()
66  struct itimerspec newValue;
67  struct itimerspec oldValue;
68  bzero(&newValue, sizeof newValue);
69  bzero(&oldValue, sizeof oldValue);
70  newValue.it_value = howMuchTimeFromNow(expiration);
71  int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue);
72  if (ret)
73  {
74    LOG_SYSERR << "timerfd_settime()";
75  }
76}
77
78}
79}
80
81using namespace muduo;
82using namespace muduo::detail;
83
84TimerQueue::TimerQueue(EventLoop* loop)
85  : loop_(loop),
86    timerfd_(createTimerfd()),
87    timerfdChannel_(loop, timerfd_),
88    timers_()
89{
90  timerfdChannel_.setReadCallback(
91      boost::bind(&TimerQueue::handleRead, this));
92  // we are always reading the timerfd, we disarm it with timerfd_settime.
93  timerfdChannel_.enableReading();
94}
95
96TimerQueue::~TimerQueue()
97{
98  ::close(timerfd_);
99  // do not remove channel, since we're in EventLoop::dtor();
100  for (TimerList::iterator it = timers_.begin();
101      it != timers_.end(); ++it)
102  {
103    delete it->second;
104  }
105}
106
107TimerId TimerQueue::addTimer(const TimerCallback& cb,
108                             Timestamp when,
109                             double interval)
110{
111  Timer* timer = new Timer(cb, when, interval);
112  loop_->runInLoop(
113      boost::bind(&TimerQueue::addTimerInLoop, this, timer));
114  return TimerId(timer);
115}
116
117void TimerQueue::addTimerInLoop(Timer* timer)
118{
119  loop_->assertInLoopThread();
120  bool earliestChanged = insert(timer);
121
122  if (earliestChanged)
123  {
124    resetTimerfd(timerfd_, timer->expiration());
125  }
126}
127
128void TimerQueue::handleRead()
129{
130  loop_->assertInLoopThread();
131  Timestamp now(Timestamp::now());
132  readTimerfd(timerfd_, now);
133
134  std::vector<Entry> expired = getExpired(now);
135
136  // safe to callback outside critical section
137  for (std::vector<Entry>::iterator it = expired.begin();
138      it != expired.end(); ++it)
139  {
140    it->second->run();
141  }
142
143  reset(expired, now);
144}
145
146std::vector<TimerQueue::Entry> TimerQueue::getExpired(Timestamp now)
147{
148  std::vector<Entry> expired;
149  Entry sentry = std::make_pair(now, reinterpret_cast<Timer*>(UINTPTR_MAX));
150  TimerList::iterator it = timers_.lower_bound(sentry);
151  assert(it == timers_.end() || now < it->first);
152  std::copy(timers_.begin(), it, back_inserter(expired));
153  timers_.erase(timers_.begin(), it);
154
155  return expired;
156}
157
158void TimerQueue::reset(const std::vector<Entry>& expired, Timestamp now)
159{
160  Timestamp nextExpire;
161
162  for (std::vector<Entry>::const_iterator it = expired.begin();
163      it != expired.end(); ++it)
164  {
165    if (it->second->repeat())
166    {
167      it->second->restart(now);
168      insert(it->second);
169    }
170    else
171    {
172      // FIXME move to a free list
173      delete it->second;
174    }
175  }
176
177  if (!timers_.empty())
178  {
179    nextExpire = timers_.begin()->second->expiration();
180  }
181
182  if (nextExpire.valid())
183  {
184    resetTimerfd(timerfd_, nextExpire);
185  }
186}
187
188bool TimerQueue::insert(Timer* timer)
189{
190  bool earliestChanged = false;
191  Timestamp when = timer->expiration();
192  TimerList::iterator it = timers_.begin();
193  if (it == timers_.end() || when < it->first)
194  {
195    earliestChanged = true;
196  }
197  std::pair<TimerList::iterator, bool> result =
198          timers_.insert(std::make_pair(when, timer));
199  assert(result.second);
200  return earliestChanged;
201}
202
203