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_->assertInLoopThread();
113  bool earliestChanged = insert(timer);
114
115  if (earliestChanged)
116  {
117    resetTimerfd(timerfd_, timer->expiration());
118  }
119  return TimerId(timer);
120}
121
122void TimerQueue::handleRead()
123{
124  loop_->assertInLoopThread();
125  Timestamp now(Timestamp::now());
126  readTimerfd(timerfd_, now);
127
128  std::vector<Entry> expired = getExpired(now);
129
130  // safe to callback outside critical section
131  for (std::vector<Entry>::iterator it = expired.begin();
132      it != expired.end(); ++it)
133  {
134    it->second->run();
135  }
136
137  reset(expired, now);
138}
139
140std::vector<TimerQueue::Entry> TimerQueue::getExpired(Timestamp now)
141{
142  std::vector<Entry> expired;
143  Entry sentry = std::make_pair(now, reinterpret_cast<Timer*>(UINTPTR_MAX));
144  TimerList::iterator it = timers_.lower_bound(sentry);
145  assert(it == timers_.end() || now < it->first);
146  std::copy(timers_.begin(), it, back_inserter(expired));
147  timers_.erase(timers_.begin(), it);
148
149  return expired;
150}
151
152void TimerQueue::reset(const std::vector<Entry>& expired, Timestamp now)
153{
154  Timestamp nextExpire;
155
156  for (std::vector<Entry>::const_iterator it = expired.begin();
157      it != expired.end(); ++it)
158  {
159    if (it->second->repeat())
160    {
161      it->second->restart(now);
162      insert(it->second);
163    }
164    else
165    {
166      // FIXME move to a free list
167      delete it->second;
168    }
169  }
170
171  if (!timers_.empty())
172  {
173    nextExpire = timers_.begin()->second->expiration();
174  }
175
176  if (nextExpire.valid())
177  {
178    resetTimerfd(timerfd_, nextExpire);
179  }
180}
181
182bool TimerQueue::insert(Timer* timer)
183{
184  bool earliestChanged = false;
185  Timestamp when = timer->expiration();
186  TimerList::iterator it = timers_.begin();
187  if (it == timers_.end() || when < it->first)
188  {
189    earliestChanged = true;
190  }
191  std::pair<TimerList::iterator, bool> result =
192          timers_.insert(std::make_pair(when, timer));
193  assert(result.second);
194  return earliestChanged;
195}
196
197