libevent源码分析--event_add()函数

event_add是第三个函数,函数参数ev是指向要注册的事件,tv是超时时间,

函数将ev注册到ev->ev_base上,事件类型由ev->ev_events指明。如果注册成功,ev讲被插入到已经注册链表中,如果tv不是null,则会同时注册定时事件,将ev添加到timer堆上,其中的巧妙之处请看以下分析

int
 720 event_add(struct event *ev, const struct timeval *tv)
 721 {
 722     struct event_base *base = ev->ev_base;  // 要注册到的event_base
 723     const struct eventop *evsel = base->evsel; //base使用的系统I/O策略
/**
  新的timer事件,调用tiamer heap接口在堆上预留以一个位置
这样能保证该操作的原子性
向系统I./O机制注册可能会失败,而当在堆上预留成功后,
定时事件的添加讲肯定不会失败,
而预留位置的可能结果是堆扩充,但是内部元素并不会改变
*/
 724     void *evbase = base->evbase;
 725     int res = 0;
 726 
 727     event_debug((
 728          "event_add: event: %p, %s%s%scall %p",
 729          ev,
 730          ev->ev_events & EV_READ ? "EV_READ " : " ",
 731          ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
 732          tv ? "EV_TIMEOUT " : " ",
 733          ev->ev_callback));
 734 
 735     assert(!(ev->ev_flags & ~EVLIST_ALL));
 736 
 737     /*
 738      * prepare for timeout insertion further below, if we get a
 739      * failure on any step, we should not change any state.
 740      */
 741     if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {
 742         if (min_heap_reserve(&base->timeheap,
 743             1 + min_heap_size(&base->timeheap)) == -1)
 744             return (-1);  /* ENOMEM == errno */
 745     }
 746 
 747     if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
 748         !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
 749         res = evsel->add(evbase, ev);
 750         if (res != -1)
 751             event_queue_insert(base, ev, EVLIST_INSERTED);
 752     }
 753 
 754     /* 
 755      * we should change the timout state only if the previous event
 756      * addition succeeded.
 757      */
 758     if (res != -1 && tv != NULL) {
 759         struct timeval now;
 760 
 761         /* 
 762          * we already reserved memory above for the case where we
 763          * are not replacing an exisiting timeout.
 764          */
 765         if (ev->ev_flags & EVLIST_TIMEOUT)
 766             event_queue_remove(base, ev, EVLIST_TIMEOUT);
 /* 
 755      * we should change the timout state only if the previous event
 756      * addition succeeded.
 757      */
 758     if (res != -1 && tv != NULL) {
 759         struct timeval now;
 760 
 761         /* 
 762          * we already reserved memory above for the case where we
 763          * are not replacing an exisiting timeout.
 764          */
 765         if (ev->ev_flags & EVLIST_TIMEOUT)
 766             event_queue_remove(base, ev, EVLIST_TIMEOUT);
 767 
 768         /* Check if it is active due to a timeout.  Rescheduling
 769          * this timeout before the callback can be executed
 770          * removes it from the active list. */
 771         if ((ev->ev_flags & EVLIST_ACTIVE) &&
 772             (ev->ev_res & EV_TIMEOUT)) {
 773             /* See if we are just active executing this
 774              * event in a loop
 775              */
 776             if (ev->ev_ncalls && ev->ev_pncalls) {
 777                 /* Abort loop */
 778                 *ev->ev_pncalls = 0;
 779             }
 780 
 781             event_queue_remove(base, ev, EVLIST_ACTIVE);
 782         }
 783 
 784         gettime(base, &now);
 785         evutil_timeradd(&now, tv, &ev->ev_timeout);
 786 
 787         event_debug((
 788              "event_add: timeout in %ld seconds, call %p",
 789              tv->tv_sec, ev->ev_callback));
 790 
 791         event_queue_insert(base, ev, EVLIST_TIMEOUT);
 792     }
 793 
 794     return (res);
 795 }

 函数的主要目的是讲event添加到相应的event_base中,首先将这个event的各个主要字段取出来,包括ev_base ,然后这个event_base的evsel和evbase都取出来,
741~745的解释就注释一样,为了保证原子操作,也是为了安全,首先将空余的位置准备好,然后再插入,而不是直接插入,前者肯定能够保证正确的完成,但是后者
不一定能报保证完成,如果这个事件是超时事件,那么就再扩充小根堆的大小,为这个事件预留一个位置。注意这里只是将小根堆扩充了一个位置,并没有实际的操作。
     在747~752行中,如果这个事件是I/O事件,那么就判断,注意判断的地方多了一个ev_flags,就是判断这个事件是否已经在队列中了。如果用户重复event_add相同的event
这里就可以避免这类事情的发生。首先evsel->add(evbase,ev),将这个event添加到相应的event_base中,这里添加是指由什么操作,下面再讲解(这里已经欠了两个详细
的讲解了,一个是上篇文章中的关于evsel->init的讲解,一个是这里的evsel->add讲解),如果添加成功,那么就event_queue_insert添加到队列中,下面看看这个函数究竟
做了啥!!
 void
 976 event_queue_insert(struct event_base *base, struct event *ev, int queue)
 977 {
 978     if (ev->ev_flags & queue) {
 979         /* Double insertion is possible for active events */
 980         if (queue & EVLIST_ACTIVE)
 981             return;
 982 
 983         event_errx(1, "%s: %p(fd %d) already on queue %x", __func__,
 984                ev, ev->ev_fd, queue);
 985     }
 986 
 987     if (~ev->ev_flags & EVLIST_INTERNAL)
 988         base->event_count++;
 989 
 990     ev->ev_flags |= queue;
 991     switch (queue) {
 992     case EVLIST_INSERTED:
 993         TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);
 994         break;
 995     case EVLIST_ACTIVE:
 996         base->event_count_active++;
 997         TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri],
 998             ev,ev_active_next);
 999         break;
1000     case EVLIST_TIMEOUT: {
1001         min_heap_push(&base->timeheap, ev);
1002         break;
1003     }
1004     default:
1005         event_errx(1, "%s: unknown queue %x", __func__, queue);
1006     }
1007 }
函数的参数说明,插入队列是有标志位的,ev_flags是libevent用于标记event信息的字段,表面其当前的状态,也就是这个event当前的状态,可能的值有:
#define EVLIST_TIMEOUT 0x01 // event在time堆中 
#define EVLIST_INSERTED 0x02 // event在已注册事件链表中 
#define EVLIST_SIGNAL 0x04 // 未见使用 
#define EVLIST_ACTIVE 0x08 // event在激活链表中 
#define EVLIST_INTERNAL 0x10 // 内部使用标记 
#define EVLIST_INIT 0x80 // event 已被初始化
在event调用event_set初始化结束以后,event的ev_flags是EVLIST_INIT,在这里的978行,首先判断event的当前状态和将要变成的状态是否一样,一样的话就没必要
有多余的操作,尤其是往活跃链表中添加,如果正常的话,就进入991行的switch判断,不同的标志位就是不同的操作,总而言之都是有插入的操作的,不管是往事件的
双向链表中还是中还是往激活链表中或者是小根堆中,这里的三种情况我们都会慢慢遇到,这里遇到的第一个简单情况就是EVLIST_INSERTED.仍旧是一句话,
TAILQ_INSERT_TAIL。如果这是一个定时事件,就是运行从758开始的代码,判断的顺序还是差不多的,首先判断这个事件是否已经在链表中存在,不存在的话才会进行一下的
工作,如果这个event的ev_flags已经是EVLIST_TIMEOUT,那么就将这个event从链表中删除,如果这个event的ev_flags是EVLIST_ACTIVE,并且这个event是由EV)TIMEOUT
激活的,也就是说,这个event由于EV_TIMEOUT激活并且当前的ev_flags是EVLIST_TIMEOUT,那么仍旧将这个event从链表中删除。其实在758~783这些都是判断如果是
timeout事件的话,在添加这类事件之前的一些列判断。785~792这几行是进行操作timeout事件的东西。785行是处理的东西,在791行最后调用的是min_heap_push操作,
也就是往小根堆中添加一个元素。





   

你可能感兴趣的:(libevent)