15eabbd388ea default tip

Add a restart for ports already in use
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Tue, 14 Mar 2017 17:46:14 +0000
parents eee835835b39
children (none)
branches/tags default tip
files src/server.lisp

Changes

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