Libevent源碼分析—event_add()


接下來就是將已經初始化的event注冊到libevent的事件鏈表上,通過event_add()來實現,源碼位於event.c中。

event_add()

這個函數主要完成了下面幾件事:
1.將event注冊到event_base的I/O多路復用要監聽的事件中
2.將event注冊到event_base的已注冊事件鏈表中
3.如果傳入了超時時間,則刪除舊的超時時間,重新設置,並將event添加到event_base的小根堆中;
   如果沒有傳入超時時間,則不會添加到小根堆中。
只有步驟1成功,才會執行步驟2和3;否則什么都沒做,直接返回,保證不會改變event的狀態。
 
從中還可以看到,將event添加到已注冊事件鏈表、添加到小根堆、從活躍事件鏈表移除、從小根堆中移除,都是通過兩個函數完成的:event_queue_insert()、event_queue_remove()
int
event_add(struct event *ev, const struct timeval *tv)
{
    struct event_base *base = ev->ev_base;    //event所屬的event_base
    const struct eventop *evsel = base->evsel;    //event_base的I/O多路復用機制
    void *evbase = base->evbase;    //event_base的I/O多路復用機制
    int res = 0;
    //DEBUG log.h
    event_debug((
         "event_add: event: %p, %s%s%scall %p",
         ev,
         ev->ev_events & EV_READ ? "EV_READ " : " ",
         ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
         tv ? "EV_TIMEOUT " : " ",
         ev->ev_callback));
    assert(!(ev->ev_flags & ~EVLIST_ALL));
    /*
     * prepare for timeout insertion further below, if we get a
     * failure on any step, we should not change any state.
     */
    //如果傳入了超時時間並且event不再time小根堆上,則在小根堆上預留一個位置
    //以保證如果后面有步驟失敗,不會改變初始狀態,保證是個原子操作
    if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {
        if (min_heap_reserve(&base->timeheap,    //min_heap.h
            1 + min_heap_size(&base->timeheap)) == -1)
            return (-1);  /* ENOMEM == errno */
    }
    //如果event不在已注冊鏈表或活躍鏈表中,
    //則調用evsel->add()注冊event事件到I/O多路復用監聽的事件上
    if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
        !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
        res = evsel->add(evbase, ev);    //將event注冊到監聽事件上
        //注冊監聽事件成功,則將event注冊到已注冊事件鏈表上
        if (res != -1)
            event_queue_insert(base, ev, EVLIST_INSERTED);  //插入  
    }
    /* 
     * we should change the timout state only if the previous event
     * addition succeeded.
     */
    //前面操作都成功情況下,才能執行下面步驟
    //改變超時狀態
    if (res != -1 && tv != NULL) {
        struct timeval now;
        /* 
         * we already reserved memory above for the case where we
         * are not replacing an exisiting timeout.
         */
        //EVLIST_TIMEOUT表明event已在定時器堆中
        //則刪除舊的定時器
        if (ev->ev_flags & EVLIST_TIMEOUT)
            event_queue_remove(base, ev, EVLIST_TIMEOUT);  //移除
        /* 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)) {
            /* 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;  //調用次數清0
            }
            //從活躍事件鏈表移除
            event_queue_remove(base, ev, EVLIST_ACTIVE);  //移除
        }
        gettime(base, &now);
        evutil_timeradd(&now, tv, &ev->ev_timeout);    //為event添加超時時間
        event_debug((
             "event_add: timeout in %ld seconds, call %p",
             tv->tv_sec, ev->ev_callback));
        //將event插入到小根堆中
        event_queue_insert(base, ev, EVLIST_TIMEOUT);  //插入
    }
    return (res);
}

event_queue_insert()

該函數根據不同的輸入隊列,即不同的事件,在不同的隊列中插入,並增加相應的事件計數,更新event狀態;
EVLIST_INSERTED:在已注冊事件鏈表event_base.eventqueue插入
EVLIST_ACTIVE:根據event優先級,在活躍事件鏈表event_base.activequeues[event.ev_pri]插入
EVLIST_TIMEOUT:在小根堆event_base.timeheap中插入
void
event_queue_insert(struct event_base *base, struct event *ev, int queue)
{
    //如果event已經在活躍鏈表中,則返回;否則,出錯
    if (ev->ev_flags & queue) {
        /* Double insertion is possible for active events */
        if (queue & EVLIST_ACTIVE)
            return;
        event_errx(1, "%s: %p(fd %d) already on queue %x", __func__,
               ev, ev->ev_fd, queue);
    }
    if (~ev->ev_flags & EVLIST_INTERNAL)
        base->event_count++;  //增加注冊事件數
    ev->ev_flags |= queue;  //改變event狀態
    switch (queue) {  //根據不同的輸入參數隊列,選擇在不同的事件集合中插入
    case EVLIST_INSERTED:  //I/O或Signal事件
        TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);  //在已注冊事件鏈表插入
        break;
    case EVLIST_ACTIVE:  //活躍事件
        base->event_count_active++;  //增加活躍事件數
        TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri],  //在活躍事件鏈表插入
            ev,ev_active_next);
        break;
    case EVLIST_TIMEOUT: {  //定時器事件
        min_heap_push(&base->timeheap, ev);  //在小根堆插入
        break;
    }
    default:
        event_errx(1, "%s: unknown queue %x", __func__, queue);
    }
}

event_queue_remove()

和event_queue_insert()相對應,這個函數主要根據不同的輸入參數,從不同的事件集合中刪除事件。
void
event_queue_remove(struct event_base *base, struct event *ev, int queue)
{
    if (!(ev->ev_flags & queue))
        event_errx(1, "%s: %p(fd %d) not on queue %x", __func__,
               ev, ev->ev_fd, queue);
    if (~ev->ev_flags & EVLIST_INTERNAL)
        base->event_count--;
    ev->ev_flags &= ~queue;
    switch (queue) {
    case EVLIST_INSERTED:  //I/O、Signal事件
        TAILQ_REMOVE(&base->eventqueue, ev, ev_next);
        break;
    case EVLIST_ACTIVE:  //活躍事件
        base->event_count_active--;
        TAILQ_REMOVE(base->activequeues[ev->ev_pri],
            ev, ev_active_next);
        break;
    case EVLIST_TIMEOUT:  //定時器事件
        min_heap_erase(&base->timeheap, ev);
        break;
    default:
        event_errx(1, "%s: unknown queue %x", __func__, queue);
    }
}

event_del()

libevent還提供了event_del()這個函數,該函數從直接刪除event事件,該函數就是主要通過調用event_queue_remove()函數完成刪除的功能。
另外,該函數還將event從I/O多路復用監聽的事件中刪除。
int
event_del(struct event *ev)
{
    struct event_base *base;
    const struct eventop *evsel;
    void *evbase;
    event_debug(("event_del: %p, callback %p",
         ev, ev->ev_callback));
    /* An event without a base has not been added */
    if (ev->ev_base == NULL)
        return (-1);
    base = ev->ev_base;
    evsel = base->evsel;
    evbase = base->evbase;
    assert(!(ev->ev_flags & ~EVLIST_ALL));
    /* See if we are just active executing this event in a loop */
    //計數清0
    if (ev->ev_ncalls && ev->ev_pncalls) {
        /* Abort loop */
        *ev->ev_pncalls = 0;
    }
    //根據event不同的狀態,從相應的event集合中刪除
    if (ev->ev_flags & EVLIST_TIMEOUT)
        event_queue_remove(base, ev, EVLIST_TIMEOUT);
    if (ev->ev_flags & EVLIST_ACTIVE)
        event_queue_remove(base, ev, EVLIST_ACTIVE);
    if (ev->ev_flags & EVLIST_INSERTED) {
        event_queue_remove(base, ev, EVLIST_INSERTED);
        return (evsel->del(evbase, ev));  //從I/O多路復用監聽的事件中刪除
    }
    return (0);
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM