// 事件的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. (转)UML类图与类的关系详解

    转载自:http://www.uml.org.cn/oobject/201104212.asp 在画类图的时候,理清类和类之间的关系是重点.类的关系有泛化(Generalization).实现(Rea ...

  2. centos7 防火墙 配置

    1.查看Firewall 服务状态 systemctl status firewalld 2.查看Firewall 的状态 firewall-cmd --state 注意: firewalld默认配置 ...

  3. 三、第一个IDEA创建的MAVEN工程——JavaWeb点滴

    一.Maven是什么? Maven是一个项目管理工具,它包含了一个项目对象模型 (Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个 ...

  4. Go Global 之怎样在全球Azure上使用Azure Free Account

    随着中国用户出海的越来越多,同学们自学Azure Global 功能的积极性也越来越高.怎样开启Azure Global 账号,有哪些Global Azure的功能可以免费使用,能不能用国内的信用卡和 ...

  5. code force 403C.C. Andryusha and Colored Balloons

    C. Andryusha and Colored Balloons time limit per test 2 seconds memory limit per test 256 megabytes ...

  6. A Simple Math Problem(矩阵快速幂)(寒假闭关第一题,有点曲折啊)

    A Simple Math Problem Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Other ...

  7. ajax跨域之---服务器端代理实现

    介绍一种不是通过js实现跨域的方式: 通过服务器端代理实现. 具体的思路:由于浏览器有同源策略限制,(同源策略即:https://developer.mozilla.org/zh-CN/docs/We ...

  8. 原生addClass 方法 添加类函数

    function addClass(id,new_class){       var i,n=0;       new_class=new_class.split(",");   ...

  9. HTML学习笔记 CSS文本及字体及连接及列表(a标签使用及缩进) 案例 第七节 (原创)参考使用表

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. Struts2开发文档

    struts2配置 struts2核心配置文件约束 <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts ...