nginx源码学习3——定时器c++实现

本文是这一系列的第三篇,用到了前两篇的内容:

nginx内存池:http://blog.csdn.net/liu3daniel/article/details/73381205

nginx红黑树封装:http://blog.csdn.net/liu3daniel/article/details/73649577


还是老规矩,先是头文件,之后cpp,最后不完全测试。

mytool_timer.h

#ifndef MYTOOL_TIMER_H
#define MYTOOL_TIMER_H
#include "mytool_rbtree.h"
#include <string>
#include <cstddef>

/*nginx的定时器超时按照timer_resolution的设置触发
  *没有设置,那么每次从时间管理的红黑树内取得最近要触发的事件事件,在主循环内poll_wait该时间,并且每一次从poll_wait被唤醒后更新系统时间和时间字符串
  *设置了,每次主循环epoll_wait时间无限,靠循环初始化时的setitimer来定时唤醒,间隔就是设置的timer_resolution毫秒,仅在收到SIGALRM信号后更新时间
  *单例,一个进程只能有一个,不然信号会冲突
   */
namespace mytool{
        #define MYTOOLTIMESLOTCOUNT 5
        #define MYTOOLTIMEMINOS 1499073451
        class Timer{
                public:
                        typedef unsigned time_type;
                        typedef struct{
                                time_type key;
                                void *privData;
                                void (*func)(void *);
                        }TimeEvent;
                public:
                        static inline void sigalrm_handler(int) noexcept(true) {_triggered = true;}
                        static inline Timer *getInstance(time_type interval = 0) noexcept(false) {
                                if(_pInstance == NULL){
                                        _pInstance = new Timer(interval);
                                }
                                return _pInstance;
                        }
                        inline bool alarmed() const noexcept(true){return _triggered;}
                        inline void resetAlarmed() noexcept(true){_triggered = false;}
                        //gettimeofday转化成time_type,减去一个近期时间,否则容易毫秒越界,当然是临时方案
                        static inline time_type toTimeKey(const struct timeval &t) noexcept(true) {
                                return (t.tv_sec - MYTOOLTIMEMINOS) * 1000 + t.tv_usec / 1000; 
                        }

                        void updateTime() noexcept(true);
                        static inline time_type getTime() noexcept(true) {return _nowArr[_timeSlot];}
                        static inline std::string getTimeArr() noexcept(true) {return _timeStrArr[_timeSlot];}

                        //timer_resolution没有设置时,需要每次手动调用setAlarm重新设置,否则通过构造函数interval持续性接收sigalrm,interval为0时设置_tt最短时间为间隔
                        void setAlarm(time_type interval = 0) const noexcept(false);
                        //这一招可能会造成泄露,todo 用share_ptr
                        //TimeEvent *getEvent() noexcept(false);
                        void addEvent(const TimeEvent &te) noexcept(false);
                        void deleteEvent(TimeEvent *pte) noexcept(false);
                        //get之后是可以调用deleteEvent释放的,所以没有返回const,但是如果不delete直接修改key是不行的,和map迭代器的first一样
                        TimeEvent *getNextEvent() const noexcept(true);
                        static inline bool isExpired(const TimeEvent &te) noexcept(true){return te.key <= _nowArr[_timeSlot];}

                        //析构函数就默认即可
                private:
                        //设置sigalrm的动作,如果interval不为空,设置定期发送信号
                        explicit Timer(time_type interval) noexcept(false);
                        static Timer *_pInstance;
                        static bool _triggered;
                        //锁,防止两个线程同时更新时间,todo
                        //mytool_lock_t....
                        static int _timeSlot;
                        time_type _interval;
                        //timeStrArr类型还能够更多,原nginx有五种
                        //MYTOOLTIMESLOTCOUNT如果是2,那么当前如果使用的slot是0,则更新线程更新1,但是如果对于很短的更新时间+_timeSlot刚被读取就休眠直到时间更新后才唤醒的情况,更新线程和读取线程可能冲突,nginx设置为64
                        static time_type _nowArr[MYTOOLTIMESLOTCOUNT];
                        static std::string _timeStrArr[MYTOOLTIMESLOTCOUNT];
                        Rbtree _tt;
        };
}

#endif
mytool_timer.cpp


#include "mytool_timer.h"
#include <sys/time.h>
#include <cstring>
#include <signal.h>
#include <stdexcept>
namespace mytool{
        Timer *Timer::_pInstance = NULL;
        bool Timer::_triggered = false;
        int Timer::_timeSlot = 0;
        typename Timer::time_type Timer::_nowArr[MYTOOLTIMESLOTCOUNT];
        std::string Timer::_timeStrArr[MYTOOLTIMESLOTCOUNT];

        static bool timerComp(void *p1,void *p2) noexcept(false) {
                typename Timer::TimeEvent *pte1 = (typename Timer::TimeEvent *)p1,*pte2 = (typename Timer::TimeEvent *)p2;
                if(pte1->key != pte2->key)
                        return pte1->key < pte2->key;
                if(pte1->privData != pte2->privData)
                                return pte1->privData < pte2->privData;
                return pte1->func < pte2->func;
        }

        Timer::Timer(time_type interval = 0):_interval(interval),_tt(timerComp) {
                struct sigaction sa;
                memset(&sa,0,sizeof(sa));
                sa.sa_handler = sigalrm_handler;
                sigaction(SIGALRM,&sa,NULL);

                if(interval != 0){
                        struct itimerval im;
                        //struct timeval now;
                        //gettimeofday(&now,NULL);
                        im.it_interval.tv_sec = interval / 1000;
                        im.it_interval.tv_usec = (interval % 1000) * 1000;
                        im.it_value.tv_sec = interval / 1000;
                        im.it_value.tv_usec = (interval % 1000) * 1000;
                        //im.it_value.tv_sec = (interval + now.tv_sec * 1000 + now.tv_usec / 1000) / 1000;
                        //im.it_value.tv_usec = ((interval % 1000) * 1000 + now.tv_usec) % (1000 * 1000);
                        if(setitimer(ITIMER_REAL,&im,NULL) == -1)
                                throw(std::runtime_error("Timer::Timer setitimer error"));
                }
        }

        void Timer::updateTime() noexcept(true) {
                int slot = ((_timeSlot + 1) >= MYTOOLTIMESLOTCOUNT) ? 0:(_timeSlot + 1);
                struct timeval tv;
                gettimeofday(&tv,NULL);
                //_nowArr[slot] = tv.tv_sec * 1000 + tv.tv_usec / 1000;
                _nowArr[slot] = toTimeKey(tv);
                _timeStrArr[slot] = std::string("time now:") + std::to_string(_nowArr[slot]);
                _timeSlot = slot;
        }

        void Timer::setAlarm(time_type interval) const noexcept(false) {
                if(interval == 0){
                        struct timeval now;
                        const TimeEvent *pte = getNextEvent();
                        if(pte == NULL)
                                return;
                        gettimeofday(&now,NULL);
                        if(toTimeKey(now) < pte->key)
                                interval = pte->key - toTimeKey(now);
                        else
                                interval = 1;
                }
                struct itimerval im;
                im.it_interval.tv_sec = 0;
                im.it_interval.tv_usec = 0;
                im.it_value.tv_sec = interval / 1000;
                im.it_value.tv_usec = (interval % 1000) * 1000;
                //im.it_value.tv_sec = (interval + now.tv_sec * 1000 + now.tv_usec / 1000) / 1000;
                //im.it_value.tv_usec = ((interval % 1000) * 1000 + now.tv_usec) % (1000 * 1000);
                //im.it_value.tv_sec = (interval / 1000 + now.tv_sec + ((interval % 1000) * 1000 + now.tv_usec)) / (1000 * 1000);
                //im.it_value.tv_usec = ((interval % 1000) * 1000 + now.tv_usec) % (1000 * 1000);
                if(setitimer(ITIMER_REAL,&im,NULL) == -1)
                        throw(std::runtime_error("Timer::setAlarm setitimer error"));
        }

        //todo 异常处理noexcept(false)那些
        void Timer::addEvent(const TimeEvent &te) noexcept(false) {
                TimeEvent *pte = (TimeEvent *)(_tt.getNodeData(sizeof(TimeEvent)));
                *pte = te;
                _tt.insertNodeData(pte,te.key);
        }

        //todo 异常处理noexcept(false)那些
        void Timer::deleteEvent(TimeEvent *pte) noexcept(false) {
                _tt.deleteNodeData(pte);
        }

        typename Timer::TimeEvent *Timer::getNextEvent() const noexcept(true) {
                return (TimeEvent *)(_tt.getMinData());
        }

        /*
        typename Timer::TimeEvent *Timer::getEvent() noexcept(false) {
        }
        */
}


testTimer.cpp

#include "mytool_timer.h"
#include <unistd.h>
#include <iostream>
#include <cstdio>
#include <sys/time.h>

using namespace mytool;
using namespace std;
void printInt(void *p){
        printf("te num:%d\n",*((int *)p));
}
int main(){
        Timer *ptm = Timer::getInstance();
        //测试定时发送信号和更新时间
        /*
        while(1){
                sleep(100);
                if(ptm->alarmed()){
                        cout<<"alarm received"<<endl;
                        ptm->resetAlarmed();
                        ptm->updateTime();
                        cout<<"time is:"<<ptm->getTimeArr()<<endl;
                }
        }
        */
        //测试timeevent事件添加和定时
        Timer::TimeEvent te1,te2,te3,te4;
        Timer::TimeEvent *pte;
        struct timeval now;
        gettimeofday(&now,NULL);
        te1.key =  Timer::toTimeKey(now) + 400;
        te2.key =  Timer::toTimeKey(now) + 300;
        te3.key =  Timer::toTimeKey(now) + 200;
        te4.key =  Timer::toTimeKey(now) + 100;
        te1.privData = new int(1);
        te2.privData = new int(2);
        te3.privData = new int(3);
        te4.privData = new int(4);
        te1.func = printInt;
        te2.func = printInt;
        te3.func = printInt;
        te4.func = printInt;

        ptm->addEvent(te1);
        ptm->addEvent(te2);
        ptm->addEvent(te3);
        ptm->addEvent(te4);

        ptm->setAlarm();
        while(1){
                if(ptm->alarmed()){
                        ptm->updateTime();
                        cout<<"alarm received"<<endl;
                        cout<<"time is:"<<ptm->getTimeArr()<<endl;
                        pte = ptm->getNextEvent();
                        pte->func(pte->privData);
                        if(Timer::isExpired(*pte)){
                                cout<<"expired and deleted"<<endl;
                                ptm->deleteEvent(pte);
                        }
                        ptm->setAlarm();
                }
                sleep(100);
        }
}

g++ --std=c++11 -g -Wall mytool_rbtree.cpp mytool_rbtree_raw.cpp mytool_timer.cpp testTimer.cpp 效果符合预期。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值