aboutsummaryrefslogtreecommitdiff
path: root/src/common/compat_libevent.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/compat_libevent.c')
-rw-r--r--src/common/compat_libevent.c130
1 files changed, 116 insertions, 14 deletions
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c
index beae9502d..67f465927 100644
--- a/src/common/compat_libevent.c
+++ b/src/common/compat_libevent.c
@@ -169,6 +169,7 @@ struct event_base *the_event_base = NULL;
#ifdef USE_BUFFEREVENTS
static int using_iocp_bufferevents = 0;
+static void tor_libevent_set_tick_timeout(int msec_per_tick);
int
tor_libevent_using_iocp_bufferevents(void)
@@ -194,16 +195,33 @@ tor_libevent_initialize(tor_libevent_cfg *torcfg)
#ifdef HAVE_EVENT2_EVENT_H
{
- struct event_config *cfg = event_config_new();
+ int attempts = 0;
+ int using_threads;
+ struct event_config *cfg;
+
+ retry:
+ ++attempts;
+ using_threads = 0;
+ cfg = event_config_new();
+ tor_assert(cfg);
#if defined(MS_WINDOWS) && 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);
@@ -218,11 +236,34 @@ tor_libevent_initialize(tor_libevent_cfg *torcfg)
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(MS_WINDOWS) && 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. */
@@ -236,6 +277,10 @@ tor_libevent_initialize(tor_libevent_cfg *torcfg)
"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
}
/** Return the current Libevent event base that we're set up to use. */
@@ -513,6 +558,60 @@ tor_check_libevent_header_compatibility(void)
#endif
}
+struct tor_libevent_action_t {
+ struct event *ev;
+ void (*cb)(void *arg);
+ void *arg;
+};
+
+/** Callback for tor_run_in_libevent_loop */
+static void
+run_runnable_cb(evutil_socket_t s, short what, void *arg)
+{
+ tor_libevent_action_t *r = arg;
+ void (*cb)(void *) = r->cb;
+ void *cb_arg = r->arg;
+ (void)what;
+ (void)s;
+ tor_event_free(r->ev);
+ tor_free(r);
+
+ cb(cb_arg);
+}
+
+/** Cause cb(arg) to run later on this iteration of the libevent loop, or in
+ * the next iteration of the libevent loop. This is useful for when you're
+ * deep inside a no-reentrant code and there's some function you want to call
+ * without worrying about whether it might cause reeentrant invocation.
+ */
+tor_libevent_action_t *
+tor_run_in_libevent_loop(void (*cb)(void *arg), void *arg)
+{
+ tor_libevent_action_t *r = tor_malloc(sizeof(tor_libevent_action_t));
+ r->cb = cb;
+ r->arg = arg;
+ r->ev = tor_event_new(tor_libevent_get_base(), -1, EV_TIMEOUT,
+ run_runnable_cb, r);
+ if (!r->ev) {
+ tor_free(r);
+ return NULL;
+ }
+ /* Make the event active immediately. */
+ event_active(r->ev, EV_TIMEOUT, 1);
+
+ return r;
+}
+
+/**
+ * Cancel <b>action</b> without running it.
+ */
+void
+tor_cancel_libevent_action(tor_libevent_action_t *action)
+{
+ tor_event_free(action->ev);
+ tor_free(action);
+}
+
/*
If possible, we're going to try to use Libevent's periodic timer support,
since it does a pretty good job of making sure that periodic events get
@@ -598,26 +697,29 @@ static const struct timeval *one_tick = NULL;
/**
* Return a special timeout to be passed whenever libevent's O(1) timeout
* implementation should be used. Only use this when the timer is supposed
- * to fire after 1 / TOR_LIBEVENT_TICKS_PER_SECOND seconds have passed.
+ * to fire after msec_per_tick ticks have elapsed.
*/
const struct timeval *
tor_libevent_get_one_tick_timeout(void)
{
- if (PREDICT_UNLIKELY(one_tick == NULL)) {
- struct event_base *base = tor_libevent_get_base();
- struct timeval tv;
- if (TOR_LIBEVENT_TICKS_PER_SECOND == 1) {
- tv.tv_sec = 1;
- tv.tv_usec = 0;
- } else {
- tv.tv_sec = 0;
- tv.tv_usec = 1000000 / TOR_LIBEVENT_TICKS_PER_SECOND;
- }
- one_tick = event_base_init_common_timeout(base, &tv);
- }
+ tor_assert(one_tick);
return one_tick;
}
+/** Initialize the common timeout that we'll use to refill the buckets every
+ * time a tick elapses. */
+static void
+tor_libevent_set_tick_timeout(int msec_per_tick)
+{
+ struct event_base *base = tor_libevent_get_base();
+ struct timeval tv;
+
+ tor_assert(! one_tick);
+ tv.tv_sec = msec_per_tick / 1000;
+ tv.tv_usec = (msec_per_tick % 1000) * 1000;
+ one_tick = event_base_init_common_timeout(base, &tv);
+}
+
static struct bufferevent *
tor_get_root_bufferevent(struct bufferevent *bev)
{