server: fix windows ctrl-c

thread.join() blocks indefinetely, not allowing python to handle the
interrupt. Same is true for sleep on windows, but when we only sleep a
second, this is fine. Instead of joining the threads, keep track of them
manually.

Change-Id: I559fe06d9ce005a15388c881e4f076d996aea9dc
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34894
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Reviewed-by: Alexander Zaft <a.zaft@fz-juelich.de>
Reviewed-by: Georg Brandl <g.brandl@fz-juelich.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
This commit is contained in:
Alexander Zaft 2024-10-29 16:33:15 +01:00 committed by Markus Zolliker
parent 632db924eb
commit 60c9737cfe

View File

@ -26,6 +26,7 @@ import os
import signal import signal
import sys import sys
import threading import threading
import time
import mlzlog import mlzlog
@ -171,17 +172,18 @@ class Server:
lock = threading.Lock() lock = threading.Lock()
failed = {} failed = {}
interfaces = [self.node_cfg['interface']] + self.node_cfg.get('secondary', []) interfaces = [self.node_cfg['interface']] + self.node_cfg.get('secondary', [])
# TODO: check if only one interface of each type is open? with lock:
for interface in interfaces: for interface in interfaces:
opts = {'uri': interface} opts = {'uri': interface}
t = mkthread( t = mkthread(
self._interfaceThread, self._interfaceThread,
opts, opts,
lock, lock,
failed, failed,
interfaces_started.get_trigger(), interfaces,
) interfaces_started.get_trigger(),
iface_threads.append(t) )
iface_threads.append(t)
if not interfaces_started.wait(): if not interfaces_started.wait():
for iface in interfaces: for iface in interfaces:
if iface not in failed and iface not in self.interfaces: if iface not in failed and iface not in self.interfaces:
@ -192,15 +194,23 @@ class Server:
if not self.interfaces: if not self.interfaces:
self.log.error('no interface started') self.log.error('no interface started')
return return
self.secnode.add_secnode_property('_interfaces', list(self.interfaces.keys())) self.secnode.add_secnode_property('_interfaces', list(self.interfaces))
self.log.info('startup done with interface(s) %s' % ', '.join(self.interfaces)) self.log.info('startup done with interface(s) %s',
', '.join(self.interfaces))
if systemd: if systemd:
systemd.daemon.notify("READY=1\nSTATUS=accepting requests") systemd.daemon.notify("READY=1\nSTATUS=accepting requests")
# we wait here on the thread finishing, which means we got a if os.name == 'nt':
# signal to shut down or an exception was raised # workaround: thread.join() on Windows blocks and is not
for t in iface_threads: # interruptible by the signal handler, so loop and check
t.join() # periodically whether the interfaces are still running.
while True:
time.sleep(1)
if not interfaces:
break
else:
for t in iface_threads:
t.join()
while failed: while failed:
iface, err = failed.popitem() iface, err = failed.popitem()
@ -230,7 +240,7 @@ class Server:
for iface in self.interfaces.values(): for iface in self.interfaces.values():
iface.shutdown() iface.shutdown()
def _interfaceThread(self, opts, lock, failed, start_cb): def _interfaceThread(self, opts, lock, failed, interfaces, start_cb):
iface = opts['uri'] iface = opts['uri']
scheme, _, _ = iface.rpartition('://') scheme, _, _ = iface.rpartition('://')
scheme = scheme or 'tcp' scheme = scheme or 'tcp'
@ -247,9 +257,12 @@ class Server:
except Exception as e: except Exception as e:
with lock: with lock:
failed[iface] = e failed[iface] = e
start_cb() interfaces.remove(iface)
return start_cb() # callback should also be called on failure
self.log.info(f'stopped {iface}') else:
with lock:
interfaces.remove(iface)
self.log.info(f'stopped {iface}')
def _processCfg(self): def _processCfg(self):
"""Processes the module configuration. """Processes the module configuration.