Event_base is the Libevent event-driven, and also the direct embodiment of reactor model. Any code that uses libevent needs to create a base at the very beginning, and then any interface functions are associated with the base, and the following is the definition of the struct event_base
Uniform interface for struct Event_base {/
* IO multiplexing functions */
const struct Eventop *evsel;
/* Data for IO multiplexing function */
void *evbase;
/* Unified interface for signal processing */
const struct Eventop *evsigsel;
/* Signal Processing data */
struct evsig_info sig;
/* Register to the event in base, excluding internal event */
int event_count;
/* Number of event activations */
int event_count_active;
/* Store socket/descriptor and corresponding event map */
struct event_io_map io;
/* Map of the stored signal */
struct event_signal_map sigmap;
/* Register queue */
struct event_list eventqueue;
/* Activate queue */
struct event_list *activequeues;
/* Minimum heap */
struct min_heap timeheap;
/* ... */
};
struct Event_base mainly includes the above variables, IO multiplexing operations, signal operations, various maps, queues, minimum heaps, etc.
The other part is in the process of programming the various judgments, a variety of signs more
The initialization of Event_base is implemented by an internal call to Event_base_with_new_config, and the function means that with some configuration constructs a event_base, the configuration mainly includes the user does not want to libevent uses the IO multiplex function name.
The function is mostly a variety of initialization, and then choose the appropriate IO multiplexing function, etc.
The user turns on the event-driven main loop via the Event_base_dispatch function, internally calls Event_base_loop to perform the wireless loop, and there's nothing special to choose IO function blocking time calls IO functions to determine the event in the smallest heap Handle all Active Event
Here's a look at how to handle activation event
/* * is called by the base master loop to handle the event in the active queue of base, * iterates through the base's activation queue array based on priority, and for each queue, calls Event_process_ Active_single_queue processing */static int event_process_active (struct event_base *base) {/* Caller must hold Th_base_lock
*/struct Event_list *activeq = NULL;
int I, c = 0;
for (i = 0; i < base->nactivequeues; ++i) {if (Tailq_first (&base->activequeues[i]) = NULL) {
Base->event_running_priority = i;
Activeq = &base->activequeues[i];
c = Event_process_active_single_queue (base, Activeq);
}} return C; }
/* * Main function to process activation/Timeout event * First determine whether the event needs to be removed from all queues in base based on whether the event is a permanent event * if it is a permanent queue, you only need to delete * from base's activation queue * before you start calling the event callback function * Note: There is a need to handle an event with a timeout, because the minimum heap is recorded in absolute time, * So if this event is an event with a timeout, then it is added to the * activation queue after it is removed from all queues in base, see Timeout_ Process () * So you need to add it to base again, call Event_add_internal again, see event_persist_closure * * Returns how many non-internal event */static int Event_
Process_active_single_queue (struct event_base *base, struct event_list *activeq) {struct event *ev;
int count = 0; for (ev = Tailq_first (ACTIVEQ), ev, Ev = Tailq_first (Activeq)) {/* If it is a permanent event, you only need to remove the */if from the activation queue (ev->e
V_events & Ev_persist) Event_queue_remove (base, EV, evlist_active);
/* Otherwise it will be removed from all queues in base, including the activation queue, because only one time */else event_del_internal (EV) is processed; /* * If the signal or the permanent event needs to be handled separately, and the other event will be called directly after the deletion of the callback function can be no longer control this event, because only once * ev_closure used to determine is the permanent/signal event, assigned in Event_new */switch (ev->ev_closure{Case Ev_closure_signal:/* Special handling of the signal, calling the user's signal processing function */event_signal_closure (base, EV);
Break Case Ev_closure_persist://io Event/* Handles IO events, mainly dealing with event with timeout, need to recalculate absolute time, and then call callback function */Event_persist_c
Losure (base, Ev);
Break
}} return count;
}
The function processes each event in the current queue, first removing it from the activation queue and, if it is a one-time event, removing it from base
The corresponding handler function is then called according to the signal/other permanent IO event
Summary
Event_base is the entire event-driven carrier, but only needs event_base_new, and event_base_dispatch, the internal implementation of the monitoring of events, the unified processing of timeout event, to all activation event priority processing , and then subdivision is permanent/disposable event,io/signal event. The advantage of this first unified redistribution is that it is possible to handle activation event in a single function, to achieve better encapsulation interface, and easier to clarify the idea.
The processing of the signal requires special attention, because not a signal occurs immediately call the user's callback function, in fact, two times the base loop, which is to unify the signal to the result of the event, or the direct sigaction binding is not very good.
An event with a time-out is also a part of the process that needs to be noted, the time-out needs to be recalculated, and then re-added using Event_add_internal (the event has been removed in timeout_process), which goes back to Event_ Add calls on the event_add_internal, if not timed out the event is simply a call to the callback function.
The whole idea is easy to clarify, the focus is on the details, need to carefully ponder