Libevent Listener event has the following file descriptors/sockets, no timeout length signal file descriptor/socket, set timeout length
For time, the Libevent internal time management is implemented through the smallest heap, for the following reasons since some FD has a specified timeout length, then the IO multiplexing function can not permanently block, need to set a timeout length (the last parameter) the user is using Event_ The time that the add is set is relative to the Event_add call, which causes all times of the timeout event to time out to be disorganized and no way to set the IO function to a certain length
For the above reasons, libevent all the event timeout length to absolute time, there are the following advantages can be sorted for all the time-out, the first time to get the event to determine whether the timeout is only compared with the existing times the size, do not need to make relative time judgment You can use the smallest heap to store all the event with a timeout, and if the heap top event does not time out, then all the event will not time out, you can choose the timeout of this event the maximum IO function blocking time
The trouble with absolute time is that if the event is a permanent incident, then it still needs to be re-registered to base when the event is activated, because the event time is absolute time, and cannot be directly called Event_add_internal Add event, Instead, a recalculation of the time-out period is required to add, which results in the need to store user-supplied timeout periods in the event, and to calculate the absolute time-out time before re-adding
/* * Event_add called intrinsic function to add event to base's registration queue * Add to corresponding map * Note: This function is not only called by Event_add, but also event_persist_closure called * by This function is called because when an event with a time-out is activated, it needs to be removed from all queues in base and then recalculated and then re-added to base, so the function is called again * Note: Event not only represents the file descriptor, It is also possible that the signal event, when it is a signal, will be recursive * Call two times this function, the first call to determine the signal is called the Evsig_map_add function, in this function * Two steps * To add the signal event to base signal map * Call Evsigo The add function of PS, called Evsig_add, binds the internal signal processing function, and adds the Socketpair event * to base, using Event_add, which is called event_add_internal * But only two times will be executed, because in the evsig_add will be judged, only the first time to add Socketpair event will be executed the second call * See Evsig_add */static inline int event_add_internal (s
Truct event *ev, const struct timeval *tv, int tv_is_absolute) {struct Event_base *base = ev->ev_base;
int res = 0;
int notify = 0;
Event_base_assert_locked (BASE); /* * * */* * Prepare for timeout insertion further below, if we get a * failure on any step, we should no
The change to the T state. */* * This step is mainly used to let the minimum heap add a position, and not actually added to the minimum heap * Judging condition is that this is a time-out EVENT, and there is no such event in the minimum heap * This will require a location on the minimum heap to hold the event * because the user can call Event_add multiple times for the same event, which may be two times event_add except the time-out difference * The others are the same, so that there is no need to set aside a position, directly replace the previous can * * if already in the smallest heap, ev_flags will be evlist_timeout */if (TV! = NULL &&! Ev->ev_flags & Evlist_timeout) {if (Min_heap_reserve (&base->timeheap, 1 + min_heap_siz E (&base->timeheap)) = =-1) return (-1); /* Enomem = errno */}/* * We should change the timeout state only if the previous event * addition
Succeeded.
*/* This step starts processing an event with a timeout time */if (res! =-1 && TV! = NULL) {struct timeval now;
int common_timeout;
/* * For persistent timeout events, we remember the * timeout value and re-add the event.
* * If Tv_is_absolute, this is already set. */* * The event is divided into permanent and one-time, which is the parameter that the user passed in when calling Event_new * for a permanent event, you need to continue listening after being activated once, * andAn event with a timeout will need to be updated with the timeout for the event * * Why: Because base is judged by absolute time when the timeout is made, that is, when adding an event * The current time + time interval The absolute time obtained as a basis for judging timeouts * This is done because you do not need to compare the time difference in the judgment timeout, only need to compare the current and timeout time * * So, if the event is permanent, then after processing once again need to update timeout
For the time, the method is to save the user * incoming interval, and then add the next time using * * Tv_is_absolute is the parameters passed in, Event_add is set to 0, indicating that the incoming time is the time interval, not the absolute time */if (ev->ev_closure = = ev_closure_persist &&!tv_is_absolute) Ev->ev_io_timeou
t = *TV;
/* * We already reserved memory above for the case where we * is not replacing an existing timeout. */* * For a user to call Event_add multiple times for the same event, first remove the previous from the minimum heap *, and then add the updated */if (E V->ev_flags & Evlist_timeout) {/* XXX I believe this is needless. * * IF (min_heap_elt_is_t
OP (ev)) notify = 1;
Event_queue_remove (base, Ev, evlist_timeout); }/* Check if it Is active due to a timeout. Rescheduling * This timeout before the callback can is executed * removes it from the active list. */* * If the event is in the activation queue, the activation queue is deleted * If it is a signal, the number of occurrences is set to 0, the signal processing function will not be called */if ((ev-& Gt;ev_flags & evlist_active) && (Ev->ev_res & Ev_timeout)) {if (Ev->ev_even
TS & ev_signal) {/* see if we is just active executing * This event in a loop
*/if (ev->ev_ncalls && ev->ev_pncalls) {/* Abort loop */
*ev->ev_pncalls = 0;
}} event_queue_remove (base, Ev, evlist_active);
}/* Calculates timeout absolute event */gettime (base, &now);
Common_timeout = is_common_timeout (TV, base);
if (tv_is_absolute) {ev->ev_timeout = *TV;
} else if (common_timeout) { struct Timeval tmp = *TV;
Tmp.tv_usec &= Microseconds_mask;
Evutil_timeradd (&now, &tmp, &ev->ev_timeout);
Ev->ev_timeout.tv_usec |= (Tv->tv_usec & ~microseconds_mask);
} else {Evutil_timeradd (&now, TV, &ev->ev_timeout); } event_debug (("Event_add:timeout in%d seconds, call%p", (int) tv->tv_sec, Ev->
; ev_callback));
/* Call Event_queue_insert () to add an event with a time-out to the base minimum heap */Event_queue_insert (base, Ev, evlist_timeout);
/* See if the earliest timeout is now earlier than it * were before:if so, we'll need to tell the main * Thread to wake up earlier than it would * otherwise.
*/if (min_heap_elt_is_top (EV)) notify = 1;
} return (res);
}
This function is called by Event_add to add all the event to base, it is obvious that the function inside is called Event_queue_insert Add event, event_add_internal is mainly used for assigning event, Determine where it should be added to base.
Here is the section Event_queue_insert added to the minimum heap
/* This function is used to add an event to a different base queue/or to the smallest heap
(struct event_base *base, depending on the queue). struct event *ev, int queue)
{
event_base_assert_locked (BASE);
/* * Ev_flags function as above,
* Note is OR operation, the previous state is still retained, in fact, there may be multiple queues at the same time */
ev->ev_flags |= queue;
/*
* INSERT into different queue according to different values of queue
* Event_add Add event as evlist_inserted, add to registration queue
* evlist_active when activating event, Add to Activation queue
* Add event with time-out to evlist_timeout, add to Minimum heap
* Note that for an event with a time-out, this function is called two times, registered to the queue, and then added to the heap */
switch (queue) {/
* ... */Case
evlist_timeout: {
min_heap_push (&base->timeheap, Ev);
break;
}
Default:
Event_errx (1, "%s:unknown queue%x", __func__, queue);
}
}
All of these are to be able to handle the timeout event to pave, and now go to Event_base_loop
/* The actual event-driven loop is actually a while loop, each time the IO multiplexing function is called for event listening * The active event is added to the active queue of base by priority before the listener returns * Returns the event in the activation queue for base after the loop calls the callback function in order of precedence * and then decides whether to delete the event from all the queues in base based on the permanent event * for an event with a time-out that requires special handling, see Timeout_
Process */int event_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;
Done = 0;
while (!done) {tv_p = &tv; /* * This step is used to get the blocking time of the IO multiplexing function, where there are two cases when there is no active event at this time, the timeout for heap top event is taken from the minimum heap * If there is an active event, the block
0, direct processing, the reason may be because other threads activated a time caused by the * Timeout_next function to obtain the minimum heap heap top elements of the timeout period, and the current time to do the difference between the calculation interval * Evutil_timeclear Direct zeroing * The reason for this blocking time is shown below */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); }/* * Call the IO multiplexing function's listener function, start blocking/nonblocking Listen * Timeout set to the minimum heap top event timeout, for the following reasons * * There are three listening at this time Kind of event * The first is not set time-out, including the signal, so when the return does not affect * the second is to obtain the minimum timeout time of the heap top event, which can be satisfied at the timeout time to return * The third is the smallest heap of other even T, the timeout for these event is after the heap top event, because the timeout is absolute time * that is, if the heap top event does not time out, then the other event will not time out * and when the minimum timeout time is returned after processing time-out
New Start listener, * because it is the absolute time, so does not affect the minimum heap of other event timeout * * In return, add active event to base's activation queue * * Note: The event with timeout is not processed because these are not added to the IO function * processing These is in the timeout_process function */res = Evsel->disp
Atch (base, tv_p); /* * Special Handling Timeout Event * Note Why timeout event can be handled separately because the event with timeout is added to the minimum heap * This way, only the minimum heap is traversed, and the time-out of the heap element is compared to the current time
, you can determine whether the timeout * actually does not need to traverse all, but when encountering the first event without a timeout can exit the traversal * because the minimum heap of the current node is always smaller than two child nodes, so the child node timeout will be longer, it is not possible to time out * * The reason to traverse instead of just the top of the heap is to return from the IO function to the execution timeout_process in the process of * other threads may have added an event with a less time-out to the minimum heap, which results in pre-used * time that the event is not the top element of the heap */timeout_proces
s (base);
} return (retval); }
The following is the Timeout_process function
/* * Add timeout event in minimum heap to activation queue * This function is called by Event_base_loop */static void timeout_process (struct event_base *base) {/* Ca Ller must hold lock.
*/struct Timeval now;
struct event *ev;
if (Min_heap_empty (&base->timeheap)) {return;
}//Gets the current time, which is used to determine if the timeout gettime (base, &now); /* requires a loop to judge instead of just the heap top reason * Because other threads may have added a smaller timeout event * See Event_base_loop in Timeout_process's call */while ((E v = min_heap_top (&base->timeheap))) {//When encountering the first event without a timeout, you can exit for the reason that the main loop of base (EVUTIL_TIMERCMP (&
; ev->ev_timeout, &now, >)) break; /* Delete This event from the I/O queues */* * * Remove one to delete the event from all queues * and then add to the activation queue *
Reason: Because you want to recalculate the time-out, you need to remove the */event_del_internal (EV) from all queues;
Event_debug (("Timeout_process:call%p", Ev->ev_callback));
/* Add event to timeout queue * Here you need to set the cause of the event being activated, Ev_timeout indicates that the timeout was activated */Event_active_nolock (EV, ev_timeout, 1); }
}
Summary
This completes the monitoring and processing of an event with a time-out-of-times, and libevent can learn by converting the relative time to absolute time, without a single effort, For users to provide relative time program design must use relative time, but can think in disguise, appropriate conversion, easy to program design. Libevent can solve the problem of blocking time of IO multiplexing function and improve the efficiency of time management (using minimum heap) after conversion.