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)
   (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

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

    (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.



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)

(defun make-socket-io-stream (socket)
  (case (preferred-communication-style)
     (sb-bsd-sockets:socket-make-stream socket
                                        :output t
                                        :input t
                                        :element-type 'base-char))
     (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)

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:

Anyway, hope this is useful in your efforts.