jcs
/subtext
/amendments
/152
telnet: Fast-track single-byte writes, spin a bit after sending
If our send succeeds and returns quickly, we can avoid having to
return and wait for uthread to cycle back to us. Since sends can take
a very long time, we can't simply switch to synchronous TCPSend calls
or we'll hang forever on a dead connection.
We can also avoid zeroing the full tcp_wds structure each time, since
it is zeroed at the node allocation and we only need to zero wds[1]
ptr and length.
jcs made amendment 152 over 2 years ago
--- telnet.c Thu Jun 16 12:54:06 2022
+++ telnet.c Fri Jun 17 13:39:41 2022
@@ -301,6 +301,10 @@ telnet_idle(void)
break;
}
+ /*
+ * Send buffered data any time we can, the user might not be
+ * in an explicit session_flush() like during chat
+ */
telnet_output(node->session);
break;
}
@@ -568,6 +572,7 @@ telnet_output(struct session *session)
struct telnet_node *node = (struct telnet_node *)session->cookie;
short n, error;
unsigned char c;
+ unsigned long now;
if (session->obuflen == 0 || session->ending)
return 0;
@@ -592,29 +597,35 @@ process_result:
return node->obuflen;
}
- /* copy obuf to node buffer, escaping IACs */
- for (node->escaped_obuflen = 0, node->obuflen = 0;
- node->obuflen < session->obuflen && node->escaped_obuflen < sizeof(node->obuf);
- node->obuflen++) {
- c = session->obuf[node->obuflen];
-
- if (!node->sending_iac && c == IAC) {
- node->obuf[node->escaped_obuflen++] = IAC;
- node->obuf[node->escaped_obuflen++] = IAC;
- } else
- node->obuf[node->escaped_obuflen++] = c;
+ if (session->obuflen == 1 && session->obuf[0] != IAC) {
+ node->obuf[0] = session->obuf[0];
+ node->escaped_obuflen = node->obuflen = 1;
+ } else {
+ /* copy obuf to node buffer, escaping IACs */
+ for (node->escaped_obuflen = 0, node->obuflen = 0;
+ node->obuflen < session->obuflen &&
+ node->escaped_obuflen < sizeof(node->obuf);
+ node->obuflen++) {
+ c = session->obuf[node->obuflen];
+
+ if (!node->sending_iac && c == IAC) {
+ node->obuf[node->escaped_obuflen++] = IAC;
+ node->obuf[node->escaped_obuflen++] = IAC;
+ } else
+ node->obuf[node->escaped_obuflen++] = c;
+ }
}
-
+
/*
* _TCPSend only knows how many wds pointers were passed in when it
- * reads the next one and its pointer is zero (or size is zero?), so
- * even though we're only sending one wds, memory after wds[0]
- * has to be zeroed out.
+ * reads the next one and its pointer is zero (or size is zero?)
*/
- memset(&node->tcp_wds, 0, sizeof(node->tcp_wds));
node->tcp_wds[0].ptr = (Ptr)&node->obuf;
node->tcp_wds[0].length = node->escaped_obuflen;
+ node->tcp_wds[1].ptr = 0;
+ node->tcp_wds[1].length = 0;
+ now = Ticks;
error = _TCPSend(&node->send_pb, node->stream, node->tcp_wds, nil, nil,
true);
if (error) {
@@ -622,10 +633,12 @@ process_result:
session->ending = 1;
}
- /* if we sent quickly enough, we won't have to cycle again */
- if (node->send_pb.ioResult <= 0)
- goto process_result;
-
+ /* if we can send in less than 500ms, avoid a uthread switch */
+ while (Ticks - now <= 30) {
+ if (node->send_pb.ioResult <= 0)
+ goto process_result;
+ }
+
return 0;
}