Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libuv源码粗读(3):uv_timer_t定时器句柄结构体介绍 #31

Open
xtx1130 opened this issue Sep 18, 2018 · 0 comments
Open

Comments

@xtx1130
Copy link
Owner

xtx1130 commented Sep 18, 2018

本篇文章主要对uv_timer_t结构体进行展开介绍

uv_timer_t声明

直接从uv.h切入,很容易就能找到uv_timer_s结构体声明:

struct uv_timer_s {
  UV_HANDLE_FIELDS
  UV_TIMER_PRIVATE_FIELDS
};

其中UV_HANDLE_FIELDS为所有句柄的抽象基础宏,在上一篇文章介绍过,在这里不做过多介绍。我们主要看一下timer的私有宏UV_TIMER_PRIVATE_FIELDS,由于平台的差异,我们在这里只对unix.h中的timer私有宏进行介绍:

#define UV_TIMER_PRIVATE_FIELDS                                               \
  uv_timer_cb timer_cb;                                                       \
  void* heap_node[3];                                                         \
  uint64_t timeout;                                                           \
  uint64_t repeat;                                                            \
  uint64_t start_id;

下面是结构体内部数据结构的介绍:

  • timer_cb 定时器的回调函数
  • heap_node 二叉堆的指针,分别指向left、right和parent
  • timeout 过期时间,在timeout毫秒之后,指定的timer_cb会被调用
  • repeat 间隔时间,回调函数在第一次timeout毫秒调用之后会间隔repeat毫秒进行调用
  • start_id 定时器启动时候注册的id,如果定时器的触发时间是一致的,会对比start_id来判别先后顺序

timer相关API

uv_timer_init

视线挪到timer.c中:

int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) {
  uv__handle_init(loop, (uv_handle_t*)handle, UV_TIMER);
  handle->timer_cb = NULL;
  handle->repeat = 0;
  return 0;
}

对基础句柄进行初始化,并设定句柄类型(h-> type)为UV_TIMER从而实现对定时器句柄的初始化。

uv_timer_start

int uv_timer_start(uv_timer_t* handle,
                   uv_timer_cb cb,
                   uint64_t timeout,
                   uint64_t repeat) {
  uint64_t clamped_timeout;

  if (cb == NULL)
    return UV_EINVAL;

  if (uv__is_active(handle))
    uv_timer_stop(handle);

  clamped_timeout = handle->loop->time + timeout; //用loop的当前时间加上过期时间为真正的调用时间
  if (clamped_timeout < timeout) 
    clamped_timeout = (uint64_t) -1; // 如果真正的调用时间要小于过期时间则设置调用时间为最大数值

  handle->timer_cb = cb;
  handle->timeout = clamped_timeout;
  handle->repeat = repeat;
  /* start_id is the second index to be compared in uv__timer_cmp() */
  handle->start_id = handle->loop->timer_counter++; //设定此timer的唯一id

  heap_insert(timer_heap(handle->loop),
              (struct heap_node*) &handle->heap_node,
              timer_less_than); //二叉堆操作(最小堆,可以快速取出过期时间最短的timer句柄)
  uv__handle_start(handle); // 向loop中注册活动句柄

  return 0;
}

uv_timer_start中,分别对timer的回调函数、过期时间、间隔时间进行了设定。在这里简单介绍一下为什么会有如下这个判断:

 if (clamped_timeout < timeout) 
    clamped_timeout = (uint64_t) -1; 

libuv会每次事件循环的伊始通过uv_update_time()来更新handle->loop->time
但是如果调用方在独立使用uv_timer_start的时候,忘记先调用uv_update_time()则会造成如上这种判断的情况。而libuv开发者没有把uv_update_time()写入到uv_timer_start中则是为了保证libuv时间循环的性能。

uv__run_timers

此函数供event-loop调用,其中有一点需要注意的地方:

void uv__run_timers(uv_loop_t* loop) {
  struct heap_node* heap_node;
  uv_timer_t* handle;

  for (;;) {
    // ...
    uv_timer_stop(handle);
    uv_timer_again(handle);
    handle->timer_cb(handle);
  }
}

uv_timer_stopuv_timer_again在这里不做过多介绍,通过函数名就能大概明白具体是做什么的。而为何要在timer回调运行之前对timer进行停止和下一次调用的注册,则是为了防止handle->timer_cb回调阻塞时间过长,导致每次uv_timer_again会逐渐增大repeat timers回调触发的时间。

node中对uv_timer_t的应用

由于之前在《node源码粗读》系列对Timers API进行过详尽的分析,所以在这里主要是借对uv_timer_t的分析串一下,在node中的调用函数如下:

int err = uv_timer_start(&wrap->handle_, OnTimeout, timeout, 0);
// timer_cb回调函数OnTimeout如下
static void OnTimeout(uv_timer_t* handle) {
    TimerWrap* wrap = static_cast<TimerWrap*>(handle->data);
    Environment* env = wrap->env();
    HandleScope handle_scope(env->isolate());
    Context::Scope context_scope(env->context());
    wrap->MakeCallback(kOnTimeout, 0, nullptr);
  }

通过如上代码可以得知,node的上下文环境,全部是通过uv_handle_t中的handle->data传递到timer_cb中的,回调最终在刚才所讲到的uv__run_timers中的handle->timer_cb(handle);触发运行。

by 小菜

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant