Back to index

tor  0.2.3.18-rc
Classes | Defines | Typedefs | Functions | Variables
compat_libevent.c File Reference

Wrappers to handle porting between different versions of libevent. More...

#include "orconfig.h"
#include "compat.h"
#include "compat_libevent.h"
#include "util.h"
#include "torlog.h"
#include <event.h>

Go to the source code of this file.

Classes

struct  periodic_timer_t
 Represents a timer that's run every N microseconds by Libevent. More...

Defines

#define LE_OLD   V(0,0,0)
 Represetns a version of libevent so old we can't figure out what version it is.
#define LE_OTHER   V(0,0,99)
 Represents a version of libevent so weird we can't figure out what version it is.
#define PERIODIC_FLAGS   0
#define V(major, minor, patch)   (((major) << 24) | ((minor) << 16) | ((patch) << 8))
 Macros: returns the number of a libevent version as a le_version_t.
#define V_OLD(major, minor, patch)   V((major), (minor), (patch)-'a'+1)

Typedefs

typedef uint32_t le_version_t
 A number representing a version of Libevent.

Functions

static le_version_t tor_get_libevent_version (const char **v_out)
 Return the version number of the currently running version of Libevent.
void configure_libevent_logging (void)
void suppress_libevent_log_msg (const char *msg)
struct event * tor_event_new (struct event_base *base, int sock, short what, void(*cb)(int, short, void *), void *arg)
 Work-alike replacement for event_new() on pre-Libevent-2.0 systems.
struct event * tor_evtimer_new (struct event_base *base, void(*cb)(int, short, void *), void *arg)
 Work-alike replacement for evtimer_new() on pre-Libevent-2.0 systems.
struct event * tor_evsignal_new (struct event_base *base, int sig, void(*cb)(int, short, void *), void *arg)
 Work-alike replacement for evsignal_new() on pre-Libevent-2.0 systems.
void tor_event_free (struct event *ev)
 Work-alike replacement for event_free() on pre-Libevent-2.0 systems.
void tor_libevent_initialize (tor_libevent_cfg *torcfg)
 Initialize the Libevent library and set up the event base.
struct event_base * tor_libevent_get_base (void)
 Return the current Libevent event base that we're set up to use.
int tor_event_base_loopexit (struct event_base *base, struct timeval *tv)
 Replacement for event_base_loopexit on some very old versions of Libevent that we are not yet brave enough to deprecate.
const char * tor_libevent_get_method (void)
 Return the name of the Libevent backend we're using.
static le_version_t tor_decode_libevent_version (const char *v)
 Return the le_version_t for the version of libevent specified in the string v.
static int le_versions_compatibility (le_version_t v)
 Return an integer representing the binary interface of a Libevent library.
const char * tor_libevent_get_version_str (void)
 Return a string representation of the version of the currently running version of Libevent.
void tor_check_libevent_version (const char *m, int server, const char **badness_out)
 Compare the current Libevent method and version to a list of versions which are known not to work.
void tor_check_libevent_header_compatibility (void)
 See whether the headers we were built against differ from the library we linked against so much that we're likely to crash.
static void periodic_timer_cb (evutil_socket_t fd, short what, void *arg)
 Libevent callback to implement a periodic event.
periodic_timer_tperiodic_timer_new (struct event_base *base, const struct timeval *tv, void(*cb)(periodic_timer_t *timer, void *data), void *data)
 Create and schedule a new timer that will run every tv in the event loop of base.
void periodic_timer_free (periodic_timer_t *timer)
 Stop and free a periodic timer.
void tor_gettimeofday_cached (struct timeval *tv)
 Return a fairly recent view of the current time.
void tor_gettimeofday_cache_clear (void)
 Reset the cached view of the current time, so that the next time we try to learn it, we will get an up-to-date value.

Variables

struct event_base * the_event_base = NULL
 Global event base for use by the main thread.
static struct timeval = {0, 0}
 Cache the current hi-res time; the cache gets reset when libevent calls us.

Detailed Description

Wrappers to handle porting between different versions of libevent.

In an ideal world, we'd just use Libevent 2.0 from now on. But as of June 2012, Libevent 1.4 is still all over, and some poor souls are stuck on Libevent 1.3e.

Definition in file compat_libevent.c.


Define Documentation

#define LE_OLD   V(0,0,0)

Represetns a version of libevent so old we can't figure out what version it is.

Definition at line 52 of file compat_libevent.c.

#define LE_OTHER   V(0,0,99)

Represents a version of libevent so weird we can't figure out what version it is.

Definition at line 55 of file compat_libevent.c.

#define PERIODIC_FLAGS   0

Definition at line 570 of file compat_libevent.c.

#define V (   major,
  minor,
  patch 
)    (((major) << 24) | ((minor) << 16) | ((patch) << 8))

Macros: returns the number of a libevent version as a le_version_t.

Definition at line 44 of file compat_libevent.c.

#define V_OLD (   major,
  minor,
  patch 
)    V((major), (minor), (patch)-'a'+1)

Definition at line 46 of file compat_libevent.c.


Typedef Documentation

typedef uint32_t le_version_t

A number representing a version of Libevent.

This is a 4-byte number, with the first three bytes representing the major, minor, and patchlevel respectively of the library. The fourth byte is unused.

This is equivalent to the format of LIBEVENT_VERSION_NUMBER on Libevent 2.0.1 or later. For versions of Libevent before 1.4.0, which followed the format of "1.0, 1.0a, 1.0b", we define 1.0 to be equivalent to 1.0.0, 1.0a to be equivalent to 1.0.1, and so on.

Definition at line 40 of file compat_libevent.c.


Function Documentation

void configure_libevent_logging ( void  )

Definition at line 108 of file compat_libevent.c.

{
}

Here is the caller graph for this function:

static int le_versions_compatibility ( le_version_t  v) [static]

Return an integer representing the binary interface of a Libevent library.

Two different versions with different numbers are sure not to be binary compatible. Two different versions with the same numbers have a decent chance of binary compatibility.

Definition at line 351 of file compat_libevent.c.

{
  if (v == LE_OTHER)
    return 0;
  if (v < V_OLD(1,0,'c'))
    return 1;
  else if (v < V(1,4,0))
    return 2;
  else if (v < V(1,4,99))
    return 3;
  else if (v < V(2,0,1))
    return 4;
  else /* Everything 2.0 and later should be compatible. */
    return 5;
}

Here is the caller graph for this function:

static void periodic_timer_cb ( evutil_socket_t  fd,
short  what,
void *  arg 
) [static]

Libevent callback to implement a periodic event.

reschedule the event as needed.

Definition at line 590 of file compat_libevent.c.

{
  periodic_timer_t *timer = arg;
  (void) what;
  (void) fd;
#ifndef HAVE_PERIODIC

  event_add(timer->ev, &timer->tv);
#endif
  timer->cb(timer, timer->data);
}

Here is the caller graph for this function:

Stop and free a periodic timer.

Definition at line 632 of file compat_libevent.c.

{
  if (!timer)
    return;
  tor_event_free(timer->ev);
  tor_free(timer);
}

Here is the call graph for this function:

Here is the caller graph for this function:

periodic_timer_t* periodic_timer_new ( struct event_base *  base,
const struct timeval tv,
void(*)(periodic_timer_t *timer, void *data)  cb,
void *  data 
)

Create and schedule a new timer that will run every tv in the event loop of base.

When the timer fires, it will run the timer in cb with the user-supplied data in data.

Definition at line 606 of file compat_libevent.c.

{
  periodic_timer_t *timer;
  tor_assert(base);
  tor_assert(tv);
  tor_assert(cb);
  timer = tor_malloc_zero(sizeof(periodic_timer_t));
  if (!(timer->ev = tor_event_new(base, -1, PERIODIC_FLAGS,
                                  periodic_timer_cb, timer))) {
    tor_free(timer);
    return NULL;
  }
  timer->cb = cb;
  timer->data = data;
#ifndef HAVE_PERIODIC
  memcpy(&timer->tv, tv, sizeof(struct timeval));
#endif
  event_add(timer->ev, (struct timeval *)tv); /*drop const for old libevent*/
  return timer;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void suppress_libevent_log_msg ( const char *  msg)

Definition at line 112 of file compat_libevent.c.

{
  (void)msg;
}

Here is the caller graph for this function:

See whether the headers we were built against differ from the library we linked against so much that we're likely to crash.

If so, warn the user.

Definition at line 493 of file compat_libevent.c.

{
  (void) le_versions_compatibility;
  (void) tor_decode_libevent_version;

  /* In libevent versions before 2.0, it's hard to keep binary compatibility
   * between upgrades, and unpleasant to detect when the version we compiled
   * against is unlike the version we have linked against. Here's how. */
#if defined(HEADER_VERSION) && defined(HAVE_EVENT_GET_VERSION)
  /* We have a header-file version and a function-call version. Easy. */
  if (strcmp(HEADER_VERSION, event_get_version())) {
    le_version_t v1, v2;
    int compat1 = -1, compat2 = -1;
    int verybad;
    v1 = tor_decode_libevent_version(HEADER_VERSION);
    v2 = tor_decode_libevent_version(event_get_version());
    compat1 = le_versions_compatibility(v1);
    compat2 = le_versions_compatibility(v2);

    verybad = compat1 != compat2;

    log(verybad ? LOG_WARN : LOG_NOTICE,
        LD_GENERAL, "We were compiled with headers from version %s "
        "of Libevent, but we're using a Libevent library that says it's "
        "version %s.", HEADER_VERSION, event_get_version());
    if (verybad)
      log_warn(LD_GENERAL, "This will almost certainly make Tor crash.");
    else
      log_info(LD_GENERAL, "I think these versions are binary-compatible.");
  }
#elif defined(HAVE_EVENT_GET_VERSION)
  /* event_get_version but no _EVENT_VERSION.  We might be in 1.4.0-beta or
     earlier, where that's normal.  To see whether we were compiled with an
     earlier version, let's see whether the struct event defines MIN_HEAP_IDX.
  */
#ifdef HAVE_STRUCT_EVENT_MIN_HEAP_IDX
  /* The header files are 1.4.0-beta or later. If the version is not
   * 1.4.0-beta, we are incompatible. */
  {
    if (strcmp(event_get_version(), "1.4.0-beta")) {
      log_warn(LD_GENERAL, "It's a little hard to tell, but you seem to have "
               "Libevent 1.4.0-beta header files, whereas you have linked "
               "against Libevent %s.  This will probably make Tor crash.",
               event_get_version());
    }
  }
#else
  /* Our headers are 1.3e or earlier. If the library version is not 1.4.x or
     later, we're probably fine. */
  {
    const char *v = event_get_version();
    if ((v[0] == '1' && v[2] == '.' && v[3] > '3') || v[0] > '1') {
      log_warn(LD_GENERAL, "It's a little hard to tell, but you seem to have "
               "Libevent header file from 1.3e or earlier, whereas you have "
               "linked against Libevent %s.  This will probably make Tor "
               "crash.", event_get_version());
    }
  }
#endif

#elif defined(HEADER_VERSION)
#warn "_EVENT_VERSION is defined but not get_event_version(): Libevent is odd."
#else
  /* Your libevent is ancient. */
#endif
}

Here is the call graph for this function:

Here is the caller graph for this function:

void tor_check_libevent_version ( const char *  m,
int  server,
const char **  badness_out 
)

Compare the current Libevent method and version to a list of versions which are known not to work.

Warn the user as appropriate.

Definition at line 407 of file compat_libevent.c.

{
  int buggy = 0, iffy = 0, slow = 0, thread_unsafe = 0;
  le_version_t version;
  const char *v = NULL;
  const char *badness = NULL;
  const char *sad_os = "";

  version = tor_get_libevent_version(&v);

  /* It would be better to disable known-buggy methods rather than warning
   * about them.  But the problem is that with older versions of Libevent,
   * it's not trivial to get them to change their methods once they're
   * initialized... and with newer versions of Libevent, they aren't actually
   * broken.  But we should revisit this if we ever find a post-1.4 version
   * of Libevent where we need to disable a given method. */
  if (!strcmp(m, "kqueue")) {
    if (version < V_OLD(1,1,'b'))
      buggy = 1;
  } else if (!strcmp(m, "epoll")) {
    if (version < V(1,1,0))
      iffy = 1;
  } else if (!strcmp(m, "poll")) {
    if (version < V_OLD(1,0,'e'))
      buggy = 1;
    if (version < V(1,1,0))
      slow = 1;
  } else if (!strcmp(m, "select")) {
    if (version < V(1,1,0))
      slow = 1;
  } else if (!strcmp(m, "win32")) {
    if (version < V_OLD(1,1,'b'))
      buggy = 1;
  }

  /* Libevent versions before 1.3b do very badly on operating systems with
   * user-space threading implementations. */
#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__)
  if (server && version < V_OLD(1,3,'b')) {
    thread_unsafe = 1;
    sad_os = "BSD variants";
  }
#elif defined(__APPLE__) || defined(__darwin__)
  if (server && version < V_OLD(1,3,'b')) {
    thread_unsafe = 1;
    sad_os = "Mac OS X";
  }
#endif

  if (thread_unsafe) {
    log(LOG_WARN, LD_GENERAL,
        "Libevent version %s often crashes when running a Tor server with %s. "
        "Please use the latest version of libevent (1.3b or later)",v,sad_os);
    badness = "BROKEN";
  } else if (buggy) {
    log(LOG_WARN, LD_GENERAL,
        "There are serious bugs in using %s with libevent %s. "
        "Please use the latest version of libevent.", m, v);
    badness = "BROKEN";
  } else if (iffy) {
    log(LOG_WARN, LD_GENERAL,
        "There are minor bugs in using %s with libevent %s. "
        "You may want to use the latest version of libevent.", m, v);
    badness = "BUGGY";
  } else if (slow && server) {
    log(LOG_WARN, LD_GENERAL,
        "libevent %s can be very slow with %s. "
        "When running a server, please use the latest version of libevent.",
        v,m);
    badness = "SLOW";
  }

  *badness_out = badness;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static le_version_t tor_decode_libevent_version ( const char *  v) [static]

Return the le_version_t for the version of libevent specified in the string v.

If the version is very new or uses an unrecognized version, format, return LE_OTHER.

Definition at line 320 of file compat_libevent.c.

{
  unsigned major, minor, patchlevel;
  char c, e, extra;
  int fields;

  /* Try the new preferred "1.4.11-stable" format.
   * Also accept "1.4.14b-stable". */
  fields = tor_sscanf(v, "%u.%u.%u%c%c", &major, &minor, &patchlevel, &c, &e);
  if (fields == 3 ||
      ((fields == 4 || fields == 5 ) && (c == '-' || c == '_')) ||
      (fields == 5 && TOR_ISALPHA(c) && (e == '-' || e == '_'))) {
    return V(major,minor,patchlevel);
  }

  /* Try the old "1.3e" format. */
  fields = tor_sscanf(v, "%u.%u%c%c", &major, &minor, &c, &extra);
  if (fields == 3 && TOR_ISALPHA(c)) {
    return V_OLD(major, minor, c);
  } else if (fields == 2) {
    return V(major, minor, 0);
  }

  return LE_OTHER;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int tor_event_base_loopexit ( struct event_base *  base,
struct timeval tv 
)

Replacement for event_base_loopexit on some very old versions of Libevent that we are not yet brave enough to deprecate.

Definition at line 296 of file compat_libevent.c.

{
  tor_assert(base == the_event_base);
  return event_loopexit(tv);
}

Here is the caller graph for this function:

void tor_event_free ( struct event *  ev)

Work-alike replacement for event_free() on pre-Libevent-2.0 systems.

Definition at line 147 of file compat_libevent.c.

{
  event_del(ev);
  tor_free(ev);
}

Here is the caller graph for this function:

struct event* tor_event_new ( struct event_base *  base,
int  sock,
short  what,
void(*)(int, short, void *)  cb,
void *  arg 
) [read]

Work-alike replacement for event_new() on pre-Libevent-2.0 systems.

Definition at line 121 of file compat_libevent.c.

{
  struct event *e = tor_malloc_zero(sizeof(struct event));
  event_set(e, sock, what, cb, arg);
  if (! base)
    base = tor_libevent_get_base();
  event_base_set(base, e);
  return e;
}

Here is the call graph for this function:

Here is the caller graph for this function:

struct event* tor_evsignal_new ( struct event_base *  base,
int  sig,
void(*)(int, short, void *)  cb,
void *  arg 
) [read]

Work-alike replacement for evsignal_new() on pre-Libevent-2.0 systems.

Definition at line 140 of file compat_libevent.c.

{
  return tor_event_new(base, sig, EV_SIGNAL|EV_PERSIST, cb, arg);
}

Here is the call graph for this function:

Here is the caller graph for this function:

struct event* tor_evtimer_new ( struct event_base *  base,
void(*)(int, short, void *)  cb,
void *  arg 
) [read]

Work-alike replacement for evtimer_new() on pre-Libevent-2.0 systems.

Definition at line 133 of file compat_libevent.c.

{
  return tor_event_new(base, -1, 0, cb, arg);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static le_version_t tor_get_libevent_version ( const char **  v_out) [static]

Return the version number of the currently running version of Libevent.

See le_version_t for info on the format.

Definition at line 371 of file compat_libevent.c.

{
  const char *v;
  le_version_t r;
#if defined(HAVE_EVENT_GET_VERSION_NUMBER)
  v = event_get_version();
  r = event_get_version_number();
#elif defined (HAVE_EVENT_GET_VERSION)
  v = event_get_version();
  r = tor_decode_libevent_version(v);
#else
  v = "pre-1.0c";
  r = LE_OLD;
#endif
  if (v_out)
    *v_out = v;
  return r;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Reset the cached view of the current time, so that the next time we try to learn it, we will get an up-to-date value.

Definition at line 721 of file compat_libevent.c.

{
  cached_time_hires.tv_sec = 0;
}

Here is the caller graph for this function:

void tor_gettimeofday_cached ( struct timeval tv)

Return a fairly recent view of the current time.

Definition at line 710 of file compat_libevent.c.

{
  if (cached_time_hires.tv_sec == 0) {
    tor_gettimeofday(&cached_time_hires);
  }
  *tv = cached_time_hires;
}

Here is the call graph for this function:

Here is the caller graph for this function:

struct event_base* tor_libevent_get_base ( void  ) [read]

Return the current Libevent event base that we're set up to use.

Definition at line 287 of file compat_libevent.c.

{
  return the_event_base;
}

Here is the caller graph for this function:

const char* tor_libevent_get_method ( void  )

Return the name of the Libevent backend we're using.

Definition at line 305 of file compat_libevent.c.

{
#ifdef HAVE_EVENT2_EVENT_H
  return event_base_get_method(the_event_base);
#elif defined(HAVE_EVENT_GET_METHOD)
  return event_get_method();
#else
  return "<unknown>";
#endif
}

Here is the caller graph for this function:

const char* tor_libevent_get_version_str ( void  )

Return a string representation of the version of the currently running version of Libevent.

Definition at line 393 of file compat_libevent.c.

{
#ifdef HAVE_EVENT_GET_VERSION
  return event_get_version();
#else
  return "pre-1.0c";
#endif
}

Here is the caller graph for this function:

Initialize the Libevent library and set up the event base.

Definition at line 182 of file compat_libevent.c.

{
  tor_assert(the_event_base == NULL);
  /* some paths below don't use torcfg, so avoid unused variable warnings */
  (void)torcfg;

#ifdef __APPLE__
  if (MACOSX_KQUEUE_IS_BROKEN ||
      tor_get_libevent_version(NULL) < V_OLD(1,1,'b')) {
    setenv("EVENT_NOKQUEUE","1",1);
  }
#endif

#ifdef HAVE_EVENT2_EVENT_H
  {
    int attempts = 0;
    int using_threads;
    struct event_config *cfg;

  retry:
    ++attempts;
    using_threads = 0;
    cfg = event_config_new();
    tor_assert(cfg);

#if defined(_WIN32) && defined(USE_BUFFEREVENTS)
    if (! torcfg->disable_iocp) {
      evthread_use_windows_threads();
      event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP);
      using_iocp_bufferevents = 1;
      using_threads = 1;
    } else {
      using_iocp_bufferevents = 0;
    }
#endif

    if (!using_threads) {
      /* Telling Libevent not to try to turn locking on can avoid a needless
       * socketpair() attempt. */
      event_config_set_flag(cfg, EVENT_BASE_FLAG_NOLOCK);
    }

#if defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER >= V(2,0,7)
    if (torcfg->num_cpus > 0)
      event_config_set_num_cpus_hint(cfg, torcfg->num_cpus);
#endif

#if LIBEVENT_VERSION_NUMBER >= V(2,0,9)
    /* We can enable changelist support with epoll, since we don't give
     * Libevent any dup'd fds.  This lets us avoid some syscalls. */
    event_config_set_flag(cfg, EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST);
#endif

    the_event_base = event_base_new_with_config(cfg);

    event_config_free(cfg);

    if (using_threads && the_event_base == NULL && attempts < 2) {
      /* This could be a socketpair() failure, which can happen sometimes on
       * windows boxes with obnoxious firewall rules.  Downgrade and try
       * again. */
#if defined(_WIN32) && defined(USE_BUFFEREVENTS)
      if (torcfg->disable_iocp == 0) {
        log_warn(LD_GENERAL, "Unable to initialize Libevent. Trying again "
                 "with IOCP disabled.");
      } else
#endif
      {
          log_warn(LD_GENERAL, "Unable to initialize Libevent. Trying again.");
      }

      torcfg->disable_iocp = 1;
      goto retry;
    }
  }
#else
  the_event_base = event_init();
#endif

  if (!the_event_base) {
    log_err(LD_GENERAL, "Unable to initialize Libevent: cannot continue.");
    exit(1);
  }

#if defined(HAVE_EVENT_GET_VERSION) && defined(HAVE_EVENT_GET_METHOD)
  /* Making this a NOTICE for now so we can link bugs to a libevent versions
   * or methods better. */
  log(LOG_NOTICE, LD_GENERAL,
      "Initialized libevent version %s using method %s. Good.",
      event_get_version(), tor_libevent_get_method());
#else
  log(LOG_NOTICE, LD_GENERAL,
      "Initialized old libevent (version 1.0b or earlier).");
  log(LOG_WARN, LD_GENERAL,
      "You have a *VERY* old version of libevent.  It is likely to be buggy; "
      "please build Tor with a more recent version.");
#endif

#ifdef USE_BUFFEREVENTS
  tor_libevent_set_tick_timeout(torcfg->msec_per_tick);
#endif
}

Here is the call graph for this function:

Here is the caller graph for this function:


Variable Documentation

struct event_base* the_event_base = NULL

Global event base for use by the main thread.

Definition at line 155 of file compat_libevent.c.

struct timeval = {0, 0} [static]

Cache the current hi-res time; the cache gets reset when libevent calls us.

Definition at line 706 of file compat_libevent.c.