libevent学习——事件循环event_loop

事件循环event_loop

文章目录

    • 运行循环
    • 停止循环
    • 时间缓存
    • 转储event_base的状态

运行循环

一旦有了一个已经注册了某些事件的 event_base, 就需要让 libevent 等待事件并且通知事件的发生。

/** @name Loop flags

    These flags control the behavior of event_base_loop().
 */
/**@{*/
/** Block until we have an active event, then exit once all active events
 * have had their callbacks run. */
#define EVLOOP_ONCE	0x01
/** Do not block: see which events are ready now, run the callbacks
 * of the highest-priority ones, then exit. */
#define EVLOOP_NONBLOCK	0x02
/** Do not exit the loop because we have no pending events.  Instead, keep
 * running until event_base_loopexit() or event_base_loopbreak() makes us
 * stop.
 */
#define EVLOOP_NO_EXIT_ON_EMPTY 0x04
/**@}*/

/**
  Wait for events to become active, and run their callbacks.

  This is a more flexible version of event_base_dispatch().

  By default, this loop will run the event base until either there are no more
  pending or active events, or until something calls event_base_loopbreak() or
  event_base_loopexit().  You can override this behavior with the 'flags'
  argument.

  @param eb the event_base structure returned by event_base_new() or
     event_base_new_with_config()
  @param flags any combination of EVLOOP_ONCE | EVLOOP_NONBLOCK
  @return 0 if successful, -1 if an error occurred, or 1 if we exited because
     no events were pending or active.
  @see event_base_loopexit(), event_base_dispatch(), EVLOOP_ONCE,
     EVLOOP_NONBLOCK
  */
EVENT2_EXPORT_SYMBOL
int event_base_loop(struct event_base *, int);

默认情况下,event_base_loop()函数运行 event_base 直到其中没有已经注册的事件为止。执行循环的时候 ,函数重复地检查是否有任何已经注册的事件被触发 (比如说,读事件 的文件描述符已经就绪,可以读取了;或者超时事件的超时时间即将到达)。如果有事件被触发,函数标记被触发的事件为 “激活的”,并且执行这些事件。
在 flags 参数中设置一个或者多个标志就可以改变 event_base_loop()的行为。如果设置了 EVLOOP_ONCE ,循环将等待某些事件成为激活的 ,执行激活的事件直到没有更多的事件可以执行,然会返回。如果设置了 EVLOOP_NONBLOCK,循环不会等待事件被触发: 循环将仅仅检测是否有事件已经就绪,可以立即触发,如果有,则执行事件的回调。
完成工作后,如果正常退出, event_base_loop()返回0;如果因为后端中的某些未处理错误而退出,则返回 -1。
为帮助理解,这里给出 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;
}

为方便起见,也可以调用

/**
   Event dispatching loop

  This loop will run the event base until either there are no more pending or
  active, or until something calls event_base_loopbreak() or
  event_base_loopexit().

  @param base the event_base structure returned by event_base_new() or
     event_base_new_with_config()
  @return 0 if successful, -1 if an error occurred, or 1 if we exited because
     no events were pending or active.
  @see event_base_loop()
 */
EVENT2_EXPORT_SYMBOL
int event_base_dispatch(struct event_base *);

event_base_dispatch()等同于没有设置标志的 event_base_loop()

// ibevent-2.1.12-stable/event.c
int
event_base_dispatch(struct event_base *event_base)
{
	return (event_base_loop(event_base, 0));
}

所以,event_base_dispatch()将一直运行,直到没有已经注册的事件了,或者调用了event_base_loopbreak()或者 event_base_loopexit()为止。

停止循环

如果想在移除所有已注册的事件之前停止活动的事件循环,可以调用两个稍有不同的函数

/**
  Exit the event loop after the specified time

  The next event_base_loop() iteration after the given timer expires will
  complete normally (handling all queued events) then exit without
  blocking for events again.

  Subsequent invocations of event_base_loop() will proceed normally.

  @param eb the event_base structure returned by event_init()
  @param tv the amount of time after which the loop should terminate,
    or NULL to exit after running all currently active events.
  @return 0 if successful, or -1 if an error occurred
  @see event_base_loopbreak()
 */
EVENT2_EXPORT_SYMBOL
int event_base_loopexit(struct event_base *, const struct timeval *);

/**
  Abort the active event_base_loop() immediately.

  event_base_loop() will abort the loop after the next event is completed;
  event_base_loopbreak() is typically invoked from this event's callback.
  This behavior is analogous to the "break;" statement.

  Subsequent invocations of event_base_loop() will proceed normally.

  @param eb the event_base structure returned by event_init()
  @return 0 if successful, or -1 if an error occurred
  @see event_base_loopexit()
 */
EVENT2_EXPORT_SYMBOL
int event_base_loopbreak(struct event_base *);

event_base_loopexit()event_base 在给定时间之后停止循环。如果 tv 参数为NULL, event_base 会立即停止循环,没有延时。
如果 event_base 当前正在执行任何激活事件的回调,则回调会继续运行,直到运行完所有激活事件的回调之才退出。
event_base_loopbreak()event_base 立即退出循环。它与event_base_loopexit(base,NULL)的不同在于,如果 event_base 当前正在执行激活事件的回调 ,它将在执行完当前正在处理的事件后立即退出。

注意 event_base_loopexit(base,NULL)event_base_loopbreak(base) 在事件循环没有运行时的行为不同:前者安排下一次事件循环在下一轮回调完成后立即停止(就好像带 EVLOOP_ONCE 标志调用一样);后者却仅仅停止当前正在运行的循环,如果事件循环没 有运行,则没有任何效果。

这两个函数都在成功时返回 0,失败时返回 -1。

实例:

#include 
/* 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);
}

实例2:执行10s的循环事件

#include 
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()的调用是正常退出的,还是因为调用 event_base_loopexit()或者 event_base_break()而退出的。可以调 用下述函数来确定是否调用了 loopexit 或者 break函数。

/**
  Checks if the event loop was told to exit by event_base_loopexit().

  This function will return true for an event_base at every point after
  event_loopexit() is called, until the event loop is next entered.

  @param eb the event_base structure returned by event_init()
  @return true if event_base_loopexit() was called on this event base,
    or 0 otherwise
  @see event_base_loopexit()
  @see event_base_got_break()
 */
EVENT2_EXPORT_SYMBOL
int event_base_got_exit(struct event_base *);

/**
  Checks if the event loop was told to abort immediately by event_base_loopbreak().

  This function will return true for an event_base at every point after
  event_base_loopbreak() is called, until the event loop is next entered.

  @param eb the event_base structure returned by event_init()
  @return true if event_base_loopbreak() was called on this event base,
    or 0 otherwise
  @see event_base_loopbreak()
  @see event_base_got_exit()
 */
EVENT2_EXPORT_SYMBOL
int event_base_got_break(struct event_base *);

这两个函数分别会在循环是因为调用 event_base_loopexit()或者event_base_break()而退出的时候返回 true,否则返回 false。下次启动事件循环的时候,这些值会被重设。
这些函数声明在中。

时间缓存

有时候需要在事件回调中获取当前时间的近似值,但不想调用 gettimeofday()(可能为了避免调用gettimeofday()产生的开销)。在回调中,可以请求 libevent 开始本轮回调时的当前时间。

/** Sets 'tv' to the current time (as returned by gettimeofday()),
    looking at the cached value in 'base' if possible, and calling
    gettimeofday() or clock_gettime() as appropriate if there is no
    cached time.

    Generally, this value will only be cached while actually
    processing event callbacks, and may be very inaccurate if your
    callbacks take a long time to execute.

    Returns 0 on success, negative on failure.
 */
EVENT2_EXPORT_SYMBOL
int event_base_gettimeofday_cached(struct event_base *base,
    struct timeval *tv);

如果当前正在执行回调,event_base_gettimeofday_cached()函数设置 tv 参数的值为缓存的时间。否则,函数调用 evutil_gettimeofday()获取真正的当前时间。成功时函数返回0,失败时返回负数。
注意,因为 libevent 在开始执行回调的时候缓存时间值,所以如果回调执行很长时间,这个值将非常不精确。

转储event_base的状态

为帮助调试程序,有时候可能需要加入到 event_base 的事件及其状态的完整列表。调用 event_base_dump_events()可以将这个列表输出到指定的文件中。

/**
   Writes a human-readable description of all inserted and/or active
   events to a provided stdio stream.

   This is intended for debugging; its format is not guaranteed to be the same
   between libevent versions.

   @param base An event_base on which to scan the events.
   @param output A stdio file to write on.
 */
EVENT2_EXPORT_SYMBOL
void event_base_dump_events(struct event_base *, FILE *);

这个列表的格式是可阅读的,但是不保证其格式在不同版本下的一致性。

你可能感兴趣的:(libevent,学习,libevent,c语言)