上一小节我们介绍了事件是如何注册/注销的,在本小节中,我们将进一步探讨事件从未注册到处理的整个过程,即事件的主循环。
事件的主循环主要是通过event_base_loop
完成的。我们下面先来看看这个函数,再进行总结。
int
event_base_loop(struct event_base *base, int flags)
{
const struct eventop *evsel = base->evsel;
void *evbase = base->evbase;
struct timeval tv;
struct timeval *tv_p;
int res, done;
/* clear time cache */
base->tv_cache.tv_sec = 0;
//如果有信号事件,则将base赋给专门处理signal的evsignal_base(global var)
if (base->sig.ev_signal_added)
evsignal_base = base;
done = 0; //标识位
while (!done) {
/* Terminate the loop if we have been asked to */
//如果设置了退出标识位,则退出循环
if (base->event_gotterm) {
base->event_gotterm = 0;
break;
}
if (base->event_break) {
base->event_break = 0;
break;
}
/* You cannot use this interface for multi-threaded apps */
while (event_gotsig) {
event_gotsig = 0;
if (event_sigcb) {
res = (*event_sigcb)();
if (res == -1) {
errno = EINTR;
return (-1);
}
}
}
//校正系统时间
timeout_correct(base, &tv);
tv_p = &tv;
if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) {
/* 没有激活的事件并且是非阻塞时,根据小根堆的最小超时时间(即堆顶),计算I/O多路复用最近的等待时间 */
timeout_next(base, &tv_p);
} else {
/*
* if we have active events, we just poll new events
* without waiting.
*/
//有激活事件,则将tv赋为0(即立即返回,不必等待)
evutil_timerclear(&tv);
}
/* If we have no events, we just exit */
//没有注册的事件,直接退出了。该函数原型如下,很简单
/*
int event_haveevents(struct event_base *base)
{
return (base->event_count > 0)
}
*/
if (!event_haveevents(base)) {
event_debug(("%s: no events registered.", __func__));
return (1);
}
/* update last old time */
gettime(base, &base->event_tv);
/* clear time cache */
base->tv_cache.tv_sec = 0;
//核心语句,调用的是某种具体的多路I/O机制的等待函数,如epoll中的epoll_wait。其中还会调用`event_active`等函数将事件插入到链表中。
res = evsel->dispatch(base, evbase, tv_p);
if (res == -1)
return (-1);
//将当前的时间缓存设为当前时间
gettime(base, &base->tv_cache);
//将激活的定时事件从小根堆上移出,插入到激活链表中
timeout_process(base);
//如果有激活事件,则调用`event_process_active`去处理。处理之后,如果已经没有激活事件了并且设置了只执行一次的标识,就将done置1,或者设置为非阻塞,也将done置1。否则会一直循环
if (base->event_count_active) {
event_process_active(base);
if (!base->event_count_active && (flags & EVLOOP_ONCE))
done = 1;
} else if (flags & EVLOOP_NONBLOCK)
done = 1;
}
/* clear time cache */
base->tv_cache.tv_sec = 0;
event_debug(("%s: asked to terminate loop.", __func__));
return (0);
}
我们简单的看了一下event_base_loop
函数。默认情况下,它会一直运行到没有注册的事件为止,并且重复检查是否有激活事件可处理。
接下来梳理一下它的过程。
可以看到,要想退出主循环,除了done设置为1之外,还可以通过设置event_gotterm
和event_break
标识位来实现,之前在第3小节的时候没讲,接下来我们就来看看设置这两个标识位的函数,明白这两个标识位的区别。
int
event_base_loopbreak(struct event_base *event_base)
{
if (event_base == NULL)
return (-1);
event_base->event_break = 1;
return (0);
}
很简单的一个设置标识位的函数,就置1这么简单。
和它相联系的还有一个函数。
int
event_loopbreak(void)
{
return (event_base_loopbreak(current_base));
}
可以看到只是做了一层简单的封装。
int
event_base_loopexit(struct event_base *event_base, const struct timeval *tv)
{
return (event_base_once(event_base, -1, EV_TIMEOUT, event_loopexit_cb,
event_base, tv));
}
这里牵扯到了另一个函数event_base_once
,既然如此,那我们就先看看event_base_once
,由于它涉及到了一个新的结构体event_once
,我们先来了解一下这个结构体。
strut event_once {
struct event ev;
void (*cb)(int, short, void *);
void *arg;
};
可以看到里面除了有一个event之外,还涉及到回调函数指针及它的参数。我们接着往下看。
/* Schedules an event once */
int
event_base_once(struct event_base *base, int fd, short events,
void (*callback)(int, short, void *), void *arg, const struct timeval *tv)
{
struct event_once *eonce;
struct timeval etv;
int res;
/* We cannot support signals that just fire once */
if (events & EV_SIGNAL)
return (-1);
//给event_once申请内存
if ((eonce = calloc(1, sizeof(struct event_once))) == NULL)
return (-1);
//将函数指针及参数根据传入的参数赋值
eonce->cb = callback;
eonce->arg = arg;
//初始化event,分成定时事件或I/O事件
if (events == EV_TIMEOUT) {
if (tv == NULL) {
evutil_timerclear(&etv);
tv = &etv;
}
evtimer_set(&eonce->ev, event_once_cb, eonce);
} else if (events & (EV_READ|EV_WRITE)) {
events &= EV_READ|EV_WRITE;
//我们之前在第6小节讲过event_set,其实就是给event设置一些初始值。如果忘记了可以回看一下。
event_set(&eonce->ev, fd, events, event_once_cb, eonce);
} else {
/* Bad event combination */
free(eonce);
return (-1);
}
//指定该事件注册到的event_base以及修改对应的优先级,这个也在第6小节讲过
res = event_base_set(base, &eonce->ev);
//如果成功了,则将其注册;失败了则释放资源
if (res == 0)
res = event_add(&eonce->ev, tv);
if (res != 0) {
free(eonce);
return (res);
}
return (0);
}
不知道有一点你注意到没有,那就是调用evtimer_set
以及event_set
函数时,倒数第二个参数传入的函数指针是event_once_cb
,对应的参数传入的是event_once
结构体。
event_once_cb
其实是早就准备好的一个函数,代码如下:
static void
event_once_cb(int fd, short events, void *arg)
{
struct event_once *eonce = arg;
(*eonce->cb)(fd, events, eonce->arg);
free(eonce);
}
它内部调用了struct event_once
结构体内部的函数指针指向的函数,并且将该结构体释放了。
再次回到开始的函数event_base_loopexit
int
event_base_loopexit(struct event_base *event_base, const struct timeval *tv)
{
return (event_base_once(event_base, -1, EV_TIMEOUT, event_loopexit_cb,
event_base, tv));
}
struct event_once
结构体中函数指针指向的就是event_loopexit_cb
。可以看出event_base_once
函数的作用就是事件被激活调度一次后就删除。
而event_base_loopexit
达到的效果就是,该事件被激活后,先回调event_once_cb
函数,event_once_cb
函数里面再回调eonce
中函数指针指向的函数(event_loopexit_cd),将event_base_loopexit
设置为1。
在本节中,我们先了解了事件主循环event_base_loop
的整个过程。再通过event_base_loopexit
函数了解了event_base_once
的工作流程。接下来的一个小节,我们将把event.c
剩余的一些主要函数分析一下。