Commit 150c006f authored by Ludovic Courtès's avatar Ludovic Courtès
Browse files

kernels: Allow for OS-allocated TCP port numbers.

* jupyter/kernels.scm (allocate-connection): #:first-port defaults to #f.
[allocate-port]: New procedure
[try-connect]: New procedure.
Use it in lieu of 'test-bind', and remove the extra 'zmq-connect'
calls.
(test-bind): Remove.
parent fe70f6da
......@@ -224,22 +224,55 @@ could not be found."
(define* (allocate-connection context transport ip key
#:key
(first-port 1024) identity)
"Allocate ports for TRANSPORT and IP, starting at FIRST-PORT, and bind zmq
sockets for them under CONTEXT. Return a <connection> and a <kernel>: the
connection can be passed to a kernel as its \"connection file\", and the
kernel can be used by the client to talk to it."
first-port identity)
"Allocate ports for TRANSPORT and IP and connect zmq sockets for them under
CONTEXT. Return a <connection> and a <kernel>: the connection can be passed
to a kernel as its \"connection file\", and the kernel can be used by the
client to talk to it.
When FIRST-PORT is true, use TCP ports from FIRST-PORT on. When FIRST-PORT
is false, use an OS-allocated port number, which will hopefully not already
be in use (but this is racy)."
(define (port->uri port)
(string-append transport "://" ip ":"
(number->string port)))
(define (allocate-port)
;; Ask the OS to give us a port number, and return it (jupyter-client
;; does the same in 'connect.py'.) XXX: There's fundamentally a TOCTTOU
;; race here because we're on the client side and a server could very
;; bind(2) to that port after we've decided to use it, but that's
;; inherent to the way the Jupyter protocol works: it's the client that
;; chooses ports the kernels should bind to.
(let ((sock (socket AF_INET SOCK_STREAM 0))
(address (make-socket-address AF_INET
(inet-pton AF_INET ip) 0)))
(setsockopt sock SOL_SOCKET SO_LINGER '(0 . 0))
(bind sock address)
(let ((port (sockaddr:port (getsockname sock))))
(close sock)
port)))
(define (try-connect socket port)
(let ((port (or port (allocate-port))))
(zmq-connect socket (port->uri port))
port))
(let* ((socket-control (zmq-create-socket context ZMQ_DEALER))
(socket-shell (zmq-create-socket context ZMQ_DEALER))
(socket-stdin (zmq-create-socket context ZMQ_DEALER))
(socket-heartbeat (zmq-create-socket context ZMQ_REQ))
(socket-iosub (zmq-create-socket context ZMQ_SUB))
(port-control (test-bind socket-control first-port))
(port-shell (test-bind socket-shell (+ 1 port-control)))
(port-stdin (test-bind socket-stdin (+ 1 port-shell)))
(port-heartbeat (test-bind socket-heartbeat (+ 1 port-stdin)))
(port-iosub (test-bind socket-iosub (+ 1 port-heartbeat)))
(port-control (try-connect socket-control first-port))
(port-shell (try-connect socket-shell
(and first-port (+ 1 port-control))))
(port-stdin (try-connect socket-stdin
(and first-port (+ 1 port-shell))))
(port-heartbeat (try-connect socket-heartbeat
(and first-port (+ 1 port-stdin))))
(port-iosub (try-connect socket-iosub
(and first-port (+ 1 port-heartbeat))))
(connection (connection "tcp" "127.0.0.1"
#:key key
......@@ -249,10 +282,6 @@ kernel can be used by the client to talk to it."
#:stdin-port port-stdin
#:iopub-port port-iosub))
(port->uri (lambda (port)
(string-append transport "://" ip ":"
(number->string port))))
(kernel (kernel #f #f ;no name and PID yet
#:key key
#:control socket-control
......@@ -270,13 +299,6 @@ kernel can be used by the client to talk to it."
(zmq-set-socket-option socket-shell ZMQ_IDENTITY
(utf8->string identity)))
;; Sub socket connection.
(zmq-connect socket-control (port->uri port-control))
(zmq-connect socket-shell (port->uri port-shell))
(zmq-connect socket-stdin (port->uri port-stdin))
(zmq-connect socket-heartbeat (port->uri port-heartbeat))
(zmq-connect socket-iosub (port->uri port-iosub))
(values connection kernel)))
(define (spawn-kernel specs connection)
......@@ -306,22 +328,6 @@ CONNECTION serialized to a \"connection file\". Return its PID."
(apply execl (first arguments) arguments)
pid)))
(define (test-bind socket port)
(catch 'zmq-error
(lambda ()
(zmq-bind-socket socket (string-append "tcp://127.0.0.1:"
(number->string port)))
(zmq-unbind-socket socket (string-append "tcp://127.0.0.1:"
(number->string port)))
port)
(lambda stuff
(let ((errno (cadr stuff)))
(cond
((= errno EADDRINUSE) ;The requested address is
;already in use.
(test-bind socket (+ port 1)))
(else #f))))))
(define search-path->list
(let ((not-colon (char-set-complement (char-set #\:))))
(match-lambda
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment