static int connection_buf_read_from_socket(connection_t *conn,
ssize_t *max_to_read,
int *socket_error);
-static int connection_process_inbuf(connection_t *conn, int package_partial);
static void client_check_address_changed(tor_socket_t sock);
static void set_constrained_socket_buffers(tor_socket_t sock, int size);
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);
- reenable_blocked_connection_schedule();
+ // Double-calls to stop-reading are correlated with stalling for
+ // ssh uploads. Might as well prevent this from happening,
+ // especially the read_blocked_on_bw flag. That was clearly getting
+ // set when it should not be, during an already-blocked XOFF
+ // condition.
+ if (!CONN_IS_EDGE(conn) || !TO_EDGE_CONN(conn)->xoff_received) {
+ conn->read_blocked_on_bw = 1;
+ connection_stop_reading(conn);
+ reenable_blocked_connection_schedule();
+ }
}
/**
(void)ev;
(void)arg;
SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
- if (conn->read_blocked_on_bw == 1) {
+ /* For conflux, we noticed logs of connection_start_reading() called
+ * multiple times while we were blocked from a previous XOFF, and this
+ * was log was correlated with stalls during ssh uploads. So we added
+ * this additional check, to avoid connection_start_reading() without
+ * getting an XON. The most important piece is always allowing
+ * the read_blocked_on_bw to get cleared, either way. */
+ if (conn->read_blocked_on_bw == 1 &&
+ (!CONN_IS_EDGE(conn) || !TO_EDGE_CONN(conn)->xoff_received)) {
connection_start_reading(conn);
- conn->read_blocked_on_bw = 0;
}
+ conn->read_blocked_on_bw = 0;
if (conn->write_blocked_on_bw == 1) {
connection_start_writing(conn);
conn->write_blocked_on_bw = 0;
* connection_*_process_inbuf() function. It also passes in
* package_partial if wanted.
*/
-static int
+int
connection_process_inbuf(connection_t *conn, int package_partial)
{
tor_assert(conn);
int connection_outbuf_too_full(struct connection_t *conn);
int connection_handle_write(struct connection_t *conn, int force);
int connection_flush(struct connection_t *conn);
+int connection_process_inbuf(struct connection_t *conn, int package_partial);
MOCK_DECL(void, connection_write_to_buf_impl_,
(const char *string, size_t len, struct connection_t *conn,
"to watched: %s",
(int)conn->s,
tor_socket_strerror(tor_socket_errno(conn->s)));
+
+ /* Process the inbuf if it is not empty because the only way to empty it is
+ * through a read event or a SENDME which might not come if the package
+ * window is proper or if the application has nothing more for us to read.
+ *
+ * If this is not done here, we risk having data lingering in the inbuf
+ * forever. */
+ if (conn->inbuf && buf_datalen(conn->inbuf) > 0) {
+ connection_process_inbuf(conn, 1);
+ }
}
}