19a1e991dSShuo Chen// excerpts from http://code.google.com/p/muduo/
29a1e991dSShuo Chen//
39a1e991dSShuo Chen// Use of this source code is governed by a BSD-style license
49a1e991dSShuo Chen// that can be found in the License file.
59a1e991dSShuo Chen//
69a1e991dSShuo Chen// Author: Shuo Chen (chenshuo at chenshuo dot com)
79a1e991dSShuo Chen
89a1e991dSShuo Chen#define __STDC_LIMIT_MACROS
99a1e991dSShuo Chen#include "TimerQueue.h"
109a1e991dSShuo Chen
119a1e991dSShuo Chen#include "logging/Logging.h"
129a1e991dSShuo Chen#include "EventLoop.h"
139a1e991dSShuo Chen#include "Timer.h"
149a1e991dSShuo Chen#include "TimerId.h"
159a1e991dSShuo Chen
169a1e991dSShuo Chen#include <boost/bind.hpp>
179a1e991dSShuo Chen
189a1e991dSShuo Chen#include <sys/timerfd.h>
199a1e991dSShuo Chen
209a1e991dSShuo Chennamespace muduo
219a1e991dSShuo Chen{
229a1e991dSShuo Chennamespace detail
239a1e991dSShuo Chen{
249a1e991dSShuo Chen
259a1e991dSShuo Chenint createTimerfd()
269a1e991dSShuo Chen{
279a1e991dSShuo Chen  int timerfd = ::timerfd_create(CLOCK_MONOTONIC,
289a1e991dSShuo Chen                                 TFD_NONBLOCK | TFD_CLOEXEC);
299a1e991dSShuo Chen  if (timerfd < 0)
309a1e991dSShuo Chen  {
319a1e991dSShuo Chen    LOG_SYSFATAL << "Failed in timerfd_create";
329a1e991dSShuo Chen  }
339a1e991dSShuo Chen  return timerfd;
349a1e991dSShuo Chen}
359a1e991dSShuo Chen
369a1e991dSShuo Chenstruct timespec howMuchTimeFromNow(Timestamp when)
379a1e991dSShuo Chen{
389a1e991dSShuo Chen  int64_t microseconds = when.microSecondsSinceEpoch()
399a1e991dSShuo Chen                         - Timestamp::now().microSecondsSinceEpoch();
409a1e991dSShuo Chen  if (microseconds < 100)
419a1e991dSShuo Chen  {
429a1e991dSShuo Chen    microseconds = 100;
439a1e991dSShuo Chen  }
449a1e991dSShuo Chen  struct timespec ts;
459a1e991dSShuo Chen  ts.tv_sec = static_cast<time_t>(
469a1e991dSShuo Chen      microseconds / Timestamp::kMicroSecondsPerSecond);
479a1e991dSShuo Chen  ts.tv_nsec = static_cast<long>(
489a1e991dSShuo Chen      (microseconds % Timestamp::kMicroSecondsPerSecond) * 1000);
499a1e991dSShuo Chen  return ts;
509a1e991dSShuo Chen}
519a1e991dSShuo Chen
529a1e991dSShuo Chenvoid readTimerfd(int timerfd, Timestamp now)
539a1e991dSShuo Chen{
549a1e991dSShuo Chen  uint64_t howmany;
559a1e991dSShuo Chen  ssize_t n = ::read(timerfd, &howmany, sizeof howmany);
569a1e991dSShuo Chen  LOG_TRACE << "TimerQueue::handleRead() " << howmany << " at " << now.toString();
579a1e991dSShuo Chen  if (n != sizeof howmany)
589a1e991dSShuo Chen  {
599a1e991dSShuo Chen    LOG_ERROR << "TimerQueue::handleRead() reads " << n << " bytes instead of 8";
609a1e991dSShuo Chen  }
619a1e991dSShuo Chen}
629a1e991dSShuo Chen
639a1e991dSShuo Chenvoid resetTimerfd(int timerfd, Timestamp expiration)
649a1e991dSShuo Chen{
659a1e991dSShuo Chen  // wake up loop by timerfd_settime()
669a1e991dSShuo Chen  struct itimerspec newValue;
679a1e991dSShuo Chen  struct itimerspec oldValue;
689a1e991dSShuo Chen  bzero(&newValue, sizeof newValue);
699a1e991dSShuo Chen  bzero(&oldValue, sizeof oldValue);
709a1e991dSShuo Chen  newValue.it_value = howMuchTimeFromNow(expiration);
71b4a5ce52SShuo Chen  int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue);
729a1e991dSShuo Chen  if (ret)
739a1e991dSShuo Chen  {
749a1e991dSShuo Chen    LOG_SYSERR << "timerfd_settime()";
759a1e991dSShuo Chen  }
769a1e991dSShuo Chen}
779a1e991dSShuo Chen
789a1e991dSShuo Chen}
799a1e991dSShuo Chen}
809a1e991dSShuo Chen
819a1e991dSShuo Chenusing namespace muduo;
829a1e991dSShuo Chenusing namespace muduo::detail;
839a1e991dSShuo Chen
849a1e991dSShuo ChenTimerQueue::TimerQueue(EventLoop* loop)
859a1e991dSShuo Chen  : loop_(loop),
869a1e991dSShuo Chen    timerfd_(createTimerfd()),
879a1e991dSShuo Chen    timerfdChannel_(loop, timerfd_),
889a1e991dSShuo Chen    timers_()
899a1e991dSShuo Chen{
909a1e991dSShuo Chen  timerfdChannel_.setReadCallback(
919a1e991dSShuo Chen      boost::bind(&TimerQueue::handleRead, this));
929a1e991dSShuo Chen  // we are always reading the timerfd, we disarm it with timerfd_settime.
939a1e991dSShuo Chen  timerfdChannel_.enableReading();
949a1e991dSShuo Chen}
959a1e991dSShuo Chen
969a1e991dSShuo ChenTimerQueue::~TimerQueue()
979a1e991dSShuo Chen{
989a1e991dSShuo Chen  ::close(timerfd_);
999a1e991dSShuo Chen  // do not remove channel, since we're in EventLoop::dtor();
1009a1e991dSShuo Chen  for (TimerList::iterator it = timers_.begin();
1019a1e991dSShuo Chen      it != timers_.end(); ++it)
1029a1e991dSShuo Chen  {
1039a1e991dSShuo Chen    delete it->second;
1049a1e991dSShuo Chen  }
1059a1e991dSShuo Chen}
1069a1e991dSShuo Chen
1079a1e991dSShuo ChenTimerId TimerQueue::addTimer(const TimerCallback& cb,
1089a1e991dSShuo Chen                             Timestamp when,
1099a1e991dSShuo Chen                             double interval)
1109a1e991dSShuo Chen{
1119a1e991dSShuo Chen  Timer* timer = new Timer(cb, when, interval);
1129a1e991dSShuo Chen  loop_->runInLoop(
113344a2ce1SShuo Chen      boost::bind(&TimerQueue::addTimerInLoop, this, timer));
1149a1e991dSShuo Chen  return TimerId(timer);
1159a1e991dSShuo Chen}
1169a1e991dSShuo Chen
117344a2ce1SShuo Chenvoid TimerQueue::addTimerInLoop(Timer* timer)
1189a1e991dSShuo Chen{
1199a1e991dSShuo Chen  loop_->assertInLoopThread();
1209a1e991dSShuo Chen  bool earliestChanged = insert(timer);
1219a1e991dSShuo Chen
1229a1e991dSShuo Chen  if (earliestChanged)
1239a1e991dSShuo Chen  {
1249a1e991dSShuo Chen    resetTimerfd(timerfd_, timer->expiration());
1259a1e991dSShuo Chen  }
1269a1e991dSShuo Chen}
1279a1e991dSShuo Chen
1289a1e991dSShuo Chenvoid TimerQueue::handleRead()
1299a1e991dSShuo Chen{
1309a1e991dSShuo Chen  loop_->assertInLoopThread();
1319a1e991dSShuo Chen  Timestamp now(Timestamp::now());
1329a1e991dSShuo Chen  readTimerfd(timerfd_, now);
1339a1e991dSShuo Chen
1349a1e991dSShuo Chen  std::vector<Entry> expired = getExpired(now);
1359a1e991dSShuo Chen
1369a1e991dSShuo Chen  // safe to callback outside critical section
1379a1e991dSShuo Chen  for (std::vector<Entry>::iterator it = expired.begin();
1389a1e991dSShuo Chen      it != expired.end(); ++it)
1399a1e991dSShuo Chen  {
1409a1e991dSShuo Chen    it->second->run();
1419a1e991dSShuo Chen  }
1429a1e991dSShuo Chen
1439a1e991dSShuo Chen  reset(expired, now);
1449a1e991dSShuo Chen}
1459a1e991dSShuo Chen
1469a1e991dSShuo Chenstd::vector<TimerQueue::Entry> TimerQueue::getExpired(Timestamp now)
1479a1e991dSShuo Chen{
1489a1e991dSShuo Chen  std::vector<Entry> expired;
1499a1e991dSShuo Chen  Entry sentry = std::make_pair(now, reinterpret_cast<Timer*>(UINTPTR_MAX));
1509a1e991dSShuo Chen  TimerList::iterator it = timers_.lower_bound(sentry);
1519a1e991dSShuo Chen  assert(it == timers_.end() || now < it->first);
1529a1e991dSShuo Chen  std::copy(timers_.begin(), it, back_inserter(expired));
1539a1e991dSShuo Chen  timers_.erase(timers_.begin(), it);
1549a1e991dSShuo Chen
1559a1e991dSShuo Chen  return expired;
1569a1e991dSShuo Chen}
1579a1e991dSShuo Chen
1589a1e991dSShuo Chenvoid TimerQueue::reset(const std::vector<Entry>& expired, Timestamp now)
1599a1e991dSShuo Chen{
1609a1e991dSShuo Chen  Timestamp nextExpire;
1619a1e991dSShuo Chen
1629a1e991dSShuo Chen  for (std::vector<Entry>::const_iterator it = expired.begin();
1639a1e991dSShuo Chen      it != expired.end(); ++it)
1649a1e991dSShuo Chen  {
1659a1e991dSShuo Chen    if (it->second->repeat())
1669a1e991dSShuo Chen    {
1679a1e991dSShuo Chen      it->second->restart(now);
1689a1e991dSShuo Chen      insert(it->second);
1699a1e991dSShuo Chen    }
1709a1e991dSShuo Chen    else
1719a1e991dSShuo Chen    {
1729a1e991dSShuo Chen      // FIXME move to a free list
1739a1e991dSShuo Chen      delete it->second;
1749a1e991dSShuo Chen    }
1759a1e991dSShuo Chen  }
1769a1e991dSShuo Chen
1779a1e991dSShuo Chen  if (!timers_.empty())
1789a1e991dSShuo Chen  {
1799a1e991dSShuo Chen    nextExpire = timers_.begin()->second->expiration();
1809a1e991dSShuo Chen  }
1819a1e991dSShuo Chen
1829a1e991dSShuo Chen  if (nextExpire.valid())
1839a1e991dSShuo Chen  {
1849a1e991dSShuo Chen    resetTimerfd(timerfd_, nextExpire);
1859a1e991dSShuo Chen  }
1869a1e991dSShuo Chen}
1879a1e991dSShuo Chen
1889a1e991dSShuo Chenbool TimerQueue::insert(Timer* timer)
1899a1e991dSShuo Chen{
1909a1e991dSShuo Chen  bool earliestChanged = false;
1919a1e991dSShuo Chen  Timestamp when = timer->expiration();
1929a1e991dSShuo Chen  TimerList::iterator it = timers_.begin();
1939a1e991dSShuo Chen  if (it == timers_.end() || when < it->first)
1949a1e991dSShuo Chen  {
1959a1e991dSShuo Chen    earliestChanged = true;
1969a1e991dSShuo Chen  }
197a20e676dSShuo Chen  std::pair<TimerList::iterator, bool> result =
198a20e676dSShuo Chen          timers_.insert(std::make_pair(when, timer));
1999a1e991dSShuo Chen  assert(result.second);
2009a1e991dSShuo Chen  return earliestChanged;
2019a1e991dSShuo Chen}
2029a1e991dSShuo Chen
203