// 事件的dispatch 
int
event_base_loop(struct event_base *base, int flags)
{
    //得到采用的事件模型 epoll/epoll/select
const struct eventop *evsel = base->evsel;
struct timeval tv;
struct timeval *tv_p;
int res, done, retval = ; /* Grab the lock. We will release it inside evsel.dispatch, and again
* as we invoke user callbacks. */
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
    //判断是否loop正在running 如果由则退出
if (base->running_loop) {
event_warnx("%s: reentrant invocation. Only one event_base_loop"
" can run on each event_base at once.", __func__);
EVBASE_RELEASE_LOCK(base, th_base_lock);
return -;
} base->running_loop = ;
    //清理时间缓存
clear_time_cache(base); if (base->sig.ev_signal_added && base->sig.ev_n_signals_added)
evsig_set_base_(base); done = ; #ifndef EVENT__DISABLE_THREAD_SUPPORT
base->th_owner_id = EVTHREAD_GET_ID();
#endif
    //终止和中断标志至0
base->event_gotterm = base->event_break = ;
    //事件循环 与 平时我们自己写的epoll_wait select 等待事件一样 在一个死循环中
while (!done) {
base->event_continue = ;
base->n_deferreds_queued = ; /* Terminate the loop if we have been asked to */
        //被其他线程中断
if (base->event_gotterm) {
break;
} if (base->event_break) {
break;
} tv_p = &tv;
        //当前没有激活的事件
if (!N_ACTIVE_CALLBACKS(base) && !(flags & EVLOOP_NONBLOCK)) {
timeout_next(base, &tv_p);//判断小根堆中的root是否已经超时, 如果超时 就将tv清0.  如果没有的话就将root的时间减去现在时间的结果赋值给tv(定时器触发的剩余
的时间长度)
} else {
/*
* if we have active events, we just poll new events
* without waiting.
*/
            //如果有激活事件 就将tv清空
evutil_timerclear(&tv);
} /* If we have no events, we just exit */
        //未注册事件就退出循环
if (==(flags&EVLOOP_NO_EXIT_ON_EMPTY) &&
!event_haveevents(base) && !N_ACTIVE_CALLBACKS(base)) {
event_debug(("%s: no events registered.", __func__));
retval = ;
goto done;
}
        
event_queue_make_later_events_active(base);
        //清理时间缓存
clear_time_cache(base);
        // 调用模型的epoll_wait/select/poll 等   tv_p 是刚才计算的最小时间间隔
res = evsel->dispatch(base, tv_p); //以epoll 为例详细说明 if (res == -) {
event_debug(("%s: dispatch returned unsuccessfully.",
__func__));
retval = -;
goto done;
}
        //更新base中的时间, 下面就是调用定时事件和IO事件。
update_time_cache(base);
        //判断定时器事件是否发生了,若发生就将事件加入激活队列
timeout_process(base); //见下文 if (N_ACTIVE_CALLBACKS(base)) {
            //执行激活队列中的事件
int n = event_process_active(base);   //见下文
if ((flags & EVLOOP_ONCE)
&& N_ACTIVE_CALLBACKS(base) ==
&& n != )
done = ;
} else if (flags & EVLOOP_NONBLOCK)
done = ;
}
event_debug(("%s: asked to terminate loop.", __func__)); done:
clear_time_cache(base);
base->running_loop = ; EVBASE_RELEASE_LOCK(base, th_base_lock); return (retval);
}

以 epoll 模型的dispatch 看一下evsel->dispatch(base, tv_p);

static int
epoll_dispatch(struct event_base *base, struct timeval *tv)
{
struct epollop *epollop = base->evbase;
struct epoll_event *events = epollop->events;
int i, res;
long timeout = -;
if (tv != NULL) {
timeout = evutil_tv_to_msec_(tv); //将tv 话为 msec传递给epoll_wait
if (timeout < 0 || timeout > MAX_EPOLL_TIMEOUT_MSEC) {
/* Linux kernels can wait forever if the timeout is
* too big; see comment on MAX_EPOLL_TIMEOUT_MSEC. */
timeout = MAX_EPOLL_TIMEOUT_MSEC;
}
} epoll_apply_changes(base);
event_changelist_remove_all_(&base->changelist, base); EVBASE_RELEASE_LOCK(base, th_base_lock);//多线程下防止惊群而加锁 res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);//等待事件 EVBASE_ACQUIRE_LOCK(base, th_base_lock); if (res == -1) {
if (errno != EINTR) {
event_warn("epoll_wait");
return (-1);
}
return (0);
} event_debug(("%s: epoll_wait reports %d", __func__, res));
EVUTIL_ASSERT(res <= epollop->nevents);
    //依次处理事件
for (i = 0; i < res; i++) {
int what = events[i].events;
short ev = 0; if (what & (EPOLLHUP|EPOLLERR)) {
ev = EV_READ | EV_WRITE;
} else {
if (what & EPOLLIN)
ev |= EV_READ;
if (what & EPOLLOUT)
ev |= EV_WRITE;
if (what & EPOLLRDHUP)
ev |= EV_CLOSED;
} if (!ev)
continue;
        //根据fd找到相应的位置  将event_callback加入到激活queue等待被调用
evmap_io_active_(base, events[i].data.fd, ev | EV_ET);
}
    // 拓展事件容量
if (res == epollop->nevents && epollop->nevents < MAX_NEVENT) {
/* We used all of the event space this time. We should
be ready for more events next time. */
int new_nevents = epollop->nevents * 2;
struct epoll_event *new_events; new_events = mm_realloc(epollop->events,
new_nevents * sizeof(struct epoll_event));
if (new_events) {
epollop->events = new_events;
epollop->nevents = new_nevents;
}
} return (0);
}

再看一下timeout_process.

static void
timeout_process(struct event_base *base)
{
/* Caller must hold lock. */
struct timeval now;
struct event *ev;
    //没有定时事件直接退出
if (min_heap_empty_(&base->timeheap)) {
return;
} gettime(base, &now);
    //取堆顶最小
while ((ev = min_heap_top_(&base->timeheap))) {
         //没有超时就直接退出
if (evutil_timercmp(&ev->ev_timeout, &now, >))
break; /* delete this event from the I/O queues */    
      //有超时事件发生就将事件从io中删除
event_del_nolock_(ev, EVENT_DEL_NOBLOCK); event_debug(("timeout_process: event: %p, call %p",
ev, ev->ev_callback));
        //加入激活队列中   最终会调用  event_queue_insert_timeout,被激活的计时和IO都放在同一个queue中
event_active_nolock_(ev, EV_TIMEOUT, );
}
}

Libevent 事件循环(1)的更多相关文章

  1. Libevent 事件循环(2)---事件被加入激活队列

    由Libevent 事件循环(1) 在上文中我们提到了libevent 事件循环event_dispatch 的大致过程,以epoll为例,我们看一下事件被如何加入激活队列. //在epoll_dis ...

  2. 【传智播客】Libevent学习笔记(三):事件循环

    目录 00. 目录 01. event_base_loop函数 02. event_base_dispatch函数 03. event_base_loopexit函数 04. event_base_l ...

  3. JavaScript单线程和浏览器事件循环简述

    JavaScript单线程 在上篇博客<Promise的前世今生和妙用技巧>的开篇中,我们曾简述了JavaScript的单线程机制和浏览器的事件模型.应很多网友的回复,在这篇文章中将继续展 ...

  4. js: 从setTimeout说事件循环模型

    一.从setTimeout说起 setTimeout()方法不是ecmascript规范定义的内容,而是属于BOM提供的功能.查看w3school对setTimeout()方法的定义,setTimeo ...

  5. Javascript并发模型和事件循环

    Javascript并发模型和事件循环 JavaScript的"并发模型"是基于事件循环的,这个并发模型有别于Java的多线程, javascript的并发是单线程的. Javas ...

  6. Node.js 事件循环

    Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高. Node.js 的每一个 API 都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发. Node.j ...

  7. JavaScript:彻底理解同步、异步和事件循环(Event Loop) (转)

    原文出处:https://segmentfault.com/a/1190000004322358 一. 单线程 我们常说"JavaScript是单线程的". 所谓单线程,是指在JS ...

  8. nodejs系列(二)REPL交互解释 事件循环

    一.REPL交互解释 命令行中输入node启动REPL: > var x =2;undefined> do{x++;... console.log("x:="+x);. ...

  9. 初学Node(四)事件循环

    Node中的事件循环 事件循环是Node的核心,正是因为有了事件循环JS才能够在服务端占有一席之地.JS是一种单线程语言,但是它的执行环境是多线程的在加上JS的事件驱动这一特点,使使JS在执行的过程中 ...

随机推荐

  1. Javascript中NaN、null和undefinded的区别

    var a1; var a2 = true; var a3 = 1; var a4 = "Hello"; var a5 = new Object(); var a6 = null; ...

  2. Java基础-数组(06)

    数组是存储多个变量(元素)的东西(容器),这多个变量的数据类型要一致 数组概念 数组是存储同一种数据类型多个元素的容器.数组既可以存储基本数据类型,也可以存储引用数据类型. 数组的定义格式 格式1:数 ...

  3. 教你用SVG画出一条龙

    先看demo,九十七度 其实使用svg画出这条龙很简单,关键不在于怎么使用svg,而在于你的美术功底,哈哈. 好吧,当然基础是不能忽略的,先看下这条龙的代码: <svg id="lon ...

  4. C#设计模式之十一外观模式(Facade)【结构型】

    一.引言 快12点半了,要开始今天的写作了.很快,转眼设计模式已经写了十个了,今天我们要讲[结构型]设计模式的第五个模式,该模式是[外观模式],英文名称是:Facade Pattern.我们先从名字上 ...

  5. Android_简易的短信发送器

    这个随笔将介绍如何完成一个简单的第三方的短信发送器(不打开短信界面,调用android的api完成功能) 1.首先,我们来做布局 由于我这里写的是一个简易的,,短信发送,所以只是一个LinearLay ...

  6. CLR Via C#: 类型基础

    所有类型都从System.Object派生 一下两个类型定义是完全一致的 class Employee { } class Employee : System.Object { } 由于所有类型最终都 ...

  7. DOM 遍历-同胞

    在 DOM 树中水平遍历 有许多有用的方法让我们在 DOM 树进行水平遍历: siblings() next() nextAll() nextUntil() prev() prevAll() prev ...

  8. css元素选择器 first-child nth-child

    E:first-child   只要E元素是它的父级的第一个子元素,就选中.它需要同时满足两个条件,    (1)是"第一个子元素",    (2)是"这个子元素刚好是E ...

  9. visual filters 滤镜 ie

    滤镜ie9开始就废弃了,用来对ie4-8实现一些多媒体动画之类的效果.可以添加到标准的HTML控件上,例如text,图片   包含:1.界面滤镜  (Procedural Surfaces)2.静态滤 ...

  10. 一些神奇的JS功效

    1: 沉睡排序 var numbers=[1,2,3,4,5,5,99,4,20,11,200]; numbers.forEach((num)=>{ setTimeout(()=>{ co ...