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 3 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;
 }