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