1
2 3 4 5 6 7 8 9 10 11 |
/// Polls the I/O events. /// Must be called in the loop thread. virtual Timestamp poll( int timeoutMs, ChannelList *activeChannels) = 0; /// Changes the interested I/O events. /// Must be called in the loop thread. virtual void updateChannel(Channel *channel) = 0; /// Remove the channel, when it destructs. /// Must be called in the loop thread. virtual void removeChannel(Channel *channel) = 0; |
1
2 3 4 |
typedef std::vector<
struct pollfd> PollFdList;
typedef std::map< int, Channel *> ChannelMap; // key是文件描述符,value是Channel* PollFdList pollfds_; ChannelMap channels_; |
1
2 3 4 |
typedef std::vector<
struct epoll_event> EventList;
typedef std::map< int, Channel *> ChannelMap; EventList events_; ChannelMap channels_; |
1
2 |
typedef std::vector<Channel *> ChannelList;
ChannelList activeChannels_; // Poller返回的活动通道 |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
/// /// A selectable I/O channel. /// /// This class doesn't own the file descriptor. /// The file descriptor could be a socket, /// an eventfd, a timerfd, or a signalfd class Channel : boost::noncopyable { public: typedef boost::function< void()> EventCallback; typedef boost::function< void(Timestamp)> ReadEventCallback; Channel(EventLoop *loop, int fd); ~Channel(); void handleEvent(Timestamp receiveTime); void setReadCallback( const ReadEventCallback &cb) { readCallback_ = cb; } void setWriteCallback( const EventCallback &cb) { writeCallback_ = cb; } void setCloseCallback( const EventCallback &cb) { closeCallback_ = cb; } void setErrorCallback( const EventCallback &cb) { errorCallback_ = cb; } void enableReading() { events_ |= kReadEvent; update(); }
............
private: boost::weak_ptr< void> tie_; const int fd_; // 文件描述符,但不负责关闭该文件描述符 int events_; // 关注的事件 int revents_; // poll/epoll返回的事件
int index_;
// used by PollPoller.表示在poll的事件数组中的序号
// used by EPollPoller. 表示某channel的状态(新创建,已关注,取消关注)
ReadEventCallback readCallback_;
EventCallback writeCallback_; EventCallback closeCallback_; EventCallback errorCallback_; }; |
|
1
2 3 4 5 6 7 8 9 10 |
#define POLLIN 0x0001
#define POLLPRI 0x0002 #define POLLOUT 0x0004 #define POLLERR 0x0008 #define POLLHUP 0x0010 #define POLLNVAL 0x0020 const int Channel::kNoneEvent = 0; const int Channel::kReadEvent = POLLIN | POLLPRI; const int Channel::kWriteEvent = POLLOUT; |
time(2) / time_t (秒)ftime(3) / struct timeb (毫秒)gettimeofday(2) / struct timeval (微秒)clock_gettime(2) / struct timespec (纳秒)gmtime / localtime / timegm / mktime / strftime / struct tm (这些与当前时 间无关)
sleepalarmusleepnanosleepclock_nanosleepgetitimer / setitimertimer_create / timer_settime / timer_gettime / timer_deletetimerfd_create / timerfd_gettime / timerfd_settime
1. time 的精度太低,ftime 已被废弃,clock_gettime 精度最高,但是它系统调用 的开销比gettimeofday 大。2. 在x86-64 平台上,gettimeofday 不是系统调用,而是在用户态实现的(搜 vsyscall),没有上下文切换和陷入内核的开销。3. gettimeofday 的分辨率(resolution) 是1 微秒,足以满足日常计时的需要。 muduo::Timestamp 用一个int64_t 来表示从Epoch 到现在的微秒数,其范围 可达上下30 万年。
sleep / alarm / usleep 在实现时有可能用了信号 SIGALRM,在多线程程序中处理信号是个相当麻烦的事情,应当尽量避免
nanosleep 和 clock_nanosleep 是线程安全的,但是在非阻塞网络编程中,绝对不能用让线程挂起的方式来等待一段时间,程序会失去响应。正确的做法是注册一个时间回调函数。
getitimer 和 timer_create 也是用信号来 deliver 超时,在多线程程序中也会有麻烦。
timer_create 可以指定信号的接收方是进程还是线程,算是一个进步,不过在信号处理函数(signal handler)能做的事情实在很受限。
timerfd_create 把时间变成了一个文件描述符,该“文件”在定时器超时的那一刻变得可读,这样就能很方便地融入到 select/poll 框架中,用统一的方式来处理 IO 事件和超时事件,这也正是 Reactor 模式的长处。
传统的Reactor 利用select/poll/epoll 的timeout 来实现定时功能,但poll 和 epoll 的定时精度只有毫秒,远低于timerfd_settime 的定时精度。
1
2 3 4 5 6 7 8 9 10 11 12 |
/// An opaque identifier, for canceling Timer. /// class TimerId : public muduo::copyable { // default copy-ctor, dtor and assignment are okay friend class TimerQueue; private: Timer *timer_; int64_t sequence_; //时钟序号 }; |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/// /// Internal class for timer event. /// class Timer : boost::noncopyable { public: void run() const { callback_(); } private: const TimerCallback callback_; // 定时器回调函数 Timestamp expiration_; // 下一次的超时时刻 const double interval_; // 超时时间间隔,如果是一次性定时器,该值为0 const bool repeat_; // 是否重复 const int64_t sequence_; // 定时器序号 static AtomicInt64 s_numCreated_; // 定时器计数,当前已经创建的定时器数量 }; |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
/// /// A best efforts timer queue. /// No guarantee that the callback will be on time. /// class TimerQueue : boost::noncopyable { public: /// /// 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: typedef std::pair<Timestamp, Timer *> Entry; typedef std::set<Entry> TimerList; EventLoop *loop_; // 所属EventLoop const int timerfd_; // timerfd_create 函数创建 Channel timerfdChannel_; // Timer list sorted by expiration TimerList timers_; // timers_是按到期时间排序 }; |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
TimerId EventLoop::runAt(
const Timestamp &time,
const TimerCallback &cb)
{ return timerQueue_->addTimer(cb, time, 0. 0); } TimerId EventLoop::runAfter( double delay, const TimerCallback &cb) { Timestamp time(addTime(Timestamp::now(), delay)); return runAt(time, cb); } TimerId EventLoop::runEvery( double interval, const TimerCallback &cb) { Timestamp time(addTime(Timestamp::now(), interval)); return timerQueue_->addTimer(cb, time, interval); } void EventLoop::cancel(TimerId timerId) { return timerQueue_->cancel(timerId); } |
1
2 3 4 |
timerfdChannel_.setReadCallback(
boost::bind(&TimerQueue::handleRead, this)); // we are always reading the timerfd, we disarm it with timerfd_settime. timerfdChannel_.enableReading(); |
TimerQueue 中有多个定时器,一次性的和重复的,事件循环开始EventLoop::loop(),当最早到期定时器超时时,poll() 返回timerfd_ 的可读事件(timerfdChannel_),调用Channel::handleEvent(),调用readCallback_(receiveTime); 进而调用Channel::setReadCallback 注册的TimerQueue::handleRead(), 在函数内先read 掉timerfd_数据,避免一直触发可读事件,接着遍历TimerQueue中此时所有超时的定时器,调用每个定时器构造时传递的回调函数。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
#include <muduo/net/EventLoop.h>
//#include <muduo/net/EventLoopThread.h> #include <muduo/base/Thread.h> #include <boost/bind.hpp> #include <stdio.h> #include <unistd.h> using namespace muduo; using namespace muduo::net; int cnt = 0; EventLoop *g_loop; void printTid() { printf( "pid = %d, tid = %d\n", getpid(), CurrentThread::tid()); printf( "now %s\n", Timestamp::now().toString().c_str()); } void print( const char *msg) { printf( "msg %s %s\n", Timestamp::now().toString().c_str(), msg); if (++cnt == 20) { g_loop->quit(); } } void cancel(TimerId timer) { g_loop->cancel(timer); printf( "cancelled at %s\n", Timestamp::now().toString().c_str()); } int main() { printTid(); sleep( 1); { EventLoop loop; g_loop = &loop; print( "main"); loop.runAfter( 1, boost::bind(print, "once1")); loop.runAfter( 1. 5, boost::bind(print, "once1.5")); loop.runAfter( 2. 5, boost::bind(print, "once2.5")); loop.runAfter( 3. 5, boost::bind(print, "once3.5")); TimerId t45 = loop.runAfter( 4. 5, boost::bind(print, "once4.5")); loop.runAfter( 4. 2, boost::bind(cancel, t45)); loop.runAfter( 4. 8, boost::bind(cancel, t45)); loop.runEvery( 2, boost::bind(print, "every2")); TimerId t3 = loop.runEvery( 3, boost::bind(print, "every3")); loop.runAfter( 9. 001, boost::bind(cancel, t3)); loop.loop(); print( "main loop exits"); } } |