TimerQueue.cc revision 65c497a3
165c497a3SShuo Chen// excerpts from http://code.google.com/p/muduo/
265c497a3SShuo Chen//
365c497a3SShuo Chen// Use of this source code is governed by a BSD-style license
465c497a3SShuo Chen// that can be found in the License file.
565c497a3SShuo Chen//
665c497a3SShuo Chen// Author: Shuo Chen (chenshuo at chenshuo dot com)
765c497a3SShuo Chen
865c497a3SShuo Chen#define __STDC_LIMIT_MACROS
965c497a3SShuo Chen#include "TimerQueue.h"
1065c497a3SShuo Chen
1165c497a3SShuo Chen#include "logging/Logging.h"
1265c497a3SShuo Chen#include "EventLoop.h"
1365c497a3SShuo Chen#include "Timer.h"
1465c497a3SShuo Chen#include "TimerId.h"
1565c497a3SShuo Chen
1665c497a3SShuo Chen#include <boost/bind.hpp>
1765c497a3SShuo Chen
1865c497a3SShuo Chen#include <sys/timerfd.h>
1965c497a3SShuo Chen
2065c497a3SShuo Chennamespace muduo
2165c497a3SShuo Chen{
2265c497a3SShuo Chennamespace detail
2365c497a3SShuo Chen{
2465c497a3SShuo Chen
2565c497a3SShuo Chenint createTimerfd()
2665c497a3SShuo Chen{
2765c497a3SShuo Chen  int timerfd = ::timerfd_create(CLOCK_MONOTONIC,
2865c497a3SShuo Chen                                 TFD_NONBLOCK | TFD_CLOEXEC);
2965c497a3SShuo Chen  if (timerfd < 0)
3065c497a3SShuo Chen  {
3165c497a3SShuo Chen    LOG_SYSFATAL << "Failed in timerfd_create";
3265c497a3SShuo Chen  }
3365c497a3SShuo Chen  return timerfd;
3465c497a3SShuo Chen}
3565c497a3SShuo Chen
3665c497a3SShuo Chenstruct timespec howMuchTimeFromNow(Timestamp when)
3765c497a3SShuo Chen{
3865c497a3SShuo Chen  int64_t microseconds = when.microSecondsSinceEpoch()
3965c497a3SShuo Chen                         - Timestamp::now().microSecondsSinceEpoch();
4065c497a3SShuo Chen  if (microseconds < 100)
4165c497a3SShuo Chen  {
4265c497a3SShuo Chen    microseconds = 100;
4365c497a3SShuo Chen  }
4465c497a3SShuo Chen  struct timespec ts;
4565c497a3SShuo Chen  ts.tv_sec = static_cast<time_t>(
4665c497a3SShuo Chen      microseconds / Timestamp::kMicroSecondsPerSecond);
4765c497a3SShuo Chen  ts.tv_nsec = static_cast<long>(
4865c497a3SShuo Chen      (microseconds % Timestamp::kMicroSecondsPerSecond) * 1000);
4965c497a3SShuo Chen  return ts;
5065c497a3SShuo Chen}
5165c497a3SShuo Chen
5265c497a3SShuo Chenvoid readTimerfd(int timerfd, Timestamp now)
5365c497a3SShuo Chen{
5465c497a3SShuo Chen  uint64_t howmany;
5565c497a3SShuo Chen  ssize_t n = ::read(timerfd, &howmany, sizeof howmany);
5665c497a3SShuo Chen  LOG_TRACE << "TimerQueue::handleRead() " << howmany << " at " << now.toString();
5765c497a3SShuo Chen  if (n != sizeof howmany)
5865c497a3SShuo Chen  {
5965c497a3SShuo Chen    LOG_ERROR << "TimerQueue::handleRead() reads " << n << " bytes instead of 8";
6065c497a3SShuo Chen  }
6165c497a3SShuo Chen}
6265c497a3SShuo Chen
6365c497a3SShuo Chenvoid resetTimerfd(int timerfd, Timestamp expiration)
6465c497a3SShuo Chen{
6565c497a3SShuo Chen  // wake up loop by timerfd_settime()
6665c497a3SShuo Chen  struct itimerspec newValue;
6765c497a3SShuo Chen  struct itimerspec oldValue;
6865c497a3SShuo Chen  bzero(&newValue, sizeof newValue);
6965c497a3SShuo Chen  bzero(&oldValue, sizeof oldValue);
7065c497a3SShuo Chen  newValue.it_value = howMuchTimeFromNow(expiration);
7165c497a3SShuo Chen  int ret = timerfd_settime(timerfd, 0, &newValue, &oldValue);
7265c497a3SShuo Chen  if (ret)
7365c497a3SShuo Chen  {
7465c497a3SShuo Chen    LOG_SYSERR << "timerfd_settime()";
7565c497a3SShuo Chen  }
7665c497a3SShuo Chen}
7765c497a3SShuo Chen
7865c497a3SShuo Chen}
7965c497a3SShuo Chen}
8065c497a3SShuo Chen
8165c497a3SShuo Chenusing namespace muduo;
8265c497a3SShuo Chenusing namespace muduo::detail;
8365c497a3SShuo Chen
8465c497a3SShuo ChenTimerQueue::TimerQueue(EventLoop* loop)
8565c497a3SShuo Chen  : loop_(loop),
8665c497a3SShuo Chen    timerfd_(createTimerfd()),
8765c497a3SShuo Chen    timerfdChannel_(loop, timerfd_),
8865c497a3SShuo Chen    timers_()
8965c497a3SShuo Chen{
9065c497a3SShuo Chen  timerfdChannel_.setReadCallback(
9165c497a3SShuo Chen      boost::bind(&TimerQueue::handleRead, this));
9265c497a3SShuo Chen  // we are always reading the timerfd, we disarm it with timerfd_settime.
9365c497a3SShuo Chen  timerfdChannel_.enableReading();
9465c497a3SShuo Chen}
9565c497a3SShuo Chen
9665c497a3SShuo ChenTimerQueue::~TimerQueue()
9765c497a3SShuo Chen{
9865c497a3SShuo Chen  ::close(timerfd_);
9965c497a3SShuo Chen  // do not remove channel, since we're in EventLoop::dtor();
10065c497a3SShuo Chen  for (TimerList::iterator it = timers_.begin();
10165c497a3SShuo Chen      it != timers_.end(); ++it)
10265c497a3SShuo Chen  {
10365c497a3SShuo Chen    delete it->second;
10465c497a3SShuo Chen  }
10565c497a3SShuo Chen}
10665c497a3SShuo Chen
10765c497a3SShuo ChenTimerId TimerQueue::addTimer(const TimerCallback& cb,
10865c497a3SShuo Chen                             Timestamp when,
10965c497a3SShuo Chen                             double interval)
11065c497a3SShuo Chen{
11165c497a3SShuo Chen  Timer* timer = new Timer(cb, when, interval);
11265c497a3SShuo Chen  loop_->runInLoop(
11365c497a3SShuo Chen      boost::bind(&TimerQueue::scheduleInLoop, this, timer));
11465c497a3SShuo Chen  return TimerId(timer);
11565c497a3SShuo Chen}
11665c497a3SShuo Chen
11765c497a3SShuo Chenvoid TimerQueue::scheduleInLoop(Timer* timer)
11865c497a3SShuo Chen{
11965c497a3SShuo Chen  loop_->assertInLoopThread();
12065c497a3SShuo Chen  bool earliestChanged = insert(timer);
12165c497a3SShuo Chen
12265c497a3SShuo Chen  if (earliestChanged)
12365c497a3SShuo Chen  {
12465c497a3SShuo Chen    resetTimerfd(timerfd_, timer->expiration());
12565c497a3SShuo Chen  }
12665c497a3SShuo Chen}
12765c497a3SShuo Chen
12865c497a3SShuo Chenvoid TimerQueue::handleRead()
12965c497a3SShuo Chen{
13065c497a3SShuo Chen  loop_->assertInLoopThread();
13165c497a3SShuo Chen  Timestamp now(Timestamp::now());
13265c497a3SShuo Chen  readTimerfd(timerfd_, now);
13365c497a3SShuo Chen
13465c497a3SShuo Chen  std::vector<Entry> expired = getExpired(now);
13565c497a3SShuo Chen
13665c497a3SShuo Chen  // safe to callback outside critical section
13765c497a3SShuo Chen  for (std::vector<Entry>::iterator it = expired.begin();
13865c497a3SShuo Chen      it != expired.end(); ++it)
13965c497a3SShuo Chen  {
14065c497a3SShuo Chen    it->second->run();
14165c497a3SShuo Chen  }
14265c497a3SShuo Chen
14365c497a3SShuo Chen  reset(expired, now);
14465c497a3SShuo Chen}
14565c497a3SShuo Chen
14665c497a3SShuo Chenstd::vector<TimerQueue::Entry> TimerQueue::getExpired(Timestamp now)
14765c497a3SShuo Chen{
14865c497a3SShuo Chen  std::vector<Entry> expired;
14965c497a3SShuo Chen  Entry sentry = std::make_pair(now, reinterpret_cast<Timer*>(UINTPTR_MAX));
15065c497a3SShuo Chen  TimerList::iterator it = timers_.lower_bound(sentry);
15165c497a3SShuo Chen  assert(it == timers_.end() || now < it->first);
15265c497a3SShuo Chen  std::copy(timers_.begin(), it, back_inserter(expired));
15365c497a3SShuo Chen  timers_.erase(timers_.begin(), it);
15465c497a3SShuo Chen
15565c497a3SShuo Chen  return expired;
15665c497a3SShuo Chen}
15765c497a3SShuo Chen
15865c497a3SShuo Chenvoid TimerQueue::reset(const std::vector<Entry>& expired, Timestamp now)
15965c497a3SShuo Chen{
16065c497a3SShuo Chen  Timestamp nextExpire;
16165c497a3SShuo Chen
16265c497a3SShuo Chen  for (std::vector<Entry>::const_iterator it = expired.begin();
16365c497a3SShuo Chen      it != expired.end(); ++it)
16465c497a3SShuo Chen  {
16565c497a3SShuo Chen    if (it->second->repeat())
16665c497a3SShuo Chen    {
16765c497a3SShuo Chen      it->second->restart(now);
16865c497a3SShuo Chen      insert(it->second);
16965c497a3SShuo Chen    }
17065c497a3SShuo Chen    else
17165c497a3SShuo Chen    {
17265c497a3SShuo Chen      // FIXME move to a free list
17365c497a3SShuo Chen      delete it->second;
17465c497a3SShuo Chen    }
17565c497a3SShuo Chen  }
17665c497a3SShuo Chen
17765c497a3SShuo Chen  if (!timers_.empty())
17865c497a3SShuo Chen  {
17965c497a3SShuo Chen    nextExpire = timers_.begin()->second->expiration();
18065c497a3SShuo Chen  }
18165c497a3SShuo Chen
18265c497a3SShuo Chen  if (nextExpire.valid())
18365c497a3SShuo Chen  {
18465c497a3SShuo Chen    resetTimerfd(timerfd_, nextExpire);
18565c497a3SShuo Chen  }
18665c497a3SShuo Chen}
18765c497a3SShuo Chen
18865c497a3SShuo Chenbool TimerQueue::insert(Timer* timer)
18965c497a3SShuo Chen{
19065c497a3SShuo Chen  bool earliestChanged = false;
19165c497a3SShuo Chen  Timestamp when = timer->expiration();
19265c497a3SShuo Chen  TimerList::iterator it = timers_.begin();
19365c497a3SShuo Chen  if (it == timers_.end() || when < it->first)
19465c497a3SShuo Chen  {
19565c497a3SShuo Chen    earliestChanged = true;
19665c497a3SShuo Chen  }
19765c497a3SShuo Chen  std::pair<TimerList::iterator, bool> result =
19865c497a3SShuo Chen          timers_.insert(std::make_pair(when, timer));
19965c497a3SShuo Chen  assert(result.second);
20065c497a3SShuo Chen  return earliestChanged;
20165c497a3SShuo Chen}
20265c497a3SShuo Chen
203