运行事件循环

一旦你在event_base上注册了一些events(下一章讲述如何创建并且注册events),你会想让libevent等待events并且通知你。

#define EVLOOP_ONCE             0x01
#define EVLOOP_NONBLOCK         0x02
#define EVLOOP_NO_EXIT_ON_EMPTY 0x04

int event_base_loop(struct event_base *base, int flags);

默认的,event_base_loop()函数运行event_base直到没有events在上面注册。当开始运行循环时,它不断的检测是否有events已经触发(例如,读取事件的文件描述符准备好读取,超时事件的超时到期),如果一旦发生,它把events标记为active,并且运行他们。

你可以给event_base_loop传递flags参数来改变默认的行为,如果设置了EVLOOP_ONCE,循环会等待直到有events变为active,然后运行它们,然后退出。如果设置了EVLOOP_NONBLOCK,循环不会等待events触发,它只会立即检测events是否准备好触发,如果是,则运行它们的回调。

一般地,如果没有pending或者active的events,循环会尽快退出。你可以设置EVLOOP_NO_EXIT_ON_EMPTY标志位来改变这一行为,比如,你准备在其它线程添加events,如果你设置了此标志位,循环会一直运行,直到event_base_loopbreak()event_base_loopexit()被调用,或者遇到了错误。

当结束时,event_base_loop()返回0代表正常退出,返回-1代表后端有未处理的错误,返回1代表没有更多pending或者active的events。

为了帮助理解,下面是一段event_base_loop()的伪代码

while (any events are registered with the loop,
        or EVLOOP_NO_EXIT_ON_EMPTY was set) {

    if (EVLOOP_NONBLOCK was set, or any events are already active)
        If any registered events have triggered, mark them active.
    else
        Wait until at least one event has triggered, and mark it active.

    for (p = 0; p < n_priorities; ++p) {
       if (any event with priority of p is active) {
          Run all active events with priority of p.
          break; /* Do not run any events of a less important priority */
       }
    }

    if (EVLOOP_ONCE was set or EVLOOP_NONBLOCK was set)
       break;
}

为了方便起见,你也可以调用:

int event_base_dispatch(struct event_base *base);

该函数等同于没有flags参数的event_base_loop(),因此,循环会一直运行直到没有注册的events或者event_base_loopbreak()event_base_loopexit()被调用。

这些函数在<event2/event.h>中定义。它们从 Libevent 1.0 开始就存在了。

停止事件循环

如果你想在所有的events被移除之前停止事件循环,可以调用如下两个方法:

int event_base_loopexit(struct event_base *base, const struct timeval *tv);
int event_base_loopbreak(struct event_base *base);

event_base_loopexit()函数告诉event_base在指定的时间tv后停止循环。如果tv参数为空的话,event_base立即停止循环,如果event_base当前正在运行active events的回调,则直到运行完之后才会停止。

event_base_loopbreak()函数告诉event_base立即退出循环,区别于event_base_loopexit(base, NULL)event_base_loopbreak()会在处理完当前active events之后立马退出。

还要注意event_base_loopexit(base,NULL)event_base_loopbreak(base)在没有事件循环运行时的行为不同:loopexit安排事件循环的下一个实例在下一轮回调运行后立即停止(就像它已经被调用一样与 EVLOOP_ONCE) 而loopbreak只停止当前正在运行的循环,如果事件循环没有运行,则无效。

两个函数均在成功时返回0,失败时返回-1.

下面是立马停止的例子:

#include <event2/event.h>

/* Here's a callback function that calls loopbreak */
void cb(int sock, short what, void *arg)
{
    struct event_base *base = arg;
    event_base_loopbreak(base);
}

void main_loop(struct event_base *base, evutil_socket_t watchdog_fd)
{
    struct event *watchdog_event;

    /* Construct a new event to trigger whenever there are any bytes to
       read from a watchdog socket.  When that happens, we'll call the
       cb function, which will make the loop exit immediately without
       running any other active events at all.
     */
    watchdog_event = event_new(base, watchdog_fd, EV_READ, cb, base);

    event_add(watchdog_event, NULL);

    event_base_dispatch(base);
}

下面是10秒钟后停止的例子:

#include <event2/event.h>

void run_base_with_ticks(struct event_base *base)
{
  struct timeval ten_sec;

  ten_sec.tv_sec = 10;
  ten_sec.tv_usec = 0;

  /* Now we run the event_base for a series of 10-second intervals, printing
     "Tick" after each.  For a much better way to implement a 10-second
     timer, see the section below about persistent timer events. */
  while (1) {
     /* This schedules an exit ten seconds from now. */
     event_base_loopexit(base, &ten_sec);

     event_base_dispatch(base);
     puts("Tick");
  }
}

有时你想知道调用event_base_dispatch()或者event_base_loop()退出是否正常,或者想知道是调用哪种方式退出的,你可以使用如下函数:

int event_base_got_exit(struct event_base *base);
int event_base_got_break(struct event_base *base);

如果循环分别用event_base_loopexit()event_base_break()停止,这两个函数将返回 true,否则返回 false。它们的值将在您下次启动事件循环时重置。

重新检查events

通常,Libevent 检查事件,然后运行具有最高优先级的所有活动事件,然后再次检查事件,依此类推。但有时您可能希望在当前回调运行后立即停止 Libevent,并告诉它再次扫描。与event_base_loopbreak()类似,您可以使用函数event_base_loopcontinue()执行此操作。

int event_base_loopcontinue(struct event_base *);

如果我们当前没有运行事件回调,则调用event_base_loopcontinue()无效。

检查内部时间缓存

有时,你希望在事件回调中获得当前时间,但是你不想调用gettimeofday(),因为此函数为系统调用,你正在避免使用系统调用。

在回调中,您可以向 Libevent 询问它开始执行这一轮回调时的当前时间:

int event_base_gettimeofday_cached(struct event_base *base,
    struct timeval *tv_out);

该函数如果正在执行回调,则将tv_out参数设置为缓存的时间,否则调用evutil_gettimeofday()函数返回真是的时间。该函数在成功时返回0,失败是返回负值。

注意,该时间为Libevent执行回调时的缓存的时间,所以可能不准确,如果你的回调执行了很长时间,他可能非常不准确,你可以强制刷新此缓存时间:

int event_base_update_cache_time(struct event_base *base);

该函数在成功时返回0,在失败时返回-1,如果在没有运行事件循环时调用此函数,则没有作用。

转储event_base状态

void event_base_dump_events(struct event_base *base, FILE *f);

为了协助调试你的程序(或者调试libevent),你有时想要所有events的完整列表以及它们的状态,调用event_base_dump_events()函数会将此列表写入你提供的文件中去。

这个列表可供阅读的,它的格式在未来的libevent中会改变。

在 event_base 中的每个事件上运行一个函数

typedef int (*event_base_foreach_event_cb)(const struct event_base *,
    const struct event *, void *);

int event_base_foreach_event(struct event_base *base,
                             event_base_foreach_event_cb fn,
                             void *arg);

您可以使用 event_base_foreach_event() 迭代与 event_base() 关联的每个active或pending的事件。提供的回调将在每个事件中以未指定的顺序被调用一次。 event_base_foreach_event()的第三个参数将作为第三个参数传递给回调的每次调用。

回调函数必须返回 0 才能继续迭代,或者返回某个其他整数才能停止迭代。回调函数最终返回的任何值都将由 event_base_foreach_function() 返回。

您的回调函数不得修改它接收到的任何事件,或向event_base添加或删除任何事件,或以其他方式修改与event_base关联的任何事件,否则可能发生未定义的行为,直至或包括崩溃和堆粉碎。

event_base锁将在调用event_base_foreach_event()的持续时间内保持不变— 这将阻止其他线程对 event_base 执行任何有用的操作,因此请确保您的回调不会花费很长时间。

标签: libevent

添加新评论