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