1、数据结构

#define ev_at(w) ((WT)(w))->at
#define ev_active(w) ((W)(w))->active

typedef ev_watcher_time *WT;

struct ev_loop
{
ev_tstamp mn_now
ANHE * timers
int timermax
int timercnt

ev_watcher * rfeeds
}

/* Heap Entry */     //是否缓存时间监控器中的at字段。
#if EV_HEAP_CACHE_AT 
/* a heap element */
typedef struct {
ev_tstamp at;
WT w;
} ANHE;

#define ANHE_w(he) (he).w /* access watcher, read-write */
#define ANHE_at(he) (he).at /* access cached at, read-only */
#define ANHE_at_cache(he) (he).at = (he).w->at /* update at from watcher */
#else
/* a heap element */
typedef WT ANHE;

#define ANHE_w(he) (he)
#define ANHE_at(he) (he)->at
#define ANHE_at_cache(he)
#endif

2、ev_timer_start 

void noinline
ev_timer_start (EV_P_ ev_timer *w) EV_THROW
{
if (expect_false (ev_is_active (w)))
return;

ev_at (w) += mn_now;

++timercnt;
ev_start (EV_A_ (W)w, timercnt + HEAP0 - 1);//w->active = 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));

EV_FREQUENT_CHECK;

/*assert (("libev: internal timer heap corruption", timers [ev_active (w)] == (WT)w));*/
}

inline_speed void
upheap (ANHE *heap, int k)
{
ANHE he = heap [k];

for (;;)
{
int p = HPARENT (k);

if (UPHEAP_DONE (p, k) || ANHE_at (heap [p]) <= ANHE_at (he))
break;

heap [k] = heap [p];
ev_active (ANHE_w (heap [k])) = k;
k = p;
}

heap [k] = he;
ev_active (ANHE_w (he)) = k;
}

3、timers_reify 

inline_size void
timers_reify (EV_P)
{
EV_FREQUENT_CHECK;

if (timercnt && ANHE_at (timers [HEAP0]) < mn_now)
{
do
{
ev_timer *w = (ev_timer *)ANHE_w (timers [HEAP0]);

/*assert (("libev: inactive timer on timer heap detected", ev_is_active (w)));*/

/* first reschedule or stop timer */
if (w->repeat)
{
ev_at (w) += w->repeat;
if (ev_at (w) < mn_now)
ev_at (w) = mn_now;

assert (("libev: negative ev_timer repeat value found while processing timers", w->repeat > 0.));

ANHE_at_cache (timers [HEAP0]);
downheap (timers, timercnt, HEAP0);
}
else
ev_timer_stop (EV_A_ w); /* nonrepeating: stop timer */

EV_FREQUENT_CHECK;
feed_reverse (EV_A_ (W)w);
}
while (timercnt && ANHE_at (timers [HEAP0]) < mn_now);

feed_reverse_done (EV_A_ EV_TIMER);
}
}

#if EV_USE_4HEAP

#define DHEAP 4
#define HEAP0 (DHEAP - 1) /* index of first element in heap */
#define HPARENT(k) ((((k) - HEAP0 - 1) / DHEAP) + HEAP0)
#define UPHEAP_DONE(p,k) ((p) == (k))

/* away from the root */
inline_speed void
downheap (ANHE *heap, int N, int k)
{
ANHE he = heap [k];
ANHE *E = heap + N + HEAP0;

for (;;)
{
ev_tstamp minat;
ANHE *minpos;
ANHE *pos = heap + DHEAP * (k - HEAP0) + HEAP0 + 1;

/* find minimum child */
if (expect_true (pos + DHEAP - 1 < E))
{
/* fast path */ (minpos = pos + 0), (minat = ANHE_at (*minpos));
if ( ANHE_at (pos [1]) < minat) (minpos = pos + 1), (minat = ANHE_at (*minpos));
if ( ANHE_at (pos [2]) < minat) (minpos = pos + 2), (minat = ANHE_at (*minpos));
if ( ANHE_at (pos [3]) < minat) (minpos = pos + 3), (minat = ANHE_at (*minpos));
}
else if (pos < E)
{
/* slow path */ (minpos = pos + 0), (minat = ANHE_at (*minpos));
if (pos + 1 < E && ANHE_at (pos [1]) < minat) (minpos = pos + 1), (minat = ANHE_at (*minpos));
if (pos + 2 < E && ANHE_at (pos [2]) < minat) (minpos = pos + 2), (minat = ANHE_at (*minpos));
if (pos + 3 < E && ANHE_at (pos [3]) < minat) (minpos = pos + 3), (minat = ANHE_at (*minpos));
}
else
break;

if (ANHE_at (he) <= minat)
break;

heap [k] = *minpos;
ev_active (ANHE_w (*minpos)) = k;

k = minpos - heap;
}

heap [k] = he;
ev_active (ANHE_w (he)) = k;
}

#else /* 4HEAP */

#define HEAP0 1
#define HPARENT(k) ((k) >> 1)
#define UPHEAP_DONE(p,k) (!(p))

/* away from the root */
inline_speed void
downheap (ANHE *heap, int N, int k)
{
ANHE he = heap [k];

for (;;)
{
int c = k << 1;

if (c >= N + HEAP0)
break;

c += c + 1 < N + HEAP0 && ANHE_at (heap [c]) > ANHE_at (heap [c + 1])
? 1 : 0;

if (ANHE_at (he) <= ANHE_at (heap [c]))
break;

heap [k] = heap [c];
ev_active (ANHE_w (heap [k])) = k;

k = c;
}

heap [k] = he;
ev_active (ANHE_w (he)) = k;
}
#endif

inline_speed void
feed_reverse (EV_P_ W w)
{
array_needsize (W, rfeeds, rfeedmax, rfeedcnt + 1, EMPTY2);
rfeeds [rfeedcnt++] = w;
}

inline_size void
feed_reverse_done (EV_P_ int revents)
{
do
ev_feed_event (EV_A_ rfeeds [--rfeedcnt], revents);
while (rfeedcnt);
}

void noinline
ev_feed_event (EV_P_ void *w, int revents) EV_THROW
{
W w_ = (W)w;
int pri = ABSPRI (w_);

if (expect_false (w_->pending))
pendings [pri][w_->pending - 1].events |= revents;
else
{
w_->pending = ++pendingcnt [pri];
array_needsize (ANPENDING, pendings [pri], pendingmax [pri], w_->pending, EMPTY2);
pendings [pri][w_->pending - 1].w = w_;
pendings [pri][w_->pending - 1].events = revents;
}

pendingpri = NUMPRI - 1;
}

4、ev_run
int  ev_run (EV_P_ int flags)
{
waittime = MAX_BLOCKTIME;

if (timercnt)
{
ev_tstamp to = ANHE_at (timers [HEAP0]) - mn_now;
if (waittime > to) waittime = to;

。。。。。
}
timers_reify(EV_A);
EV_INVOKE_PENDING;
}

Libev中在管理定时器时,使用了堆这种结构存储ev_timer,除了最小2叉堆之外,还有4叉堆,可用通过宏定义来设置使用哪个。

对于n叉堆来说,使用数组进行存储时,下标为x的元素,其孩子节点的下标范围是[nx+1, nx+n]。比如2叉堆,下标为x的元素,其孩子节点的下标为2x+1和2x+2.

根据定理,对于4叉堆而言,下标为x的元素,其孩子节点的下标范围是[4x+1, 4x+4]。还可以得出,其父节点的下标是(x-1)/4。然而在Libev的代码中,使用数组a存储堆时,4叉堆的第一个元素存放在a[3],2叉堆的第一个元素存放在a[1]。

所以,对于Libev中的4叉堆实现而言,下标为k的元素(对应在正常实现中的下标是k-3),其孩子节点的下标范围是[4(k-3)+1+3, 4(k-3)+4+3];其父节点的下标是((k-3-1)/4)+3。

对于Libev中的2叉堆实现而言,下标为k的元素(对应在正常实现中,其下标是k-1),其孩子节点的下标范围是[2(k-1)+1+1,  2(k-1)+2+1],也就是[2k, 2k+1];其父节点的下标是((k-1-1)/2)+1,也就是k/2。

downheap和upheap函数就是使用以上原则,不断与子结点或者父结点比较,交换,最终形成堆。

首先,ev_timer_start 将时间监控器添加到timers中(通过upheap),loop->timer是一个数组形式的最小堆。根据timer->at做的比较,即堆顶为时间最小的监控器,timer->active是数组的下标。

ev_run中,先计算超时时间,使其不大于最小的时间。

最后,timers_reify 中取出到时的时间监听器,添加到pendings队列。如果是repeat的话,则更新下一次触发时间,调用downheap操作将这个节点下移至合适的位置;否则直接删除该watcher。

ev_prepare, ev_check, ev_idle

从角色上来看,这三个类型的watcher其实都是事件循环的一个扩展点。通过这三个watcher,开发者可以在事件循环的一些特殊时刻获得回调的机会。

  • ev_prepare 在事件循环发生阻塞前会被触发。

  • ev_check 在事件循环阻塞结束后会被触发。ev_check的触发是按优先级划分的。可以保证,ev_check是同一个优先级上阻塞结束后最先被触发的watcher。所以,如果要保证ev_check是最先被执行的,可以把它的优先级设成最高。

  • ev_idle 当没有其他watcher被触发时被触发。ev_idle也是按优先级划分的。它的语义是,在当前优先级以及更高的优先级上没有watcher被触发,那么它就会被触发,无论之后在较低优先级上是否有其他watcher被触发。

这三类watcher给外部的开发者提供了非常便利的扩展机制,在这个基础上,开发者可以做很多有意思的事情,也对事件循环有了更多的控制权。具体到底能做些什么,做到什么程度,那就要看开发者们的想象力和创造力了:)

ev_signal 是信号监听器,实现方式是通过signalfd、eventfd、pipe等方法将对信号的处理,转化为对文件描述符的处理。signalfd、eventfd是linux提供的同步信号处理的方式。

http://blog.csdn.net/gqtcgq/article/details/49716601

libev中timer时间事件监控器的更多相关文章

  1. Libev源码分析04:Libev中的相对时间定时器

    Libev中的超时监视器ev_timer,就是简单的相对时间定时器,它会在给定的时间点触发超时事件,还可以在固定的时间间隔之后再次触发超时事件. 所谓的相对时间,指的是如果你注册了一个1小时的超时事件 ...

  2. Libev源码分析02:Libev中的IO监视器

    一:代码流程 在Libev中,启动一个IO监视器,等待该监视器上的事件触发,然后调用该监视器的回调函数.整个的流程是这样的: 首先调用ev_default_loop初始化struct  ev_loop ...

  3. Libev——ev_timer 相对时间定时器

    Libev中的超时监视器ev_timer,是简单的相对时间定时器,它会在给定的时间点触发超时事件,还可以在固定的时间间隔之后再次触发超时事件. 1.超时监视器ev_timer结构 typedef st ...

  4. C#中Timer使用及解决重入问题

    C#中Timer使用及解决重入问题 ★介绍 首先简单介绍一下timer,这里所说的timer是指的System.Timers.timer,顾名思义,就是可以在指定的间隔是引发事件.官方介绍在这里,摘抄 ...

  5. C#中timer类的用法

    C#中timer类的用法 关于C#中timer类  在C#里关于定时器类就有3个   1.定义在System.Windows.Forms里   2.定义在System.Threading.Timer类 ...

  6. 关于C#中timer类

    ·关于C#中timer类 在C#里关于定时器类就有3个 1.定义在System.Windows.Forms里 2.定义在System.Threading.Timer类里 3.定义在System.Tim ...

  7. C#中Timer定时器的使用示例

    关于C#中timer类 在C#里关于定时器类就有3个: 1.定义在System.Windows.Forms里 2.定义在System.Threading.Timer类里 3.定义在System.Tim ...

  8. [转]C#中Timer使用及解决重入问题

    本文转自:http://www.cnblogs.com/hdkn235/archive/2014/12/27/4187925.html ★前言 打开久违的Live Writer,又已经好久没写博客了, ...

  9. 浅析linux内核中timer定时器的生成和sofirq软中断调用流程(转自http://blog.chinaunix.net/uid-20564848-id-73480.html)

    浅析linux内核中timer定时器的生成和sofirq软中断调用流程 mod_timer添加的定时器timer在内核的软中断中发生调用,__run_timers会spin_lock_irq(& ...

随机推荐

  1. 克隆虚拟机win8系统后注意修改安全标识(SID)

    克隆虚拟机win8系统后注意修改安全标识(SID)   克隆虚拟机系统后两个系统硬件配置一样,需要注意修改:security ID ,MAC,计算机名,IP地址,产品激活 重置工具:sysprep.e ...

  2. AngularJS 跨站请求- jsonp请求

    今天写东西的时候遇到了 一种情况 ,因为用的不是自己公司人员写的接口 ,而我要写的东西是抓别的网页上的接口 所以出现了 一下这种情况 这是我的请求: 我在浏览器模板赋值的时候发现赋值成功了, 在浏览器 ...

  3. express搭建简易web的服务器

    express搭建简易web的服务器 说到express我们就会想到nodejs,应为它是一款基于nodejs平台的web应用开发框架.既然它是基于nodejs平台的框架那么就得先安装nodejs. ...

  4. 【.NET】Linq几样特点

    LinQ 是 .net framework 3.5的技术,对集合的查询和操作十分方便,结合LinQ Provider ,可以更方便操作XML和SQL(或Entity). LinQ有6样常用的特性: 自 ...

  5. Objective-C运行时态消息传递--拼接方法名

    做IOS开发的人都知道,Objective-C语言中方法的调用是运行时采取绑定的,在编译过程中只声明该方法的存在. 那么我们来简单说下在运行时,类的消息传递. 在运行时,每个方法如[self meth ...

  6. android 蓝牙连接与通讯(Bluetooth)

    最近做了一个小项目,关于蓝牙的一个智能硬件.其中涉及到了蓝牙模块的操作.特记下蓝牙模块的操作过程.只记录下关于蓝牙部分的操作,具体业务逻辑不涉及其中.重点是记录下蓝牙的扫描.链接.通讯. 在使用蓝牙模 ...

  7. 挖坑:CF712E

    #include<cstdio> #include<cstring> #include<algorithm> #define maxn 1000005 using ...

  8. java学习笔记:反射

    1.什么是反射? Reflection(反射)是被视为动态语言的关键,反射机制允许程序做执行期间借助于ReflectionAPI取得任何类的内部信息,并能直接操作任意对象内部属性及方法 2.反射相关的 ...

  9. CoreJavaE10V1P3.7 第3章 Java的基本编程结构-3.7 输入输出(Input ,Output)

    3.7.1 读取输入 Scanner in = new Scanner(System.in); System.out.print("What is your name? "); S ...

  10. Linux 分区挂载方案

    /boot 1G swap 2G(看内存决定) / 10-15G /home 5G