summaryrefslogtreecommitdiff
path: root/src/or/connection.c
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2018-04-17 18:20:03 -0400
committerNick Mathewson <nickm@torproject.org>2018-04-17 18:20:03 -0400
commit47df912f1c5a8783fb6edafc72b044ec0ae774ec (patch)
tree8688b10542b2ca6532ba1e86f5e18caa0a62be0e /src/or/connection.c
parent780d1b44cf24ad8ef321b99d8fc591f110456f98 (diff)
downloadtor-47df912f1c5a8783fb6edafc72b044ec0ae774ec.tar.gz
tor-47df912f1c5a8783fb6edafc72b044ec0ae774ec.zip
Remove the periodic refill event entirely.
Now that we update our buckets on demand before reading or writing, we no longer need to update them all every TokenBucketRefillInterval msec. When a connection runs out of bandwidth, we do need a way to reenable it, however. We do this by scheduling a timer to reenable all blocked connections for TokenBucketRefillInterval msec after a connection becomes blocked. (If we were using PerConnBWRate more, it might make sense to have a per-connection timer, rather than a single timeout. But since PerConnBWRate is currently (mostly) unused, I'm going to go for the simpler approach here, since usually whenever one connection has become blocked on bandwidth, most connections are blocked on bandwidth.) Implements ticket 25373.
Diffstat (limited to 'src/or/connection.c')
-rw-r--r--src/or/connection.c116
1 files changed, 74 insertions, 42 deletions
diff --git a/src/or/connection.c b/src/or/connection.c
index d80c680462..444294ee71 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -138,6 +138,8 @@ static const char *proxy_type_to_string(int proxy_type);
static int get_proxy_type(void);
const tor_addr_t *conn_get_outbound_address(sa_family_t family,
const or_options_t *options, unsigned int conn_type);
+static void blocked_connection_reenable_init(const or_options_t *options);
+static void schedule_blocked_connection_reenable(void);
/** The last addresses that our network interface seemed to have been
* binding to. We use this as one way to detect when our IP changes.
@@ -3091,6 +3093,7 @@ connection_read_bw_exhausted(connection_t *conn, bool is_global_bw)
(void)is_global_bw;
conn->read_blocked_on_bw = 1;
connection_stop_reading(conn);
+ schedule_blocked_connection_reenable();
}
/**
@@ -3105,6 +3108,7 @@ connection_write_bw_exhausted(connection_t *conn, bool is_global_bw)
(void)is_global_bw;
conn->write_blocked_on_bw = 1;
connection_stop_reading(conn);
+ schedule_blocked_connection_reenable();
}
/** If we have exhausted our global buckets, or the buckets for conn,
@@ -3117,7 +3121,8 @@ connection_consider_empty_read_buckets(connection_t *conn)
if (!connection_is_rate_limited(conn))
return; /* Always okay. */
- bool is_global = true;
+ int is_global = 1;
+
if (token_bucket_rw_get_read(&global_bucket) <= 0) {
reason = "global read bucket exhausted. Pausing.";
} else if (connection_counts_as_relayed_traffic(conn, approx_time()) &&
@@ -3185,6 +3190,8 @@ connection_bucket_init(void)
(int32_t)options->BandwidthBurst,
now_ts);
}
+
+ blocked_connection_reenable_init(options);
}
/** Update the global connection bucket settings to a new value. */
@@ -3233,55 +3240,76 @@ connection_bucket_refill_single(connection_t *conn, uint32_t now_ts)
}
}
-/** Time has passed; increment buckets appropriately and re-enable formerly
- * blocked connections. */
-void
-connection_bucket_refill_all(time_t now, uint32_t now_ts)
-{
- smartlist_t *conns = get_connection_array();
+/**
+ * Event to re-enable all connections that were previously blocked on read or
+ * write.
+ */
+static mainloop_event_t *reenable_blocked_connections_ev = NULL;
- /* refill the global buckets */
- token_bucket_rw_refill(&global_bucket, now_ts);
- token_bucket_rw_refill(&global_relayed_bucket, now_ts);
- last_refilled_global_buckets_ts = now_ts;
+/** True iff reenable_blocked_connections_ev is currently scheduled. */
+static int reenable_blocked_connections_is_scheduled = 0;
- /* refill the per-connection buckets */
- SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
- if (connection_speaks_cells(conn)) {
- or_connection_t *or_conn = TO_OR_CONN(conn);
+/** Delay after which to run reenable_blocked_connections_ev. */
+static struct timeval reenable_blocked_connections_delay;
- if (conn->state == OR_CONN_STATE_OPEN) {
- token_bucket_rw_refill(&or_conn->bucket, now_ts);
- }
- }
-
- if (conn->read_blocked_on_bw == 1 /* marked to turn reading back on now */
- && token_bucket_rw_get_read(&global_bucket) > 0 /* and we can read */
- && (!connection_counts_as_relayed_traffic(conn, now) ||
- token_bucket_rw_get_read(&global_relayed_bucket) > 0)
- && (!connection_speaks_cells(conn) ||
- conn->state != OR_CONN_STATE_OPEN ||
- token_bucket_rw_get_read(&TO_OR_CONN(conn)->bucket) > 0)) {
- /* and either a non-cell conn or a cell conn with non-empty bucket */
- LOG_FN_CONN(conn, (LOG_DEBUG,LD_NET,
- "waking up conn (fd %d) for read", (int)conn->s));
- conn->read_blocked_on_bw = 0;
+/**
+ * Re-enable all connections that were previously blocked on read or write.
+ * This event is scheduled after enough time has elapsed to be sure
+ * that the buckets will refill when the connections have something to do.
+ */
+static void
+reenable_blocked_connections_cb(mainloop_event_t *ev, void *arg)
+{
+ (void)ev;
+ (void)arg;
+ SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
+ if (conn->read_blocked_on_bw == 1) {
connection_start_reading(conn);
+ conn->read_blocked_on_bw = 0;
}
-
- if (conn->write_blocked_on_bw == 1
- && token_bucket_rw_get_write(&global_bucket) > 0 /* and we can write */
- && (!connection_counts_as_relayed_traffic(conn, now) ||
- token_bucket_rw_get_write(&global_relayed_bucket) > 0)
- && (!connection_speaks_cells(conn) ||
- conn->state != OR_CONN_STATE_OPEN ||
- token_bucket_rw_get_write(&TO_OR_CONN(conn)->bucket) > 0)) {
- LOG_FN_CONN(conn, (LOG_DEBUG,LD_NET,
- "waking up conn (fd %d) for write", (int)conn->s));
- conn->write_blocked_on_bw = 0;
+ if (conn->write_blocked_on_bw == 1) {
connection_start_writing(conn);
+ conn->write_blocked_on_bw = 0;
}
} SMARTLIST_FOREACH_END(conn);
+
+ reenable_blocked_connections_is_scheduled = 0;
+}
+
+/**
+ * Initialize the mainloop event that we use to wake up connections that
+ * find themselves blocked on bandwidth.
+ */
+static void
+blocked_connection_reenable_init(const or_options_t *options)
+{
+ if (! reenable_blocked_connections_ev) {
+ reenable_blocked_connections_ev =
+ mainloop_event_new(reenable_blocked_connections_cb, NULL);
+ reenable_blocked_connections_is_scheduled = 0;
+ }
+ time_t sec = options->TokenBucketRefillInterval / 1000;
+ int msec = (options->TokenBucketRefillInterval % 1000);
+ reenable_blocked_connections_delay.tv_sec = sec;
+ reenable_blocked_connections_delay.tv_usec = msec * 1000;
+}
+
+/**
+ * Called when we have blocked a connection for being low on bandwidth:
+ * schedule an event to reenable such connections, if it is not already
+ * scheduled.
+ */
+static void
+schedule_blocked_connection_reenable(void)
+{
+ if (reenable_blocked_connections_is_scheduled)
+ return;
+ if (BUG(reenable_blocked_connections_ev == NULL)) {
+ blocked_connection_reenable_init(get_options());
+ }
+ mainloop_event_schedule(reenable_blocked_connections_ev,
+ &reenable_blocked_connections_delay);
+ reenable_blocked_connections_is_scheduled = 1;
}
/** Read bytes from conn-\>s and process them.
@@ -5216,6 +5244,10 @@ connection_free_all(void)
tor_free(last_interface_ipv4);
tor_free(last_interface_ipv6);
last_recorded_accounting_at = 0;
+
+ mainloop_event_free(reenable_blocked_connections_ev);
+ reenable_blocked_connections_is_scheduled = 0;
+ memset(&reenable_blocked_connections_delay, 0, sizeof(struct timeval));
}
/** Log a warning, and possibly emit a control event, that <b>received</b> came