event_base

build default event_base

==event_base_new()==函数分配并且返回一个新的具有默认设置的event_base。函数会检测环境变量,返回一个到event_base的指针。如果发生错误,则返回NULL。选择各种方法时,函数会选择OS支持的最快方法。

event_base_new(void)

event_base_new()函数声明在<event2/event.h>

struct event_base * event_base_new(void)
{struct event_base *base = NULL;//配置config 创建一个默认的event_configstruct event_config *cfg = event_config_new();if (cfg) {base = event_base_new_with_config(cfg);event_config_free(cfg);}return base;
}

build complex event_base

要对取得什么类型的event_base有更多的控制,就需要使用event_config。event_config是一个容纳event_base配置信息的不透明结构体。需要event_base时,将event_config传递给event_base_new_with_config().

这些函数和类型在<event2/event.h>中声明。
以下代码相关宏函数见:Macro definition

event_config_new()

#define INT_MAX __INT_MAX__/**在 C/C++ 中用于表示 int 类型所能表示的最大值。它是一个编译器常量,值为 2147483647(0x7ffffffff)*/ 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);
}

TAILQ_INIT 相关定义: Macro function

要使用这些函数分配event_base,先调用event_config_new()分配一个event_config。然后,对event_config调用其它函数,设置所需要的event_base特征。最后,调用其它函数,设置所需要的event_base特征。最后,调用event_base_new_with_config()获取新的event_base。完成工作后,使用event_config_free()释放event_config

event_base_new_with_config

这个函数在new_event_base被调用,传入的参数是event_config,根据cfg的内容来配置event_base

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// 安全分配内存用于存储 event_base 结构体,并初始化为 0if ((base = mm_calloc(1, sizeof(struct event_base))) == NULL) { //安全分配内存// 如果内存分配失败,打印警告信息并返回 NULLevent_warn("%s: calloc", __func__);return NULL;}// 如果传入了配置结构体,设置 base 的标志if (cfg)base->flags = cfg->flags;// 确定是否需要检查环境变量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);// 初始化信号处理相关的文件描述符为 -1,表示未使用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);// 初始化 IO 映射和信号映射,用于管理 IO 和信号事件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;// 遍历所有可用的事件操作方法,选择一个适合的事件后端for (i = 0; eventops[i] && !base->evbase; i++) {if (cfg != NULL) {// 如果配置中避免了某些方法,跳过这些方法/* determine if this backend should be avoided */if (event_config_is_avoided_method(cfg,eventops[i]->name))continue;// 如果方法不满足所需的特性,跳过该方法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;// 选择该方法作为事件后端base->evsel = eventops[i];// 初始化该事件后端base->evbase = base->evsel->init(base);}// 如果没有可用的事件后端,释放资源并返回 NULLif (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);// 为事件基分配一个单独的激活事件队列if (event_base_priority_init(base, 1) < 0) {event_base_free(base);return NULL;}// 准备线程支持
#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// 如果在 Windows 系统上并且配置要求启动 IOCP,启动 IOCPif (cfg && (cfg->flags & EVENT_BASE_FLAG_STARTUP_IOCP))event_base_start_iocp_(base, cfg->n_cpus_hint);
#endif// 返回初始化好的 event_base 对象return (base);
}
  • 内存分配和初始化:首先,函数通过 mm_calloc 安全地分配内存,并将其初始化为零。然后根据传入的配置 cfg 设置 event_base 结构体中的一些标志和参数。

  • 时间和计时器配置:函数检查是否需要启用精确的计时器,配置单调计时器,并获取当前时间以供后续操作。

  • 数据结构初始化:初始化了用于管理事件的各种数据结构,例如最小堆、信号处理、IO 映射、信号映射等。

  • 选择事件后端:函数遍历可用的事件操作方法(如 epoll、kqueue 等),选择一个合适的作为事件后端,并通过初始化函数进行配置。

  • 线程支持:如果启用了线程支持,则分配必要的锁和条件变量,并确保事件基可以在多线程环境下正常工作。

  • 平台特定操作:在 Windows 系统上,如果配置要求启用 IOCP,则会启动相应的机制。

  • 错误处理:在初始化过程中,如果出现任何错误,函数会释放已经分配的资源并返回 NULL,以防止内存泄漏或其他未定义行为。

最后,函数返回一个完全初始化好的 event_base 结构体,用于后续的事件管理。

event_config_free()

//用来释放config
void event_config_free(struct event_config *cfg)
{struct event_config_entry *entry;while ((entry = TAILQ_FIRST(&cfg->entries)) != NULL) {TAILQ_REMOVE(&cfg->entries, entry, next);event_config_entry_free(entry);}mm_free(cfg);
}

event_config_avoid_method()

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);}TAILQ_INSERT_TAIL(&cfg->entries, entry, next);return (0);
}

调用event_config_avoid_method()可以通过名字让libevent避免使用特定的可用后端。
调用event_config_require_feature()让libevent不使用不能提供所有指定特征的后端
调用调用event_config_set_flag()让libevent在创建event_base时设置一个或者多个将在下面介绍的运行时标志。

event_config_require_feature()

int event_config_require_features(struct event_config *cfg,int features)
{if (!cfg)return (-1);cfg->require_features = features;return (0);
}

相关支持的宏

EV_FEATURE_ET要求支持边沿触发的后端
EV_FEATURE_ET要求添加、删除单个事件,或者确定哪个事件激活的操作是O(1)复杂度后 端
EV_FEATURE_ET要求支持任意文件描述符,而不仅仅是套接字的后端

event_config_set_flag

int event_config_set_flag(struct event_config *cfg, int flag)
{if (!cfg)return -1;cfg->flags |= flag;return 0;
}
EVENT_BASE_FLAG_NOLOCK不要为event_base分配锁。设置这个选项可以为event_base节省一点用于锁定和解锁的时间,但是让在多个线程中访event_base成为不安全的。
EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST告诉libevent,如果决定使用epoll后端,可以安全地使用更快的基于changelist的后端。epoll-changelist后端可以在后端的分发函数调用之间,同样的fd多次修改其状态的情况下,避免不必要的系统调用。但是如果传递任何使用dup()或者其变体克隆的fd给libevent,epoll-changelist后端会触发一个内核bug,导致不正确的结果。在不使用epoll后端的情况下,这个标志是没有效果的。也可以通过设置EVENT_EPOLL_USE_CHANGELIST环境变量来打开epoll-changelist选项。
EVENT_BASE_FLAG_IGNORE_ENV选择使用的后端时,不要检测EVENT_*环境变量。使用这个标志需要三思:这会让用户更难调试你的程序与libevent的交互。
EVENT_BASE_FLAG_NO_CACHE_TIME不是在事件循环每次准备执行超时回调时检测当前时间,而是在每次超时回调后进行检测。注意:这会消耗更多的CPU时间。
EVENT_BASE_FLAG_STARTUP_IOCP仅用于Windows,让libevent在启动时就启用任何必需的IOCP分发逻辑,而不是按需启用。
上述操作event_config的函数都在成功时返回0,失败时返回-1。

注意

设置event_config,请求OS不能提供的后端是很容易的。比如说,对于libevent 2.0.1-alpha,在Windows中是没有O(1)后端的;在Linux中也没有同时提供EV_FEATURE_FDSEV_FEATURE_O1特征的后端。如果创建了libevent不能满足的配置,event_base_new_with_config()会返回NULL

event_config_set_num_cpus_hint()

int event_config_set_num_cpus_hint(struct event_config *cfg, int cpus)
{if (!cfg)return (-1);cfg->n_cpus_hint = cpus;return (0);
}

这个函数当前仅在Windows上使用IOCP时有用,虽然将来可能在其他平台上有用。这个函数告诉event_config在生成多线程event_base的时候,应该试图使用给定数目的CPU。注意这仅仅是一个提示:event_base使用的CPU可能比你选择的要少。

EVENT_BASE_FLAG_IGNORE_ENV标志首次出现在2.0.2-alpha版本event_config_set_num_cpus_hint()函数是2.0.7-rc版本新引入的。检查event_base的后端方法’

event_get_supported_methods()


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);
}

event_get_supported_methods()函数返回一个指针,指向libevent支持的方法名字数组。这个数组的最后一个元素是NULL。

注意

这个函数返回libevent被编译以支持的方法列表。然而libevent运行的时候,操作系统可能不能支持所有方法。比如说,可能OS X版本中的kqueue的bug太多,无法使用。

event_base_get_method()

const char * event_base_get_method(const struct event_base *base)
{EVUTIL_ASSERT(base);//检查给定的条件 cond 是否满足return (base->evsel->name);
}

EVUTIL_ASSERT 见:[[Macro function]]

free event_base

使用完 event_base 之后,使用event_base_free()进行释放

event_base_free()

void event_base_free(struct event_base *base)
{event_base_free_(base, 1);
}

参数 base 是指向需要释放的 event_base 结构的指针,run_finalizers 是一个标志,用于指示是否需要运行终结器。


static void
event_base_free_(struct event_base *base, int run_finalizers)
{int i, n_deleted=0;struct event *ev;/* XXXX grab the lock? If there is contention when one thread frees* the base, then the contending thread will be very sad soon. *//* 这段注释表明,在一个线程释放资源(通常指的是一个共享的基础对象或结构)时,可能需要获取一个锁(lock)来防止其他线程同时	访问该资源。如果不加锁,而另一个线程尝试访问同一个资源,就会发生竞争(contention)。竞争会导致程序不正确的行为,甚至崩	溃。*//* event_base_free(NULL) is how to free the current_base if we* made it with event_init and forgot to hold a reference to it. *//*这段注释解释了如何释放当前的基础对象。如果您使用 event_init 函数创建了一个基础对象,但忘记了保存对它的引用,那么可以通		过调用 event_base_free(NULL) 来释放它。这表明 event_base_free 函数允许一个 NULL 参数,这种情况下,它将释放当前	   上下文中的基础对象。*/if (base == NULL && current_base)base = current_base;/* Don't actually free NULL. */if (base == NULL) {event_warnx("%s: no base to free", __func__);return;}/* XXX(niels) - check for internal events first */#ifdef _WIN32event_base_stop_iocp_(base);
#endif/* threading fds if we have them */if (base->th_notify_fd[0] != -1) {event_del(&base->th_notify);EVUTIL_CLOSESOCKET(base->th_notify_fd[0]);if (base->th_notify_fd[1] != -1)EVUTIL_CLOSESOCKET(base->th_notify_fd[1]);base->th_notify_fd[0] = -1;base->th_notify_fd[1] = -1;event_debug_unassign(&base->th_notify);}/* Delete all non-internal events. */evmap_delete_all_(base);while ((ev = min_heap_top_(&base->timeheap)) != NULL) {event_del(ev);++n_deleted;}for (i = 0; i < base->n_common_timeouts; ++i) {struct common_timeout_list *ctl =base->common_timeout_queues[i];event_del(&ctl->timeout_event); /* Internal; doesn't count */event_debug_unassign(&ctl->timeout_event);for (ev = TAILQ_FIRST(&ctl->events); ev; ) {struct event *next = TAILQ_NEXT(ev,ev_timeout_pos.ev_next_with_common_timeout);if (!(ev->ev_flags & EVLIST_INTERNAL)) {event_del(ev);++n_deleted;}ev = next;}mm_free(ctl);}if (base->common_timeout_queues)mm_free(base->common_timeout_queues);for (;;) {/* For finalizers we can register yet another finalizer out from* finalizer, and iff finalizer will be in active_later_queue we can* add finalizer to activequeues, and we will have events in* activequeues after this function returns, which is not what we want* (we even have an assertion for this).** A simple case is bufferevent with underlying (i.e. filters).*/int i = event_base_free_queues_(base, run_finalizers);event_debug(("%s: %d events freed", __func__, i));if (!i) {break;}n_deleted += i;}if (n_deleted)event_debug(("%s: %d events were still set in base",__func__, n_deleted));while (LIST_FIRST(&base->once_events)) {struct event_once *eonce = LIST_FIRST(&base->once_events);LIST_REMOVE(eonce, next_once);mm_free(eonce);}if (base->evsel != NULL && base->evsel->dealloc != NULL)base->evsel->dealloc(base);for (i = 0; i < base->nactivequeues; ++i)EVUTIL_ASSERT(TAILQ_EMPTY(&base->activequeues[i]));EVUTIL_ASSERT(min_heap_empty_(&base->timeheap));min_heap_dtor_(&base->timeheap);mm_free(base->activequeues);evmap_io_clear_(&base->io);evmap_signal_clear_(&base->sigmap);event_changelist_freemem_(&base->changelist);EVTHREAD_FREE_LOCK(base->th_base_lock, 0);EVTHREAD_FREE_COND(base->current_event_cond);/* If we're freeing current_base, there won't be a current_base. */if (base == current_base)current_base = NULL;mm_free(base);
}
notes
/* Global state; deprecated */
//全局的event_base current_base(event_global_current_base)
EVENT2_EXPORT_SYMBOL
struct event_base *event_global_current_base_ = NULL;
#define current_base event_global_current_base_
  1. 处理 NULL 基础对象: 如果 baseNULL 并且 current_base 存在,那么将 base 设置为 current_base
    if (base == NULL && current_base)  base = current_base;d  if (base == NULL) {  event_warnx("%s: no base to free", __func__);  return;  }
  1. 停止 Windows 特定的 IOCP 处理(仅在 Windows 平台上有效):
    #ifdef _WIN32  event_base_stop_iocp_(base);//此处不做讨论  #endif
  1. 处理线程通知文件描述符: 删除线程通知事件并关闭文件描述符。
  if (base->th_notify_fd[0] != -1) {  event_del(&base->th_notify);  EVUTIL_CLOSESOCKET(base->th_notify_fd[0]);  if (base->th_notify_fd[1] != -1)  EVUTIL_CLOSESOCKET(base->th_notify_fd[1]);  base->th_notify_fd[0] = -1;  base->th_notify_fd[1] = -1;  event_debug_unassign(&base->th_notify);  
}
  1. 删除所有非内部事件: 删除时间堆中的所有事件,并释放公共超时队列中的事件。
    evmap_delete_all_(base);  while ((ev = min_heap_top_(&base->timeheap)) != NULL) {  event_del(ev);  ++n_deleted;  }  for (i = 0; i < base->n_common_timeouts; ++i) {  struct common_timeout_list *ctl = base->common_timeout_queues[i];  event_del(&ctl->timeout_event); // Internal; doesn't count  event_debug_unassign(&ctl->timeout_event);  for (ev = TAILQ_FIRST(&ctl->events); ev; ) {  struct event *next = TAILQ_NEXT(ev,                                                           ev_timeout_pos.ev_next_with_common_timeout);  if (!(ev->ev_flags & EVLIST_INTERNAL)) {  event_del(ev);  ++n_deleted;  }  ev = next;  }  mm_free(ctl);  }  if (base->common_timeout_queues)  mm_free(base->common_timeout_queues);
  1. 释放一次性事件: 释放 once_events 列表中的事件。
    while (LIST_FIRST(&base->once_events)) {  struct event_once *eonce = LIST_FIRST(&base->once_events);  LIST_REMOVE(eonce, next_once);  mm_free(eonce);  }
  1. 释放选择器和锁: 释放事件选择器和相关的锁与条件变量。

    if (base->evsel != NULL && base->evsel->dealloc != NULL)  base->evsel->dealloc(base);for (i = 0; i < base->nactivequeues; ++i)  EVUTIL_ASSERT(TAILQ_EMPTY(&base->activequeues[i]));EVUTIL_ASSERT(min_heap_empty_(&base->timeheap));  
    min_heap_dtor_(&base->timeheap);mm_free(base->activequeues);evmap_io_clear_(&base->io);  
    evmap_signal_clear_(&base->sigmap);  
    event_changelist_freemem_(&base->changelist);EVTHREAD_FREE_LOCK(base->th_base_lock, 0);  
    EVTHREAD_FREE_COND(base->current_event_cond);
    
    
7. **释放基础对象**: 如果正在释放 `current_base`,则将其设置为 `NULL`,并最终释放 `base`。
```cif (base == current_base)  current_base = NULL;  mm_free(base);

这个函数通过清理和释放所有相关资源和内存来销毁一个 event_base 对象。注释中提到的竞争条件提醒开发者在多线程环境中小心处理共享资源,确保程序的稳定性和正确性。

set event_base 's priority

libevent支持为事件设置多个优先级。然而,event_base默认只支持单个优先级。可以调用 event_base_priority_init()设置event_base的优先级数目

int event_base_priority_init(struct event_base *base, int npriorities)

成功时这个函数返回0,失败时返回-1。base是要修改的 event_base,n_priorities是要支持的优先级数目,这个数目至少是1。每个新的事件可用的优先级将从0(最高)到n_priorities-1(最低)。

常量EVENT_MAX_PRIORITIES表示n_priorities的上限。调用这个函数时为 n_priorities 给出更大的值是错误的。

🔔
必须在任何事件激活之前调用这个函数,最好在创建event_base后立刻调用。
默认情况下,与event_base相关联的事件将被初始化为具有优先级n_priorities / 2event_base_priority_init()函数定义在<event2/event.h>中,从libevent 1.0版就可用了。

这个宏用于获取event_base_的锁(if support)
EVBASE_ACQUIRE_LOCK 见:[[Macro function]]

event_base_priority_init()


int event_base_priority_init(struct event_base *base, int npriorities)
{int i, r;r = -1; //用于指示返回值的状态	//在多线程环境中,需要获取锁以确保对 base 的修改是线程安全的。EVBASE_ACQUIRE_LOCK(base, th_base_lock);//展开类似 EVLOCK_LOCK((base)->th_base_lock, 0);//参数和状态检查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);
}

这个函数的主要目的是为事件基础结构 base 初始化或重新初始化优先级队列。它通过以下步骤实现:

  1. 获取锁以确保线程安全。

  2. 检查输入参数和当前状态。

  3. 如果需要,释放现有的优先级队列。

  4. 分配并初始化新的优先级队列。

  5. 设置返回状态并释放锁。

通过这些步骤,确保 event_base 可以正确管理不同优先级的事件。

Reinitializes event_base after fork()

event_reinit()

不是所有事件后端都在调用fork()创建一个新的进程之后可以正确工作。所以,如果在使用fork()或者其他相关系统调用启动新进程之后,希望在新进程中继续使用event_base,就需要进行重新初始化。
/* reinitialize the event base after a fork */
int  event_reinit(struct event_base *base)
int
event_reinit(struct event_base *base)
{const struct eventop *evsel;//指向事件操作结构的指针int res = 0;//操作结果//两个标记变量int was_notifiable = 0;int had_signal_added = 0;//获取锁EVBASE_ACQUIRE_LOCK(base, th_base_lock);//保存但前的操作结构evsel = base->evsel;//检查是否需要重新初始化/* check if this event mechanism requires reinit on the backend */if (evsel->need_reinit) {/* We're going to call event_del() on our notify events (the* ones that tell about signals and wakeup events).  But we* don't actually want to tell the backend to change its* state, since it might still share some resource (a kqueue,* an epoll fd) with the parent process, and we don't want to* delete the fds from _that_ backend, we temporarily stub out* the evsel with a replacement.*/base->evsel = &nil_eventop;}/* We need to re-create a new signal-notification fd and a new* thread-notification fd.  Otherwise, we'll still share those with* the parent process, which would make any notification sent to them* get received by one or both of the event loops, more or less at* random.*///处理信号通知和线程通知	if (base->sig.ev_signal_added) {event_del_nolock_(&base->sig.ev_signal, EVENT_DEL_AUTOBLOCK);event_debug_unassign(&base->sig.ev_signal);memset(&base->sig.ev_signal, 0, sizeof(base->sig.ev_signal));had_signal_added = 1;base->sig.ev_signal_added = 0;}if (base->sig.ev_signal_pair[0] != -1)EVUTIL_CLOSESOCKET(base->sig.ev_signal_pair[0]);if (base->sig.ev_signal_pair[1] != -1)EVUTIL_CLOSESOCKET(base->sig.ev_signal_pair[1]);if (base->th_notify_fn != NULL) {was_notifiable = 1;base->th_notify_fn = NULL;}if (base->th_notify_fd[0] != -1) {event_del_nolock_(&base->th_notify, EVENT_DEL_AUTOBLOCK);EVUTIL_CLOSESOCKET(base->th_notify_fd[0]);if (base->th_notify_fd[1] != -1)EVUTIL_CLOSESOCKET(base->th_notify_fd[1]);base->th_notify_fd[0] = -1;base->th_notify_fd[1] = -1;event_debug_unassign(&base->th_notify);}/* Replace the original evsel. */base->evsel = evsel;//重新初始化后端if (evsel->need_reinit) {/* Reconstruct the backend through brute-force, so that we do* not share any structures with the parent process. For some* backends, this is necessary: epoll and kqueue, for* instance, have events associated with a kernel* structure. If didn't reinitialize, we'd share that* structure with the parent process, and any changes made by* the parent would affect our backend's behavior (and vice* versa).*/if (base->evsel->dealloc != NULL)base->evsel->dealloc(base);base->evbase = evsel->init(base);if (base->evbase == NULL) {event_errx(1,"%s: could not reinitialize event mechanism",__func__);res = -1;goto done;}/* Empty out the changelist (if any): we are starting from a* blank slate. */event_changelist_freemem_(&base->changelist);/* Tell the event maps to re-inform the backend about all* pending events. This will make the signal notification* event get re-created if necessary. */if (evmap_reinit_(base) < 0)res = -1;} else {res = evsig_init_(base);if (res == 0 && had_signal_added) {res = event_add_nolock_(&base->sig.ev_signal, NULL, 0);if (res == 0)base->sig.ev_signal_added = 1;}}/* If we were notifiable before, and nothing just exploded, become* notifiable again. *///恢复通知功能if (was_notifiable && res == 0)res = evthread_make_base_notifiable_nolock_(base);
//释放锁并返回结果
done:EVBASE_RELEASE_LOCK(base, th_base_lock);return (res);
}

notes

event_reinit 函数主要用于在多进程环境中重新初始化事件基础结构,以确保不同进程不会共享文件描述符或其他资源。它通过删除和重新创建信号通知和线程通知事件,以及重新初始化后端结构来实现这一点。在多线程环境中,使用锁来确保线程安全

example

![[Pasted image 20240831140329.png]]

deprecated event_base

老版本的libevent严重依赖“当前”event_base的概念。“当前”event_base是一个由所有线程共享的全局设置。如果忘记指定要使用哪个event_base,则得到的是当前的。因为event_base不是线程安全的,这很容易导致错误。

老版本的libevent没有event_base_new(),而有:

sturct event_base* event_init(void)

这个函数的工作与event_base_new()类似,它将分配的event_base设置成当前的。没有其他方法改变当前event_base。
本文描述的函数有一些用于操作当前event_base的变体,这些函数与新版本函数的行为类似,只是它们没有event_base参数。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/473876.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PyTorch使用教程-深度学习框架

PyTorch使用教程-深度学习框架 1. PyTorch简介 1.1-什么是PyTorch ​ PyTorch是一个广泛使用的开源机器学习框架&#xff0c;特别适合深度学习的应用。它以其动态计算图而闻名&#xff0c;允许在运行时修改模型&#xff0c;使得实验和调试更加灵活。PyTorch提供了强大的GPU加…

数据科学与SQL:如何计算排列熵?| 基于SQL实现

目录 0 引言 1 排列熵的计算原理 2 数据准备 3 问题分析 4 小结 0 引言 把“熵”应用在系统论中的信息管理方法称为熵方法。熵越大&#xff0c;说明系统越混乱&#xff0c;携带的信息越少&#xff1b;熵越小&#xff0c;说明系统越有序&#xff0c;携带的信息越多。在传感…

28.<Spring博客系统⑤(部署的整个过程(CentOS))>

引入依赖 Spring-boot-maven-plugin 用maven进行打包的时候必须用到这个插件。看看自己pom.xml中有没有这个插件 并且看看配置正确不正常。 注&#xff1a;我们这个项目打的jar包在30MB左右。 <plugin><groupId>org.springframework.boot</groupId><artif…

无人机在森林中的应用!

一、森林资源调查 无人机可以利用遥感技术快速获取所需区域高精度的空间遥感信息&#xff0c;对森林图斑进行精确区划。相较于传统手段&#xff0c;无人机调查具有低成本、高效率、高时效的特点&#xff0c;尤其在地理环境条件不好的区域&#xff0c;调查人员无法或难以到达的…

esp32c3开发板通过micropython的mqtt库连MQTT物联网消息服务器

MQTT介绍 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是一种轻量级的消息协议&#xff0c;旨在设备之间进行通信&#xff0c;尤其是在网络条件较差的情况下。MQTT v3.1.1 和 MQTT v5 是该协议的两个主要版本。 MQTT v3.1.1&#xff1a; 优点&#xff…

什么是SMARC?模块电脑(核心板)规范标准简介三

1. 概念 SMARC&#xff08;Smart Mobility ARChitecture&#xff0c;智能移动架构&#xff09;是一种通用的小型计算机模块定义&#xff0c;基于ARM和X86技术的模块化计算机低功耗嵌入式架构平台&#xff0c;旨在满足低功耗、低成本和高性能的应用需求。这些模块通常使用与平板…

resnet50,clip,Faiss+Flask简易图文搜索服务

一、实现 文件夹目录结构&#xff1a; templates -----upload.html faiss_app.py 前端代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widt…

JavaWeb——JS、Vue

目录 1.JavaScript a.概述 b.引入方式 c.JS的基础语法 d.JS函数 e.JS对象 f.JS事件监听 2.Vue a.概述 b.Vue常用指令 d.生命周期 1.JavaScript a.概述 JavaScript是一门跨平台、面向对象的脚本语言。是用来控制网页行为的&#xff0c;它能使网页可交互。JavaScript和…

MySQL的编程语言

一、MySQL基础 使用系统的全局变量@@VERSION查看当前使用的MySQL的版本信息,SQL语句如下: select @@version; 将局部变量varl声明为char的类型,长度值为10,并为其赋值为“程菲” begin declare var1 char(10); set @var1="程菲"; end 通过局部变量查看d_eams数…

小程序-基于java+SpringBoot+Vue的驾校预约平台设计与实现

项目运行 1.运行环境&#xff1a;最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可以。 2.IDE环境&#xff1a;IDEA&#xff0c;Eclipse,Myeclipse都可以。推荐IDEA; 3.tomcat环境&#xff1a;Tomcat 7.x,8.x,9.x版本均可 4.硬件环境&#xff1a…

阿里云引领智算集群网络架构的新一轮变革

阿里云引领智算集群网络架构的新一轮变革 云布道师 11 月 8 日~ 10 日在江苏张家港召开的 CCF ChinaNet&#xff08;即中国网络大会&#xff09;上&#xff0c;众多院士、教授和业界技术领袖齐聚一堂&#xff0c;畅谈网络未来的发展方向&#xff0c;聚焦智算集群网络的创新变…

【ASR技术】WhisperX安装使用

介绍 WhisperX 是一个开源的自动语音识别&#xff08;ASR&#xff09;项目&#xff0c;由 m-bain 开发。该项目基于 OpenAI 的 Whisper 模型&#xff0c;通过引入批量推理、强制音素对齐和语音活动检测等技术。提供快速自动语音识别&#xff08;large-v2 为 70 倍实时&#xf…

android framework ams/wms常见系统日志(main\system\events\crash,protoLog使用)

重要性 wms和ams的一些系统原生日志能够帮助我们快速定位问题 日志分类 在日常framework工作中常见的日志类别如下&#xff1a; -b , --buffer Request alternate ring buffer, ‘main’, ‘system’, ‘radio’, ‘events’, ‘crash’, ‘default’ or ‘all’. Additiona…

2024年11月16日 星期六 重新整理Go技术

今日格言 坚持每天进步一点点~ 一个人也可以是一个团队~ 学习全栈开发, 做自己喜欢的产品~~ 简介 大家好, 我是张大鹏, 今天是2024年11月16日星期六, 很高兴在这里给大家分享技术. 今天又是休息的一天, 做了很多的思考, 整理了自己掌握的技术, 比如Java, Python, Golang,…

深度解读混合专家模型(MoE):算法、演变与原理

假设一个专家团队共同解决复杂问题。每位专家都拥有独特的技能&#xff0c;团队通过高效分配任务实现了前所未有的成功。这就是混合专家&#xff08;Mixture-of-Experts&#xff0c;MoE&#xff09;模型架构背后的基本思想&#xff0c;这种方法允许机器学习系统&#xff0c;特别…

Area-Composition模型部署指南

一、介绍 本模型可以通过输入不同的提示词&#xff0c;然后根据各部分提示词进行融合生成图片。如下图&#xff1a; 此图像包含 4 个不同的区域&#xff1a;夜晚、傍晚、白天、早晨 二、部署 环境要求&#xff1a; 最低显存&#xff1a;10G 1. 部署ComfyUI 本篇的模型部署…

HTML之列表学习记录

练习题&#xff1a; 图所示为一个问卷调查网页&#xff0c;请制作出来。要求&#xff1a;大标题用h1标签&#xff1b;小题目用h3标签&#xff1b;前两个问题使用有序列表&#xff1b;最后一个问题使用无序列表。 代码&#xff1a; <!DOCTYPE html> <html> <he…

Java基础-内部类与异常处理

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 一、Java 内部类 什么是内部类&#xff1f; 使用内部类的优点 访问局部变量的限制 内部类和继承 内部…

C/C++中使用MYSQL

首先要保证下载好mysql的库和头文件&#xff0c;头文件在/usr/include/mysql/目录下&#xff0c;库在/usr/lib64/mysql/目录下&#xff1a; 一般情况下&#xff0c;在我们安装mysql的时候&#xff0c;这些都提前配置好了&#xff0c;如果没有就重装一下mysql。如果重装mysql还是…

华为ensp实验二--mux vlan的应用

一、实验内容 1.实验要求&#xff1a; 在交换机上创建三个vlan&#xff0c;vlan10、vlan20、vlan100&#xff0c;将vlan100设置为mux-vlan&#xff0c;将vlan10设置为group vlan&#xff0c;将vlan20设置为separate vlan&#xff1b;实现vlan10的设备在局域网内可以进行互通&…