源码阅读 libevent - 优先级管理
event_base
允许用户对它里面的 event
设置优先级,这样可以使得有些更重要的 event
能够得到优先处理。
实现优先级
libevent
实现优先级功能的方法是:用一个激活队列数组来存放激活 event
。即数组的元素是一个激活队列,所以有多个激活队列。并且规定不同的队列有不同的优先级。
可以通过 event_base_priority_init
函数设置 event_base
的优先级个数,该函数实现如下:
int event_base_priority_init(struct event_base *base, int npriorities) {
int i, r = -1;
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
if (N_ACTIVE_CALLBACKS(base) || npriorities < 1 || npriorities >= EVENT_MAX_PRIORITIES) goto err;
if (npriorities == base->nactivequeues) goto ok;
if (base->nactivequeues) {
mm_free(base->activequeues);
base->nactivequeues = 0;
}
/* Allocate our priority queues */
base->activequeues = (struct evcallback_list *)mm_calloc(npriorities, sizeof(struct evcallback_list));
if (base->activequeues == NULL) {
event_warn("%s: calloc", __func__);
goto err;
}
base->nactivequeues = npriorities;
for (i = 0; i < base->nactivequeues; ++i) TAILQ_INIT(&base->activequeues[i]);
ok:
r = 0;
err:
EVBASE_RELEASE_LOCK(base, th_base_lock);
return (r);
}
从前面一个判断可知,因为 event_base_dispatch
函数会改动激活事件的个数,即会使得 N_ACTIVE_CALLBACKS(base)
为真。所以 event_base_priority_init
函数要在 event_base_dispatch
函数之前调用。此外要设置的优先级个数,要小于 EVENT_MAX_PRIORITIES
。这个宏是在 event.h
文件中定义,在 2.1.11
版本中,该宏被定义成 256
。在调用 event_base_new
得到的 event_base
只有一个优先级,也就是所有 event
都是同级的。
上面的代码调用 mm_alloc
分配了一个优先级数组。不同优先级的 event
会被放到数组的不同位置上 (下面可以看到这一点)。这样就可以区分不同 event
的优先级了。以后处理 event
时,就可以从高优先级到低优先级处理 event
。
设置优先级
上面是设置 event_base
的优先级个数。现在来看一下怎么设置 event
的优先级。可以通过 event_priority_set
函数设置,该函数如下:
/* Set's the priority of an event - if an event is already scheduled changing the priority is going to fail. */
int event_priority_set(struct event *ev, int pri) {
event_debug_assert_is_setup_(ev);
if (ev->ev_flags & EVLIST_ACTIVE) return (-1);
if (pri < 0 || pri>= ev->ev_base->nactivequeues) return (-1);
ev->ev_pri = pri;
return (0);
}
在上面代码的第一个判断中,可以知道当 event
的状态是 EVLIST_ACTIVE
时,就不能对这个 event
进行优先级设置了。因此,如果要对 event
进行优先级设置,那么得在调用 event_base_dispatch
函数之前。因为一旦调用了 event_base_dispatch
,那么 event
就随时可能变成 EVLIST_ACTIVE
状态。
按优先级激活队列
现在看一下一个 event
是怎么插入到 event_base
的优先级数组中。
static void event_queue_insert_active(struct event_base *base, struct event_callback *evcb)
{
... ...
/* #define ev_pri ev_evcallback.evcb_pri 事件优先级 */
EVUTIL_ASSERT(evcb->evcb_pri < base->nactivequeues);
/* 插入到对应优先级的激活队列尾部 */
TAILQ_INSERT_TAIL(&base->activequeues[evcb->evcb_pri], evcb, evcb_active_next);
}
event
插入到 event_base
的优先级数组中后,会被按照优先级顺序被调用:
static int event_process_active(struct event_base *base) {
... ...
/* 按优先级遍历激活队列中的事件 */
for (i = 0; i < base->nactivequeues; ++i) {
if (TAILQ_FIRST(&base->activequeues[i]) != NULL) { /* 同一个优先级下可以有多个事件 */
base->event_running_priority = i; /* 设置当前的优先级 */
activeq = &base->activequeues[i]; /* 获取优先级 i 下的所有 event 组成的链表 */
/* 遍历 activeq 链表,调用其中每个 event 的回调函数 */
if (i < limit_after_prio) c = event_process_active_single_queue(base, activeq, INT_MAX, NULL);
else c = event_process_active_single_queue(base, activeq, maxcb, endtime);
if (c < 0) goto done; /* c 是执行的非内部事件数目 */
else if (c> 0) /* Processed a real event; do not consider lower-priority events */
break; /* If we get here, all of the events we processed were internal. Continue. */
}
}
... ...
}
默认优先级
默认优先级是在新建 event
结构体时设置的。不错,看下面的 event_assign
函数。
int event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd,
short events, void (*callback)(evutil_socket_t, short, void *), void *arg) {
... ...
if (base != NULL) {
/* by default, we put new events into the middle priority */
ev->ev_pri = base->nactivequeues / 2;
}
... ...
}