As far as I can tell, the only way a Swank connection object with style :fd-handler will ever be closed due to EOF (with close-connection) is if either the fd-handler implementation detects the EOF state and knows how to destroy Swank connections, or a condition is raised in the dynamic context of with-reader-error-handler. Looking at the topmost form in that context, however, it can only happen if (listen socket) answers T, so that the attempt to read can proceed and raise an eof-error, which gets wrapped up, etc.
I modified process-available-input to test (listen socket) only after calling FN at least once; after all, it always returns T when the handler is called for a normal input state. Without this patch, my fd-handler backend gets into an endless loop calling the handler in install-fd-handler (with the :eof state); with this patch, the socket is closed, removed from *connections*, and from the fd-handler backend properly.
Attached is the patch.
On Thu, 2005-12-01 at 02:47 -0600, I wrote:
As far as I can tell, the only way a Swank connection object with style :fd-handler will ever be closed due to EOF (with close-connection) is if either the fd-handler implementation detects the EOF state and knows how to destroy Swank connections, or a condition is raised in the dynamic context of with-reader-error-handler. Looking at the topmost form in that context, however, it can only happen if (listen socket) answers T, so that the attempt to read can proceed and raise an eof-error, which gets wrapped up, etc.
I modified process-available-input to test (listen socket) only after calling FN at least once; after all, it always returns T when the handler is called for a normal input state. Without this patch, my fd-handler backend gets into an endless loop calling the handler in install-fd-handler (with the :eof state); with this patch, the socket is closed, removed from *connections*, and from the fd-handler backend properly.
Attached is the patch.
I thought maybe the DO loop was a bad idea, so here is a version that uses LOOP.
* Stephen Compall [2005-12-28 01:52+0100] writes:
On Thu, 2005-12-01 at 02:47 -0600, I wrote:
As far as I can tell, the only way a Swank connection object with style :fd-handler will ever be closed due to EOF (with close-connection) is if either the fd-handler implementation detects the EOF state and knows how to destroy Swank connections, or a condition is raised in the dynamic context of with-reader-error-handler. Looking at the topmost form in that context, however, it can only happen if (listen socket) answers T, so that the attempt to read can proceed and raise an eof-error, which gets wrapped up, etc.
I modified process-available-input to test (listen socket) only after calling FN at least once; after all, it always returns T when the handler is called for a normal input state. Without this patch, my fd-handler backend gets into an endless loop calling the handler in install-fd-handler (with the :eof state); with this patch, the socket is closed, removed from *connections*, and from the fd-handler backend properly.
Attached is the patch.
I thought maybe the DO loop was a bad idea, so here is a version that uses LOOP.
It's not the DO loop, it's because I can't reproduce the behavior. When I do a M-x slime-disconnect, everything seems to get closed just fine. Not that I can explain why open-stream-p in process-available-input returns true in that case, but apparently it does.
So, how to you actually produce the endless loop?
Sorry for not replying; I didn't get your reply by email, and only saw it when browsing gmane to check for just such an instance.
Helmut Eller writes:
It's not the DO loop, it's because I can't reproduce the behavior. When I do a M-x slime-disconnect, everything seems to get closed just fine. Not that I can explain why open-stream-p in process-available-input returns true in that case, but apparently it does.
So, how to you actually produce the endless loop?
In SBCL 0.9.8, here's what happens:
(defmacro debug-output (form) (let ((var (gensym))) `(let ((,var ,form)) (format t "~S: ~S~%" ',form ,var) ,var)))
(defun swank::process-available-input (stream fn) (loop while (and (debug-output (open-stream-p stream)) (debug-output (listen stream))) do (funcall fn)))
Now, in the SLIME REPL, you eval something like T and get this in sbcl's native terminal:
(OPEN-STREAM-P STREAM): T (LISTEN STREAM): T (OPEN-STREAM-P STREAM): T (LISTEN STREAM): NIL
This is fine. serve-event wakes up, and there is indeed input available, so LISTEN should answer T. Then it returns to process-available-input, and there is no more input, so it answers NIL.
Something weird happens when I slime-disconnect in emacs, though:
(OPEN-STREAM-P STREAM): T (LISTEN STREAM): T ;; Event history start: ...
That is, LISTEN answers T even though you are at EOF, which is after all why serve-event woke up. I have no idea how that worked out.
Now, I turn to a similar definition in CLISP 2.37, with communication style set to :FD-HANDLER [1]:
(defun swank::process-available-input (stream fn) (loop while (and (debug-output (open-stream-p stream)) (debug-output (listen stream))) do (funcall fn)) (sleep 10))
For ordinary evaluation:
(OPEN-STREAM-P STREAM): T (LISTEN STREAM): T (OPEN-STREAM-P STREAM): T (LISTEN STREAM): NIL
For slime-disconnect:
(OPEN-STREAM-P STREAM): T (LISTEN STREAM): NIL
Repeated every 10 seconds forever. This behavior seems more ANSI, by http://www.xach.com/clhs?q=listen :
Returns true if there is a character immediately available from input-stream; otherwise, returns false. On a non-interactive input-stream, listen returns true except when at end of file[1]. If an end of file is encountered, listen returns false.
[1] To actually test FD-HANDLER in CLISP, grab ASDF package nocandy-util, and the attached patch to swank-clisp.lisp; see http://nocandysw.com/util . The patch and fd-dispatch are not quite ready for mass consumption; for example, the way fd-handlers are removed just works enough to implement remove-fd-handlers as needed by Swank.
On Tue, 2006-01-17 at 21:28 -0600, Stephen Compall wrote:
That is, LISTEN answers T even though you are at EOF, which is after all why serve-event woke up. I have no idea how that worked out.
CVS SBCL should soon no longer do this; that is, it will answer NIL at EOF, thereby matching the behavior of CLISP by bouncing forever at disconnect. Due to the nature of this particular bounce, you can still connect and do work; it just chews CPU. Attached is a slightly-improved version of the previous patch, which I am currently using.