From: Karol Skocik
Subject: sbcl - how to get a REPL for a created thread?
Date: 
Message-ID: <1139616680.997838.98530@o13g2000cwo.googlegroups.com>
Hi guys,
  I am on SBCL 0.98/Linux and want to do one nice thing, but it seems
quite tricky :

I want to make a new thread, a then - either create a new slime buffer
with REPL talking to that new thread - or - having a normal REPL, call
some function which will connect to that thread, (also grabbing
output). after :quit, the function should quit to normal REPL (or close
slime buffer)

Is something like this possible? Having just that function will be
perfect, changing slime/emacs is too much for me, but if somebody
already did that...

Any ideas how to make such a useful thing? 
Thanks,
  Karol

From: ·············@specastro.com
Subject: Re: sbcl - how to get a REPL for a created thread?
Date: 
Message-ID: <1139621168.742078.306220@g43g2000cwa.googlegroups.com>
Karol,

With that version of SBCL, you need to get the CVS version of Slime.
There was a problem with how Slime initialized some threads inside
SBCL. I reported this on the slime list about a month ago and Helmut
fixed it for me.  Additionally, the Slime startup sequence is slightly
different, so here's the procedure for doing what youy want to do.

The steps you need to do are:

1. In a shell, start SBCL up manually.  You'll then need to start Swank
up manually.  At the SBCL REPL in your shell, you'll need to do these
steps:

- Load the Swank loader lisp file:

(load "swank-loader.lisp") ; or wherever you place slime

- Have Swank load the rest of Swank in for you:

(swank-loader:load-swank)

- Optionally, load the Slime presentation code:

(load "present") ; because I have mine compiled

- Finally, you need to start Swank running within SBCL.  I have this
convenience function loaded in from my lisp startup file:

(defun start-swank (&key (port swank::default-server-port)
		    (style swank:*communication-style*)
		    (dont-close t))
  "Start the SLIME swank server, listening on PORT.  Multiple
connections are allowed."
  (swank:create-server :port port
		       :style style
		       :dont-close dont-close))

So, I just start up swank with:

(start-swank) ; this starts up Swank with the Slime default port.

2. In Emacs then, you need to start up Slime, but you need to use
slime-connect instead of the slime command:

M-x slime-connect

It will prompt you for several parameters, like the port.  If you
started up Swank like I showed above, you can just keep hitting
<enter>.

At this point, Slime should start up.

3.  So you now have Slime with 1 Slime REPL.  To get additional Slime
REPLs, just do M-x slime-connect again, but you'll want to make sure
that you answer N to the question about closing the current connection.
 If you do it correctly, then you should have a second REPL buffer and
you can use the normal Emacs buffer commands or windowing to manager
your REPLs.  You can also use the slime connection chooser, but I find
it to work a little odd.

I've used this many times in debugging socket and threading code and it
works pretty cleanly.

One thing that might not be obvious is that if you use the Slime
compilation commands and what not in a Lisp buffer, that those commands
go to the last REPL created, unless you use the Slime connection thingy
to change the default REPL.

Hope this helps.

Glenn
From: Karol Skocik
Subject: Re: sbcl - how to get a REPL for a created thread?
Date: 
Message-ID: <1139623191.019205.206940@o13g2000cwo.googlegroups.com>
Perfect, I am going to try it. 

Thanks and have a nice day,
  Karol
From: Karol Skocik
Subject: Re: sbcl - how to get a REPL for a created thread?
Date: 
Message-ID: <1139660884.027266.236180@g47g2000cwa.googlegroups.com>
Glenn, I hope that you can help me with last one thing. Now I can
create more REPLs, but how do I connect some REPL to the input/output
of some thread? I mean :

I have a server, which creates thread for communication with client
like this :

(defun start-server (&key client-function
		     (port +listener-port+)
		     (max-connections +listener-max-waiting-connections+))
  (when (and (null *listener-thread*) (null *listener-socket*))
    (let* ((listener-socket (make-tcp-socket))
	   (listener-thread (lambda ()
			      (handler-case
				  (progn
				    (setf (sockopt-reuse-address listener-socket) t)
				    (socket-bind listener-socket sb-bsd-sockets::inet-address-any
port)
				    (socket-listen listener-socket max-connections)
				    (loop
				     (multiple-value-bind (client-socket peer)
					 (socket-accept listener-socket)
				       (make-client-thread client-socket peer client-function))))
				(socket-error (condition)
				  (break "Listener socket : ~a" condition)
				  (socket-close listener-socket)
				  (setf *listener-socket* nil
					*listener-thread* nil))))))
      (setf *listener-socket* listener-socket
	    *listener-thread* (make-thread listener-thread
					   :name (format nil "tactix listener on port ~a" port))))))

and I have (now only bare bones) of the player-worker like this :

(defun player-worker (socket peer)
  (declare (ignore peer))
  (handle-socket-errors socket
    (let ((str (socket-make-stream socket :input t :output t
				   :buffering :full :element-type 'unsigned-byte)))
      (handler-case
	  (loop
	   (format t "playershell> ")
	   (let ((command (read)))
	     (format t "~a~%" command)))

;	   (with-mutex (*shell-mutex*)
;	     (condition-wait *shell-waitqueue* *shell-mutex*)
;	     (let ((function-name (car *shell-command*)))
;	       (apply (funcall function-name)
;		      (append (list str 0) (cdr *shell-command*))))))
;	     (apply (car *shell-command*)
;		    `(,str 0 ,@(cdr *shell-command*)))))

	(error ()
	  (make-condition 'end-of-file
			  :format-control "connection lost unexpextedly"))))))

I was hoping to change the additional REPL to the one which can read
s-exprs using (read) and output to that reply using (format t "...") -
so I can directly invoke calls on server which communicate with client,
and see what is happening interactively.

How to do that, if it's possible?

Thanks for any ideas,
  Karol
From: ·············@specastro.com
Subject: Re: sbcl - how to get a REPL for a created thread?
Date: 
Message-ID: <1139704019.452921.214810@g43g2000cwa.googlegroups.com>
Karol,

1) I discussed something very similar to what I *think* you're trying
to do on the sbcl list.  It may give you a few more web pointers to
help you out:

http://thread.gmane.org/gmane.lisp.steel-bank.general/1029

If you have questions about the methodology of your particular
approach, please indicate what you're actually trying to accomplish.

2) I'm not quite sure what your exact question is, but you won't be
able to directly use (format t ...) in multi-threaded code because the
output won't go anywhere you can easily see it.  FORMAT with t goes to
the dynamic (or special) variable *STANDARD-OUTPUT*.  Multi-threaded CL
implementations usually have a different value of *standard-output* for
each REPL (which is why the i/o from different REPLs don't stomp on
each other).  To be able to see debugging output go to a common stream,
such as the REPL that you launch your threads from, you'll need to
lexically capture the value of the REPL's *standard-output* and use
that inside the function running in the thread.

For example, something along the lines of:

(defun spawn-a-thread ()
  (let ((debug-output *standard-output*))
    (loop for i from 1 to 10 do
      (sb-thread:make-thread #'(lambda () (format debug-output
"thread~%"))))))

This is discussed in the Common Lisp Cookbook entry on threads (see
"Where's My Output" and "Per Thread State"):

http://cl-cookbook.sourceforge.net/process.html

and in the SBCL manual on how it treats special variables in
multi-threaded code:

http://www.sbcl.org/manual/Special-Variables.html

Glenn
From: Karol Skocik
Subject: Re: sbcl - how to get a REPL for a created thread?
Date: 
Message-ID: <1139711147.179786.295200@z14g2000cwz.googlegroups.com>
Thank you, I am going to check out :)

Best wishes,
  Karol