TimerQueue类详解
来源:互联网 发布:c语言制表符是什么 编辑:程序博客网 时间:2024/06/01 19:13
说明:
TimerQueue构造中创建时间文件描述符timerfd_ 并关联的Channel 为timerfdChannel_,接着设置该Channel 的回调函数为TimerQueue::handleRead,当超时时刻去执行TimerQueue::handleRead,最后一句timerfdChannel_.enableReading();==>>
Channel::update()==>>即调用主线程中的loop_->updateChannel(this);==>>poller_->updateChannel(channel); 即EPollPoller类的EPollPoller::updateChannel函数。
EPollPoller::updateChannel函数功能:
根据channel->index()知道是新的连接,先看key=socketfd 在channels_ 中是否存在,存在则
让event.data.ptr保存channel;并将该socketfd 加入epollfd_集合,Channel加入到EPollPoller::channels_;老的连接则将该socketfd 从epollfd_集合修改或者删除。
最后主线程中会调用loop->loop()功能:
EPollPoller 类的poll 函数会收集所有发生事件的socketfd的Channel存于activeChannels_ 中,因为每一个socketfd 在对应的Channel 中会设置读/写/及错误发生时候的回调函数的;
下面即是遍历收集到的activeChannels_, 并执行每一个Channel 的handleEvent 进而去调用设置的回调函数;
当别的类想异步执行某个函数时候只需要调用EventLoop::runInLoop(const Functor& cb)
该函数里面会判断如果执行该函数的线程与主线程不同则利用往evtfd 中写个字符触发epoll ,继而这里得到执行了,即会执行上层注册的函数cb
因此:
看看TimerQueue::handleRead干了哪些活?
依次执行即将超时队列(按照距离超时的间隔从小到大放入的)中每个timer 的回调函数
TimerQueue.h
#ifndef MUDUO_NET_TIMERQUEUE_H
#define MUDUO_NET_TIMERQUEUE_H
#include <set>
#include <vector>
#include <boost/noncopyable.hpp>
#include <muduo/base/Mutex.h>
#include <muduo/base/Timestamp.h>
#include <muduo/net/Callbacks.h>
#include <muduo/net/Channel.h>
namespace muduo
{
namespace net
{
class EventLoop;
class Timer;
class TimerId;
///
/// A best efforts timer queue.
/// No guarantee that the callback will be on time.
///
class TimerQueue : boost::noncopyable
{
public:
TimerQueue(EventLoop* loop);
~TimerQueue();
///
/// Schedules the callback to be run at given time,
/// repeats if @c interval > 0.0.
///
/// Must be thread safe. Usually be called from other threads.
TimerId addTimer(const TimerCallback& cb,
Timestamp when,
double interval);
void cancel(TimerId timerId);
private:
// FIXME: use unique_ptr<Timer> instead of raw pointers.
typedef std::pair<Timestamp, Timer*> Entry;
typedef std::set<Entry> TimerList;
typedef std::pair<Timer*, int64_t> ActiveTimer;
typedef std::set<ActiveTimer> ActiveTimerSet;
void addTimerInLoop(Timer* timer);
void cancelInLoop(TimerId timerId);
// called when timerfd alarms
void handleRead();
// move out all expired timers
std::vector<Entry> getExpired(Timestamp now);
void reset(const std::vector<Entry>& expired, Timestamp now);
bool insert(Timer* timer);
EventLoop* loop_;
const int timerfd_;
Channel timerfdChannel_;
// Timer list sorted by expiration
TimerList timers_;
// for cancel()
ActiveTimerSet activeTimers_;
bool callingExpiredTimers_; /* atomic */
ActiveTimerSet cancelingTimers_;
};
}
}
#endif // MUDUO_NET_TIMERQUEUE_H
TimerQueue.cc
#define __STDC_LIMIT_MACROS
#include <muduo/net/TimerQueue.h>
#include <muduo/base/Logging.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/Timer.h>
#include <muduo/net/TimerId.h>
#include <boost/bind.hpp>
#include <sys/timerfd.h>
namespace muduo
{
namespace net
{
namespace detail
{
//可以利用timerfd 操作timer
int createTimerfd()
{
int timerfd = ::timerfd_create(CLOCK_MONOTONIC,
TFD_NONBLOCK | TFD_CLOEXEC);
if (timerfd < 0)
{
LOG_SYSFATAL << "Failed in timerfd_create";
}
return timerfd;
}
/*将时间差以返回值: 秒, 纳秒存放*/
struct timespec howMuchTimeFromNow(Timestamp when)
{
//得到距离现在的时间差微妙
int64_t microseconds = when.microSecondsSinceEpoch()
- Timestamp::now().microSecondsSinceEpoch();
if (microseconds < 100)
{
microseconds = 100;
}
struct timespec ts;
ts.tv_sec = static_cast<time_t>(
microseconds / Timestamp::kMicroSecondsPerSecond);
ts.tv_nsec = static_cast<long>(
(microseconds % Timestamp::kMicroSecondsPerSecond) * 1000);
return ts;
}
/*从时间文件描述符获得在now时刻有多少个定时器超时*/
void readTimerfd(int timerfd, Timestamp now)
{
uint64_t howmany;
ssize_t n = ::read(timerfd, &howmany, sizeof howmany);
LOG_TRACE << "TimerQueue::handleRead() " << howmany << " at " << now.toString();
if (n != sizeof howmany)
{
LOG_ERROR << "TimerQueue::handleRead() reads " << n << " bytes instead of 8";
}
}
/*设置新的超时时间为newValue*/
void resetTimerfd(int timerfd, Timestamp expiration)
{
// wake up loop by timerfd_settime()
struct itimerspec newValue;
struct itimerspec oldValue;
bzero(&newValue, sizeof newValue);
bzero(&oldValue, sizeof oldValue);
newValue.it_value = howMuchTimeFromNow(expiration);
int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue);
if (ret)
{
LOG_SYSERR << "timerfd_settime()";
}
}
}
}
}
using namespace muduo;
using namespace muduo::net;
using namespace muduo::net::detail;
/*
创建时间文件描述符timerfd_ 对应的Channel 为timerfdChannel_
并设置该Channel 的回调函数为TimerQueue::handleRead,
当超时时刻去执行TimerQueue::handleRead
*/
TimerQueue::TimerQueue(EventLoop* loop)
: loop_(loop),
timerfd_(createTimerfd()),
timerfdChannel_(loop, timerfd_),
timers_(),
callingExpiredTimers_(false)
{
timerfdChannel_.setReadCallback(
boost::bind(&TimerQueue::handleRead, this));
// we are always reading the timerfd, we disarm it with timerfd_settime.
timerfdChannel_.enableReading();
}
TimerQueue::~TimerQueue()
{
::close(timerfd_);
// do not remove channel, since we're in EventLoop::dtor();
for (TimerList::iterator it = timers_.begin();
it != timers_.end(); ++it)
{
delete it->second;
}
}
/*
利用evfd 通知主线程中epoll 去执行函数TimerQueue::addTimerInLoop
*/
TimerId TimerQueue::addTimer(const TimerCallback& cb,
Timestamp when,
double interval)
{
Timer* timer = new Timer(cb, when, interval);
loop_->runInLoop(
boost::bind(&TimerQueue::addTimerInLoop, this, timer));
return TimerId(timer, timer->sequence());
}
/*
利用evfd 通知主线程中epoll 去执行函数TimerQueue::cancelInLoop
*/
void TimerQueue::cancel(TimerId timerId)
{
loop_->runInLoop(
boost::bind(&TimerQueue::cancelInLoop, this, timerId));
}
/*
将timer 插入TimerQueue的timers_ 和activeTimers_ 中
当插入的超时时间小于队列中第一个的超时时间,
则改变时间描述符的超时时间为timer;
*/
void TimerQueue::addTimerInLoop(Timer* timer)
{
loop_->assertInLoopThread();
bool earliestChanged = insert(timer);
if (earliestChanged)
{
resetTimerfd(timerfd_, timer->expiration());
}
}
/*
将activeTimers_ 和timers_ 中时间全部取消
*/
void TimerQueue::cancelInLoop(TimerId timerId)
{
loop_->assertInLoopThread();
assert(timers_.size() == activeTimers_.size());
ActiveTimer timer(timerId.timer_, timerId.sequence_);
ActiveTimerSet::iterator it = activeTimers_.find(timer);
if (it != activeTimers_.end())
{
size_t n = timers_.erase(Entry(it->first->expiration(), it->first));
assert(n == 1); (void)n;
delete it->first; // FIXME: no delete please
activeTimers_.erase(it);
}
else if (callingExpiredTimers_)
{
cancelingTimers_.insert(timer);
}
assert(timers_.size() == activeTimers_.size());
}
/*
*/
void TimerQueue::handleRead()
{
loop_->assertInLoopThread();
Timestamp now(Timestamp::now());
readTimerfd(timerfd_, now);
/*获得timers_ 中从小直到now 的timer 并返回
即将超时的vector*/
std::vector<Entry> expired = getExpired(now);
/*依次执行expired 中每个timer 的回调函数*/
callingExpiredTimers_ = true;
cancelingTimers_.clear();
// safe to callback outside critical section
for (std::vector<Entry>::iterator it = expired.begin();
it != expired.end(); ++it)
{
it->second->run();
}
callingExpiredTimers_ = false;
reset(expired, now);
}
/*
获得timers_ 中从小直到sentry 的timer 并返回
即即将超时的vector
*/
std::vector<TimerQueue::Entry> TimerQueue::getExpired(Timestamp now)
{
assert(timers_.size() == activeTimers_.size());
std::vector<Entry> expired;
/*时间==>> Timer* */
Entry sentry(now, reinterpret_cast<Timer*>(UINTPTR_MAX));
/*返回timers_ 中第一个大于等于值sentry 的位置*/
TimerList::iterator end = timers_.lower_bound(sentry);
assert(end == timers_.end() || now < end->first);
/*也就是将timers_ 中从小直到sentry 拷贝到expired 中,再将其从
timers_中删除*/
std::copy(timers_.begin(), end, back_inserter(expired));
timers_.erase(timers_.begin(), end);
/*将expired 中的从activeTimers_ 中删除*/
for (std::vector<Entry>::iterator it = expired.begin();
it != expired.end(); ++it)
{
ActiveTimer timer(it->second, it->second->sequence());
size_t n = activeTimers_.erase(timer);
assert(n == 1); (void)n;
}
assert(timers_.size() == activeTimers_.size());
return expired;
}
/*
说明:expired 为即将超时的vector
vector 中的timer 是循环使用的,且不存在于cancelingTimers_ 中,
则将timer 插入TimerQueue::timers_ 和TimerQueue::activeTimers_ 中
否则将其从expired 中删除;
如果timers_ 不为空,则重新设置时间描述符timerfd_ 的超时
时间为timers_中的第一个的超时时间;
*/
void TimerQueue::reset(const std::vector<Entry>& expired, Timestamp now)
{
Timestamp nextExpire;
for (std::vector<Entry>::const_iterator it = expired.begin();
it != expired.end(); ++it)
{
ActiveTimer timer(it->second, it->second->sequence());
if (it->second->repeat()
&& cancelingTimers_.find(timer) == cancelingTimers_.end())
{
it->second->restart(now);
insert(it->second);
}
else
{
// FIXME move to a free list
delete it->second; // FIXME: no delete please
}
}
if (!timers_.empty())
{
nextExpire = timers_.begin()->second->expiration();
}
if (nextExpire.valid())
{
resetTimerfd(timerfd_, nextExpire);
}
}
/*
将timer 插入TimerQueue的timers_ 和activeTimers_ 中
注意: Entry(when, timer) 中when代表时间点,timer即是该时刻到来时刻对应的定时器。
*/
bool TimerQueue::insert(Timer* timer)
{
loop_->assertInLoopThread();
assert(timers_.size() == activeTimers_.size());
bool earliestChanged = false;
Timestamp when = timer->expiration();
/*由于timers_ 是个set , STL中的set和map一样是有序的:
当timer的时刻小于timers_中的第一个时候表明需要在addTimerInLoop函数调用中修改文件描述符的超时到来时刻为 timer->expiration();*/
TimerList::iterator it = timers_.begin();
if (it == timers_.end() || when < it->first)
{
earliestChanged = true;
}
/*when表示时刻点, 超时时候执行timer 的函数;
将Entry(when, timer) 插入到timers_ 中*/
{
std::pair<TimerList::iterator, bool> result
= timers_.insert(Entry(when, timer));
assert(result.second); (void)result;
}
/*timer->sequence()是个全局的编号, timer代表对应的定时器;
将ActiveTimer(timer, timer->sequence() 插入到activeTimers_ 中*/
{
std::pair<ActiveTimerSet::iterator, bool> result
= activeTimers_.insert(ActiveTimer(timer, timer->sequence()));
assert(result.second); (void)result;
}
assert(timers_.size() == activeTimers_.size());
return earliestChanged;
}
- TimerQueue类详解
- 带有TimerQueue的EventLoop
- windows下的TimerQueue
- muduo::TimerId、Timer、TimerQueue分析
- muduo::TimerId、Timer、TimerQueue分析
- muduo源码分析之定时器TimerQueue的设计与实现
- muduo学习笔记(一)——TimerQueue
- muduo网络库学习(三)定时器TimerQueue的设计
- muduo库阅读(33)——Net部分:定时器队列TimerQueue
- Volatile关键字详解,AtomicInteger类详解
- Php类详解
- ComboBox 类详解
- Java-util类详解
- CArchive 类详解
- phplib类中文详解
- UML类图详解
- UML类图详解
- 接口、类详解
- Java线程笔记(一)
- android_分享 图片 和 文字
- leetcode第一刷_Triangle
- leetcode第一刷_Implement strStr()
- 百度2013校园招聘笔试题[软件研发]及答案
- TimerQueue类详解
- hdu1398
- asdfdsaf
- 在EL中调用静态函数
- 小米论坛用户数据遭泄漏
- install MEAN(MongoDB,Expressjs,Angularjs,Nodejs)
- 调试的一些技巧
- leetcode第一刷_Remove Element
- 1、免账号访问Windows共享文件