1. 程式人生 > >libevent原始碼分析(8)--2.1.8--事件申請與釋放

libevent原始碼分析(8)--2.1.8--事件申請與釋放

一、event_new

主要用來建立事件結構體,根據監聽事件型別,檔案描述符,以及回撥函式,回撥函式引數等建立,可以看成是事件的初始化過程,主要是設定事件的初始狀態,此時事件結構體剛剛創建出來還沒有新增到event_base的啟用或者等待列表中,是孤立存在的,需要呼叫event_add函式將此事件新增到event_base中。

/**
  Allocate and asssign a new event structure, ready to be added.
  The function event_new() returns a new event that can be used in
  future calls to event_add() and event_del().  The fd and events
  arguments determine which conditions will trigger the event; the
  callback and callback_arg arguments tell Libevent what to do when the
  event becomes active.

  If events contains one of EV_READ, EV_WRITE, or EV_READ|EV_WRITE, then
  fd is a file descriptor or socket that should get monitored for
  readiness to read, readiness to write, or readiness for either operation
  (respectively).  If events contains EV_SIGNAL, then fd is a signal
  number to wait for.  If events contains none of those flags, then the
  event can be triggered only by a timeout or by manual activation with
  event_active(): In this case, fd must be -1.

  The EV_PERSIST flag can also be passed in the events argument: it makes
  event_add() persistent until event_del() is called.
  The EV_ET flag is compatible with EV_READ and EV_WRITE, and supported
  only by certain backends.  It tells Libevent to use edge-triggered
  events.
  The EV_TIMEOUT flag has no effect here.
  It is okay to have multiple events all listening on the same fds; but
  they must either all be edge-triggered, or all not be edge triggerd.
  When the event becomes active, the event loop will run the provided
  callbuck function, with three arguments.  The first will be the provided
  fd value.  The second will be a bitfield of the events that triggered:
  EV_READ, EV_WRITE, or EV_SIGNAL.  Here the EV_TIMEOUT flag indicates
  that a timeout occurred, and EV_ET indicates that an edge-triggered
  event occurred.  The third event will be the callback_arg pointer that
  you provide.

  @param base the event base to which the event should be attached.
  @param fd the file descriptor or signal to be monitored, or -1.
  @param events desired events to monitor: bitfield of EV_READ, EV_WRITE,
      EV_SIGNAL, EV_PERSIST, EV_ET.
  @param callback callback function to be invoked when the event occurs
  @param callback_arg an argument to be passed to the callback function
  @return a newly allocated struct event that must later be freed with
    event_free().
  @see event_free(), event_add(), event_del(), event_assign()
 */
// 分配新的event結構,並準備好新增。函式event_new返回新的事件,可以用在
// event_add和event_del函式中。fd和events引數決定了什麼條件可以觸發這個事件;
// 回撥函式和回撥函式引數告訴libevent當event啟用時需要做什麼。
// 如果events包括EV_READ,EV_WRITE,EV_READ|EV_WRITE之一,那麼fd是檔案描述符或者socket,
// 這兩個都是可以在讀、寫條件下被監控的。如果events包含EV_SIGNAL,那麼fd是等待的訊號值。
// 如果events不包含上述任何一種,那麼事件是定時事件或者通過event_active進行人工觸發:如果是
// 這種情況,fd必需是-1。
// EV_PERSIST可以通過events引數傳遞:它使得event_add永久生效,直到event_del呼叫;
// EV_ET與EV_READ以及EV_WRITE是相容的,並且只能被部分後臺方法支援。它告訴libevent使用邊沿觸發事件;
// EV_TIMEOUT此處沒有影響;
// 在同一個fd上監聽多個events是合法的;但是它們必需都是邊沿觸發,或者都不是邊沿觸發。
// 當事件啟用時,event loop將執行提供的回撥函式,就使用這三個引數:第一個就是提供的fd;
// 第二個是觸發的events,EV_READ,EV_WRITE,EV_SIGNAL,EV_TIMEOUT表示超時事件發生,EV_ET
// 表示邊沿觸發事件發生;第三個引數是提供的callback_arg指標。
// base:需要繫結的base
// fd:需要監聽的檔案描述符或者訊號,或者為-1,如果為-1則是定時事件
// events:事件型別,訊號事件和IO事件不能同時存在
// callback:回撥函式,訊號發生或者IO事件或者定時事件發生時
// callback_arg:傳遞給回撥函式的引數,一般是base
// 返回值:是指事件結構體,必須由event_free釋放
// 相關檢視event_free,event_add,event_del,event_assign
struct event *
event_new(struct event_base *base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void *), void *arg)
{
    struct event *ev;
    ev = mm_malloc(sizeof(struct event));
    if (ev == NULL)
        return (NULL);
    if (event_assign(ev, base, fd, events, cb, arg) < 0) {
        mm_free(ev);
        return (NULL);
    }

    return (ev);
}




二、event_assign
準備將新建立的event新增到event_base中,注意,這裡的新增到event_base中只是將事件和event_base關聯起來,並不是加入到event_base的啟用佇列或者等待佇列中。

/**
  Prepare a new, already-allocated event structure to be added.
  The function event_assign() prepares the event structure ev to be used
  in future calls to event_add() and event_del().  Unlike event_new(), it
  doesn't allocate memory itself: it requires that you have already
  allocated a struct event, probably on the heap.  Doing this will
  typically make your code depend on the size of the event structure, and
  thereby create incompatibility with future versions of Libevent.
  The easiest way to avoid this problem is just to use event_new() and
  event_free() instead.
  A slightly harder way to future-proof your code is to use
  event_get_struct_event_size() to determine the required size of an event
  at runtime.
  Note that it is NOT safe to call this function on an event that is
  active or pending.  Doing so WILL corrupt internal data structures in
  Libevent, and lead to strange, hard-to-diagnose bugs.  You _can_ use
  event_assign to change an existing event, but only if it is not active
  or pending!
  The arguments for this function, and the behavior of the events that it
  makes, are as for event_new().
  @param ev an event struct to be modified
  @param base the event base to which ev should be attached.
  @param fd the file descriptor to be monitored
  @param events desired events to monitor; can be EV_READ and/or EV_WRITE
  @param callback callback function to be invoked when the event occurs
  @param callback_arg an argument to be passed to the callback function
  @return 0 if success, or -1 on invalid arguments.
  @see event_new(), event_add(), event_del(), event_base_once(),
    event_get_struct_event_size()
  */
// 準備新的已經分配的事件結構體以新增到event_base中;
// 函式event_assign準備事件結構體ev,以備將來event_add和event_del呼叫。
// 不像event_new,他沒有自己分配記憶體:他需要你已經分配了一個事件結構體,應該是在堆上分配的;
// 這樣做一般會讓你的程式碼依賴於事件結構體的尺寸,並且會造成與未來版本不相容。
// 避免自己分配出現問題的最簡單的辦法是使用event_new和event_free來申請和釋放。
// 讓程式碼未來相容的稍微困難的方式是使用event_get_struct_event_size來確定執行時事件的尺寸。
// 注意,如果事件處於啟用或者未決狀態,呼叫這個函式是不安全的。這樣會破壞libevent內部資料結構,
// 導致一些奇怪的,難以調查的問題。你只能使用event_assign來改變一個存在的事件,但是事件狀態不能
// 是啟用或者未決的。
// ev:需要修正的事件結構體
// base: 將ev新增到的event_base
// fd: 需要監控的檔案描述符
// events: 需要監控的事件型別;可以是EV_READ或者EV_WRITE
// callback:當事件發生時的回撥函式
// callback_arg: 傳遞給回撥函式的引數,一般是event_base控制代碼
// 如果成功則返回0,如果失敗則返回-1
// 相關檢視 event_new,event_add,event_del,event_base_once,event_get_struct_event_size

int
event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg)
{
    if (!base)
        base = current_base;
    if (arg == &event_self_cbarg_ptr_)
        arg = ev;

    event_debug_assert_not_added_(ev);

     // 事件結構體初始設定
    ev->ev_base = base;

    ev->ev_callback = callback;
    ev->ev_arg = arg;
    ev->ev_fd = fd;
    ev->ev_events = events;
    ev->ev_res = 0;
    ev->ev_flags = EVLIST_INIT;
    ev->ev_ncalls = 0;
    ev->ev_pncalls = NULL;

     // 如果是訊號事件
    if (events & EV_SIGNAL) {
          // 訊號事件與IO事件不能同時存在
        if ((events & (EV_READ|EV_WRITE|EV_CLOSED)) != 0) {
            event_warnx("%s: EV_SIGNAL is not compatible with "
                "EV_READ, EV_WRITE or EV_CLOSED", __func__);
            return -1;
        }
          // 設定事件關閉時執行回撥函式的型別:evcb_callback
        ev->ev_closure = EV_CLOSURE_EVENT_SIGNAL;
    } else {
     // 如果是其它型別的事件:IO事件、定時事件

          // 如果事件是永久事件,即每次呼叫之後不會移除出事件列表
          // 清空IO超時控制,並設定事件關閉時回撥函式型別:evcb_callback
        if (events & EV_PERSIST) {
            evutil_timerclear(&ev->ev_io_timeout);
            ev->ev_closure = EV_CLOSURE_EVENT_PERSIST;
        } else {
               // 設定事件回撥函式型別:evcb_callback
            ev->ev_closure = EV_CLOSURE_EVENT;
        }
    }

     // 事件超時控制最小堆初始化
    min_heap_elem_init_(ev);

     // 預設情況下,事件優先順序設定為中間優先順序
    if (base != NULL) {
        /* by default, we put new events into the middle priority */
        ev->ev_pri = base->nactivequeues / 2;
    }

    event_debug_note_setup_(ev);

    return 0;
}


三、event_free

釋放由event_new申請的事件,可以看出原始碼中是呼叫事件刪除函式event_del將事件從event_base中刪除,然後呼叫內部空間釋放函式mm_free釋放事件結構體的空間。

/**
   Deallocate a struct event * returned by event_new().
   If the event is pending or active, first make it non-pending and
   non-active.
 */
// 釋放event_new建立的事件結構體;如果事件正處於未決或者啟用狀態,首先需要將事件
// 變為非未決或者非活躍狀態
void
event_free(struct event *ev)
{
    /* This is disabled, so that events which have been finalized be a
     * valid target for event_free(). That's */
    // event_debug_assert_is_setup_(ev);

    /* make sure that this event won't be coming back to haunt us. */
     // 呼叫實際執行函式
    event_del(ev);
    event_debug_note_teardown_(ev);
    mm_free(ev);

}