diff options
Diffstat (limited to 'src/or/main.c')
-rw-r--r-- | src/or/main.c | 201 |
1 files changed, 157 insertions, 44 deletions
diff --git a/src/or/main.c b/src/or/main.c index f15b18243b..9e96afce3a 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -74,6 +74,10 @@ static connection_t *connection_array[MAXCONNECTIONS+1] = /** List of connections that have been marked for close and need to be freed * and removed from connection_array. */ static smartlist_t *closeable_connection_lst = NULL; +/** DOCDOC */ +static smartlist_t *active_linked_connection_lst = NULL; +/** DOCDOC */ +static int called_loop_once = 0; static int n_conns=0; /**< Number of connections currently active. */ @@ -155,7 +159,7 @@ int connection_add(connection_t *conn) { tor_assert(conn); - tor_assert(conn->s >= 0); + tor_assert(conn->s >= 0 || conn->linked); tor_assert(conn->conn_array_index == -1); /* can only connection_add once */ if (n_conns == MAXCONNECTIONS) { @@ -198,13 +202,12 @@ connection_remove(connection_t *conn) tor_assert(conn->conn_array_index >= 0); current_index = conn->conn_array_index; + connection_unregister_events(conn); /* This is redundant, but cheap. */ if (current_index == n_conns-1) { /* this is the end */ n_conns--; return 0; } - connection_unregister(conn); - /* replace this one with the one at the end */ n_conns--; connection_array[current_index] = connection_array[n_conns]; @@ -213,23 +216,31 @@ connection_remove(connection_t *conn) return 0; } -/** If it's an edge conn, remove it from the list +/** If <b>conn</b> is an edge conn, remove it from the list * of conn's on this circuit. If it's not on an edge, * flush and send destroys for all circuits on this conn. * - * If <b>remove</b> is non-zero, then remove it from the - * connection_array and closeable_connection_lst. + * Remove it from connection_array (if applicable) and + * from closeable_connection_list. * * Then free it. */ static void -connection_unlink(connection_t *conn, int remove) +connection_unlink(connection_t *conn) { connection_about_to_close_connection(conn); - if (remove) { + if (conn->conn_array_index >= 0) { connection_remove(conn); } + if (conn->linked_conn) { + conn->linked_conn->linked_conn = NULL; + if (! conn->linked_conn->marked_for_close && + conn->linked_conn->reading_from_linked_conn) + connection_start_reading(conn->linked_conn); + conn->linked_conn = NULL; + } smartlist_remove(closeable_connection_lst, conn); + smartlist_remove(active_linked_connection_lst, conn); if (conn->type == CONN_TYPE_EXIT) { assert_connection_edge_not_dns_pending(TO_EDGE_CONN(conn)); } @@ -286,16 +297,23 @@ get_connection_array(connection_t ***array, int *n) void connection_watch_events(connection_t *conn, short events) { - int r; + int r = 0; tor_assert(conn); tor_assert(conn->read_event); tor_assert(conn->write_event); - if (events & EV_READ) { - r = event_add(conn->read_event, NULL); + if (conn->linked) { + if (events & EV_READ) + connection_start_reading(conn); + else + connection_stop_reading(conn); } else { - r = event_del(conn->read_event); + if (events & EV_READ) { + r = event_add(conn->read_event, NULL); + } else { + r = event_del(conn->read_event); + } } if (r<0) @@ -305,10 +323,17 @@ connection_watch_events(connection_t *conn, short events) conn->s, (events & EV_READ)?"":"un", tor_socket_strerror(tor_socket_errno(conn->s))); - if (events & EV_WRITE) { - r = event_add(conn->write_event, NULL); + if (conn->linked) { + if (events & EV_WRITE) + connection_start_writing(conn); + else + connection_stop_writing(conn); } else { - r = event_del(conn->write_event); + if (events & EV_WRITE) { + r = event_add(conn->write_event, NULL); + } else { + r = event_del(conn->write_event); + } } if (r<0) @@ -325,7 +350,8 @@ connection_is_reading(connection_t *conn) { tor_assert(conn); - return conn->read_event && event_pending(conn->read_event, EV_READ, NULL); + return conn->reading_from_linked_conn || + (conn->read_event && event_pending(conn->read_event, EV_READ, NULL)); } /** Tell the main loop to stop notifying <b>conn</b> of any read events. */ @@ -335,12 +361,16 @@ connection_stop_reading(connection_t *conn) tor_assert(conn); tor_assert(conn->read_event); - log_debug(LD_NET,"entering."); - if (event_del(conn->read_event)) - log_warn(LD_NET, "Error from libevent setting read event state for %d " - "to unwatched: %s", - conn->s, - tor_socket_strerror(tor_socket_errno(conn->s))); + if (conn->linked) { + conn->reading_from_linked_conn = 0; + connection_stop_reading_from_linked_conn(conn); + } else { + if (event_del(conn->read_event)) + log_warn(LD_NET, "Error from libevent setting read event state for %d " + "to unwatched: %s", + conn->s, + tor_socket_strerror(tor_socket_errno(conn->s))); + } } /** Tell the main loop to start notifying <b>conn</b> of any read events. */ @@ -350,11 +380,17 @@ connection_start_reading(connection_t *conn) tor_assert(conn); tor_assert(conn->read_event); - if (event_add(conn->read_event, NULL)) - log_warn(LD_NET, "Error from libevent setting read event state for %d " - "to watched: %s", - conn->s, - tor_socket_strerror(tor_socket_errno(conn->s))); + if (conn->linked) { + conn->reading_from_linked_conn = 1; + if (connection_should_read_from_linked_conn(conn)) + connection_start_reading_from_linked_conn(conn); + } else { + if (event_add(conn->read_event, NULL)) + log_warn(LD_NET, "Error from libevent setting read event state for %d " + "to watched: %s", + conn->s, + tor_socket_strerror(tor_socket_errno(conn->s))); + } } /** Return true iff <b>conn</b> is listening for write events. */ @@ -363,7 +399,8 @@ connection_is_writing(connection_t *conn) { tor_assert(conn); - return conn->write_event && event_pending(conn->write_event, EV_WRITE, NULL); + return conn->writing_to_linked_conn || + (conn->write_event && event_pending(conn->write_event, EV_WRITE, NULL)); } /** Tell the main loop to stop notifying <b>conn</b> of any write events. */ @@ -373,11 +410,17 @@ connection_stop_writing(connection_t *conn) tor_assert(conn); tor_assert(conn->write_event); - if (event_del(conn->write_event)) - log_warn(LD_NET, "Error from libevent setting write event state for %d " - "to unwatched: %s", - conn->s, - tor_socket_strerror(tor_socket_errno(conn->s))); + if (conn->linked) { + conn->writing_to_linked_conn = 0; + if (conn->linked_conn) + connection_stop_reading_from_linked_conn(conn->linked_conn); + } else { + if (event_del(conn->write_event)) + log_warn(LD_NET, "Error from libevent setting write event state for %d " + "to unwatched: %s", + conn->s, + tor_socket_strerror(tor_socket_errno(conn->s))); + } } /** Tell the main loop to start notifying <b>conn</b> of any write events. */ @@ -387,11 +430,58 @@ connection_start_writing(connection_t *conn) tor_assert(conn); tor_assert(conn->write_event); - if (event_add(conn->write_event, NULL)) - log_warn(LD_NET, "Error from libevent setting write event state for %d " - "to watched: %s", - conn->s, - tor_socket_strerror(tor_socket_errno(conn->s))); + if (conn->linked) { + conn->writing_to_linked_conn = 1; + if (conn->linked_conn && + connection_should_read_from_linked_conn(conn->linked_conn)) + connection_start_reading_from_linked_conn(conn->linked_conn); + } else { + if (event_add(conn->write_event, NULL)) + log_warn(LD_NET, "Error from libevent setting write event state for %d " + "to watched: %s", + conn->s, + tor_socket_strerror(tor_socket_errno(conn->s))); + } +} + +/** DOCDOC*/ +void +connection_start_reading_from_linked_conn(connection_t *conn) +{ + tor_assert(conn); + tor_assert(conn->linked == 1); + + if (!conn->active_on_link) { + conn->active_on_link = 1; + smartlist_add(active_linked_connection_lst, conn); + if (!called_loop_once) { + /* This is the first event on the list; we won't be in LOOP_ONCE mode, + * so we need to make sure that the event_loop() actually exits at the + * end of its run through the current connections and + * lets us activate read events for linked connections. */ + struct timeval tv = { 0, 0 }; + event_loopexit(&tv); + } + } else { + tor_assert(smartlist_isin(active_linked_connection_lst, conn)); + } +} + +/** DOCDOC*/ +void +connection_stop_reading_from_linked_conn(connection_t *conn) +{ + tor_assert(conn); + tor_assert(conn->linked == 1); + + if (conn->active_on_link) { + conn->active_on_link = 0; + /* XXXX020 maybe we should keep an index here so we can smartlist_del + * cleanly. */ + smartlist_remove(active_linked_connection_lst, conn); + } else { + tor_assert(!smartlist_isin(active_linked_connection_lst, conn)); + } } /** Close all connections that have been scheduled to get closed */ @@ -402,7 +492,7 @@ close_closeable_connections(void) for (i = 0; i < smartlist_len(closeable_connection_lst); ) { connection_t *conn = smartlist_get(closeable_connection_lst, i); if (conn->conn_array_index < 0) { - connection_unlink(conn, 0); /* blow it away right now */ + connection_unlink(conn); /* blow it away right now */ } else { if (!conn_close_if_marked(conn->conn_array_index)) ++i; @@ -500,7 +590,7 @@ conn_close_if_marked(int i) assert_all_pending_dns_resolves_ok(); log_debug(LD_NET,"Cleaning up connection (fd %d).",conn->s); - if (conn->s >= 0 && connection_wants_to_flush(conn)) { + if ((conn->s >= 0 || conn->linked_conn) && connection_wants_to_flush(conn)) { /* s == -1 means it's an incomplete edge connection, or that the socket * has already been closed as unflushable. */ int sz = connection_bucket_write_limit(conn); @@ -512,7 +602,21 @@ conn_close_if_marked(int i) conn->s, conn_type_to_string(conn->type), conn->state, (int)conn->outbuf_flushlen, conn->marked_for_close_file, conn->marked_for_close); - if (connection_speaks_cells(conn)) { + if (conn->linked_conn) { + retval = move_buf_to_buf(conn->linked_conn->inbuf, conn->outbuf, + &conn->outbuf_flushlen); + if (retval >= 0) { + /* The linked conn will notice that it has data when it notices that + * we're gone. */ + connection_start_reading_from_linked_conn(conn->linked_conn); + } + /* XXXX020 Downgrade to debug. */ + log_info(LD_GENERAL, "Flushed last %d bytes from a linked conn; " + "%d left; flushlen %d; wants-to-flush==%d", retval, + (int)buf_datalen(conn->outbuf), + (int)conn->outbuf_flushlen, + connection_wants_to_flush(conn)); + } else if (connection_speaks_cells(conn)) { if (conn->state == OR_CONN_STATE_OPEN) { retval = flush_buf_tls(TO_OR_CONN(conn)->tls, conn->outbuf, sz, &conn->outbuf_flushlen); @@ -553,7 +657,7 @@ conn_close_if_marked(int i) conn->marked_for_close); } } - connection_unlink(conn, 1); /* unlink, remove, free */ + connection_unlink(conn); /* unlink, remove, free */ return 1; } @@ -1270,8 +1374,14 @@ do_main_loop(void) /* Make it easier to tell whether libevent failure is our fault or not. */ errno = 0; #endif - /* poll until we have an event, or the second ends */ - loop_result = event_dispatch(); + /* All active linked conns should get their read events activated. */ + SMARTLIST_FOREACH(active_linked_connection_lst, connection_t *, conn, + event_active(conn->read_event, EV_READ, 1)); + called_loop_once = smartlist_len(active_linked_connection_lst) ? 1 : 0; + + /* poll until we have an event, or the second ends, or until we have + * some active linked connections to triggger events for. */ + loop_result = event_loop(called_loop_once ? EVLOOP_ONCE : 0); /* let catch() handle things like ^c, and otherwise don't worry about it */ if (loop_result < 0) { @@ -1601,6 +1711,8 @@ tor_init(int argc, char *argv[]) time_of_process_start = time(NULL); if (!closeable_connection_lst) closeable_connection_lst = smartlist_create(); + if (!active_linked_connection_lst) + active_linked_connection_lst = smartlist_create(); /* Initialize the history structures. */ rep_hist_init(); /* Initialize the service cache. */ @@ -1673,6 +1785,7 @@ tor_free_all(int postfork) tor_tls_free_all(); /* stuff in main.c */ smartlist_free(closeable_connection_lst); + smartlist_free(active_linked_connection_lst); tor_free(timeout_event); /* Stuff in util.c */ escaped(NULL); |