Bypass event_add when using event_base_once() for a 0-sec timeout

Some people use event_base_once(EV_TIMEOUT) to make a callback get
called "immediately".  But this is pretty roundabout: it uses the
timeout heap to immediately put the event onto the active queue, when
it could just use event_active.  Additionally, it can lead to
surprising re-ordering behavior.

This patch changes event_base_once so it bypasses event_add() and
called event_active() directly on a pure-timeout event with an empty
timeout.
This commit is contained in:
Nick Mathewson 2011-12-07 11:49:52 -05:00
parent 748a0d2794
commit 35c5c9558a
2 changed files with 27 additions and 7 deletions

15
event.c
View File

@ -1765,7 +1765,6 @@ event_base_once(struct event_base *base, evutil_socket_t fd, short events,
void *arg, const struct timeval *tv)
{
struct event_once *eonce;
struct timeval etv;
int res = 0;
/* We cannot support signals that just fire once, or persistent
@ -1780,12 +1779,16 @@ event_base_once(struct event_base *base, evutil_socket_t fd, short events,
eonce->arg = arg;
if (events == EV_TIMEOUT) {
if (tv == NULL) {
evutil_timerclear(&etv);
tv = &etv;
}
evtimer_assign(&eonce->ev, base, event_once_cb, eonce);
if (tv == NULL || ! evutil_timerisset(tv)) {
/* If the event is going to become active immediately,
* don't put it on the timeout queue. This is one
* idiom for scheduling a callback, so let's make
* it fast (and order-preserving). */
event_active(&eonce->ev, EV_TIMEOUT, 1);
return 0;
}
} else if (events & (EV_READ|EV_WRITE)) {
events &= EV_READ|EV_WRITE;

View File

@ -2018,6 +2018,15 @@ end:
;
}
static void
immediate_called_twice_cb(evutil_socket_t fd, short event, void *arg)
{
tt_int_op(event, ==, EV_TIMEOUT);
called += 1000;
end:
;
}
static void
test_event_once(void *ptr)
{
@ -2036,6 +2045,14 @@ test_event_once(void *ptr)
tt_int_op(r, ==, 0);
r = event_base_once(data->base, -1, 0, NULL, NULL, NULL);
tt_int_op(r, <, 0);
r = event_base_once(data->base, -1, EV_TIMEOUT,
immediate_called_twice_cb, NULL, NULL);
tt_int_op(r, ==, 0);
tv.tv_sec = 0;
tv.tv_usec = 0;
r = event_base_once(data->base, -1, EV_TIMEOUT,
immediate_called_twice_cb, NULL, &tv);
tt_int_op(r, ==, 0);
if (write(data->pair[1], TEST1, strlen(TEST1)+1) < 0) {
tt_fail_perror("write");
@ -2045,7 +2062,7 @@ test_event_once(void *ptr)
event_base_dispatch(data->base);
tt_int_op(called, ==, 101);
tt_int_op(called, ==, 2101);
end:
;
}