sbcl-devel

Hi all,
Firstly, thanks to all the SBCL developers for all the great work
you're doing.
I'm using SBCL-0.9.5 (x86) with Portable Allegroserve for a commercial
web application and I've run into a bug which causes Lisp to leak
memory not managed by the garbage collector.
The function in question is SB!IMPL::FROB-OUTPUT-LATER in
sys:src;code;fd-stream.lisp, and it has to do with the first clause of
the COND (the following is from 0.9.5, but I believe CVS head has the
same code):
(defun frob-output-later (stream)
(let* ((stuff (pop (fd-stream-output-later stream)))
(base (car stuff))
(start (cadr stuff))
(end (caddr stuff))
(reuse-sap (cadddr stuff))
(length (- end start)))
(declare (type index start end length))
(multiple-value-bind (count errno)
(sb!unix:unix-write (fd-stream-fd stream)
base
start
length)
(cond ((not count)
(if (=3D errno sb!unix:ewouldblock)
(error "Write would have blocked, but SERVER told us to go=
.")
(simple-stream-perror "couldn't write to ~S" stream errno)=
))
((eql count length) ; Hot damn, it worked.
(when reuse-sap
(with-available-buffers-lock ()
(push base *available-buffers*))))
((not (null count)) ; sorta worked..
(push (list base
(the index (+ start count))
end)
(fd-stream-output-later stream))))))
(unless (fd-stream-output-later stream)
(sb!sys:remove-fd-handler (fd-stream-handler stream))
(setf (fd-stream-handler stream) nil)))
In the event that either (error ...) or (simple-stream-perror ...) is
called, then the buffer referred to by BASE and the potential buffers
in the (FD-STREAM-OUTPUT-LATER ...) queue won't get freed.
This probably isn't as big an issue when writing to files, but when a
socket is wrapped in a FD-STREAM (as is the case in portable
allegroserve) and the remote peer closes the connection in the middle
of a conversation, that causes an exception which in turn triggers
this bug. Also, if the client is slow then the OUTPUT-LATER queue
could get quite long and the memory leak that much larger.
One possible fix could be as follows:
:
:
(cond ((not count)
(when reuse-sap
(with-available-buffers-lock ()
(push base *available-buffers*)
(dolist (buf (fd-stream-output-later stream))
(push (car buf) *available-buffers*))
(setf (fd-stream-output-later stream) nil)))
(unless (fd-stream-output-later stream)
(sb!sys:remove-fd-handler (fd-stream-handler stream))
(setf (fd-stream-handler stream) nil))
(if (=3D errno sb!unix:ewouldblock)
(error "Write would have blocked, but SERVER told us to go=
.")
(simple-stream-perror "couldn't write to ~S" stream errno)=
))
:
:
I've only tested this fix minimally, and it's very possible that I've
overlooked something. I look forward to a better fix.
Peace.
-ram