diff --git a/ipykernel/heartbeat.py b/ipykernel/heartbeat.py index 3f0de81f4..f19f7b5c6 100644 --- a/ipykernel/heartbeat.py +++ b/ipykernel/heartbeat.py @@ -13,6 +13,7 @@ import errno import socket +from concurrent.futures import Future from pathlib import Path from threading import Thread @@ -46,6 +47,7 @@ def __init__(self, context, addr=None, *, curve_publickey=None, curve_secretkey= addr = ("tcp", localhost(), 0) Thread.__init__(self, name="Heartbeat") self.context = context + self._bind_future: Future[None] = Future() self.transport, self.ip, self.port = addr self.original_port = self.port self._curve_publickey = curve_publickey @@ -104,6 +106,11 @@ def _bind_socket(self): else: return + def start(self): + """Start the heartbeat thread and wait for its socket to bind.""" + super().start() + self._bind_future.result() + def run(self): """Run the heartbeat thread.""" self.name = "Heartbeat" @@ -119,6 +126,9 @@ def run(self): self.socket.close() raise + self.addr = (self.ip, self.port) + self._bind_future.set_result(None) + while True: try: zmq.device(zmq.QUEUE, self.socket, self.socket) diff --git a/ipykernel/kernelapp.py b/ipykernel/kernelapp.py index 667df15d9..a0608f88c 100644 --- a/ipykernel/kernelapp.py +++ b/ipykernel/kernelapp.py @@ -444,9 +444,10 @@ def init_heartbeat(self): curve_publickey=self.curve_publickey, curve_secretkey=self.curve_secretkey, ) + # Binding may select a new port if the original candidate was taken. + self.heartbeat.start() self.hb_port = self.heartbeat.port self.log.debug("Heartbeat REP Channel on port: %i", self.hb_port) - self.heartbeat.start() def close(self): """Close zmq sockets in an orderly fashion"""