As it is, EV_PERSIST is somewhat obtuse when trying to use it in combination
with EV_READ or EV_WRITE while still handling timeout events. The standard
idiom is basically:

if (select_epoll_etc_indicates_something)
        do_something_and_add_fd_back_to_something_set();
else if (time_since_last_something > something_timeout)
        our_client_or_server_is_dead_lets_cleanup();

One method is to do something like this with EV_PERSIST is:

void read_cb(int fd, short event, void *a)
{
        opaque *obj = a;
        ssize_t l;

        switch (event) {
        case EV_TIMEOUT:
                close_cleanup_whatever(fd);
                opaque_reset(obj);
                event_del(obj->ev_r);
                break;
        case EV_READ:
                l = recv(fd, ...);
                /*
                 * Here we force a timeout reschedule. if we don't,
                 * our timeout will eventually trigger, regardless
                 * of the fact that we've been processing active events.
                 * It isn't really added with EV_PERSIST, just the timeout
                 * is rescheduled.
                 */
                event_add(obj->ev_r, obj->tout_recv_tv);
                break;
        default: break;
        }

        return;
}

Same goes for EV_SIGNAL and EV_TIMEOUT events. event_add(event, timeout),
always, if you want to reschedule the timer associated with it (which for
all intensive purposes you almost always will if you're using EV_PERSIST
in the first place).

Now it's not like this is a huge hassle, but it does seem like it would
be more inutitive for libevent to handle latching timers of this sort when
using EV_PERSIST. I've written a patch to handle latching timers when using
EV_PERSIST and currently I can see some pros and cons.

Pros:
1. EV_PERSIST + anything else works intuitively without having to worry
        about managing the timeout one passed to event_add(). Set it and
        forget it.
2. For some people it may be one less thing to truck around in their god
        objects. I.e. the space taken up by storing the relative timer within
        the event struct itself is balanced by the space saved by not carrying
        it around in user objects. Not a gain, however, for people using
        global timeout structs.
3. It's not really much of a boogeyman to current users of libevent as the
        people who are using EV_PERSIST typically are aware of said timeout
        issues. Also, the current workaround of event_add() will work fine
        in existing code.
4. Focuses on optimizing the standard case rather than the exception (timeout).

Cons:
1. Cyclic timer still needs event_add() in the callback, as a timeout is
        still setup to delete the event automatically. I'm torn on this. If
        it's changed it will break current behaviour. If it isn't changed,
        then it's another thing to remember when using the library - that is
        events registered with EV_PERSIST will have their timeouts reset on
        activity, however a timeout occuring will automatically delete the
        event as per normal. In day to day usage this is probably a preferred
        idiom though, even though it's an exception to the latching timer
        pattern. One concern though is that since things are less explicit,
        flow is not as self-evident.
2. Timeout rescheduling is done when event_active() is called. This is not
        entirely precise because event timeouts may end up being rescheduled
        to a slightly earlier moment in time. One way to get around this is to
        move timeout_schedule() to event_process_active(), but it would take
        some reworking of timeout_process().
3. Maybe this is all just a waste of time and we should just continue to use
        event_add(obj, obj->tout);


With the model changed, it would look like this:

void read_cb(int fd, short event, void *a)
{
        opaque *obj = a;
        ssize_t l;

        switch (event) {
        case EV_TIMEOUT:
                close_cleanup_whatever(fd);
                opaque_reset(obj);
                break;
        case EV_READ:
                l = recv(fd, ...);
                /* etc */
                break;
        default: break;
        }

        return;
}

void dispatcher(opaque *obj)
{
        event_set(obj->ev_r, obj->fd, EV_READ | EV_PERSIST, read_cb, obj);
        event_add(obj->ev_r, obj->tout_recv_tv);
        return;
}

Interested to hear thoughts before I go posting patches.

-cl
_______________________________________________
Libevent-users mailing list
Libevent-users@monkey.org
http://monkeymail.org/mailman/listinfo/libevent-users

Reply via email to