User triggered event in libevent

Quentin picture Quentin · Oct 4, 2011 · Viewed 8.2k times · Source

I am currently writing a multi-threaded application using libevent.

Some events are triggered by IO, but I need a couple of events that are triggered accross threads by the code itself, using event_active().

I have tried to write a simple program that shows where my problem is:

The event is created using event_new(), and the fd set to -1.

When calling event_add(), if a timeout struct is used, the event is later properly handled by event_base_dispatch.

If event_add(ev, NULL) is used instead, it returns 0 (apparently successful), but event_base_dispatch() returns 1 (which means no the event was not properly registered.)

This behavior can be tested using the following code and swapping the event_add lines:

#include <event2/event.h>
#include <unistd.h>

void cb_func (evutil_socket_t fd, short flags, void * _param) {
  puts("Callback function called!");
}

void run_base_with_ticks(struct event_base *base)
{
  struct timeval one_sec;

  one_sec.tv_sec = 1;
  one_sec.tv_usec = 0;
  struct event * ev1;
  ev1 = event_new(base, -1, EV_PERSIST, cb_func, NULL);
  //int result = event_add(ev1, NULL);
  int result = event_add(ev1, &one_sec);
  printf("event_add result: %d\n",result);

  while (1) {
     result = event_base_dispatch(base);
     if (result == 1) {
       printf("Failed: event considered as not pending dispite successful event_add\n");
       sleep(1);
     } else {
       puts("Tick");
     }
  }
}

int main () {
  struct event_base *base = event_base_new();
  run_base_with_ticks(base);
  return 0;
}

Compilation: g++ sample.cc -levent

The thing is, I do not need the timeout, and do not want to use a n-years timeout as a workaround. So if this is not the right way to use user-triggered events, I would like to know how it is done.

Answer

nickm picture nickm · Oct 4, 2011

Your approach is sound. In Libevent 2.0, you can use event_active() to activate an event from another thread. Just make sure that you use evthread_use_windows_threads() or evthread_use_pthreads() as appropriate beforehand, to tell Libevent to use the right threading library.

As for needing an extra event: in Libevent 2.0 and earlier, an event loop will exit immediately when there are no pending events added. Your best bet there is probably the timeout trick you discovered.

If you don't like that, you can use the internal "event_base_add_virtual" function to tell the event_base that it has a virtual event. This function isn't exported, though, so you'll have to say something like:

    void event_base_add_virtual(struct event_base *);
    // ...
    base = event_base_new();
    event_base_add_virtual(base); // keep it from exiting

That's a bit of a hack, though, and it uses an undocumented function, so you'd need to watch out in case it doesn't work with a later version of Libevent.

Finally, this method won't help you now, but there's a patch pending for future versions of Libevent (2.1 and later) to add a new flag to event_base_loop() to keep it from exiting when the loop is out of events. The patch is over on Github; it is mainly waiting for code review, and for a better name for the option.