Libev源码分析04:Libev中的相对时间定时器
Libev中的超时监视器ev_timer,就是简单的相对时间定时器,它会在给定的时间点触发超时事件,还可以在固定的时间间隔之后再次触发超时事件。
所谓的相对时间,指的是如果你注册了一个1小时的超时事件,然后调整系统时间到了去年的一月份,该超时事件依然会在1个小时之后触发。
一:数据结构
1:超时监视器ev_timer结构:
typedef struct ev_timer
{
int active;
int pending;
int priority;
void *data;
void (*cb)(struct ev_loop *loop, struct ev_timer *w, int revents); ev_tstamp at;
ev_tstamp repeat; /* rw */
} ev_timer;
其中的前五个成员是监视器的公共成员,其中的active在超时监视器中有特殊作用,那就是标明该监视器在堆数组timers中的下标。后两个成员at和repeat是ev_timer特有的。at表明定时器第一次触发的时间点,该是是根据mn_now设置的,repeat必须大于等于0,它表示每隔repeat秒,该定时器再次触发。如果repeat为0,表明该定时器只触发一次。
2:ev_watcher_time结构
typedef struct ev_watcher_time
{
int active;
int pending;
int priority;
void *data;
void (*cb)(struct ev_loop *loop, struct ev_watcher_time *w, int revents); ev_tstamp at;
} ev_watcher_time; typedef ev_watcher_time *WT;
ev_watcher_time的结构与ev_timer几乎一样,只是少了最后一个成员。该结构其实是ev_timer和ev_periodic的父类,它包含了ev_timer和ev_periodic的共有成员。
3:堆元素ANHE
#if EV_HEAP_CACHE_AT
typedef struct
{
ev_tstamp at;
WT w;
} ANHE;
#else
typedef WT ANHE;
#endif
宏EV_HEAP_CACHE_AT的作用,是为了提高在堆中的缓存利用率,如果没有定义该宏,堆元素就是指向ev_watcher_time结构的指针。如果定义了该宏,则还将堆元素的关键成员at进行缓存。
二:超时监视器函数
1:设置超时监视器ev_timer_set
#define ev_timer_set(ev, after_, repeat_) do {\
((ev_watcher_time *)(ev))->at = (after_); \
(ev)->repeat = (repeat_); \
} while (0)
2:启动超时监视器ev_timer_start
#if EV_HEAP_CACHE_AT
#define ANHE_w(he) (he).w
#define ANHE_at(he) (he).at
#define ANHE_at_cache(he) (he).at = (he).w->at
#else
#define ANHE_w(he) (he)
#define ANHE_at(he) (he)->at
#define ANHE_at_cache(he)
#endif void ev_timer_start (struct ev_loop *loop, ev_timer *w)
{
if (expect_false (ev_is_active (w)))
return; ev_at (w) += mn_now; assert (("libev: ev_timer_start called with negative timer repeat value", w->repeat >= 0.)); ++timercnt;
ev_start (EV_A_ (W)w, timercnt + HEAP0 - 1);
array_needsize (ANHE, timers, timermax, ev_active (w) + 1, EMPTY2);
ANHE_w (timers [ev_active (w)]) = (WT)w;
ANHE_at_cache (timers [ev_active (w)]);
upheap (timers, ev_active (w));
}
代码比较简单,首先设置监视器的at成员,表明在at时间点,超时事件会触发,注意at是根据mn_now设置的,也就是相对于系统启动时间而言的(或者是日历时间)。之后,就是将该监视器加入到堆timer中,首先将该监视器加到堆中的最后一个元素,然后调用upheap调整堆。注意监视器的active成员,表明该监视器在堆数组中的下标。
3:停止超时监视器ev_timer_stop
void ev_timer_stop (EV_P_ ev_timer *w) EV_THROW
{
clear_pending (EV_A_ (W)w);
if (expect_false (!ev_is_active (w)))
return; int active = ev_active (w); --timercnt; if (expect_true (active < timercnt + HEAP0))
{
timers [active] = timers [timercnt + HEAP0];
adjustheap (timers, timercnt, active);
} ev_at (w) -= mn_now; ev_stop (EV_A_ (W)w);
}
首先调用clear_pending,如果该监视器已经处于pending状态,将其从pendings中删除。然后根据监视器中的active成员,得到其在timers堆上的索引,将该监视器从堆timers上删除,重新调整堆结构。然后调用ev_stop停止该监视器。
4:timers_reschedule更新定时器的时间
在ev_run中,每次loop中,在调用backend_poll前后都会调用time_update更新当前时间,如果发现时间被人调整,则需要更新定时器,更新相对定时器时,调用timers_reschedule(loop, ev_rt_now - mn_now),其中,ev_rt_now是最新的当前日历时间,mn_now是之前记录的日历时间,他们之间的差值就表示时间调整了多少,timers_reschedule代码如下:
static void timers_reschedule (struct ev_loop *loop, ev_tstamp adjust)
{
int i; for (i = 0; i < timercnt; ++i)
{
ANHE *he = timers + i + HEAP0;
ANHE_w (*he)->at += adjust;
ANHE_at_cache (*he);
}
}
代码较简单,就是更新堆timers中的每个元素的at值。
5:timers_reify将激活的超时事件排队
每次调用backend_poll之前,都会根据ANHE_at (timers [HEAP0]) - mn_now的值,校准backend_poll的阻塞时间waittime,这样就能尽可能的保证定时器能够按时触发。
调用backend_poll之后,就会调用timers_reify查看timers中哪些定时器触发了,代码如下:
void timers_reify(struct ev_loop *loop)
{
if (timercnt && ANHE_at (timers [HEAP0]) < mn_now)
{
do
{
ev_timer *w = (ev_timer *)ANHE_w (timers [HEAP0]); /* first reschedule or stop timer */
if (w->repeat)
{
ev_at (w) += w->repeat;
if (ev_at (w) < mn_now)
ev_at (w) = mn_now; ANHE_at_cache (timers [HEAP0]);
downheap (timers, timercnt, HEAP0);
}
else
ev_timer_stop (EV_A_ w); /* nonrepeating: stop timer */ feed_reverse (EV_A_ (W)w);
}
while (timercnt && ANHE_at (timers [HEAP0]) < mn_now); feed_reverse_done (loop, EV_TIMER);
}
}
void feed_reverse (struct ev_loop *loop, W w)
{
array_needsize (W, rfeeds, rfeedmax, rfeedcnt + 1, EMPTY2);
rfeeds [rfeedcnt++] = w;
} void feed_reverse_done (struct ev_loop *loop, int revents)
{
do
ev_feed_event (EV_A_ rfeeds [--rfeedcnt], revents);
while (rfeedcnt);
}
如果堆顶元素的超时时间点at小于mn_now(是小于,而不是等于,原因见:http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#The_special_problem_of_being_too_ear),说明堆顶元素的超时时间到时,进入循环,取出堆顶元素的监视器,如果w->repeat大于0,则设置该超时监视器下一次触发的时间点,然后调用downheap调整堆结构。否则,直接调用ev_timer_stop停止该监视器。最后,调用feed_reverse将该监视器放入rfeeds数组中,该数组对当前已经触发的超时监视器缓存。然后,继续查看新的堆顶元素是否已经超时,超时的话接着上面的步骤进行处理。
将所有本次触发的超时事件都处理完之后,调用feed_reverse_done,将缓存数组rfeeds中每个元素通过ev_feed_event添加到pendings数组中。
三:例子
void timer_action(struct ev_loop *main_loop,ev_timer *timer_w,int e)
{
time_t now;
now = time(NULL);
printf("in tiemr cb%d , cur time is %s\n", (int)(timer_w->data), ctime(&now));
} int main(int argc ,char *argv[])
{
struct ev_loop *main_loop = ev_default_loop(0); timer_w1.data = (void *)1;
ev_init(&timer_w1,timer_action);
ev_timer_set(&timer_w1,10,5);
ev_timer_start(main_loop,&timer_w1); timer_w2.data = (void *)2;
ev_init(&timer_w2,timer_action);
ev_timer_set(&timer_w2,5,10);
ev_timer_start(main_loop,&timer_w2); time_t now;
now = time(NULL);
printf("begin time time is %s\n", ctime(&now)); ev_run(main_loop,0);
return;
}
结果打印:
begin time time is Wed Oct 21 21:55:31 2015
in tiemr cb2 , cur time is Wed Oct 21 21:55:36 2015
in tiemrcb1 , cur time is Wed Oct 21 21:55:41 2015
in tiemrcb1 , cur time is Wed Oct 21 21:55:46 2015
in tiemr cb2 , cur time is Wed Oct 21 21:55:46 2015
in tiemrcb1 , cur time is Wed Oct 21 21:55:51 2015
in tiemrcb1 , cur time is Wed Oct 21 21:55:56 2015
in tiemr cb2 , cur time is Wed Oct 21 21:55:56 2015
in tiemrcb1 , cur time is Wed Oct 21 21:56:01 2015
...
Libev源码分析04:Libev中的相对时间定时器的更多相关文章
- [转]Libev源码分析 -- 整体设计
Libev源码分析 -- 整体设计 libev是Marc Lehmann用C写的高性能事件循环库.通过libev,可以灵活地把各种事件组织管理起来,如:时钟.io.信号等.libev在业界内也是广受好 ...
- NIO 源码分析(04) 从 SelectorProvider 看 JDK SPI 机制
目录 一.SelectorProvider SPI 二.SelectorProvider 加载过程 2.1 SelectorProvider 加载 2.2 Windows 下 DefaultSelec ...
- angular源码分析:angular中脏活累活的承担者之$interpolate
一.首先抛出两个问题 问题一:在angular中我们绑定数据最基本的方式是用两个大括号将$scope的变量包裹起来,那么如果想将大括号换成其他什么符号,比如换成[{与}],可不可以呢,如果可以在哪里配 ...
- angular源码分析:angular中入境检察官$sce
一.ng-bing-html指令问题 需求:我需要将一个变量$scope.x = '<a href="http://www.cnblogs.com/web2-developer/&qu ...
- angular源码分析:angular中各种常用函数,比较省代码的各种小技巧
angular的工具函数 在angular的API文档中,在最前面就是讲的就是angular的工具函数,下面列出来 angular.bind //用户将函数和对象绑定在一起,返回一个新的函数 angu ...
- angular源码分析:angular中的依赖注入式如何实现的
一.准备 angular的源码一份,我这里使用的是v1.4.7.源码的获取,请参考我另一篇博文:angular源码分析:angular源代码的获取与编译环境安装 二.什么是依赖注入 据我所知,依赖注入 ...
- Libev源码分析09:select突破处理描述符个数的限制
众所周知,Linux下的多路复用函数select采用描述符集表示处理的描述符.描述符集的大小就是它所能处理的最大描述符限制.通常情况下该值为1024,等同于每个进程所能打开的描述符个数. 增大描述符集 ...
- angular源码分析:angular中$rootscope的实现——scope的一生
在angular中,$scope是一个关键的服务,可以被注入到controller中,注入其他服务却只能是$rootscope.scope是一个概念,是一个类,而$rootscope和被注入到cont ...
- angular源码分析:angular中jqLite的实现——你可以丢掉jQuery了
一.从function JQLite(element)函数开始. function JQLite(element) { if (element instanceof JQLite) { //情况1 r ...
随机推荐
- Java review-basic1
1. Dependency Injection Answer: Any application is composed of many objects that collaborate with ea ...
- SPSS详细操作:样本均数间的多重比较
SPSS详细操作:样本均数间的多重比较 下面我们用例子来探讨:方差分析得出各组总体均数间的差异有统计学意义之后,如何进行样本均数间的多重比较. 一.问题与数据 为调查A.B.C三种治疗措施对患者谷丙转 ...
- Docker安装elasticsearch-head监控ES步骤 - gmijie的专栏 - CSDN博客
原文:Docker安装elasticsearch-head监控ES步骤 - gmijie的专栏 - CSDN博客 Docker安装elasticsearch-head监控ES步骤 docker拉取镜像 ...
- 提升mysql服务器性能(HA MMM MHA MaxScale)
原文:提升mysql服务器性能(HA MMM MHA MaxScale) 版权声明:皆为本人原创,复制必究 https://blog.csdn.net/m493096871/article/detai ...
- 系统日志和内核消息 $ dmesg$ less /var/log/messages$ less /var/log/secure$ less /var/log/auth
查看错误和警告消息,比如看看是不是很多关于连接数过多导致? 看看是否有硬件错误或文件系统错误? 分析是否能将这些错误事件和前面发现的疑点进行时间上的比对.
- iOS 7: 如何为iPhone 5s编译64位应用
随着iPhone 5S的推出,大家开始关心5S上所使用的64位CPU A7. 除了关心A7的性能以外,大家还会关心一个问题,那就是使用A7的64位系统对应用有没有什么要求.特别是应用开发者,大家都比较 ...
- linux下监测进程是否存在
因为有的统计脚本需要执行很久,而有不能总去人工的检查进程是否在跑,所以就用shell脚本来循环监测进程是否存在 尝试了网上说的$?表示上一条命令返回值总是达不到预期的结果,后来直接改成用一个变量记录返 ...
- hive行转列的高级用法later view explode
先贴出一个示例: 参考链接
- 使用Centos7 安装docker的步骤
1.Linuxyum源切换阿里云软件源 备份本机软件源 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.b ...
- 官方支持的全新版Neo4j-JDBC驱动3.0
原文:The All-New, Officially Supported Neo4j-JDBC Driver 3.0 作者: Michael Hunger 译者:仲培艺,关注数据库领域,纠错.寻求报道 ...