本文是这一系列的第三篇,用到了前两篇的内容:
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 效果符合预期。