Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >libuv源码阅读(15)--ref-timer

libuv源码阅读(15)--ref-timer

原创
作者头像
wanyicheng
修改于 2021-03-14 09:03:17
修改于 2021-03-14 09:03:17
65800
代码可运行
举报
运行总次数:0
代码可运行
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

#include <uv.h>

uv_loop_t *loop;
uv_timer_t gc_req;
uv_timer_t fake_job_req;

void gc(uv_timer_t *handle) {
    fprintf(stderr, "Freeing unused objects\n");
}

void fake_job(uv_timer_t *handle) {
    fprintf(stdout, "Fake job done\n");
}

int main() {
    loop = uv_default_loop();

    uv_timer_init(loop, &gc_req);
    // 垃圾回收信号函数 持续执行 解除引用 loop的运行是由下面那个定时器驱动的 所以在它结束后loop也会结束
    uv_unref((uv_handle_t*) &gc_req);
    // 重复执行参数设置
    uv_timer_start(&gc_req, gc, 0, 2000);

    // could actually be a TCP download or something
    // 虚假的任务执行 9秒后结束 只执行一次 它结束后由于loop没有保持引用的激活的timer了 就停止了
    uv_timer_init(loop, &fake_job_req);
    uv_timer_start(&fake_job_req, fake_job, 9000, 0);
    return uv_run(loop, UV_RUN_DEFAULT);
}

先看下结构体:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct uv_timer_s {
  UV_HANDLE_FIELDS
  UV_TIMER_PRIVATE_FIELDS
};

#define UV_HANDLE_FIELDS                                                      \
  /* public */                                                                \
  void* data;                                                                 \
  /* read-only */                                                             \
  uv_loop_t* loop;                                                            \
  uv_handle_type type;                                                        \
  /* private */                                                               \
  uv_close_cb close_cb;                                                       \
  void* handle_queue[2];                                                      \
  union {                                                                     \
    int fd;                                                                   \
    void* reserved[4];                                                        \
  } u;                                                                        \
  UV_HANDLE_PRIVATE_FIELDS                                                    \
  
#define UV_HANDLE_PRIVATE_FIELDS                                              \
  uv_handle_t* next_closing;                                                  \
  unsigned int flags;                                                         \
  
#define UV_TIMER_PRIVATE_FIELDS                                               \
  uv_timer_cb timer_cb;                                                       \
  void* heap_node[3];                                                         \
  uint64_t timeout;                                                           \
  uint64_t repeat;                                                            \
  uint64_t start_id;

主要包含 timercb 和用于最小时间堆节点字段heap_node等;它由一个基础hanlder类型和自身独有的属性构成

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) {
  // 同用handler的初始化
  uv__handle_init(loop, (uv_handle_t*)handle, UV_TIMER);
  handle->timer_cb = NULL;
  handle->timeout = 0;
  handle->repeat = 0;
  return 0;
}


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

  if (uv__is_closing(handle) || cb == NULL)
    return UV_EINVAL;

  if (uv__is_active(handle))
    uv_timer_stop(handle);
  
  // 计算绝对时间超时值
  clamped_timeout = handle->loop->time + timeout;
  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 timer_less_than() */
  handle->start_id = handle->loop->timer_counter++;

  // 插入loop的最小时间堆中
  heap_insert(timer_heap(handle->loop),
              (struct heap_node*) &handle->heap_node,
              timer_less_than);
  // 激活handler
  uv__handle_start(handle);

  return 0;
}

int uv_timer_stop(uv_timer_t* handle) {
  if (!uv__is_active(handle))
    return 0;

  heap_remove(timer_heap(handle->loop),
              (struct heap_node*) &handle->heap_node,
              timer_less_than);
  uv__handle_stop(handle);

  return 0;
}

// 第一个定时器会重置执行 因为设置了repeat的重复时间
int uv_timer_again(uv_timer_t* handle) {
  if (handle->timer_cb == NULL)
    return UV_EINVAL;

  if (handle->repeat) {
    uv_timer_stop(handle);
    uv_timer_start(handle, handle->timer_cb, handle->repeat, handle->repeat);
  }

  return 0;
}

//  一次性定时器执行完回调后就会清除
void uv__run_timers(uv_loop_t* loop) {
  struct heap_node* heap_node;
  uv_timer_t* handle;

  for (;;) {
    heap_node = heap_min(timer_heap(loop));
    if (heap_node == NULL)
      break;

    handle = container_of(heap_node, uv_timer_t, heap_node);
    if (handle->timeout > loop->time)
      break;

    uv_timer_stop(handle);
    uv_timer_again(handle);
    handle->timer_cb(handle);
  }
}

总结:定时器就是指定时间:一次性或者重复执行的任务,但是由于loop自身循环过程中会有监听io事件等一系列逻辑,还要跑其他类型的任务如idle check等,所以这个定时时间并不完全精准。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
libuv的定时器原理源码解析
执行定时器的时候首先会先移除该定时器,然后如果设置了repeat的话,再次加入到最小堆里,最后执行超时回调。这里有个需要注意的是设置了repeat的定时器,意思是timeout时间后触发第一次超时,后面每隔repeat的时间,触发一次超时。
theanarkh
2019/03/15
1.5K0
libuv的定时器原理源码解析
你了解红黑树么?告诉你一个不一样的红黑树,说点有意思的吧!
关于红黑树的介绍网上非常多,红黑树的应用也非常广泛。问一下度娘,她会告诉你各种各样的实现方法,C和C++版本都有,linux内核使用的版本也有。代码都大同小异,就是插入或删除时如何修正,如何搞平衡。很多文章图文并茂、写实而生动,当你在脑海里试图左旋一把,右旋一把搞平衡时,基本也到了精神崩溃的边缘。
冬夜先生
2021/09/05
4410
nodejs事件循环阶段之定时器
上一篇分析了prepare阶段,check和idle阶段是一样的,所以就不分析了。今天分析定时器阶段。nodejs中setTimeout和setInterval就是使用libuv的定时器阶段实现的。libuv中,定时器是以最小堆实现的。即最快过期的节点是根节点。我看看定时器的数据结构。
theanarkh
2020/03/12
1.3K0
libuv之async.c源码解析
libuv的async.c实现了线程和主线程的通信。在uv_loop_init函数中对async进行初始化。
theanarkh
2019/03/15
1.2K0
libuv之async.c源码解析
理解libuv的基本原理
libuv的实现是一个很经典生产者-消费者模型。libuv在整个生命周期中,每一次循环都执行每个阶段(phase)维护的任务队列。逐个执行节点里的回调,在回调中,不断生产新的任务,从而不断驱动libuv。今天我们分析一下libuv的整体架构,从而学会如何使用libuv。我们从libuv的一个小例子开始。
theanarkh
2020/05/11
1.9K0
libuv源码阅读(18)--progress
总结:用户自己初始化的async handler 也可以被插入到异步handler队列中,当管道[0]可读的时候,代表某个异步handler可以处理了,这时候遍历队列,处理pengding状态的handler。
wanyicheng
2021/03/13
5750
nodejs源码解析之事件循环
nodejs的的事件循环由libuv的uv_run函数实现。在该函数中执行while循环,然后处理各种阶段(phase)的事件回调。事件循环的处理相当于一个消费者,消费由各业务代码生产的任务。下面看一下代码。
theanarkh
2020/01/15
8450
nodejs源码解析之事件循环
libuv之文件监听的实现
nodejs的文件监听原理就是由libuv实现的。文件监听的原理是,第一次先执行stat函数获取文件基本信息,然后在stat的回调函数里设置定时器,定时器超时后会执行stat,然后获取stat信息,再次执行stat回调函数重新设置定时器,如此反复,如果stat不一样就执行用户的回调。
theanarkh
2020/02/25
1.1K0
libuv之idle、check、prepare阶段
idle、check、prepare是libuv事件循环中的三个阶段,这三个阶段主要是从各自的队列里拿出任务执行,有各自对应的数据结构。nodejs的setImmediate会使用这些阶段。
theanarkh
2020/02/25
1.2K0
libuv源码阅读(7)--idle-basic
1. init初始化一个 idle handler, start目标handler ,同时绑定执行函数 wait_for_a_while;
wanyicheng
2021/03/12
4070
2020-5-27-Nodejs源码阅读——事件循环
Once the poll queue is empty the event loop will check for timers whose time thresholds have been reached. If one or more timers are ready, the event loop will wrap back to the timers phase to execute those timers’ callbacks.
黄腾霄
2020/06/10
1K0
libuv源码学习笔记:tcp-echo-server
事件循环是 libuv 功能的核心部分,它负责对 I/O 进行轮询,并基于不同的事件源执行它们的回调函数。libuv 的设计目标之一是为了让异步 I/O 操作变得简单易用,同时保持高性能。
晨星成焰
2024/10/20
3710
libuv源码学习笔记:tcp-echo-server
libuv线程池和主线程通信原理
代码很简单,就是设置一下async_io_watcher的fd和回调,在epoll_wait返回的时候用到。再看uv__io_start。
theanarkh
2020/01/15
1.5K0
「Nodejs进阶」一文吃透异步I/O和事件循环
本文讲详细讲解 nodejs 中两个比较难以理解的部分异步I/O和事件循环,对 nodejs 核心知识点,做梳理和补充。
用户6835371
2021/09/06
2.4K0
libuv源码阅读(21)--uvtee
uv_write_s 类型是由普通ref以及cb和一些写操作有关的信息组成,然后它需要一个 uv_stream_t* handle 来配合使用。
wanyicheng
2021/03/13
1.2K0
彻底搞懂nodejs事件循环
nodejs是单线程执行的,同时它又是基于事件驱动的非阻塞IO编程模型。这就使得我们不用等待异步操作结果返回,就可以继续往下执行代码。当异步事件触发之后,就会通知主线程,主线程执行相应事件的回调。
coder2028
2022/09/26
1.2K0
原 结合源码分析 setTimeout /
环境准备 工具: git/cmake/vscode(安装js和C++插件)/python vscode :https://code.visualstudio.com/Download 编译参考:https://github.com/nodejs/node/blob/master/BUILDING.md 需要注意的是为了调试方便,需要在make命令中开启debug,如果觉得编译过程慢也可以适当调大并发数。 make -d j4 命令行:在根目录输入: gdb node // 调试目标
魂祭心
2018/05/17
1.2K0
libuv源码阅读(22)--spawn
总结:父进程fork出子进程去执行指定的文件或者应用,双方根据参数设置的共享fd来通信,这个示例比较简单,父进程只需要等待结束捕获信号就可以了。
wanyicheng
2021/03/14
2.7K0
libuv源码阅读(6)--helloworld
每一种都是一种hanlder类型或者request类型,代表某种资源类型或者请求操作的包装结构体,里面的属性字段是为了支持它可以正常工作的而设置的:
wanyicheng
2021/03/12
8351
libuv之inotify源码分析
inotify是linux系统提供用于监听文件系统的机制。inotify机制的逻辑大致是 1 init_inotify创建一个inotify机制的实例,返回一个文件描述符。类似epoll。 2 inotify_add_watch往inotify实例注册一个需监听的文件(inotify_rm_watch是移除)。 3 read((inotify实例对应的文件描述符, &buf, sizeof(buf))),如果没有事件触发,则阻塞(除非设置了非阻塞)。否则返回待读取的数据长度。buf就是保存了触发事件的信息。 libuv在inotify机制的基础上做了一层封装。 今天分析一下libuv中的实现。我们从一个使用例子开始。
theanarkh
2020/05/08
1.2K0
libuv之inotify源码分析
相关推荐
libuv的定时器原理源码解析
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档