libevent(2)

来源:互联网 发布:php 2016 大麦户源码 编辑:程序博客网 时间:2024/06/03 18:15

event_set()函数:在将事件注册事件处理框架之前,应该先调用event_set对事件进行相关设置。

intevent_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) {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;}ev->ev_closure = EV_CLOSURE_EVENT_SIGNAL;} else {if (events & EV_PERSIST) {evutil_timerclear(&ev->ev_io_timeout);ev->ev_closure = EV_CLOSURE_EVENT_PERSIST;} else {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对象
**ev:事件对象
**fd:事件对应的文件描述符或信号,对于定时器设为-1
**events:事件类型,比如 EV_READ,EV_PERSIST, EV_WRITE, EV_SIGNAL
**callback:事件的回调函数
**arg:回调函数参数
*/


event_set(struct event *ev, evutil_socket_t fd, short events,  void (*callback)(evutil_socket_t, short, void *), void *arg){int r;r = event_assign(ev, current_base, fd, events, callback, arg);EVUTIL_ASSERT(r == 0);}


event结构有3个链表结点域和一个小根堆索引,libevent通过3个链表和一个小根堆对I/O事件、signal事件和timer事件进行管理。
对于I/O事件,通过event_add将其加入event_base的注册事件链表eventqueue ;就绪时会加入event_base的就绪链表activequeues[];
对于timer事件,event_add将其加入到event_base的小根堆timeheap;
Signale事件的管理相对复杂些,event_add将其加入到注册事件链表,同时,event_add内部会调用I/O demultiplex的add函数(对于I/O事件也一样),比如epoll_add。而add函数又会调用evsignal_add将其加入到evsignal_info的evsigevents[signo]链表(关于signal,后面会详细介绍)。

事件处理框架主循环

Libevent将I/O事件、signal事件和timer事件用统一的模型进行处理,这是非常精妙的。libevent主循环函数不断检测注册事件,如果有事件发生,则将其放入就绪链表,并调用事件的回调函数,完成业务逻辑处理。
//事件处理主循环
int
event_dispatch(void)
这是呈现给外部的接口,它的实现很简单,即调用event_loop,而event_loop调用event_base_loop,event_base_loop完成实际的主循环逻辑。
intevent_base_loop(struct event_base *base, int flags){const struct eventop *evsel = base->evsel;struct timeval tv;struct timeval *tv_p;int res, done, retval = 0;/* Grab the lock.  We will release it inside evsel.dispatch, and again * as we invoke user callbacks. */EVBASE_ACQUIRE_LOCK(base, th_base_lock);if (base->running_loop) {event_warnx("%s: reentrant invocation.  Only one event_base_loop"    " can run on each event_base at once.", __func__);EVBASE_RELEASE_LOCK(base, th_base_lock);return -1;}base->running_loop = 1;clear_time_cache(base);if (base->sig.ev_signal_added && base->sig.ev_n_signals_added)evsig_set_base_(base);done = 0;#ifndef EVENT__DISABLE_THREAD_SUPPORTbase->th_owner_id = EVTHREAD_GET_ID();#endifbase->event_gotterm = base->event_break = 0;while (!done) {base->event_continue = 0;base->n_deferreds_queued = 0;/* Terminate the loop if we have been asked to */if (base->event_gotterm) {break;}if (base->event_break) {break;}tv_p = &tv;
               /*  如果没有就绪事件,根据timer heap中事件的最小超时间,计算I/O demultipex的
               ****最大等待时间,相反如果有就绪事件,则清除tv,即I/Odemultipex不应该等待
               */if (!N_ACTIVE_CALLBACKS(base) && !(flags & EVLOOP_NONBLOCK)) {timeout_next(base, &tv_p);} else {/* * if we have active events, we just poll new events * without waiting. */evutil_timerclear(&tv);}                 /* 如果没有事件,则退出循环*//* If we have no events, we just exit */if (0==(flags&EVLOOP_NO_EXIT_ON_EMPTY) &&    !event_haveevents(base) && !N_ACTIVE_CALLBACKS(base)) {event_debug(("%s: no events registered.", __func__));retval = 1;goto done;}event_queue_make_later_events_active(base);clear_time_cache(base);                /*  tv_p 为I/O demultipex 的超时时间 
                *** 处理信号signal 事件和I/O事件
                */res = evsel->dispatch(base, tv_p);if (res == -1) {event_debug(("%s: dispatch returned unsuccessfully.",__func__));retval = -1;goto done;}update_time_cache(base);             // 处理timeout事件,对于超时的事件,将其放到就绪事件链表timeout_process(base);if (N_ACTIVE_CALLBACKS(base)) {<span style="font-family: Arial, Helvetica, sans-serif;">                       //处理就绪事件</span>
int n = event_process_active(base);if ((flags & EVLOOP_ONCE)    && N_ACTIVE_CALLBACKS(base) == 0    && n != 0)done = 1;} else if (flags & EVLOOP_NONBLOCK)done = 1;}event_debug(("%s: asked to terminate loop.", __func__));done:clear_time_cache(base);base->running_loop = 0;EVBASE_RELEASE_LOCK(base, th_base_lock);return (retval);}


/* not thread safe */intevent_loop(int flags){return event_base_loop(current_base, flags);}


intevent_dispatch(void){return (event_loop(0));}

timeout_next:
根据timer heap中事件的最小超时时间,计算I/Odemultipex的最大等待时间。
为了及时处理timer事件;I/O demultipex的最大等待时间应该不超过timer事件中最小的超时时间;否则,timer事件就不能得到及时的处理
static inttimeout_next(struct event_base *base, struct timeval **tv_p){/* Caller must hold th_base_lock */struct timeval now;struct event *ev;struct timeval *tv = *tv_p;int res = 0;     //如果没有timer事件,则直接返回</span>ev = min_heap_top_(&base->timeheap);if (ev == NULL) {/* if no time-based events are active wait for I/O */*tv_p = NULL;goto out;}if (gettime(base, &now) == -1) {res = -1;goto out;}        //如果最小的timer事件已经超时,则清除tv,即I/O demultiplex不应该等待.if (evutil_timercmp(&ev->ev_timeout, &now, <=)) {evutil_timerclear(tv);goto out;}       更新I/O demultiplex可以等待的最大时间:ev->ev_timeout - nowevutil_timersub(&ev->ev_timeout, &now, tv);EVUTIL_ASSERT(tv->tv_sec >= 0);EVUTIL_ASSERT(tv->tv_usec >= 0);event_debug(("timeout_next: event: %p, in %d seconds, %d useconds", ev, (int)tv->tv_sec, (int)tv->tv_usec));out:return (res);}

3.4 dispatch函数
调用底层I/O multiplex的dispatch函数,具体的实现可以参见epoll的实现epoll_dispatch。

event_process_active函数的处理
/*  Helper for event_process_active to process all the events in a single queue,  releasing the lock as we go.  This function requires that the lock be held  when it's invoked.  Returns -1 if we get a signal or an event_break that  means we should stop processing any active events now.  Otherwise returns  the number of non-internal event_callbacks that we processed.*/static intevent_process_active_single_queue(struct event_base *base,    struct evcallback_list *activeq,    int max_to_process, const struct timeval *endtime){struct event_callback *evcb;int count = 0;EVUTIL_ASSERT(activeq != NULL);for (evcb = TAILQ_FIRST(activeq); evcb; evcb = TAILQ_FIRST(activeq)) {struct event *ev=NULL;if (evcb->evcb_flags & EVLIST_INIT) {ev = event_callback_to_event(evcb);if (ev->ev_events & EV_PERSIST || ev->ev_flags & EVLIST_FINALIZING)event_queue_remove_active(base, evcb);elseevent_del_nolock_(ev, EVENT_DEL_NOBLOCK);event_debug((    "event_process_active: event: %p, %s%s%scall %p",    ev,    ev->ev_res & EV_READ ? "EV_READ " : " ",    ev->ev_res & EV_WRITE ? "EV_WRITE " : " ",    ev->ev_res & EV_CLOSED ? "EV_CLOSED " : " ",    ev->ev_callback));} else {event_queue_remove_active(base, evcb);event_debug(("event_process_active: event_callback %p, ""closure %d, call %p",evcb, evcb->evcb_closure, evcb->evcb_cb_union.evcb_callback));}if (!(evcb->evcb_flags & EVLIST_INTERNAL))++count;base->current_event = evcb;#ifndef EVENT__DISABLE_THREAD_SUPPORTbase->current_event_waiters = 0;#endifswitch (evcb->evcb_closure) {case EV_CLOSURE_EVENT_SIGNAL:EVUTIL_ASSERT(ev != NULL);event_signal_closure(base, ev);break;case EV_CLOSURE_EVENT_PERSIST:EVUTIL_ASSERT(ev != NULL);event_persist_closure(base, ev);break;case EV_CLOSURE_EVENT: {void (*evcb_callback)(evutil_socket_t, short, void *);EVUTIL_ASSERT(ev != NULL);evcb_callback = *ev->ev_callback;EVBASE_RELEASE_LOCK(base, th_base_lock);evcb_callback(ev->ev_fd, ev->ev_res, ev->ev_arg);}break;case EV_CLOSURE_CB_SELF: {void (*evcb_selfcb)(struct event_callback *, void *) = evcb->evcb_cb_union.evcb_selfcb;EVBASE_RELEASE_LOCK(base, th_base_lock);evcb_selfcb(evcb, evcb->evcb_arg);}break;case EV_CLOSURE_EVENT_FINALIZE:case EV_CLOSURE_EVENT_FINALIZE_FREE: {void (*evcb_evfinalize)(struct event *, void *);int evcb_closure = evcb->evcb_closure;EVUTIL_ASSERT(ev != NULL);base->current_event = NULL;evcb_evfinalize = ev->ev_evcallback.evcb_cb_union.evcb_evfinalize;EVUTIL_ASSERT((evcb->evcb_flags & EVLIST_FINALIZING));EVBASE_RELEASE_LOCK(base, th_base_lock);evcb_evfinalize(ev, ev->ev_arg);event_debug_note_teardown_(ev);if (evcb_closure == EV_CLOSURE_EVENT_FINALIZE_FREE)mm_free(ev);}break;case EV_CLOSURE_CB_FINALIZE: {void (*evcb_cbfinalize)(struct event_callback *, void *) = evcb->evcb_cb_union.evcb_cbfinalize;base->current_event = NULL;EVUTIL_ASSERT((evcb->evcb_flags & EVLIST_FINALIZING));EVBASE_RELEASE_LOCK(base, th_base_lock);evcb_cbfinalize(evcb, evcb->evcb_arg);}break;default:EVUTIL_ASSERT(0);}EVBASE_ACQUIRE_LOCK(base, th_base_lock);base->current_event = NULL;#ifndef EVENT__DISABLE_THREAD_SUPPORTif (base->current_event_waiters) {base->current_event_waiters = 0;EVTHREAD_COND_BROADCAST(base->current_event_cond);}#endifif (base->event_break)return -1;if (count >= max_to_process)return count;if (count && endtime) {struct timeval now;update_time_cache(base);gettime(base, &now);if (evutil_timercmp(&now, endtime, >=))return count;}if (base->event_continue)break;}return count;}


/* * Active events are stored in priority queues.  Lower priorities are always * process before higher priorities.  Low priority events can starve high * priority ones.
 */// 处理事件位于优先队列中,低优先级的通常比高优先级的先处理 有可能高优先级的队列会饿死static intevent_process_active(struct event_base *base){/* Caller must hold th_base_lock */struct evcallback_list *activeq = NULL;int i, c = 0;const struct timeval *endtime;struct timeval tv;const int maxcb = base->max_dispatch_callbacks;const int limit_after_prio = base->limit_callbacks_after_prio;if (base->max_dispatch_time.tv_sec >= 0) {update_time_cache(base);gettime(base, &tv);evutil_timeradd(&base->max_dispatch_time, &tv, &tv);endtime = &tv;} else {endtime = NULL;}for (i = 0; i < base->nactivequeues; ++i) {if (TAILQ_FIRST(&base->activequeues[i]) != NULL) {base->event_running_priority = i;activeq = &base->activequeues[i];if (i < limit_after_prio)
                            //调用事件处理函数c = event_process_active_single_queue(base, activeq,    INT_MAX, NULL);elsec = event_process_active_single_queue(base, activeq,    maxcb, endtime);if (c < 0) {goto done;} else if (c > 0)break; /* Processed a real event; do not* consider lower-priority events *//* If we get here, all of the events we processed * were internal.  Continue. */}}done:base->event_running_priority = -1;return c;}




0 0
原创粉丝点击