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;//返回最早到期的時間有沒有改變
}

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM