# HG changeset patch
# User Steve Losh <steve@stevelosh.com>
# Date 1489513574 0
# Node ID 15eabbd388eaac9f3a6dc6461a38b38ede98b41a
# Parent  eee835835b39fab07bba9d59e5152cdf1bad6a90
Add a restart for ports already in use

diff -r eee835835b39 -r 15eabbd388ea src/server.lisp
--- a/src/server.lisp	Tue Mar 14 13:33:00 2017 +0000
+++ b/src/server.lisp	Tue Mar 14 17:46:14 2017 +0000
@@ -66,6 +66,12 @@
 ;;;; Server
 (defvar *server-thread* nil)
 
+(define-condition port-error (error)
+  ((port :initarg :port))
+  (:report (lambda (err stream)
+             (format stream "Port ~D is already in use"
+                     (slot-value err 'port)))))
+
 (defmacro run-in-thread (thread-name &rest body)
   "Run `body` in a thread called `name` (usually).  Return the thread.
 
@@ -78,6 +84,26 @@
       (progn (funcall thunk) nil)
       (bt:make-thread thunk :name ,thread-name))))
 
+(defun socket-listen (address port)
+  (handler-case
+      (values (usocket:socket-listen
+                address port
+                :reuse-address t
+                ;; have to specify element-type here too because usocket+CCL
+                ;; fucks it up if you only specify it in socket-accept
+                :element-type '(unsigned-byte 8))
+              port)
+    (usocket:address-in-use-error ()
+      (restart-case (error 'port-error :port port)
+        (use-different-port (new-port)
+          :report (lambda (stream)
+                    (format stream "Select a port other than ~D" port))
+          :interactive (lambda ()
+                         (format *query-io* "~&Type a form to be evaluated:~%")
+                         (finish-output *query-io*)
+                         (list (read *query-io*)))
+          (socket-listen address new-port))))))
+
 
 (defun accept-connections (server-socket)
   "Accept connections to the server and spawn threads to handle each."
@@ -98,12 +124,7 @@
 (defun start-server (&key (address "127.0.0.1") (port 8675))
   "Fire up a server thread that will listen for connections."
   (log-message "Starting server...~%")
-  (let ((socket (usocket:socket-listen
-                  address port
-                  :reuse-address t
-                  ;; have to specify element-type here too because usocket+CCL
-                  ;; fucks it up if you only specify it in socket-accept
-                  :element-type '(unsigned-byte 8))))
+  (multiple-value-bind (socket port) (socket-listen address port)
     (setf *server-thread*
           (run-in-thread (format nil "NREPL Server (~a/~a)" address port)
             (unwind-protect