From b79376c3699e97a4dc5434d6f85a14c533e26f65 Mon Sep 17 00:00:00 2001 From: Philipp Marek Date: Tue, 13 Dec 2022 09:45:16 +0100 Subject: [PATCH] When sending large buffers to a socket, use MSG_MORE. --- contrib/sb-bsd-sockets/sockets.lisp | 1 + src/code/fd-stream.lisp | 30 ++++++++++++++++++++------- src/code/unix.lisp | 32 +++++++++++++++++++++++++++++ src/cold/exports.lisp | 4 +++- 4 files changed, 59 insertions(+), 8 deletions(-) diff --git a/contrib/sb-bsd-sockets/sockets.lisp b/contrib/sb-bsd-sockets/sockets.lisp index b8f265892e..424f20bc5e 100644 --- a/contrib/sb-bsd-sockets/sockets.lisp +++ b/contrib/sb-bsd-sockets/sockets.lisp @@ -286,6 +286,7 @@ request an input stream and get an output stream in response\)." :name (format nil "socket~@[ ~A~]~@[, peer: ~A~]" (socket-namestring socket) (socket-peerstring socket)) + :class 'sb-sys:socket-stream :dual-channel-p t :input input :output output diff --git a/src/code/fd-stream.lisp b/src/code/fd-stream.lisp index c4a5922542..9f271e7f07 100644 --- a/src/code/fd-stream.lisp +++ b/src/code/fd-stream.lisp @@ -162,6 +162,10 @@ (char-size 1 :type (or fixnum function)) (output-bytes #'ill-out :type function)) +(defstruct (socket-stream + (:constructor %make-socket-stream) + (:include fd-stream))) + (defun fd-stream-bivalent-p (stream) (eq (fd-stream-element-mode stream) :bivalent)) @@ -264,7 +268,7 @@ (length (- end start))) (cond ((and (not (fd-stream-serve-events stream)) (>= length buffer-length)) - (flush-output-buffer stream) + (flush-output-buffer stream t) (finish-writing-sequence thing stream start end) (return-from buffer-output)) ((plusp space) @@ -284,12 +288,23 @@ (define-symbol-macro +write-failed+ "Couldn't write to ~S") +(declaim (inline fd-stream-write)) +(defun fd-stream-write (stream sap offset len &optional more?) + (if (typep stream 'socket-stream) + (sb-unix:unix-sendto (fd-stream-fd stream) sap + offset len + (if more? + sb-unix:msg-more + 0)) + (sb-unix:unix-write (fd-stream-fd stream) sap + offset len))) + ;;; Flush the current output buffer of the stream, ensuring that the ;;; new buffer is empty. Returns (for convenience) the new output ;;; buffer -- which may or may not be EQ to the old one. If the is no ;;; queued output we try to write the buffer immediately -- otherwise ;;; we queue it for later. -(defun flush-output-buffer (stream) +(defun flush-output-buffer (stream &optional more?) (let ((obuf (fd-stream-obuf stream))) (when obuf (let ((head (buffer-head obuf)) @@ -311,8 +326,8 @@ (loop (let ((length (- tail head))) (multiple-value-bind (count errno) - (sb-unix:unix-write (fd-stream-fd stream) (buffer-sap obuf) - head length) + (fd-stream-write stream (buffer-sap obuf) + head length more?) (flet ((queue-or-wait () (if (fd-stream-serve-events stream) (return (%queue-and-replace-output-buffer stream)) @@ -347,7 +362,7 @@ (loop (let ((length (- end start))) (multiple-value-bind (count errno) - (sb-unix:unix-write (fd-stream-fd stream) sequence start length) + (fd-stream-write stream sequence start length) (flet ((wait () (or (wait-until-fd-usable (fd-stream-fd stream) :output (fd-stream-timeout stream) @@ -408,7 +423,7 @@ (declare (index head length)) (aver (>= length 0)) (multiple-value-bind (count errno) - (sb-unix:unix-write (fd-stream-fd stream) (buffer-sap buffer) + (fd-stream-write stream (buffer-sap buffer) head length) (cond ((eql count length) ;; Complete write, see if we can do another right @@ -455,7 +470,7 @@ (let ((length (- end start))) (synchronize-stream-output stream) (multiple-value-bind (count errno) - (sb-unix:unix-write (fd-stream-fd stream) thing start length) + (fd-stream-write stream thing start length) (cond ((eql count length) ;; Complete write -- done! ) @@ -2270,6 +2285,7 @@ (error "File descriptor must be opened either for input or output."))) (let* ((constructor (ecase class (fd-stream '%make-fd-stream) + (socket-stream '%make-socket-stream) (form-tracking-stream '%make-form-tracking-stream))) (element-mode (stream-element-type-stream-element-mode element-type)) (stream (funcall constructor diff --git a/src/code/unix.lisp b/src/code/unix.lisp index 49777b896c..a71231ad97 100644 --- a/src/code/unix.lisp +++ b/src/code/unix.lisp @@ -350,6 +350,38 @@ corresponds to NAME, or NIL if there is none." (system-area-pointer (%write buf))))) + +(defconstant msg-more (+ 0 #+linux #x8000)) ; will send more data + +;;; UNIX-SENDTO accepts a file descriptor, a buffer, an offset, the +;;; length to write, and a bitmask for flags. +;;; It attempts to write len bytes to the device +;;; associated with fd from the buffer starting at offset. It returns +;;; the actual number of bytes written. +;;; The sockaddr argument allowed by unix "sendto" is currently not available. +#-win32 +(defun unix-sendto (fd buf offset len flags) + (declare (optimize (debug 1))) + (declare (type unix-fd fd) + (type (unsigned-byte 32) offset len flags)) + (flet ((%sendto (sap) + (declare (system-area-pointer sap)) + (int-syscall ("sendto" + int (* char) int int (* char) int) + fd + (with-alien ((ptr (* char) sap)) + (addr (deref ptr offset))) + len + flags + (int-sap 0) 0))) + (etypecase buf + ((simple-array * (*)) + (with-pinned-objects (buf) + (%sendto (vector-sap buf)))) + (system-area-pointer + (%sendto buf))))) + + ;;; Set up a unix-piping mechanism consisting of an input pipe and an ;;; output pipe. Return two values: if no error occurred the first ;;; value is the pipe to be read from and the second is can be written diff --git a/src/cold/exports.lisp b/src/cold/exports.lisp index efc5e9c708..8c9f87ed2d 100644 --- a/src/cold/exports.lisp +++ b/src/cold/exports.lisp @@ -98,7 +98,7 @@ interface stability.") "UNIX-OPEN" "UNIX-OPENDIR" "UNIX-PATHNAME" "UNIX-PID" "UNIX-PIPE" "UNIX-POLL" "UNIX-SIMPLE-POLL" "UNIX-READ" "UNIX-READDIR" "UNIX-READLINK" "UNIX-REALPATH" - "UNIX-RENAME" "UNIX-STAT" "UNIX-UID" + "UNIX-RENAME" "UNIX-SENDTO" "UNIX-STAT" "UNIX-UID" "UNIX-UNLINK" "UNIX-WRITE" "WCONTINUED" "WNOHANG" "WUNTRACED" "W_OK" "X_OK" @@ -107,6 +107,7 @@ interface stability.") "CLOCK-THREAD-CPUTIME-ID" "CLOCK-PROCESS-CPUTIME-ID" "CLOCK-REALTIME" + "MSG-MORE" ;; signals @@ -1603,6 +1604,7 @@ SB-KERNEL) have been undone, but probably more remain.") "EXTERN-ALIEN-NAME" "EXIT-CODE" "FD-STREAM" "FD-STREAM-FD" "FD-STREAM-P" + "SOCKET-STREAM" "SOCKET-STREAM-P" "FIND-DYNAMIC-FOREIGN-SYMBOL-ADDRESS" "FIND-FOREIGN-SYMBOL-ADDRESS" #+(and win32 x86-64) "FOREIGN-HEAP-CORRUPTION"