啥都不会啊!怎么办啊!

Fitz

慢慢来生活总会好起来的!!!

muduo源码分析之定时器TimerQueue

 

  (muduo源码系列大多是我在看muduo源码的时候结合网上博客总结的,我尽可能多的是对源码注释)

简介

         Muduo的定时器功能主要由三个class实现,TimerIdTimerTimerQueueTimerQueue的接口只有两个addTimer()cancel()addTimer()是提供给EventLoop使用的, EventLoop会把它封装成更好用的三个函数:runAt()runAfter()runEvery()

   大体实现

        muduo 定时器封装了 Timer.h 里面保存的是超时时间和回调函数, TimerQueue.h 使用set容器保存多个定时器, 然后在TimerQueue中使用timerfd_create创建一个timerfd句柄, 插入定时器A后先比较A的触发时间和TimerQueue的触发时间, 如果A的触发时间比其小就使用timerfd_settime重置TimerQueue的timerfd的触发时间, TimerQueue中的timerfd的触发时间永远与保存的定时器中触发时间最小的那个相同, 然后timerfd触发可读后, 遍历保存的多个定时器, 看看有没有同时到期的, 有执行回调函数

    TimerQueue的封装是为了让未到期的时间Timer有序的排列起来,这样,能够更具当前时间找到已经到期的Timer也能高效的添加和删除Timer

所谓的到期与未到期,与当前在当前时间之前表示已经到期,之后则是未到期。为了方便计算,muduo重载了operator<主要是用来比较微秒大小。

到期的时间应该被清除去执行相应的回调,未到期的时间则应该有序的排列起来。

 

对于TimerQueue的数据结构,作者提出了几个方案。

1.传统线性表,查找复杂度为O(n)

2.二叉堆实现优先级队列,不过C++标准的make_heap()不能高效地完成删除操作。

 

最终,为了防止时间相同所导致的Key相同的情况,使用setpair

typedef std::pair<Timestamp, Timer*>Entry;

typedef std::set<Entry>TimerList;

TimerList timers_;

 

Linux时间函数介绍

linux中用以获取当前时间的的函数有:

time(2) / time_t(秒)

ftime(3) / struct timeb(毫秒)

gettimeofday(2) / struct timeval(微秒)

clock_gettime(2) / struct timespec(微秒)

 

还有gmtime / localtime / timegm / mktime / strftime / struct tm等与当前时间无关的时间格式转换函数。

 

定时函数

sleep(3)

alarm(3)

usleep(3)

nanosleep(2)

clock_nanosleep(2)

gettimer(2) / settitimer(2)

timer_create(2) / timer_settime(2) / tiemr_gettime(2) / timer_delete(2)

timerfd_create(2) / timerfd_gettime(2) / timerfd_settime(2)

 

取舍如下

1、计时只使用gettimeofday(2)来获取当前时间。

2、定时只使用timerfd_*系列函数来处理定时任务。

timerfd介绍

这节介绍muduo中定时器的实现。先看一个2.6内核新增的有关定时的系统调用,基于这几个系统调用可以实现基于文件描述符的定时器。即可是定时,使文件描述符在某一特定时间可读。

 

#include <sys/timerfd.h>

    int timerfd_create(int clockid, int flags);

    int timerfd_settime(int fd, int flags,
           const struct itimerspec * new_value,
           struct itimerspec * old_value);

    int timerfd_gettime(int fd, struct itimerspec *curr_value);

 

1timerfd_create用于创建一个定时器文件,函数返回值是一个文件句柄fd

2timerfd_settime用于设置新的超时时间,并开始计时。flag0表示相对时间,为1表示绝对时间。new_value为这次设置的新时间,old_value为上次设置的时间。返回0表示设置成功。

3timerfd_gettime用于获得定时器距离下次超时还剩下的时间。如果调用时定时器已经到期,并且该定时器处于循环模式(设置超时时间时struct itimerspec::it_interval不为0),那么调用此函数之后定时器重新开始计时。

TimerId介绍

TimerId非常简单,它被设计用来取消Timer的,它的结构很简单,只有一个Timer指针和其序列号。

// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
// This is a public header file, it must only include public header files.
/*封装了timer类到构造和析构函数中,我的理解就是RAII的思想
 * TimerId非常简单,它被设计用来取消Timer的,它的结构很简单,只有一个Timer指针和其序列号。*/
#ifndef MUDUO_NET_TIMERID_H
#define MUDUO_NET_TIMERID_H

#include <muduo/base/copyable.h>

namespace muduo {
    namespace net {

        class Timer;

///
/// An opaque identifier, for canceling Timer.
///
        class TimerId : public muduo::copyable {
        public:
            TimerId()
                    : timer_(NULL),
                      sequence_(0) {
            }

            TimerId(Timer *timer, int64_t seq)
                    : timer_(timer),
                      sequence_(seq) {
            }

            // default copy-ctor, dtor and assignment are okay

            friend class TimerQueue;//友元,就是可以访问类的私有成员变量,但不是类中的成员

        private:
            Timer *timer_;
            int64_t sequence_;
        };

    }
}

#endif  // MUDUO_NET_TIMERID_H

Timer

Timer封装了定时器的一些参数,例如超时回调函数、超时时间、定时器是否重复、重复间隔时间、定时器的序列号。其函数大都是设置这些参数,run()用来调用回调函数,restart()用来重启定时器(如果设置为重复)。其源码相对简单

 

// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
// This is an internal header file, you should not include this.
/*计时器类*/
#ifndef MUDUO_NET_TIMER_H
#define MUDUO_NET_TIMER_H

#include <boost/noncopyable.hpp>

#include <muduo/base/Atomic.h>
#include <muduo/base/Timestamp.h>
#include <muduo/net/Callbacks.h>

namespace muduo {
    namespace net {
///
/// Internal class for timer event.
///
        class Timer : boost::noncopyable {
        public:
            Timer(const TimerCallback &cb, Timestamp when, double interval)
                    : callback_(cb),//回调函数
                      expiration_(when),//超时时间
                      interval_(interval),//如果重复,间隔时间
                      repeat_(interval > 0.0),//如果间隔大于0,就重复
                      sequence_(s_numCreated_.incrementAndGet()) {}//当前定时器的序列号
            //调用回调函数.
            void run() const {
                callback_();
            }

            Timestamp expiration() const { return expiration_; }//返回超时时刻
            bool repeat() const { return repeat_; }//返回是否重复
            int64_t sequence() const { return sequence_; }//返回定时器序号

            void restart(Timestamp now);//重新开始

            static int64_t numCreated() { return s_numCreated_.get(); }//返回最新的序号值

        private:
            const TimerCallback callback_;        // 定时器回调函数
            Timestamp expiration_;                // 下一次的超时时间戳类
            const double interval_;                // 超时时间间隔,如果是一次性定时器,该值为0
            const bool repeat_;                    // 是否重复
            const int64_t sequence_;                // 定时器序号,不会重复

            static AtomicInt64 s_numCreated_;        // 定时器计数,当前已经创建的定时器数量
        };
    }
}
#endif  // MUDUO_NET_TIMER_H

 

 

 

Timestamp

时间戳一般用来唯一地标识某一刻的时间,通常是指格林威治时间19700101000000(北京时间19700101080000)起至现在的总毫秒数。

 

Timestamp类的参数有一个常量kMicroSecondsPerSecond表示每秒所对应的微秒数,成员变量microSecondsSinceEpoch_表示到1970-01-01 00:00:00 UTC的微秒数。成员函数包括swap()交换操作,toString()toFormattedString()将时间转换为string类型或指定格式,valid()判断Timestamp是否有效,invalid()返回一个无效的Timestampnow()返回当前时间的TimestampsecondsSinceEpoch()/microSecondsSinceEpoch()返回到1970-01-01 00:00:00 UTC的秒数/微秒数。

Timestamp.h源码(带注释)

#ifndef MUDUO_BASE_TIMESTAMP_H
#define MUDUO_BASE_TIMESTAMP_H

/*时间戳函数,我的理解是创建一个microSecondsSinceEpoch_为0的结构体,
 *然后每次通过这个结构体创建子的microSecondsSinceEpoch_为当前时间的结构体,并且子结构体都在microSecondsSinceEpoch_为0的结构体
 *中进行运算操作*/
#include <muduo/base/copyable.h>
#include <muduo/base/Types.h>

#include <boost/operators.hpp>

namespace muduo {

///Timestamp
/// Time stamp in UTC, in microseconds resolution.
///
/// This class is immutable.
/// It's recommended to pass it by value, since it's passed in register on x64.
///
    class Timestamp : public muduo::copyable,
                      public boost::less_than_comparable<Timestamp>//继承这个类,对于<,>,<=,>=这些运算符号,只需要定义
        //<号,其他的都可以自动帮你定义了
    {
    public:
        ///
        /// Constucts an invalid Timestamp.
        ///
        Timestamp()
                : microSecondsSinceEpoch_(0) {
        }

        ///
        /// Constucts a Timestamp at specific time
        ///
        /// @param microSecondsSinceEpoch
        explicit Timestamp(int64_t microSecondsSinceEpoch);

        void swap(Timestamp &that)//将两个Timestamp类中的microSecondsSinceEpoch_变量交换
        {
            std::swap(microSecondsSinceEpoch_, that.microSecondsSinceEpoch_);
        }

        // default copy/assignment/dtor are Okay

        string toString() const;

        string toFormattedString() const;

        bool valid() const { return microSecondsSinceEpoch_ > 0; }//判断microSecondsSinceEpoch_是否有效,大于0就有效

        // for internal usage.
        int64_t microSecondsSinceEpoch() const { return microSecondsSinceEpoch_; }//返回microSecondsSinceEpoch_
        time_t secondsSinceEpoch() const//返回以秒为单位的microSecondsSinceEpoch_
        { return static_cast<time_t>(microSecondsSinceEpoch_ / kMicroSecondsPerSecond); }

        ///
        /// Get time of now.
        ///
        static Timestamp now();//创建一个当前的时间的Timestamp结构体
        static Timestamp invalid();//创建一个microSecondsSinceEpoch_=0的Timestamp结构体,都是静态函数,可以直接调用

        static const int kMicroSecondsPerSecond = 1000 * 1000;

    private:
        int64_t microSecondsSinceEpoch_;//表示到1970-01-01 00:00:00 UTC的微秒数。
    };

/*放在类外重载,但是普通数据类型可以使用原来的,遇到特定类型才会使用这个*/
    inline bool operator<(Timestamp lhs, Timestamp rhs)//只需要定义<号,其他都自动定义,less_than_comparable模板作用
    {
        return lhs.microSecondsSinceEpoch() < rhs.microSecondsSinceEpoch();
    }

    inline bool operator==(Timestamp lhs, Timestamp rhs) {
        return lhs.microSecondsSinceEpoch() == rhs.microSecondsSinceEpoch();
    }

///
/// Gets time difference of two timestamps, result in seconds.
///
/// @param high, low
/// @return (high-low) in seconds
/// @c double has 52-bit precision, enough for one-microseciond
/// resolution for next 100 years.

//计算两个Timestamp之间的差,以秒为单位
    inline double timeDifference(Timestamp high, Timestamp low) {
        int64_t diff = high.microSecondsSinceEpoch() - low.microSecondsSinceEpoch();
        return static_cast<double>(diff) / Timestamp::kMicroSecondsPerSecond;
    }

///
/// Add @c seconds to given timestamp.
///
/// @return timestamp+seconds as Timestamp
///
//加时间
    inline Timestamp addTime(Timestamp timestamp, double seconds) {
        int64_t delta = static_cast<int64_t>(seconds * Timestamp::kMicroSecondsPerSecond);
        return Timestamp(timestamp.microSecondsSinceEpoch() + delta);
    }

}
#endif  // MUDUO_BASE_TIMESTAMP_H

  Timestamp.cc源码(带注释)

  

#include <muduo/base/Timestamp.h>

#include <sys/time.h>
#include <stdio.h>

#define __STDC_FORMAT_MACROS

#include <inttypes.h>

#undef __STDC_FORMAT_MACROS

#include <boost/static_assert.hpp>

using namespace muduo;

BOOST_STATIC_ASSERT(sizeof(Timestamp) == sizeof(int64_t));

Timestamp::Timestamp(int64_t microseconds)
        : microSecondsSinceEpoch_(microseconds) {
}

string Timestamp::toString() const//转化成xxx.xxx秒的格式
{
    char buf[32] = {0};
    int64_t seconds = microSecondsSinceEpoch_ / kMicroSecondsPerSecond;
    int64_t microseconds = microSecondsSinceEpoch_ % kMicroSecondsPerSecond;
    snprintf(buf, sizeof(buf) - 1, "%"
    PRId64
    ".%06"
    PRId64
    "", seconds, microseconds);
    return buf;
}

//将时间加上1900年1月1日之后转成"年月日 小时:分钟:秒"的格式
string Timestamp::toFormattedString() const {
    char buf[32] = {0};
    time_t seconds = static_cast<time_t>(microSecondsSinceEpoch_ / kMicroSecondsPerSecond); // 秒数
    int microseconds = static_cast<int>(microSecondsSinceEpoch_ % kMicroSecondsPerSecond);
    struct tm tm_time;// 微秒数
    // 把time_t结构中的信息转换成真实世界所使用的时间日期,存储在tm_time结构中
    gmtime_r(&seconds, &tm_time);//将总秒数转换成————多少年多少月多少日多少小时多少分多少秒为单位
    // 格式化输出时间戳
    snprintf(buf, sizeof(buf), "%4d%02d%02d %02d:%02d:%02d.%06d",
             tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,
             tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec,
             microseconds);//总秒数加上1900年1月1日然后转换成固定格式
    return buf;
}

Timestamp Timestamp::now() {
    struct timeval tv;
    gettimeofday(&tv, NULL);//得到的是系统从当前时间从1900年1月1日开始算起的总秒数
    int64_t seconds = tv.tv_sec;
    return Timestamp(seconds * kMicroSecondsPerSecond + tv.tv_usec);
    //返回一个Timestamp结构体,相当于创建一个当前时间的Timestamp结构体
}

Timestamp Timestamp::invalid() {
    return Timestamp();
}

 

TimerQueue

虽然TimerQueue中有Queue,但是其实现时基于Set的,而不是Queue。这样可以高效地插入、删除定时器,且找到当前已经超时的定时器。TimerQueuepublic接口只有两个,添加和删除。

 

void addTimerInLoop(Timer* timer);

void cancelInLoop(TimerId timerId);

 

定时器管理类,其中timer类就是TimerQueue需要管理的元素,而timerId就是一个简单的timer封装,避免销毁和创建操作

但是要注意的是timer并没有自己计时的功能,所以需要依靠timerfd这个系统函数统一计时timerfd是一个系统计时函数,当所设置的时间到了,会通过timerfd这个文件描述符进行提示通信,而其他计时函数可能是通过信号量,或者其他方式,但是都没有文件描述符好,并且也可以用timerfd监听,具体原因可以查看一下博客的网络库定时器实现

如何使用timerfd来为所有的计时器计时:timerfd每次都设置在计时器列表中到期时间最近的那个到期时间,这样timerfd到期以后,也就是最近的那个计时器到期,所以每次都是手动重置timerfd的计时时间,为最近的计时器到期时间

 

内部有channel,和timerfd关联。添加新的Timer后,在超时后,timerfd可读,会处理channel事件,之后调用Timer的回调函数;在timerfd的事件处理后,还有检查一遍超时定时器,如果其属性为重复还有再次添加到定时器集合中。

内部有两种类型的Set

typedef std::pair<Timestamp, Timer*> Entry;

typedef std::set<Entry> TimerList;

typedef std::pair<Timer*, int64_t> ActiveTimer;

typedef std::set<ActiveTimer> ActiveTimerSet;

 

 

下面给出TimerQueue.hTimerQueue.cc源码分析有详细的注释

TimerQueue.h

// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
// This is an internal header file, you should not include this.
/* 定时器管理类,其中timer类就是TimerQueue需要管理的元素,而timerId就是一个简单的timer封装,避免销毁和创建操作
 * 但是要注意的是timer并没有自己计时的功能,所以需要依靠timerfd这个系统函数统一计时
 * timerfd是一个系统计时函数,当所设置的时间到了,会通过timerfd这个文件描述符进行提示通信,而其他计时函数可能是通过信号量,或者
 * 其他方式,但是都没有文件描述符好,并且也可以用timerfd监听,具体原因可以查看一下博客的网络库定时器实现
 * 如何使用timerfd来为所有的计时器计时:timerfd每次都设置在计时器列表中到期时间最近的那个到期时间,这样timerfd到期以后,也就是最近的那个计时器到期
 * 所以每次都是手动重置timerfd的计时时间,为最近的计时器到期时间
 * ?timestamp::now获得的时间是从1960年1月1日开始的,但是timerfd据说是从系统开机的那一刻开始的,那么在设置timefd时时间不统一怎么办
 * 注意在timerfd设置延时的时候,使用的是相对时间,所以无所谓最终时间是多少,只要相对时间没问题就好了
 * ?重置timerfd导致延时怎么办
 * ?关于线程执行,究竟哪些函数靠IO线程来执行
 * 
 * */
#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.
            // unique_ptr是C++ 11标准的一个独享所有权的智能指针
            // 无法得到指向同一对象的两个unique_ptr指针
            // 但可以进行移动构造与移动赋值操作,即所有权可以移动到另一个对象(而非拷贝构造)
            typedef std::pair<Timestamp, Timer *> Entry;
            typedef std::set <Entry> TimerList;
            typedef std::pair<Timer *, int64_t> ActiveTimer;
            typedef std::set <ActiveTimer> ActiveTimerSet;
            //set中存储的是pair类型,那么默认先按照pair的第一个元素排序,如果相同,再按照第二个元素排序。
            //所以这两种set都是存放定时器的列表,但是一个根据定时器的到时时间来存储,
            //一个根据定时器地址来存储,但是存储的定时器都是同一个,目的是为了区分同一到期时间的定时器???

            // 以下成员函数只可能在其所属的I/O线程中调用,因而不必加锁。
            // 服务器性能杀手之一是锁竞争,所以要尽可能少用锁
            void addTimerInLoop(Timer *timer);

            void cancelInLoop(TimerId timerId);

            // called when timerfd alarms
            void handleRead();//timerfdChannel_的读函数
            // 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_;        // 所属EventLoop
            const int timerfd_;
            //过一段事件,就筛选一次,看看TimerList中有多少定时器到时间了,就处理一下,但是这样延迟很高,不太理解
            Channel timerfdChannel_;//与timefd绑定
            // Timer list sorted by expiration
            TimerList timers_;    // timers_是按到期时间排序,也是存放未到时间的定时器

            // for cancel()
            // timers_与activeTimers_保存的是相同的数据
            // timers_是按到期时间排序,activeTimers_是按对象地址排序
            ActiveTimerSet activeTimers_;//还未到时间的定时器,这里面存放的定时器是和timers_一样的,只是顺序不同
            bool callingExpiredTimers_; /* atomic *///是否在处理过期定时器的标志
            ActiveTimerSet cancelingTimers_;    // 保存的是被取消的定时器
            // 用这个列表的作用是,当出现一个循环的计时器被取消时,就要通过reset函数中对
            //ActiveTimerSet列表来暂停对这个计时器的重置
        };

    }
}
#endif  // MUDUO_NET_TIMERQUEUE_H

 

TimerQueue.cc

 

// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Shuo Chen (chenshuo at chenshuo dot com)

#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 {

            // 创建定时器
            int createTimerfd() {
                int timerfd = ::timerfd_create(CLOCK_MONOTONIC,
                                               TFD_NONBLOCK | TFD_CLOEXEC);//CLOCK_MONOTONIC参数表明计时器的时间是从系统打开开始计时的
                //CLOCK_MONOTONIC表示的是时间类型
                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;
            }

            // 清除定时器,避免一直触发//处理超时事件。超时后,timerfd变为可读
            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";
                }
            }

            // 重置定时器的超时时间(不是周期性的定时器,时间到expiration就结束了)
            // 在这里面itimerspec.it_interval都是设置的0,每次都是计时结束以后手动重新设置,然后再计时的。
            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;

TimerQueue::TimerQueue(EventLoop *loop)
        : loop_(loop),
          timerfd_(createTimerfd()),
          timerfdChannel_(loop, timerfd_),//timerfd相关的channel
          timers_(),
          callingExpiredTimers_(false) {
    timerfdChannel_.setReadCallback(
            boost::bind(&TimerQueue::handleRead, this));
    // we are always reading the timerfd, we disarm it with timerfd_settime.
    timerfdChannel_.enableReading();//设置关注读事件,并且加入epoll队列
}

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;
    }
}

TimerId TimerQueue::addTimer(const TimerCallback &cb,
                             Timestamp when,
                             double interval)//创建并增加Timer进队列中
{
    Timer *timer = new Timer(cb, when, interval);

    loop_->runInLoop(
            boost::bind(&TimerQueue::addTimerInLoop, this, timer));

    //addTimerInLoop(timer);
    return TimerId(timer, timer->sequence());
}

void TimerQueue::cancel(TimerId timerId)//取消
{
    loop_->runInLoop(
            boost::bind(&TimerQueue::cancelInLoop, this, timerId));
    //cancelInLoop(timerId);
}

void TimerQueue::addTimerInLoop(Timer *timer) {
    loop_->assertInLoopThread();
    // 插入一个定时器,有可能会使得最早到期的定时器发生改变
    bool earliestChanged = insert(timer);

    if (earliestChanged) {
        // 重置timefd定时器的超时时刻(timerfd_settime)
        resetTimerfd(timerfd_, timer->expiration());
    }
}

void TimerQueue::cancelInLoop(TimerId timerId)//取消的回调函数
//取消计时器,就是把该计时器从两个队列中删除,
//现在有一种特殊情况,就是如果刚好在处理定时器的过程中,并且这个要取消的定时器就是在被处理的,并且是循环定时器,那么如果不加入cancelingTimers_列表
//就会出现,在重置时又把这个定时器重启了,但是这个定时器应该是要被取消的
{
    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,如果用了unique_ptr,这里就不需要手动删除了
        activeTimers_.erase(it);
    }//用activeTimers_列表来搜索,然后找到先删除timers_,再删除activeTimers_
    else if (callingExpiredTimers_)
        //如果在未到时间的定时器中没有找到,并且线程正在处理过期的定时器,那么可能这个定时器正在被处理,就将这些定时器放到cancelingTimers_数组中
    {
        // 已经到期,并且正在调用回调函数的定时器,为了在重置时,避免被重置,而是被忽略
        cancelingTimers_.insert(timer);
    }
    assert(timers_.size() == activeTimers_.size());
}

void TimerQueue::handleRead()//TimerChannel的回调函数,也就是当timefd定时器到时的时候,就会调用这个函数
{
    loop_->assertInLoopThread();
    Timestamp now(Timestamp::now());
    readTimerfd(timerfd_, now);        // 清除该事件,避免一直触发

    // 获取该时刻之前所有的定时器列表(即超时定时器列表)
    std::vector <Entry> expired = getExpired(now);

    callingExpiredTimers_ = true;//处理到期的定时器
    cancelingTimers_.clear();//每次处理前,把要取消的定时器列表清空
    // safe to callback outside critical section
    for (std::vector<Entry>::iterator it = expired.begin();
         it != expired.end(); ++it) {
        // 这里回调定时器timer处理函数
        it->second->run();
    }
    callingExpiredTimers_ = false;

    // 不是一次性定时器,需要重启
    reset(expired, now);//如果之前处理定时器回调函数时间较长,那么在这段时间中,已经有定时器到期了,轻则产生延迟,重则
}

// rvo
std::vector <TimerQueue::Entry> TimerQueue::getExpired(Timestamp now)//得到已经过期的计时器
{
    assert(timers_.size() == activeTimers_.size());
    std::vector <Entry> expired;//存放已经过期的定时器
    Entry sentry(now, reinterpret_cast<Timer *>(UINTPTR_MAX));//我理解是找了一个指针可以取到的最大数,为了避免和其他指针冲突,
    //因为这个指针没有什么意义,仅仅是为了构成一个Entry结构体,有意义的是第一个元素now

    // 返回第一个未到期的Timer的迭代器
    // lower_bound的含义是返回第一个值>=sentry的元素的iterator
    // 即*end >= sentry,从而end->first > now
    TimerList::iterator end = timers_.lower_bound(sentry);
    assert(end == timers_.end() || now < end->first);
    // 将到期的定时器插入到expired中
    std::copy(timers_.begin(), end, back_inserter(expired));//back_inserter是迭代器的一种操作,效果和expired.push_back()一样
    // 从timers_中移除到期的定时器
    timers_.erase(timers_.begin(), end);

    // 从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;//避免编译器出现变量n未使用的警告???
    }

    assert(timers_.size() == activeTimers_.size());
    return expired;
}

void TimerQueue::reset(const std::vector <Entry> &expired, Timestamp now)//重启两种定时器,一种是timefd,另外一种是定时器列表中需要重复的定时器
{
    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
        }
    }
    //重启timefd,设置的时间就是定时器列表中最快到期的时间
    if (!timers_.empty()) {
        // 获取最早到期的定时器超时时间
        nextExpire = timers_.begin()->second->expiration();
    }

    if (nextExpire.valid()) {
        // 重置定时器的超时时刻(timerfd_settime)
        resetTimerfd(timerfd_, nextExpire);
    }
}

bool TimerQueue::insert(Timer *timer)//把定时器插入到timers_和activeTimers_队列中去
{
    loop_->assertInLoopThread();
    assert(timers_.size() == activeTimers_.size());
    // 最早到期时间是否改变
    bool earliestChanged = false;//这个变量的意义是显示最早到期时间是否改变,通俗点说就是这个插入的定时器的位置在timers_的
    //首位,也就是这个插入的定时器的到期时间是timers_中已经存储的定时器中最早的,那么这个标志位就会置true
    Timestamp when = timer->expiration();//超时时刻
    TimerList::iterator it = timers_.begin();
    // 如果timers_为空或者when小于timers_中的最早到期时间
    if (it == timers_.end() || when < it->first) {
        earliestChanged = true;//表示定时器最早,所以置true
    }
    //要分别插入到两个set中
    {
        // 插入到timers_中
        std::pair<TimerList::iterator, bool> result
                = timers_.insert(Entry(when, timer));
        assert(result.second);
        (void) result;
    }
    {
        // 插入到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;//返回最早到期的时间有没有改变
}

 

 

 

posted @ 2020-04-14 13:31  Fitz~  阅读(1380)  评论(0编辑  收藏  举报