libevent源碼分析(9)--2.1.8--事件註冊和刪除

一、事件註冊-event_add
1、將事件添加到等待事件中去,需要注意的是,event_add在event_new或者event_assign之後執行,即添加的事件必須是經過基本初始化過後的事件;
2、此處添加的事件包括IO事件、信號事件、定時事件,根據事件申請時設置的事件類型決定添加的流程;
3、超時控制包括兩種方式:
     (1)最小堆:時間超時時間存儲在最小堆,每次執行超時任務都從最小堆堆頂取任務執行
     (2)最小堆+公用超時隊列:相同超時的任務存儲在同一個超時隊列,每一個超時隊列的隊首事件存儲在最小堆,每次執行超時任務時都從最小堆堆頂取任務執行,然後遍歷執行該任務所在公用超時隊列中的所有超時任務。
/**
  Add an event to the set of pending events.
  The function event_add() schedules the execution of the event 'ev' when the
  condition specified by event_assign() or event_new() occurs, or when the time
  specified in timeout has elapesed.  If atimeout is NULL, no timeout
  occurs and the function will only be
  called if a matching event occurs.  The event in the
  ev argument must be already initialized by event_assign() or event_new()
  and may not be used
  in calls to event_assign() until it is no longer pending.
  If the event in the ev argument already has a scheduled timeout, calling
  event_add() replaces the old timeout with the new one if tv is non-NULL.
  @param ev an event struct initialized via event_assign() or event_new()
  @param timeout the maximum amount of time to wait for the event, or NULL
         to wait forever
  @return 0 if successful, or -1 if an error occurred
  @see event_del(), event_assign(), event_new()
  */
// 將事件增加到一系列等待的事件中;
// 函數event_add規劃以下事情:當event_assign或者event_new函數
// 創建’ev’時指定的條件發生時,或者超時時間達到時,事件’ev’的執行情況。
// 如果超時時間爲NULL,那麼沒有超時事件發生,只能等匹配的事件發生時纔會調用回調函數;
// 參數ev中的事件必須已經通過event_assign或者event_new進行過初始化了,而且只能當事件不再處於
//  等待狀態時才能調用event_assign或者event_new函數。
// 如果ev參數中的事件有特定的超時時間,調用event_add時如果指定tc不爲NULL的話,就會替代老的超時時間;
// 參數 ev:通過event_assign或者event_new初始化過的事件
// 參數timeout:等待事件執行的最長時間,如果NULL則從來不會超時,即永久等待
// 成功則返回0,失敗則-1
// 相關查看event_del,event_assign,event_new

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

    if (EVUTIL_FAILURE_CHECK(!ev->ev_base)) {
        event_warnx("%s: event has no event_base set.", __func__);
        return -1;
    }

    EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock);
    // 實際時調用內部實現函數event_add_nolock實現的,下文會分析
    res = event_add_nolock_(ev, tv, 0);

    EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);

    return (res);
}

二、事件註冊內部實現函數-event_add_nolock_

此函數真正實現將事件添加到event_base的等待列表中。

/* Implementation function to add an event.  Works just like event_add,
 * except: 1) it requires that we have the lock.  2) if tv_is_absolute is set,
 * we treat tv as an absolute time, not as an interval to add to the current
 * time */
// 添加事件的實現函數;就像event_add一樣,異常:
// 1)它需要使用者加鎖;2)如果tv_is_absolute設置了,則將tv作爲絕對時間對待,而不是相對於當前添加時間的時間間隔

int
event_add_nolock_(struct event *ev, const struct timeval *tv,
    int tv_is_absolute)
{
    struct event_base *base = ev->ev_base;
    int res = 0;
    int notify = 0;

    EVENT_BASE_ASSERT_LOCKED(base);
    event_debug_assert_is_setup_(ev);

    event_debug((
         "event_add: event: %p (fd "EV_SOCK_FMT"), %s%s%s%scall %p",
         ev,
         EV_SOCK_ARG(ev->ev_fd),
         ev->ev_events & EV_READ ? "EV_READ " : " ",
         ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
         ev->ev_events & EV_CLOSED ? "EV_CLOSED " : " ",
         tv ? "EV_TIMEOUT " : " ",
         ev->ev_callback));

     // 事件狀態必須處於合法的某種事件狀態,否則報錯
    EVUTIL_ASSERT(!(ev->ev_flags & ~EVLIST_ALL));

     // 已經處於結束狀態的事件再次添加會報錯
    if (ev->ev_flags & EVLIST_FINALIZING) {
        /* XXXX debug */
        return (-1);
    }

    /*
     * prepare for timeout insertion further below, if we get a
     * failure on any step, we should not change any state.
     */
     // 爲超時插入做準備,如果超時控制不爲空,且事件沒有處於超時狀態
     // 首先爲將要插入的超時事件準備插入節點,主要是爲了防止後面出現這種情況:
     //  事件狀態改變已經完成,但是最小堆申請節點卻失敗;
     // 因此,如果在任何一步出現錯誤,都不能改變事件狀態,這是前提條件。
    if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {
        if (min_heap_reserve_(&base->timeheap,
            1 + min_heap_size_(&base->timeheap)) == -1)
            return (-1);  /* ENOMEM == errno */
    }

    /* If the main thread is currently executing a signal event's
     * callback, and we are not the main thread, then we want to wait
     * until the callback is done before we mess with the event, or else
     * we can race on ev_ncalls and ev_pncalls below. */
     // 如果主線程當前正在執行信號事件的回調函數,同時又不在主線程,則
     // 需要等待回調函數執行完畢才能繼續添加事件,或者可能會在
     // ev_ncalls和ev_pncalls上產生競爭。
#ifndef EVENT__DISABLE_THREAD_SUPPORT
    if (base->current_event == event_to_event_callback(ev) &&
        (ev->ev_events & EV_SIGNAL)
        && !EVBASE_IN_THREAD(base)) {
        ++base->current_event_waiters;
        EVTHREAD_COND_WAIT(base->current_event_cond, base->th_base_lock);
    }
#endif

     // 如果事件類型是IO事件/信號事件,同時事件狀態不是已經插入/激活/下一次激活狀態,
     // 則根據事件類型將事件添加到不同的映射表或者隊列中
    if ((ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED|EV_SIGNAL)) &&
        !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))) {
          // 如果事件是IO事件,則將事件插入到IO事件與文件描述符的映射表中
        if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED))
            res = evmap_io_add_(base, ev->ev_fd, ev);
          // 如果事件是信號事件,則將事件插入信號與文件描述符的映射表中
        else if (ev->ev_events & EV_SIGNAL)
            res = evmap_signal_add_(base, (int)ev->ev_fd, ev);
          // 如果上述添加行爲正確,則將事件插入到event_base的事件列表中
        if (res != -1)
            event_queue_insert_inserted(base, ev);
          // 如果上述添加行爲正確,則設置通知主線程的標誌,因爲已經添加了新事件,
          // 防止1)優先級高的事件被優先級低的事件倒掛,2)防止主線程忙等,會通知主線程有新事件
        if (res == 1) {
            /* evmap says we need to notify the main thread. */
            notify = 1;
            res = 0;
        }
    }

    /*
     * we should change the timeout state only if the previous event
     * addition succeeded.
     */
     // 只有當前面事件條件成功執行之後,才能改變超時狀態
    if (res != -1 && tv != NULL) {
        struct timeval now;
        int common_timeout;
#ifdef USE_REINSERT_TIMEOUT
        int was_common;
        int old_timeout_idx;
#endif

        /*
         * for persistent timeout events, we remember the
         * timeout value and re-add the event.
         *
         * If tv_is_absolute, this was already set.
         */
          // 對於持久化的定時事件,需要記住超時時間,並重新註冊事件
          //  如果tv_is_absolute設置,則事件超時時間就等於輸入時間參數
        if (ev->ev_closure == EV_CLOSURE_EVENT_PERSIST && !tv_is_absolute)
            ev->ev_io_timeout = *tv;

// 如果沒有使用USE_REINSERT_TIMEOUT,則當事件處於超時狀態時,需要從隊列中移除事件
// 因爲同樣的事件不能重新插入,所以當一個事件已經處於超時狀態時,爲防止執行,需要先移除後插入
#ifndef USE_REINSERT_TIMEOUT
        if (ev->ev_flags & EVLIST_TIMEOUT) {
            event_queue_remove_timeout(base, ev);
        }
#endif

        /* Check if it is active due to a timeout.  Rescheduling
         * this timeout before the callback can be executed
         * removes it from the active list. */
          // 檢查事件當前狀態是否已經激活,而且是超時事件的激活狀態,
          // 則在回調函數執行之前,需要重新調度這個超時事件,因此需要把它移出激活隊列
        if ((ev->ev_flags & EVLIST_ACTIVE) &&
            (ev->ev_res & EV_TIMEOUT)) {
            if (ev->ev_events & EV_SIGNAL) {
                /* See if we are just active executing
                 * this event in a loop
                 */
                if (ev->ev_ncalls && ev->ev_pncalls) {
                    /* Abort loop */
                    *ev->ev_pncalls = 0;
                }
            }
               // 將此事件的回調函數從激活隊列中移除
            event_queue_remove_active(base, event_to_event_callback(ev));
        }

          // 獲取base中的緩存時間
        gettime(base, &now);

          // 檢查base是否使用了公用超時隊列機制
        common_timeout = is_common_timeout(tv, base);
#ifdef USE_REINSERT_TIMEOUT
        was_common = is_common_timeout(&ev->ev_timeout, base);
        old_timeout_idx = COMMON_TIMEOUT_IDX(&ev->ev_timeout);
#endif

          // 1)如果設置絕對超時時間,則設置時間超時時間爲輸入時間參數
          // 2)如果使用的公用超時隊列機制,則根據當前base中時間和輸入超時時間間隔計算出時間超時時間,並對超時時間進行公用超時掩碼計算
          // 3)如果是其他情況,則直接根據base中時間和輸入超時時間間隔計算事件的超時時間
        if (tv_is_absolute) {
            ev->ev_timeout = *tv;
        } else if (common_timeout) {
            struct timeval tmp = *tv;
            tmp.tv_usec &= MICROSECONDS_MASK;
            evutil_timeradd(&now, &tmp, &ev->ev_timeout);
            ev->ev_timeout.tv_usec |=
                (tv->tv_usec & ~MICROSECONDS_MASK);
        } else {
            evutil_timeradd(&now, tv, &ev->ev_timeout);
        }

        event_debug((
             "event_add: event %p, timeout in %d seconds %d useconds, call %p",
             ev, (int)tv->tv_sec, (int)tv->tv_usec, ev->ev_callback));

     // 將事件插入超時隊列
#ifdef USE_REINSERT_TIMEOUT
          // event_queue_reinsert_timeout會插入兩個隊列:一個是公用超時隊列,一個超時隊列
        event_queue_reinsert_timeout(base, ev, was_common, common_timeout, old_timeout_idx);
#else
          // 只會插入超時隊列
        event_queue_insert_timeout(base, ev);
#endif

          // 如果使用了公用超時隊列機制,則需要根據當前事件的超時時間將當前事件插入具有相同超時時間的時間列表
        if (common_timeout) {
               // 根據事件超時時間獲取應該插入的公用超時隊列,注意此處是從隊尾插入
            struct common_timeout_list *ctl =
                get_common_timeout_list(base, &ev->ev_timeout);
               // 如果當前事件是公用超時隊列的第一個事件,則因此需要將此超時事件插入最小堆
               // 解釋:公用超時隊列機制:處於同一個公用超時隊列中的所有事件具有相同的超時控制,因此只需要將公用超時隊列
               //  的第一個事件插入最小堆,當超時觸發時,可以通過遍歷公用超時隊列獲取同樣的超時事件。
            if (ev == TAILQ_FIRST(&ctl->events)) {
                common_timeout_schedule(ctl, &now, ev);
            }
        } else {
               // 如果沒有使用公用超時隊列,則調整最小堆
            struct event* top = NULL;
            /* See if the earliest timeout is now earlier than it
             * was before: if so, we will need to tell the main
             * thread to wake up earlier than it would otherwise.
             * We double check the timeout of the top element to
             * handle time distortions due to system suspension.
             */
               // 查看當前事件是否位於最小堆根部,如果是,則需要通知主線程
               // 否則,需要查看最小堆根部超時時間是否已經小於當前時間,即已經超時了,如果是,則需要通知主線程
            if (min_heap_elt_is_top_(ev))
                notify = 1;
            else if ((top = min_heap_top_(&base->timeheap)) != NULL &&
                     evutil_timercmp(&top->ev_timeout, &now, <))
                notify = 1;
        }
    }

    /* if we are not in the right thread, we need to wake up the loop */
     // 通知主線程
    if (res != -1 && notify && EVBASE_NEED_NOTIFY(base))
        evthread_notify_base(base);

    event_debug_note_add_(ev);

    return (res);
}


三、IO事件註冊:evmap_io_add_
因爲同一個文件描述符上可能會註冊多個IO事件,因此base內部維護一個文件描述符和事件之間的映射表,此函數完成將事件和文件描述符映射。
/* return -1 on error, 0 on success if nothing changed in the event backend,
 * and 1 on success if something did. */
// 如果沒有任何改變的話,則返回0;如果某些東西改變了,則返回1;錯誤則返回-1
int
evmap_io_add_(struct event_base *base, evutil_socket_t fd, struct event *ev)
{
     // 後臺方法
    const struct eventop *evsel = base->evsel;
     // IO事件和文件描述符的映射
    struct event_io_map *io = &base->io;
    struct evmap_io *ctx = NULL;
    int nread, nwrite, nclose, retval = 0;
    short res = 0, old = 0;
    struct event *old_ev;

    EVUTIL_ASSERT(fd == ev->ev_fd);

    if (fd < 0)
        return 0;

// 如果不使用EVMAP_USE_HT,即非win32下
#ifndef EVMAP_USE_HT
     // 如果文件描述符大於項目數,則需要爲新描述符創建空間
    if (fd >= io->nentries) {
          // 此函數爲新添加的fd開闢空間,io中entries是二級指針,存放的是具體entry的地址,爲了達到快速查找的目的,
          // 此處申請的空間大小爲最大的fd,fd存放的位置就是相對於entries起始位置的便宜,即將fd作爲二級指針的偏移量
          // 因此可能會浪費一些空間,通過空間換時間來提高查找速度
        if (evmap_make_space(io, fd, sizeof(struct evmap_io *)) == -1)
            return (-1);
    }
#endif

     // 如果不使用EVMAP_USE_HT的情況下,
     // #define GET_IO_SLOT_AND_CTOR(x,map,slot,type,ctor,fdinfo_len)    \
    // GET_SIGNAL_SLOT_AND_CTOR(x,map,slot,type,ctor,fdinfo_len)
     // 獲取IO映射的槽以及存儲的環境變量
     // 其實就是根據fd,從evmap_io中獲取該fd上註冊的事件集合,因爲同一個fd上可以註冊多個事件。
    GET_IO_SLOT_AND_CTOR(ctx, io, fd, evmap_io, evmap_io_init,
                         evsel->fdinfo_len);

     // 獲取原有事件的讀事件個數、寫事件個數、關閉事件個數
    nread = ctx->nread;
    nwrite = ctx->nwrite;
    nclose = ctx->nclose;

     // old變量用來存儲原有事件類型
     // 如果讀、寫、關閉事件個數不爲0,則具有該事件類型
    if (nread)
        old |= EV_READ;
    if (nwrite)
        old |= EV_WRITE;
    if (nclose)
        old |= EV_CLOSED;

     // res變量保存當前新增的事件類型,注意是新增的,而不是所有的
     // 如果當前事件類型包含讀事件,則如果原來沒有讀事件類型,則將新增讀事件類型保存到res中;
     // 如果當前事件類型包含寫事件,則如果原來沒有寫事件類型,則將新增寫事件類型保存到res中;
     // 如果當前事件類型包含關閉事件,則如果原來沒有關閉事件類型,則將新增關閉事件類型保存到res中;
    if (ev->ev_events & EV_READ) {
        if (++nread == 1)
            res |= EV_READ;
    }
    if (ev->ev_events & EV_WRITE) {
        if (++nwrite == 1)
            res |= EV_WRITE;
    }
    if (ev->ev_events & EV_CLOSED) {
        if (++nclose == 1)
            res |= EV_CLOSED;
    }
     // 如果讀、寫、關閉事件註冊次數大於65535,則報錯
    if (EVUTIL_UNLIKELY(nread > 0xffff || nwrite > 0xffff || nclose > 0xffff)) {
        event_warnx("Too many events reading or writing on fd %d",
            (int)fd);
        return -1;
    }
    if (EVENT_DEBUG_MODE_IS_ON() &&
        (old_ev = LIST_FIRST(&ctx->events)) &&
        (old_ev->ev_events&EV_ET) != (ev->ev_events&EV_ET)) {
        event_warnx("Tried to mix edge-triggered and non-edge-triggered"
            " events on fd %d", (int)fd);
        return -1;
    }

     // 如果有新增事件類型,則將新增事件類型註冊到後臺方法
    if (res) {
        void *extra = ((char*)ctx) + sizeof(struct evmap_io);
        /* XXX(niels): we cannot mix edge-triggered and
         * level-triggered, we should probably assert on
         * this. */
        if (evsel->add(base, ev->ev_fd,
            old, (ev->ev_events & EV_ET) | res, extra) == -1)
            return (-1);
        retval = 1;
    }

    ctx->nread = (ev_uint16_t) nread;
    ctx->nwrite = (ev_uint16_t) nwrite;
    ctx->nclose = (ev_uint16_t) nclose;
     // 將事件插入到環境變量的隊列中,從頭部插入
    LIST_INSERT_HEAD(&ctx->events, ev, ev_io_next);

    return (retval);
}




四、信號事件註冊:evmap_signal_add_
由於同一個信號上可能會註冊多個事件,因此,base內部會維護一份信號到事件的映射表,此函數完成事件到信號的映射
int
evmap_signal_add_(struct event_base *base, int sig, struct event *ev)
{
     // 獲取信號事件處理的後臺方法以及信號與事件映射的map
    const struct eventop *evsel = base->evsigsel;
    struct event_signal_map *map = &base->sigmap;
    struct evmap_signal *ctx = NULL;

     // 如果註冊的信號大於映射表中的項目個數,則需要爲新註冊信號申請空間
    if (sig >= map->nentries) {
          // 此函數爲新添加的sig開闢空間,io中entries是二級指針,存放的是具體entry的地址,爲了達到快速查找的目的,
          // 此處申請的空間大小爲最大的sig, sig存放的位置就是相對於entries起始位置的便宜,即將sig作爲二級指針的偏移量
          // 因此可能會浪費一些空間,通過空間換時間來提高查找速度
        if (evmap_make_space(
            map, sig, sizeof(struct evmap_signal *)) == -1)
            return (-1);
    }

     // 根據信號獲取信號與事件映射的環境變量ctx
    GET_SIGNAL_SLOT_AND_CTOR(ctx, map, sig, evmap_signal, evmap_signal_init,
        base->evsigsel->fdinfo_len);

     // 如果此信號下映射的事件爲空,則將信號與文件描述符註冊到後臺方法中
     // evsel->add實際上調用的是base->evsigsel->add函數,查看evsig_init函數中初始化過程
     // 得知,實際上調用的是evsig_add函數
     // 這裏就把外部信號添加到信號捕捉函數中了
    if (LIST_EMPTY(&ctx->events)) {
        if (evsel->add(base, ev->ev_fd, 0, EV_SIGNAL, NULL)
            == -1)
            return (-1);
    }

     // 將新註冊的信號事件插入到環境變量頭部
    LIST_INSERT_HEAD(&ctx->events, ev, ev_signal_next);

    return (1);
}




五、事件刪除event_del
這裏只是刪除,並不是釋放事件空間
/**
  Remove an event from the set of monitored events.
  The function event_del() will cancel the event in the argument ev.  If the
  event has already executed or has never been added the call will have no
  effect.
  @param ev an event struct to be removed from the working set
  @return 0 if successful, or -1 if an error occurred
  @see event_add()
 */
// 從一系列監聽的事件中移除事件,函數event_del將取消參數ev中的event。如果event
// 已經執行或者還沒有添加成功,則此調用無效
int
event_del(struct event *ev)
{
    return event_del_(ev, EVENT_DEL_AUTOBLOCK);
}


六、內部刪除事件函數:event_del_

static int
event_del_(struct event *ev, int blocking)
{
    int res;

    if (EVUTIL_FAILURE_CHECK(!ev->ev_base)) {
        event_warnx("%s: event has no event_base set.", __func__);
        return -1;
    }

    EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock);

    res = event_del_nolock_(ev, blocking);

    EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);

    return (res);
}


七、event_del_nolock_
事件刪除的內部實現,不加鎖;
需要根據事件狀態判斷是否能夠執行刪除行爲;
需要根據事件類型決定刪除的具體操作;
/** Helper for event_del: always called with th_base_lock held.
 *
 * "blocking" must be one of the EVENT_DEL_{BLOCK, NOBLOCK, AUTOBLOCK,
 * EVEN_IF_FINALIZING} values. See those for more information.
 */
int
event_del_nolock_(struct event *ev, int blocking)
{
    struct event_base *base;
    int res = 0, notify = 0;

    event_debug(("event_del: %p (fd "EV_SOCK_FMT"), callback %p",
        ev, EV_SOCK_ARG(ev->ev_fd), ev->ev_callback));

    /* An event without a base has not been added */
    if (ev->ev_base == NULL)
        return (-1);

    EVENT_BASE_ASSERT_LOCKED(ev->ev_base);

     // 如果事件已經處於結束中的狀態,則不需要重複刪除
    if (blocking != EVENT_DEL_EVEN_IF_FINALIZING) {
        if (ev->ev_flags & EVLIST_FINALIZING) {
            /* XXXX Debug */
            return 0;
        }
    }

    base = ev->ev_base;

    EVUTIL_ASSERT(!(ev->ev_flags & ~EVLIST_ALL));

    /* See if we are just active executing this event in a loop */
     // 如果是信號事件,同時信號觸發事件不爲0,則放棄執行這些回調函數
    if (ev->ev_events & EV_SIGNAL) {
        if (ev->ev_ncalls && ev->ev_pncalls) {
            /* Abort loop */
            *ev->ev_pncalls = 0;
        }
    }

     // 如果事件狀態處於已超時狀態
     // 從來不需要因爲一個已刪除的超時事件而通知主線程:即使我們不通知主線程,可能會發生的就是調度loop會過早的喚醒;
     // 但是通知主線程的點是儘可能早地喚醒調度loop,因此即使通知也不會獲得更好的性能。
     // 從超時隊列中移除事件
    if (ev->ev_flags & EVLIST_TIMEOUT) {
        /* NOTE: We never need to notify the main thread because of a
         * deleted timeout event: all that could happen if we don't is
         * that the dispatch loop might wake up too early.  But the
         * point of notifying the main thread _is_ to wake up the
         * dispatch loop early anyway, so we wouldn't gain anything by
         * doing it.
         */
        event_queue_remove_timeout(base, ev);
    }

     // 如果事件正處於激活狀態,則需要將事件的回調函數從激活隊列中刪除
     // 如果事件正處於下一次激活狀態,則需要將事件的回調函數從下一次激活隊列中刪除
    if (ev->ev_flags & EVLIST_ACTIVE)
        event_queue_remove_active(base, event_to_event_callback(ev));
    else if (ev->ev_flags & EVLIST_ACTIVE_LATER)
        event_queue_remove_active_later(base, event_to_event_callback(ev));

     // 如果事件正處於已插入狀態,則需要將事件從已插入狀態隊列中刪除
    if (ev->ev_flags & EVLIST_INSERTED) {
        event_queue_remove_inserted(base, ev);
          // 如果事件是IO事件,則將事件從IO映射中刪除
          // 如果是信號事件,則將信號從信號映射刪除
          // 如果刪除正確,則需要通知主線程
        if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED))
            res = evmap_io_del_(base, ev->ev_fd, ev);
        else
            res = evmap_signal_del_(base, (int)ev->ev_fd, ev);
        if (res == 1) {
            /* evmap says we need to notify the main thread. */
            notify = 1;
            res = 0;
        }
    }

    /* if we are not in the right thread, we need to wake up the loop */
    if (res != -1 && notify && EVBASE_NEED_NOTIFY(base))
        evthread_notify_base(base);

    event_debug_note_del_(ev);

    /* If the main thread is currently executing this event's callback,
     * and we are not the main thread, then we want to wait until the
     * callback is done before returning. That way, when this function
     * returns, it will be safe to free the user-supplied argument.
     */
#ifndef EVENT__DISABLE_THREAD_SUPPORT
    if (blocking != EVENT_DEL_NOBLOCK &&
        base->current_event == event_to_event_callback(ev) &&
        !EVBASE_IN_THREAD(base) &&
        (blocking == EVENT_DEL_BLOCK || !(ev->ev_events & EV_FINALIZE))) {
        ++base->current_event_waiters;
        EVTHREAD_COND_WAIT(base->current_event_cond, base->th_base_lock);
    }
#endif

    return (res);
}

八、從隊列中刪除超時事件event_queue_remove_timeout
static void
event_queue_remove_timeout(struct event_base *base, struct event *ev)
{
    EVENT_BASE_ASSERT_LOCKED(base);
    if (EVUTIL_FAILURE_CHECK(!(ev->ev_flags & EVLIST_TIMEOUT))) {
        event_errx(1, "%s: %p(fd "EV_SOCK_FMT") not on queue %x", __func__,
            ev, EV_SOCK_ARG(ev->ev_fd), EVLIST_TIMEOUT);
        return;
    }
     // base中事件數量-1,同時設置事件狀態爲非超時狀態
    DECR_EVENT_COUNT(base, ev->ev_flags);
    ev->ev_flags &= ~EVLIST_TIMEOUT;

     // 如果使用了公用超時隊列,則找到待刪除事件所在公用超時隊列,
     // 然後刪除公用超時隊列
     // 如果使用最小堆,則將事件從最小堆刪除即可
    if (is_common_timeout(&ev->ev_timeout, base)) {
        struct common_timeout_list *ctl =
            get_common_timeout_list(base, &ev->ev_timeout);
        TAILQ_REMOVE(&ctl->events, ev,
            ev_timeout_pos.ev_next_with_common_timeout);
    } else {
        min_heap_erase_(&base->timeheap, ev);
    }
}


九、從隊列中刪除激活事件event_queue_remove_active
實際上是從base維護的激活事件列表中刪除事件的回調函數;

static void
event_queue_remove_active(struct event_base *base, struct event_callback *evcb)
{
    EVENT_BASE_ASSERT_LOCKED(base);
    if (EVUTIL_FAILURE_CHECK(!(evcb->evcb_flags & EVLIST_ACTIVE))) {
        event_errx(1, "%s: %p not on queue %x", __func__,
               evcb, EVLIST_ACTIVE);
        return;
    }
     // base中事件個數-1,同時設置事件回調函數狀態爲非激活狀態
     // 同時base中激活事件個數-1
    DECR_EVENT_COUNT(base, evcb->evcb_flags);
    evcb->evcb_flags &= ~EVLIST_ACTIVE;
    base->event_count_active--;

     // 將base中激活隊列中該事件的回調函數刪除即可
    TAILQ_REMOVE(&base->activequeues[evcb->evcb_pri],
        evcb, evcb_active_next);
}

十、從下一次激活隊列中刪除事件回調函數event_queue_remove_active_later
實際上是從base維護的下一次激活列表中刪除事件的回調函數
static void
event_queue_remove_active_later(struct event_base *base, struct event_callback *evcb)
{
    EVENT_BASE_ASSERT_LOCKED(base);
    if (EVUTIL_FAILURE_CHECK(!(evcb->evcb_flags & EVLIST_ACTIVE_LATER))) {
        event_errx(1, "%s: %p not on queue %x", __func__,
               evcb, EVLIST_ACTIVE_LATER);
        return;
    }
     // base中事件個數-1,同時事件回調函數狀態設置爲非下一次激活狀態
     // 同時base中激活事件個數-1
    DECR_EVENT_COUNT(base, evcb->evcb_flags);
    evcb->evcb_flags &= ~EVLIST_ACTIVE_LATER;
    base->event_count_active--;

     // 將base中下一次激活隊列中刪除該事件的回調函數即可
    TAILQ_REMOVE(&base->active_later_queue, evcb, evcb_active_next);
}


十一、從IO事件與文件描述符的映射中刪除事件

/** Remove an IO event (some combination of EV_READ or EV_WRITE) to an
    event_base's list of events on a given file descriptor, and tell the
    underlying eventops about the fd if its state has changed.
    @param base the event_base to operate on.
    @param fd the file descriptor corresponding to ev.
    @param ev the event to remove.
 */
// 刪除IO事件;並告訴潛在的eventops,輸入的fd狀態已經改變了;
// base:操作的event_base
// fd:和事件相關的文件描述符
// ev:要移除的事件
int
evmap_io_del_(struct event_base *base, evutil_socket_t fd, struct event *ev)
{
    const struct eventop *evsel = base->evsel;
    struct event_io_map *io = &base->io;
    struct evmap_io *ctx;
    int nread, nwrite, nclose, retval = 0;
    short res = 0, old = 0;

    if (fd < 0)
        return 0;

    EVUTIL_ASSERT(fd == ev->ev_fd);

#ifndef EVMAP_USE_HT
    if (fd >= io->nentries)
        return (-1);
#endif

     根據fd獲取相關事件的ctx
    GET_IO_SLOT(ctx, io, fd, evmap_io);

     // 獲取當前fd上註冊的讀、寫、關閉事件個數
    nread = ctx->nread;
    nwrite = ctx->nwrite;
    nclose = ctx->nclose;

     // 使用old變量保存原有的事件類型
    if (nread)
        old |= EV_READ;
    if (nwrite)
        old |= EV_WRITE;
    if (nclose)
        old |= EV_CLOSED;

     // 使用res保存刪除的事件類型
    if (ev->ev_events & EV_READ) {
        if (--nread == 0)
            res |= EV_READ;
        EVUTIL_ASSERT(nread >= 0);
    }
    if (ev->ev_events & EV_WRITE) {
        if (--nwrite == 0)
            res |= EV_WRITE;
        EVUTIL_ASSERT(nwrite >= 0);
    }
    if (ev->ev_events & EV_CLOSED) {
        if (--nclose == 0)
            res |= EV_CLOSED;
        EVUTIL_ASSERT(nclose >= 0);
    }

     //  如果刪除的事件不爲空,則調用evsel->del刪除事件
     //  實際上是調用的後臺方法的del方法,這是在event_base創建時指定的
    if (res) {
        void *extra = ((char*)ctx) + sizeof(struct evmap_io);
        if (evsel->del(base, ev->ev_fd, old, res, extra) == -1) {
            retval = -1;
        } else {
            retval = 1;
        }
    }

    ctx->nread = nread;
    ctx->nwrite = nwrite;
    ctx->nclose = nclose;
    LIST_REMOVE(ev, ev_io_next);

    return (retval);
}


十二、從信號事件與信號的映射表中刪除事件evmap_signal_del_
int
evmap_signal_del_(struct event_base *base, int sig, struct event *ev)
{
    const struct eventop *evsel = base->evsigsel;
    struct event_signal_map *map = &base->sigmap;
    struct evmap_signal *ctx;

    if (sig >= map->nentries)
        return (-1);

     // 根據sig獲取相關ctx
    GET_SIGNAL_SLOT(ctx, map, sig, evmap_signal);

     // 刪除事件
    LIST_REMOVE(ev, ev_signal_next);

     // 如果信號上註冊的事件爲空,則需要從信號的後臺處理方法上刪除
     //  實際上調用的是evsig_del函數,這是在evsig_init函數中指定的
    if (LIST_FIRST(&ctx->events) == NULL) {
        if (evsel->del(base, ev->ev_fd, 0, EV_SIGNAL, NULL) == -1)
            return (-1);
    }

    return (1);
}






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章