Libev——ev_timer 相对时间定时器

  • 2021 年 12 月 12 日
  • 笔记

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

1.超时监视器ev_timer结构

typedef struct ev_timer
{
   /*前五行为EV_WATCHER 参数具体含义在libev I/O事件中有描述*/
    int active; 
    int pending;
    int priority;
    void *data;
    void (*cb)(struct ev_loop *loop, struct ev_timer *w, int revents);  //回调函数
 
    ev_tstamp at; // 定时器第一次触发的时间点,根据mn_now设置
    ev_tstamp repeat; //repeat 必须>=0,当大于0时表示每隔repeat秒该定时器再次触发;0表示只触发一次;double 型
} ev_timer;watcher_

at和repeat两个成员为是ev_timer特有的

2.ev_watcher_timer

#define EV_WATCHER_TIME(type)			\ //定时器
  EV_WATCHER (type)				\
  ev_tstamp at;     // 定时器第一次触发的时间点,根据mn_now设置

 ev_watcher_time的结构与ev_timer几乎一样,只是少了最后一个成员。该结构其实是ev_timer和ev_periodic的父类,它包含了ev_timer和ev_periodic的共有成员。

3.堆元素ANHE

//----- 宏EV_HEAP_CACHE_AT是为了提高在堆中的缓存利用率,主要是为了对at进行缓
#if EV_HEAP_CACHE_AT
    typedef struct 
    {
        ev_tstamp at;
        WT w;
    } ANHE;//除了包含该指针WT之外,还缓存了 ev_watcher_time 中的成员 at,堆中元素就是根据 at 的值进行组织的,具有最小 at 值得节点就是根节点。
#else  
typedef WT ANHE;//是一个指向时间监视器结构 ev_watcher_time 的指针 WT
#endif

 宏EV_HEAP_CACHE_AT的作用,是为了提高在堆中的缓存利用率,如果没有定义该宏,堆元素就是指向ev_watcher_time结构的指针。如果定义了该宏,则还将堆元素的关键成员at进行缓存。

4.定时器事件的初始化与设置

//初始化,意味着在after秒后执行,设置为0则会立即执行一次;然后每隔repeat秒执行一次
#define ev_timer_init(ev,cb,after,repeat)        \
    do { ev_init ((ev), (cb)); ev_timer_set ((ev),(after),(repeat)); } while (0)
//设置
#define ev_timer_set(ev,after_,repeat_)   
  do { ((ev_watcher_time *)(ev))->at = (after_); (ev)->repeat = (repeat_); } while (0)

5.启动定时器ev_timer_stavoid noinlinev_timer_start (EV_P_ ev_timer *w) EV_THROW

{
  if (expect_false (ev_is_active (w)))
    return;

  ev_at (w) += mn_now;//首先设置监视器的at成员,表明在at时间点,超时事件会触发,注意at是根据mn_now设置的,也就是相对于系统启动时间而言的(或者是日历时间)

  assert (("libev: ev_timer_start called with negative timer repeat value", w->repeat >= 0.));

  EV_FREQUENT_CHECK;

  //定时器数量 有多少个定时器
  ++timercnt;
  //事件激活相关 其中active即为timercnt(timers下标)(这一块还要仔细看一下)
  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));

  EV_FREQUENT_CHECK;

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

当对某一节点执行 upheap() 时,就是与其父节点进行比较,如果其值比父节点小,则交换,然后在对这个父节点重复 upheap() ,直到顶层。

inline_speed void
ev_start (EV_P_ W w, int active)
{
  pri_adjust (EV_A_ w);
  w->active = active;//监视器的active成员,表明该监视器在堆数组中的下标
  ev_ref (EV_A);
}

6 timers_reify

每次调用backend_poll之前,都会根据ANHE_at (timers [HEAP0]) – mn_now的值,校准backend_poll的阻塞时间waittime,这样就能尽可能的保证定时器能够按时触发。

调用backend_poll之后,就会调用timers_reify查看timers中哪些定时器触发了,代码如下:

/* make timers pending 挂起状态*/
inline_size void
timers_reify (EV_P)
{
  EV_FREQUENT_CHECK;

  if (timercnt && ANHE_at (timers [HEAP0]) < mn_now)//定时器值不为0,且堆顶值小于,mn_now相对于系统启动时间
    {
      //timercnt 为定时器的值,在timer_start中累加
      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)//若为重复定时器,一次性定时器w->repeat=0
            {
              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]);//  #define ANHE_at_cache(he) (he).at = (he).w->at /* update at from watcher */
             //向下调整堆,定时器仍然存在该数组中
              downheap (timers, timercnt, HEAP0);
            }
          else
            ev_timer_stop (EV_A_ w); /* nonrepeating: stop timer 一次性定时器 停止 */

          EV_FREQUENT_CHECK;
          //将超时的事件加入rfeeds[]结构中
          feed_reverse (EV_A_ (W)w);
        }
      while (timercnt && ANHE_at (timers [HEAP0]) < mn_now);//定时器不为0且堆顶的值一直小于当前时间就重复上述操作

      feed_reverse_done (EV_A_ EV_TIMER);
    }
}

定时器就是在backend_poll之前通过定时器堆顶的超时时间,保证blocking的时间不超过最近的定时器时间,在backend_poll返回后,从定时器堆中取得超时的watcher放入到pendings二维数组中,从而在后续处理中可以执行其上注册的触发动作。然后从定时器管理堆上删除该定时器。最后调用和ev_start呼应的ev_stop修改驱动器loop的状态,即loop->activecnt减少一。并将该watcher的active置零。

6 停止定时器ev_stop_timer

void noinline
ev_timer_stop (EV_P_ ev_timer *w) EV_THROW
{

    //清除pendings[]激活事件队列中,关于w的事件
  clear_pending (EV_A_ (W)w);
  if (expect_false (!ev_is_active (w)))
    return;

  EV_FREQUENT_CHECK;

  {
    int active = ev_active (w);//这个地方还不是很理解

    assert (("libev: internal timer heap corruption", ANHE_w (timers [active]) == (WT)w));
    //定时器数量减1
    --timercnt;

    if (expect_true (active < timercnt + HEAP0))
      {

        //timers [active]中的元素用数组最后一个元素替换,最后一个元素正常情况下为最大值
        timers [active] = timers [timercnt + HEAP0];

        //比较下标为active处的元素与其父节点的元素,来决定采用向上、向下调整
        adjustheap (timers, timercnt, active);
      }
  }

  ev_at (w) -= mn_now;

  ev_stop (EV_A_ (W)w);

  EV_FREQUENT_CHECK;
}

  首先调用clear_pending,如果该监视器已经处于pending状态,将其从pendings中删除。然后根据监视器中的active成员,得到其在timers堆上的索引,将该监视器从堆timers上删除,重新调整堆结构。然后调用ev_stop停止该监视器。

用图来示意一下