• 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);
    }
     
  • 相关阅读:
    c# Exception 异常信息归整
    解决PKIX(PKIX path building failed) 问题 unable to find valid certification path to requested target
    java Thumbnails 加载网络图片,处理返回base64
    C# 解析 Json
    mysql 中文乱码解决方法
    .Net WinForm下配置Log4Net(总结不输出原因)
    uploadify 后台动态传参数
    C# rename方法重命名文件
    将Qt5.5 动态链接生成的exe及依赖dll打包方法
    远程桌面Default.rdp 中各个参数的含义(转)
  • 原文地址:https://www.cnblogs.com/zxiner/p/6929108.html
Copyright © 2020-2023  润新知