struct event_config {
// 这个队列中存放的是config中需要避免的IO多路复用模型
TAILQ_HEAD(event_configq, event_config_entry) entries;
// CPU的个数,仅仅在win下的iocp设置了CPU个数后会智能的调整
// event_config_set_num_cpus_hint 可以设置
int n_cpus_hint;
// 检查两次新事件之间的最大时间间隔
struct timeval max_dispatch_interval;
// 两次检查新事件之间的执行回调函数个数的最大个数
int max_dispatch_callbacks;
// 如果任务的优先级小于limit_callbacks_after_prio。
// 那么上面的两项就不会执行
int limit_callbacks_after_prio;
// 指定了IO多路复用使用的模型应该具有的特征
// 通过event_config_require_features 设置
enum event_method_feature require_features;
enum event_base_config_flag flags;
};
// 创建一个 event_config, 调用event_config_new()
struct event_config *
event_config_new(void)
{
struct event_config *cfg = mm_calloc(1, sizeof(*cfg));
if (cfg == NULL)
return (NULL);
TAILQ_INIT(&cfg->entries);
cfg->max_dispatch_interval.tv_sec = -1;
cfg->max_dispatch_callbacks = INT_MAX;
cfg->limit_callbacks_after_prio = 1;
return (cfg);
}
struct event_base *
event_base_new(void)
{
struct event_base *base = NULL;
struct event_config *cfg = event_config_new();
// 使用默认的配置
if (cfg) {
base = event_base_new_with_config(cfg);
event_config_free(cfg);
}
return base;
}
// 使用config来生成base,但是如果无法满足config的要求那么返回NULL
// 所以需要对返回值进行判断
struct event_base *
event_base_new_with_config(const struct event_config *cfg)
{
int i;
struct event_base *base;
int should_check_environment;
#ifndef EVENT__DISABLE_DEBUG_MODE
event_debug_mode_too_late = 1;
#endif
// 为base申请空间
// mm_calloc 会把申请到的空间清零
// mm_malloc 不会清零
if ((base = mm_calloc(1, sizeof(struct event_base))) == NULL) {
event_warn("%s: calloc", __func__);
return NULL;
}
if (cfg)
base->flags = cfg->flags;
// 如果没有设置EVENT_BASE_FLAG_IGNORE_ENV, 忽略检查环境
should_check_environment =
!(cfg && (cfg->flags & EVENT_BASE_FLAG_IGNORE_ENV));
{
struct timeval tmp;
int precise_time =
cfg && (cfg->flags & EVENT_BASE_FLAG_PRECISE_TIMER);
int flags;
if (should_check_environment && !precise_time) {
precise_time = evutil_getenv_("EVENT_PRECISE_TIMER") != NULL;
if (precise_time) {
base->flags |= EVENT_BASE_FLAG_PRECISE_TIMER;
}
}
flags = precise_time ? EV_MONOT_PRECISE : 0;
evutil_configure_monotonic_time_(&base->monotonic_timer, flags);
gettime(base, &tmp);
}
min_heap_ctor_(&base->timeheap);
base->sig.ev_signal_pair[0] = -1;
base->sig.ev_signal_pair[1] = -1;
base->th_notify_fd[0] = -1;
base->th_notify_fd[1] = -1;
TAILQ_INIT(&base->active_later_queue);
evmap_io_initmap_(&base->io);
evmap_signal_initmap_(&base->sigmap);
event_changelist_init_(&base->changelist);
base->evbase = NULL;
if (cfg) {
memcpy(&base->max_dispatch_time,
&cfg->max_dispatch_interval, sizeof(struct timeval));
base->limit_callbacks_after_prio =
cfg->limit_callbacks_after_prio;
} else {
base->max_dispatch_time.tv_sec = -1;
base->limit_callbacks_after_prio = 1;
}
if (cfg && cfg->max_dispatch_callbacks >= 0) {
base->max_dispatch_callbacks = cfg->max_dispatch_callbacks;
} else {
base->max_dispatch_callbacks = INT_MAX;
}
if (base->max_dispatch_callbacks == INT_MAX &&
base->max_dispatch_time.tv_sec == -1)
base->limit_callbacks_after_prio = INT_MAX;
// 在这里确定base使用哪种IO复用模型
// eventops 中存储了所有的IO复用模型,遍历这些模型
for (i = 0; eventops[i] && !base->evbase; i++) {
if (cfg != NULL) {
/* determine if this backend should be avoided */
// 判断是否处于被avoid的队列中
if (event_config_is_avoided_method(cfg,
eventops[i]->name))
continue;
//判断当前模型具有的features和我们提供的features是否匹配
if ((eventops[i]->features & cfg->require_features)
!= cfg->require_features)
continue;
}
/* also obey the environment variables */
if (should_check_environment &&
event_is_method_disabled(eventops[i]->name))
continue;
// 如果满足了所有条件就可以确定当前的IO多路复用模型
base->evsel = eventops[i];
// 调用多路复用函数的init函数
base->evbase = base->evsel->init(base);
}
if (base->evbase == NULL) {
event_warnx("%s: no event mechanism available",
__func__);
base->evsel = NULL;
event_base_free(base);
return NULL;
}
if (evutil_getenv_("EVENT_SHOW_METHOD"))
event_msgx("libevent using: %s", base->evsel->name);
/* allocate a single active event queue */
// 默认状态下base的优先级数只有1级,在这个base下面创建的event的优先级是 1/2 = 0
if (event_base_priority_init(base, 1) < 0) {
event_base_free(base);
return NULL;
}
/* prepare for threading */
#if !defined(EVENT__DISABLE_THREAD_SUPPORT) && !defined(EVENT__DISABLE_DEBUG_MODE)
event_debug_created_threadable_ctx_ = 1;
#endif
#ifndef EVENT__DISABLE_THREAD_SUPPORT
if (EVTHREAD_LOCKING_ENABLED() &&
(!cfg || !(cfg->flags & EVENT_BASE_FLAG_NOLOCK))) {
int r;
EVTHREAD_ALLOC_LOCK(base->th_base_lock, 0);
EVTHREAD_ALLOC_COND(base->current_event_cond);
r = evthread_make_base_notifiable(base);
if (r<0) {
event_warnx("%s: Unable to make base notifiable.", __func__);
event_base_free(base);
return NULL;
}
}
#endif
#ifdef _WIN32
if (cfg && (cfg->flags & EVENT_BASE_FLAG_STARTUP_IOCP))
event_base_start_iocp_(base, cfg->n_cpus_hint);
#endif
return (base);
}
// 设置base的优先级有几级,范围在0~npriorities-1之间,数值越小优先级越高
int event_base_priority_init(struct event_base *base, int npriorities)
{
int i, r;
r = -1;
// 操作base的时候要先上锁
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
// N_ACTIVE_CALLBACKS 说明,这个函数要在event_base_dispatch之前调用
// 然后对npriorities的范围进行了限制,最大是EVENT_MAX_PRIORITIES - 1 = 255
if (N_ACTIVE_CALLBACKS(base) || npriorities < 1
|| npriorities >= EVENT_MAX_PRIORITIES)
goto err;
// 如果之前的优先级和现在是相等的就没必要设置了
if (npriorities == base->nactivequeues)
goto ok;
// 那么满足条件就进行优先级数的修改
if (base->nactivequeues) {
// 先释放之前的activequeues数组,每一个元素都是同等优先级的任务队列
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);
}
// 得到当前base的优先级数,也就是返回nactivequeues
int
event_base_get_npriorities(struct event_base *base)
{
int n;
if (base == NULL)
base = current_base;
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
n = base->nactivequeues;
EVBASE_RELEASE_LOCK(base, th_base_lock);
return (n);
}
// 设置config中的flag
int
event_config_set_flag(struct event_config *cfg, int flag)
{
if (!cfg)
return -1;
cfg->flags |= flag;
return 0;
}
// 可以设置的flag为:
// 生成的base具有的特征
enum event_base_config_flag {
//不要为base分配锁,设置这个选项可以为base节省一点加锁和解锁的时间
// 但是如果在多线程中访问的时候可能会出错
EVENT_BASE_FLAG_NOLOCK = 0x01,
// 选择IO多路复用函数的时候,不要去检测 EVENT_* 变量
EVENT_BASE_FLAG_IGNORE_ENV = 0x02,
// 仅仅适用于windows,设置了之后那么evconn_listener_new和bufferevent_socket_new函数的内部将使用IOCP
EVENT_BASE_FLAG_STARTUP_IOCP = 0x04,
// 没有cache时间, 不是在每次事件循环中都要检测当前时间
// 而是在每次回调后才检测
// 一般情况下如果cache中有时间就用cache中的
// 没有时间会使用系统调用获取时间
EVENT_BASE_FLAG_NO_CACHE_TIME = 0x08,
// 这个标志仅仅在epoll下有用
// 设置了这个标志后。epoll会使用更快的基于changelist 的多路IO复用函数
// changelist 可以在多次修改同一fd的情况下避免不必要的系统调用
EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10,
// 通常情况下我们使用的是更快的计数器,如果设置了这个标志的话我们
// 会使用更精确的时间
EVENT_BASE_FLAG_PRECISE_TIMER = 0x20
};
// 配置config具有的特征
int
event_config_require_features(struct event_config *cfg,
int features)
{
if (!cfg)
return (-1);
cfg->require_features = features;
return (0);
}
// 可以具有的特征为
enum event_method_feature {
EV_FEATURE_ET = 0x01, // 支持边缘触发
// 要求事件的添加,删除,触发激活等的事件复杂度是O(1),其中select and poll的时间复杂度
// 是O(N),不满足。只有epoll支持
EV_FEATURE_O1 = 0x02,
// 要求支持任意的文件描述符,而不能仅仅支持socket
EV_FEATURE_FDS = 0x04,
// 要求可以使用EV_CLOSED来检测链接的关闭, 而不需要读取所有的未决数据才能判断
EV_FEATURE_EARLY_CLOSE = 0x08
};
// 设置CPU的个数
int
event_config_set_num_cpus_hint(struct event_config *cfg, int cpus)
{
if (!cfg)
return (-1);
cfg->n_cpus_hint = cpus;
return (0);
}
// 查看当前的网络模型
const char *
event_base_get_method(const struct event_base *base)
{
EVUTIL_ASSERT(base);
return (base->evsel->name);
}
// 查看当前IO多路复用模型具有的features
int
event_base_get_features(const struct event_base *base)
{
return base->evsel->features;
}
// 配置需要避开的IO复用模型
int
event_config_avoid_method(struct event_config *cfg, const char *method)
{
struct event_config_entry *entry = mm_malloc(sizeof(*entry));
if (entry == NULL)
return (-1);
if ((entry->avoid_method = mm_strdup(method)) == NULL) {
mm_free(entry);
return (-1);
}
// 加入到 event_config 的第一个队列中
TAILQ_INSERT_TAIL(&cfg->entries, entry, next);
return (0);
}
// 得到当前os支持的所有多路复用模型
const char **
event_get_supported_methods(void)
{
static const char **methods = NULL;
const struct eventop **method;
const char **tmp;
int i = 0, k;
/* count all methods */
for (method = &eventops[0]; *method != NULL; ++method) {
++i;
}
/* allocate one more than we need for the NULL pointer */
tmp = mm_calloc((i + 1), sizeof(char *));
if (tmp == NULL)
return (NULL);
/* populate the array with the supported methods */
for (k = 0, i = 0; eventops[k] != NULL; ++k) {
tmp[i++] = eventops[k]->name;
}
tmp[i] = NULL;
if (methods != NULL)
mm_free((char**)methods);
methods = tmp;
return (methods);
}