[Ecls-list] Problems with slime

Ram Krishnan kriyative at gmail.com
Tue Feb 9 19:38:49 UTC 2010


Tobias C. Rittweiler wrote:
> "Tobias C. Rittweiler" writes:
>
>    
>> Juan Jose Garcia-Ripoll writes:
>>
>>      
>>> Slime gets my computer at 100% activity because it has some strange way of
>>> waiting for input: this is a loop over streams, inifite, using
>>> read-char-no-hang without ever waiting. I am sure there are better ways of
>>> doing this. This is in swank-backend.lisp -- do all implementations have
>>> this problem?
>>>
>>> (defun wait-for-streams (streams timeout)
>>>    (loop
>>>     (when (check-slime-interrupts) (return :interrupt))
>>>     (let ((ready (remove-if-not #'stream-readable-p streams)))
>>>       (when ready (return ready)))
>>>     (when timeout (return nil))
>>>     (sleep 0.1)))
>>>        
>> The problem is that not more advanced ways are implemented. Slime can
>> also me made to use threads, and serve-event, but ECL's backend does not
>> support that.
>>
>> I tried to do both in past, but encountered non-obvious problems. I'll
>> polish up, and commit the work in progress.
>>      
>
> OK, juanjo, I committed the work in progress.
>
> The threading stuff is defined at the end of swank-ecl.lisp.
>
> To activate it, you have to put
>
>     ;; for debugging messages of the swank server
>    (setq swank:*log-events* t)
>    (setq swank:*communication-style* :spawn)
>
> into ~/.swank.lisp
>
> Also make sure to put
>
>    (slime-setup '(slime-fancy))
>
> into your .emacs, after (require 'slime).
>
>
> If you start slime, you'll see it gets stuck.
>
> It gets stuck in ENCODE-MESSAGE in swank-rpc.lisp, before writing to the
> STREAM parameter.
>
> The stream that is passed as STREAM parameters comes from:
>
>    make-socket-io-stream in swank-ecl.lisp
>      <- accept-connection in swank-ecl.lisp
>           <- accept-authenticated-connection in swank.lisp
>                <- serve-connection in swank.lisp
>
> SERVE-CONNECTION will store the result of MAKE-SOCKET-IO-STREAM into the
> SOCKET-IO slot of a CONNECTION.
>
> DISPATCH-EVENT (which is run in a background control thread) will call
> ENCODE-MESSAGE with the sockets-io stream.
>
> The first thing the Emacs side will do once a socket connection has been
> established is to send
>
>    (:emacs-rex
>      (swank:connection-info)      ;<-- expression to be evaluated
>      "COMMON-LISP-USER" t 1)      ;<-- metainformation
>
> over to the Swank server. You can see that in the *slime-events*
> buffer. (*slime-events* contains the events sent/received by the Emacs
> client side; if swank:*log-events* is T, the Common Lisp swank server
> will print events sent/received to error-output.
>
> :EMACS-REX is a Remote-EXpression, or in other words: an RPC.
>
> I.e. the Emacs side wants (SWANK:CONNECTION-INFO) to be evaluated by the
> swank server.
>
> Due to *LOG-EVENTS*, the last thing you'll see in *inferior-lisp* is
>
>    WRITE: (:return (:ok (:pid 17981
>                          :style :spawn
>                          :lisp-implementation (:type "ECL"
>                                                :name "ECL"
>                                                :version "10.2.1")
>                          ...)))
>
> which is exactly what the Emacs side expects.
>
> But because *slime-events* does only contain the (:EMACS-REX ...), but
> no (:RETURN (:OK ...)), this means that this return value is not
> transfered over the socket.
>
> Hm, I hope that's enough to get you going.
>
>    -T.
>
>    
Tobias,

I ran into this issue on MacOSX, and it turned out to be because SLIME 
has multiple threads (control and reader threads) which want to read and 
write the connection socket stream. The issue (on MacOSX atleast) was 
that the standard buffered I/O library (which handles the FILE* data 
structure and functions), has been made thread safe, and locks the file 
stream when any thread uses it. The tragedy is that there seems to be 
only one lock, for both read and write, which means if one thread locks 
the stream to read then another thread cannot do anything with it, not 
even write. Anyway, the hack I came up with was to modify the swank-ecl 
backend to return a two-way-stream with two independent file-streams for 
the two directions:

(defun socket-make-stream (socket &rest args)
   (let ((stream (apply 'sb-bsd-sockets:socket-make-stream socket args)))
     (setf (slot-value socket 'sb-bsd-sockets::stream) nil)
     stream))

(defun make-socket-io-stream (socket)
   (case (preferred-communication-style)
     (:fd-handler
      (sb-bsd-sockets:socket-make-stream socket
                                         :output t
                                         :input t
                                         :element-type 'base-char))
     (:spawn
      (let* ((input (socket-make-stream socket
                                        :direction :input
                                        :element-type 'base-char))
             (output (socket-make-stream socket
                                         :direction :output
                                         :element-type 'base-char))
             (stream (make-two-way-stream input output)))
        (setf (slot-value socket 'sb-bsd-sockets::stream) stream)
        stream))))

Admittedly this hack is ugly, as it knows way too much about what the 
underlying socket implementation is doing. The right place to fix this 
is probably in SOCKETS::SOCKET-MAKE-STREAM, based on a DIRECTION keyword 
arg or something.

Here's a link to the complete set of patches:

   
http://github.com/kriyative/ecl-iphone-builder/blob/master/swank-ecl-patches.txt

Anyway, hope this is useful in your efforts.

Cheers,

-ram

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mailman.common-lisp.net/pipermail/ecl-devel/attachments/20100209/058b02a4/attachment.html>


More information about the ecl-devel mailing list