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