事件

event是libevent的基本操作单元,每一个event代表一系列条件的集合,包括:

  • 一个文件描述符准备好了读、写
  • 一个文件描述符准备好了读、写(仅限于边缘触发)
  • 超时到期
  • 发生了信号
  • 用户触发的事件

events拥有类似的生命周期。当你调用libevent的函数设置了一个event,并且将它关联到一个event_base,它的状态是initialized,在此时,你可以将它add进event_base,此时它的状态变为pending,当触发条件的事件发生时(比如,文件描述符状态改变或者超时到期),它的状态会变为active,此时用户提供的回调函数将会被调用,如果event被设置为persistent,它仍然保持pending状态,如果不是persistent的,当回调函数结束后,将不再是pending状态,你可以将pending 的event删除,使其变为non-pending状态,你可以将non-pending状态的event调用add使其变为pending状态。

构造event对象

调用event_new()创建一个新的event。

#define EV_TIMEOUT      0x01
#define EV_READ         0x02
#define EV_WRITE        0x04
#define EV_SIGNAL       0x08
#define EV_PERSIST      0x10
#define EV_ET           0x20

typedef void (*event_callback_fn)(evutil_socket_t, short, void *);

struct event *event_new(struct event_base *base, evutil_socket_t fd,
    short what, event_callback_fn cb,
    void *arg);

void event_free(struct event *event);

event_new()使用指定的event_base分配并构造一个新的event,what参数是上面列举的一些标志位(具体语义将在下面描述),如果fd非负,我们将监控此fd的读写事件,当event变为active,libevent会调用cb回调函数,并传递参数:

  • fd,文件描述符
  • bitfield,代表触发的事件的位集合
  • args,构造事件时指定的参数

当发生内部错误或参数无效时,返回NULL。

所有新创建的事件均为initialized,并且是non-pending状态,可以调用event_add()函数使其变为pending状态。

可以调用event_free()函数来释放一个event,当event处于pengding或者active状态时调用该函数是安全的。

#include <event2/event.h>

void cb_func(evutil_socket_t fd, short what, void *arg)
{
        const char *data = arg;
        printf("Got an event on socket %d:%s%s%s%s [%s]",
            (int) fd,
            (what&EV_TIMEOUT) ? " timeout" : "",
            (what&EV_READ)    ? " read" : "",
            (what&EV_WRITE)   ? " write" : "",
            (what&EV_SIGNAL)  ? " signal" : "",
            data);
}

void main_loop(evutil_socket_t fd1, evutil_socket_t fd2)
{
        struct event *ev1, *ev2;
        struct timeval five_seconds = {5,0};
        struct event_base *base = event_base_new();

        /* The caller has already set up fd1, fd2 somehow, and make them
           nonblocking. */

        ev1 = event_new(base, fd1, EV_TIMEOUT|EV_READ|EV_PERSIST, cb_func,
           (char*)"Reading event");
        ev2 = event_new(base, fd2, EV_WRITE|EV_PERSIST, cb_func,
           (char*)"Writing event");

        event_add(ev1, &five_seconds);
        event_add(ev2, NULL);
        event_base_dispatch(base);
}

event flags

  • EV_TIMEOUT

    在指定的时间过去之后,event将变为active。

  • EV_READ

    标志着文件描述符变为可读。

  • EV_WRITE

    标志着文件描述符变为可写。

  • EV_SIGNAL

    用来实现信号检测,参考“构造信号事件”小节

  • EV_PERSIST

    标志着event为persistent状态,参考“关于事件持久性”小节

  • EV_ET

    指示事件应该是边缘触发的,如果底层 event_base 后端支持边缘触发的事件。这会影响 EV_READEV_WRITE 的语义。

从 Libevent 2.0.1-alpha 开始,任何数量的事件都可能同时在相同条件下等待处理。例如,如果给定的 fd 准备好读取,您可能有两个事件将变为活动状态。它们的回调运行的顺序是未定义的。

关于事件持久性

默认情况下,当一个pending的事件变为active(因为fd变为可读、可写或者超时),当回调被执行前,它变为non-pending状态,因此,如果你想让事件继续变为pending状态,你可以在回调函数里面调用event_add()函数。

如果设置了EV_PERSIST标志位,事件则是持久的,当回调被调用后,事件仍然保持pending状态。你可以在回调函数中调用event_del()函数来使事件变为non-pending状态。

每当事件的回调运行时,持久性事件的超时就会重置。因此,如果一个event有如下标志位:EV_READ|EV_PERSIST和5秒的超时,事件会在以下情况下变为active:

  • 当socket变为可读时
  • 当距离上次事件变为active状态已经过去了5秒

创建一个将自己作为回调参数的事件

你可能经常会需要创建一个将自己作为回调参数的事件,你不可以将该事件的指针作为参数传递到event_new()中,因为它还不存在,为了解决此问题,你可以使用event_self_cbarg()

void *event_self_cbarg();

event_self_cbarg()函数返回一个“魔术”指针,当作为事件回调参数传递时,它告诉 event_new() 创建一个接收自身作为其回调参数的事件。

#include <event2/event.h>

static int n_calls = 0;

void cb_func(evutil_socket_t fd, short what, void *arg)
{
    struct event *me = arg;

    printf("cb_func called %d times so far.\n", ++n_calls);

    if (n_calls > 100)
       event_del(me);
}

void run(struct event_base *base)
{
    struct timeval one_sec = { 1, 0 };
    struct event *ev;
    /* We're going to set up a repeating timer to get called called 100
       times. */
    ev = event_new(base, -1, EV_PERSIST, cb_func, event_self_cbarg());
    event_add(ev, &one_sec);
    event_base_dispatch(base);
}

此函数还可与 event_new()evtimer_new()evsignal_new()event_assign()evtimer_assign()evsignal_assign() 一起使用。但是,它不能用作非事件的回调参数。

仅超时事件

为方便起见,您可以使用一组以 evtimer_ 开头的宏来代替 event_* 调用来分配和操作纯超时事件。除了提高代码的清晰度之外,使用这些宏没有任何好处。

#define evtimer_new(base, callback, arg) \
    event_new((base), -1, 0, (callback), (arg))
#define evtimer_add(ev, tv) \
    event_add((ev),(tv))
#define evtimer_del(ev) \
    event_del(ev)
#define evtimer_pending(ev, tv_out) \
    event_pending((ev), EV_TIMEOUT, (tv_out))

译者注:

从宏定义可以看出,evtimer_new被定义为flag为0的event_new别名,所以evtimer_new创建的定时器不是persistent的,如果想让定时器间隔一段时间就被执行一次,还需要手动的在回调函数里面再次执行event_pending函数,所以使用此方式创建定时器仅仅是可读性高一些。

构造信号事件

libevent可以监视POSIX风格的信号,可以调用如下函数:

#define evsignal_new(base, signum, cb, arg) \
    event_new(base, signum, EV_SIGNAL|EV_PERSIST, cb, arg)

除了用信号来代替文件描述符外,其余参数和event_new一样。

struct event *hup_event;
struct event_base *base = event_base_new();

/* call sighup_function on a HUP signal */
hup_event = evsignal_new(base, SIGHUP, sighup_function, NULL);

请注意,信号回调在信号发生后在事件循环中运行,因此它们可以安全地调用您不应该从常规 POSIX 信号处理程序调用的函数。

警告:不要给信号事件设定超时,它可能并不被支持。

译者注:

经译者测试,如果给一个信号事件添加了超时之后,该事件经过指定时间之后会被触发,不过事件为非persistent,即仅仅触发一次。

在处理信号事件时,您还可以使用一组方便的宏。

#define evsignal_add(ev, tv) \
    event_add((ev),(tv))
#define evsignal_del(ev) \
    event_del(ev)
#define evsignal_pending(ev, what, tv_out) \
    event_pending((ev), (what), (tv_out))

注意事项

使用当前版本的 Libevent,对于大多数后端,每个进程一次只有一个 event_base 可以监听信号。如果您一次将信号事件添加到两个 event_base 即使信号不同!只有一个 event_base 会接收信号。

kqueue 后端没有这个限制。

在没有堆分配的情况下设置事件

出于性能和其他原因,有些人喜欢将事件作为更大结构的一部分进行分配。对于事件的每次使用,这会为他们节省:

  • 用于在堆上分配小对象的内存分配器开销。
  • 取消引用结构事件指针的时间开销。
  • 如果事件不在缓存中,则可能的额外缓存未命中的时间开销。

使用这种方法可能会破坏与其他版本的 Libevent 的二进制兼容性,这些版本的事件结构可能具有不同的大小。

这些开销对大多数应用程序来说影响微乎其微,你应该坚持使用event_new(),除非你明确知道在堆上分配内存导致巨大的性能开销。如果未来版本的 Libevent 使用比您正在构建的事件结构更大的事件结构,则使用 event_assign()可能会导致难以诊断的错误。

int event_assign(struct event *event, struct event_base *base,
    evutil_socket_t fd, short what,
    void (*callback)(evutil_socket_t, short, void *), void *arg);

event_assign() 的所有参数都与 event_new() 相同,但 event 参数必须指向未初始化的事件。它在成功时返回 0,在内部错误或错误参数时返回 -1。

#include <event2/event.h>
/* Watch out!  Including event_struct.h means that your code will not
 * be binary-compatible with future versions of Libevent. */
#include <event2/event_struct.h>
#include <stdlib.h>

struct event_pair {
         evutil_socket_t fd;
         struct event read_event;
         struct event write_event;
};
void readcb(evutil_socket_t, short, void *);
void writecb(evutil_socket_t, short, void *);
struct event_pair *event_pair_new(struct event_base *base, evutil_socket_t fd)
{
        struct event_pair *p = malloc(sizeof(struct event_pair));
        if (!p) return NULL;
        p->fd = fd;
        event_assign(&p->read_event, base, fd, EV_READ|EV_PERSIST, readcb, p);
        event_assign(&p->write_event, base, fd, EV_WRITE|EV_PERSIST, writecb, p);
        return p;
}

您还可以使用 event_assign() 来初始化栈分配或静态分配的event。

警告

不要对一个pendingevent_base的event调用event_assign(),这样做会导致极度难以调试的bug,如果event已经initialized并且处于pending状态,在调用event_assign()之前应该先调用event_del()

有一些方便的宏可以用于 event_assign() 仅超时或信号事件:

#define evtimer_assign(event, base, callback, arg) \
    event_assign(event, base, -1, 0, callback, arg)
#define evsignal_assign(event, base, signum, callback, arg) \
    event_assign(event, base, signum, EV_SIGNAL|EV_PERSIST, callback, arg)

如果您需要使用 event_assign() 并保持与 Libevent 未来版本的二进制兼容性,您可以要求 Libevent 库在运行时告诉您结构事件应该有多大:

size_t event_get_struct_event_size(void);

此函数返回您需要为event留出的字节数。和以前一样,只有当您知道堆分配是您程序中的一个重要问题时,才应该使用此函数,因为它会使您的代码更难以阅读和编写。

请注意, event_get_struct_event_size()将来可能会给您一个小于sizeof(struct event)的值。如果发生这种情况,则意味着 struct 事件末尾的任何额外字节只是为未来版本的 Libevent 保留的填充字节。

这是与上面相同的示例,但我们不依赖于 event_struct.hstruct event 的大小,而是使用 event_get_struct_size() 在运行时使用正确的大小。

#include <event2/event.h>
#include <stdlib.h>

/* When we allocate an event_pair in memory, we'll actually allocate
 * more space at the end of the structure.  We define some macros
 * to make accessing those events less error-prone. */
struct event_pair {
         evutil_socket_t fd;
};

/* Macro: yield the struct event 'offset' bytes from the start of 'p' */
#define EVENT_AT_OFFSET(p, offset) \
            ((struct event*) ( ((char*)(p)) + (offset) ))
/* Macro: yield the read event of an event_pair */
#define READEV_PTR(pair) \
            EVENT_AT_OFFSET((pair), sizeof(struct event_pair))
/* Macro: yield the write event of an event_pair */
#define WRITEEV_PTR(pair) \
            EVENT_AT_OFFSET((pair), \
                sizeof(struct event_pair)+event_get_struct_event_size())

/* Macro: yield the actual size to allocate for an event_pair */
#define EVENT_PAIR_SIZE() \
            (sizeof(struct event_pair)+2*event_get_struct_event_size())

void readcb(evutil_socket_t, short, void *);
void writecb(evutil_socket_t, short, void *);
struct event_pair *event_pair_new(struct event_base *base, evutil_socket_t fd)
{
        struct event_pair *p = malloc(EVENT_PAIR_SIZE());
        if (!p) return NULL;
        p->fd = fd;
        event_assign(READEV_PTR(p), base, fd, EV_READ|EV_PERSIST, readcb, p);
        event_assign(WRITEEV_PTR(p), base, fd, EV_WRITE|EV_PERSIST, writecb, p);
        return p;
}

事件的pending 和 non-pending

一旦你构造了一个事件,直到你将它变为pending状态之前,他都没有其它任何用处,你可以调用如下函数将其变为pending状态:

int event_add(struct event *ev, const struct timeval *tv);

在处于non-pending状态的event上调用此函数,会将其在event_base上变为pending状态,此函数在成功时返回0,失败是返回-1,如果tv参数为NULL,则event没有超时,否则,tv参数指定其超时的秒数和微秒数。

如果您对已经pending的事件调用 event_add(),它将使变为pending,并使用提供的超时重新安排它。如果事件已经处于pending状态,并且您使用超时 NULL 重新添加它,则 event_add() 将无效。

注意: tv为相对事件,并非绝对时间,如果你想将超时设置在10秒后,请将tv->tv_sec赋值为10,而不是time(NULL)+10

int event_del(struct event *ev);

initialized事件上调用 event_del 使其变为non-pendingnon-active状态。如果事件处于non-pendingnon-active状态,则没有任何影响。成功时返回值为 0,失败时返回 -1。

如果在事件变为活动状态后但在其回调有机会执行之前删除事件,则不会执行回调。

int event_remove_timer(struct event *ev);

最后,您可以在不删除其 IO 或信号组件的情况下完全删除挂起事件的超时。如果事件没有超时挂起,则 event_remove_timer() 无效。如果事件只有超时但没有 IO 或信号组件,则event_remove_timer() event_del() 具有相同的效果。成功时返回值为 0,失败时返回 -1。

具有优先级的事件

当多个事件同时触发时,libevent并没有规定执行回调的顺序。你可以使用priorities,来定义一些事件比另一些事件重要。

就像在以前的章节中讨论的一样,每一个event_base有一个或多个优先级与之关联。在初始化event之后,添加到event_base之前,你可以设置优先级

int event_priority_set(struct event *event, int priority);

priority参数取值范围为0至event_base的priorities-1,该函数在成功时返回0,失败时返回-1.

当多个具有不同优先级的事件同时变为active时,低优先级的事件不会运行,libevent会优先运行高优先级的事件,然后再次检查事件。只有当所有高优先级的事件没有处于active状态的时候才会运行低优先级的事件。

#include <event2/event.h>

void read_cb(evutil_socket_t, short, void *);
void write_cb(evutil_socket_t, short, void *);

void main_loop(evutil_socket_t fd)
{
  struct event *important, *unimportant;
  struct event_base *base;

  base = event_base_new();
  event_base_priority_init(base, 2);
  /* Now base has priority 0, and priority 1 */
  important = event_new(base, fd, EV_WRITE|EV_PERSIST, write_cb, NULL);
  unimportant = event_new(base, fd, EV_READ|EV_PERSIST, read_cb, NULL);
  event_priority_set(important, 0);
  event_priority_set(unimportant, 1);

  /* Now, whenever the fd is ready for writing, the write callback will
     happen before the read callback.  The read callback won't happen at
     all until the write callback is no longer active. */
}

当你没有设置event的优先级时,默认值为队列的长度除以2。

译者注:

经过实际测试发现,当没有设置event的优先级时,默认值为event_base的优先级除以2。测试代码如下所示

void signal_cb(evutil_socket_t fd, short events, void *args)
{
    event *me = (event*)args;
    int priority = event_get_priority(me);
    cout << "\tpriority:" << priority << endl;
}

int main()
{
    event_base *base = event_base_new();
    event_base_priority_init(base, 21);
    event *hup_event = evsignal_new(base, SIGINT, signal_cb, event_self_cbarg());
    evsignal_add(hup_event, NULL);
    event_base_dispatch(base);
    evsignal_del(hup_event);

    return 0;
}

检测事件状态

有时你想判断是否添加了一个事件。

int event_pending(const struct event *ev, short what, struct timeval *tv_out);

#define event_get_signal(ev) /* ... */
evutil_socket_t event_get_fd(const struct event *ev);
struct event_base *event_get_base(const struct event *ev);
short event_get_events(const struct event *ev);
event_callback_fn event_get_callback(const struct event *ev);
void *event_get_callback_arg(const struct event *ev);
int event_get_priority(const struct event *ev);

void event_get_assignment(const struct event *event,
        struct event_base **base_out,
        evutil_socket_t *fd_out,
        short *events_out,
        event_callback_fn *callback_out,
        void **arg_out);

event_pending函数确定给定的事件是pending或者active的。如果是,并且在 what 参数中设置了任何标志 EV_READ、EV_WRITE、EV_SIGNAL 和 EV_TIMEOUT,则该函数返回事件当前pending或active的所有标志。如果提供了 tv_out,并且 EV_TIMEOUT 设置在 what 中,并且事件当前处于挂起状态或超时处于活动状态,则 tv_out 设置为保持事件超时到期的时间。

event_get_fd()event_get_signal() 函数返回为事件配置的文件描述符或信号编号。 event_get_base() 函数返回其配置的 event_baseevent_get_events() 函数返回事件的事件标志(EV_READ、EV_WRITE 等)。 event_get_callback()event_get_callback_arg() 函数返回回调函数和参数指针。 event_get_priority() 函数返回事件当前分配的优先级。

event_get_assignment() 函数将事件的所有分配字段复制到提供的指针中。如果任何指针为 NULL,则将其忽略。

#include <event2/event.h>
#include <stdio.h>

/* Change the callback and callback_arg of 'ev', which must not be
 * pending. */
int replace_callback(struct event *ev, event_callback_fn new_callback,
    void *new_callback_arg)
{
    struct event_base *base;
    evutil_socket_t fd;
    short events;

    int pending;

    pending = event_pending(ev, EV_READ|EV_WRITE|EV_SIGNAL|EV_TIMEOUT,
                            NULL);
    if (pending) {
        /* We want to catch this here so that we do not re-assign a
         * pending event.  That would be very very bad. */
        fprintf(stderr,
                "Error! replace_callback called on a pending event!\n");
        return -1;
    }

    event_get_assignment(ev, &base, &fd, &events,
                         NULL /* ignore old callback */ ,
                         NULL /* ignore old callback argument */);

    event_assign(ev, base, fd, events, new_callback, new_callback_arg);
    return 0;
}

查找当前正在运行的事件

出于调试或其他目的,您可以获得指向当前正在运行的事件的指针。

struct event *event_base_get_running_event(struct event_base *base);

请注意,此函数的行为仅在从提供的 event_base 循环中调用时才定义。不支持从另一个线程调用它,并且可能导致未定义的行为。

配置一次性事件

如果你不需要多次添加一个事件,或者一旦添加就删除它,并且它不必是持久的,你可以使用 event_base_once()

int event_base_once(struct event_base *, evutil_socket_t, short,
  void (*)(evutil_socket_t, short, void *), void *, const struct timeval *);

该函数的接口与event_new()相同,只是它不支持 EV_SIGNAL 或 EV_PERSIST。计划事件以默认优先级插入和运行。当回调最终完成时,Libevent 释放内部事件结构本身。成功时返回值为 0,失败时返回 -1。

使用 event_base_once 插入的事件无法删除或手动激活:如果您希望能够取消事件,请使用常规的 event_new()event_assign() 接口创建它。

手动激活事件

极少情况下,即使事件的条件尚未触发,您也可能希望使变为active状态。

void event_active(struct event *ev, int what, short ncalls);

该函数以what的标志位(EV_READ, EV_WRITE, and EV_TIMEOUT的位与)来使一个event变为active状态。事件不需要之前处于pending状态,并且激活它不会使其处于pending状态。

警告:对同一事件递归地调用event_active()函数可能会导致资源耗尽。下面的代码片段展示了这一情况。

struct event *ev;

static void cb(int sock, short which, void *arg) {
        /* Whoops: Calling event_active on the same event unconditionally
           from within its callback means that no other events might not get
           run! */

        event_active(ev, EV_WRITE, 0);
}

int main(int argc, char **argv) {
        struct event_base *base = event_base_new();

        ev = event_new(base, -1, EV_PERSIST | EV_READ, cb, NULL);

        event_add(ev, NULL);

        event_active(ev, EV_WRITE, 0);

        event_base_loop(base, 0);

        return 0;
}

这会造成事件循环只执行一次并永远调用函数cb的情况。

可用定时器解决上述问题:

struct event *ev;
struct timeval tv;

static void cb(int sock, short which, void *arg) {
   if (!evtimer_pending(ev, NULL)) {
       event_del(ev);
       evtimer_add(ev, &tv);
   }
}

int main(int argc, char **argv) {
   struct event_base *base = event_base_new();

   tv.tv_sec = 0;
   tv.tv_usec = 0;

   ev = evtimer_new(base, cb, NULL);

   evtimer_add(ev, &tv);

   event_base_loop(base, 0);

   return 0;
}

也可使用event_config_set_max_dispatch_interval()解决上述问题:

struct event *ev;

static void cb(int sock, short which, void *arg) {
        event_active(ev, EV_WRITE, 0);
}

int main(int argc, char **argv) {
        struct event_config *cfg = event_config_new();
        /* Run at most 16 callbacks before checking for other events. */
        event_config_set_max_dispatch_interval(cfg, NULL, 16, 0);
        struct event_base *base = event_base_new_with_config(cfg);
        ev = event_new(base, -1, EV_PERSIST | EV_READ, cb, NULL);

        event_add(ev, NULL);

        event_active(ev, EV_WRITE, 0);

        event_base_loop(base, 0);

        return 0;
}

优化common timeouts

当前版本的 Libevent 使用二叉堆算法来跟踪挂起事件的超时。二叉堆为添加和删除每个事件超时提供了 O(lg n) 的性能。如果您添加具有随机分布的超时值集的事件,这是最佳选择,但如果您有大量具有相同超时值的事件,则不是这样。

例如,假设您有一万个事件,每个事件都应在添加5秒后触发其超时。在这种情况下,您可以通过使用双向链接队列实现为每次超时获得 O(1) 性能。

自然,您不会希望为所有超时值使用队列,因为只有对于恒定超时值,队列才会更快。如果某些超时或多或少是随机分布的,那么将这些超时之一添加到队列将花费 O(n) 时间,这将比二叉堆要糟糕得多。

Libevent 允许您通过将一些超时放在队列中,将其他超时放在二叉堆中来解决这个问题。为此,您需要向 Libevent 请求一个特殊的common timeout时间值,然后您可以使用它来添加具有该时间值的事件。如果您有大量具有单个公共超时的事件,则使用此优化应该可以提高超时性能。

const struct timeval *event_base_init_common_timeout(
    struct event_base *base, const struct timeval *duration);

此函数将 event_base 和要初始化的公共超时的持续时间作为其参数。它返回一个指向特殊结构 timeval 的指针,您可以使用该指针指示应将事件添加到 O(1) 队列而不是 O(lg n) 堆。这个特殊的时间值可以在你的代码中自由复制或分配。它仅适用于您用来构建它的特定基础。不要依赖它的实际内容:Libevent 使用它们来告诉自己使用哪个队列。

#include <event2/event.h>
#include <string.h>

/* We're going to create a very large number of events on a given base,
 * nearly all of which have a ten-second timeout.  If initialize_timeout
 * is called, we'll tell Libevent to add the ten-second ones to an O(1)
 * queue. */
struct timeval ten_seconds = { 10, 0 };

void initialize_timeout(struct event_base *base)
{
    struct timeval tv_in = { 10, 0 };
    const struct timeval *tv_out;
    tv_out = event_base_init_common_timeout(base, &tv_in);
    memcpy(&ten_seconds, tv_out, sizeof(struct timeval));
}

int my_event_add(struct event *ev, const struct timeval *tv)
{
    /* Note that ev must have the same event_base that we passed to
       initialize_timeout */
    if (tv && tv->tv_sec == 10 && tv->tv_usec == 0)
        return event_add(ev, &ten_seconds);
    else
        return event_add(ev, tv);
}

与所有优化函数一样,您应该避免使用 common_timeout 功能,除非您非常确定它对您很重要。

区分正常的event

Libevent 提供了一些函数,您可以使用这些函数将initialized的event与内存被复位的event区分开(例如,通过使用 calloc() 分配它或使用 memset() bzero() 清除它)。

int event_initialized(const struct event *ev);

#define evsignal_initialized(ev) event_initialized(ev)
#define evtimer_initialized(ev) event_initialized(ev)

警告:

这些函数无法可靠地区分initialized的事件和uninitialized的内存。除非您知道有问题的内存已清除或为initialized事件,否则不应使用它们。

一般来说,你不需要使用这些函数。 event_new() 返回的事件总是被初始化。

#include <event2/event.h>
#include <stdlib.h>

struct reader {
    evutil_socket_t fd;
};

#define READER_ACTUAL_SIZE() \
    (sizeof(struct reader) + \
     event_get_struct_event_size())

#define READER_EVENT_PTR(r) \
    ((struct event *) (((char*)(r))+sizeof(struct reader)))

struct reader *allocate_reader(evutil_socket_t fd)
{
    struct reader *r = calloc(1, READER_ACTUAL_SIZE());
    if (r)
        r->fd = fd;
    return r;
}

void readcb(evutil_socket_t, short, void *);
int add_reader(struct reader *r, struct event_base *b)
{
    struct event *ev = READER_EVENT_PTR(r);
    if (!event_initialized(ev))
        event_assign(ev, b, r->fd, EV_READ, readcb, r);
    return event_add(ev, NULL);
}

标签: libevent

添加新评论