Fix threading to avoid race-conditions and handle signals correctly.
This commit is contained in:
parent
20d21ea01c
commit
6c87df3364
64
radicale.py
64
radicale.py
@ -85,36 +85,54 @@ if options.daemon:
|
|||||||
sys.exit()
|
sys.exit()
|
||||||
sys.stdout = sys.stderr = open(os.devnull, "w")
|
sys.stdout = sys.stderr = open(os.devnull, "w")
|
||||||
|
|
||||||
# Launch calendar servers
|
# Create calendar servers
|
||||||
servers = []
|
servers = []
|
||||||
server_class = radicale.HTTPSServer if options.ssl else radicale.HTTPServer
|
server_class = radicale.HTTPSServer if options.ssl else radicale.HTTPServer
|
||||||
|
|
||||||
def exit():
|
|
||||||
"""Cleanly shutdown servers."""
|
|
||||||
while servers:
|
|
||||||
servers.pop().shutdown()
|
|
||||||
|
|
||||||
def serve_forever(server):
|
|
||||||
"""Serve a server forever with no traceback on keyboard interrupts."""
|
|
||||||
try:
|
|
||||||
server.serve_forever()
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
# No unwanted traceback
|
|
||||||
pass
|
|
||||||
finally:
|
|
||||||
exit()
|
|
||||||
|
|
||||||
# Clean exit on SIGTERM
|
|
||||||
signal.signal(signal.SIGTERM, lambda *_: exit())
|
|
||||||
|
|
||||||
for host in options.hosts.split(','):
|
for host in options.hosts.split(','):
|
||||||
address, port = host.strip().rsplit(':', 1)
|
address, port = host.strip().rsplit(':', 1)
|
||||||
address, port = address.strip('[] '), int(port)
|
address, port = address.strip('[] '), int(port)
|
||||||
|
|
||||||
servers.append(server_class((address, port), radicale.CalendarHTTPHandler))
|
servers.append(server_class((address, port), radicale.CalendarHTTPHandler))
|
||||||
|
|
||||||
for server in servers[:-1]:
|
# this event marks that the program should be shut down
|
||||||
# More servers to come, launch a new thread
|
server_exited = threading.Event()
|
||||||
|
|
||||||
|
# SIGTERM and SIGINT (aka KeyboardInterrupt) should just mark this for shutdown
|
||||||
|
signal.signal(signal.SIGTERM, lambda *_: server_exited.set())
|
||||||
|
signal.signal(signal.SIGINT, lambda *_: server_exited.set())
|
||||||
|
|
||||||
|
def serve_forever(server):
|
||||||
|
"""Serve a server forever, and mark the process for shut down if things go wrong."""
|
||||||
|
try:
|
||||||
|
server.serve_forever()
|
||||||
|
finally:
|
||||||
|
server_exited.set()
|
||||||
|
|
||||||
|
# start the servers in a different loop to avoid possible race-conditions,
|
||||||
|
# when a server exists but another server is added to the list at the same time
|
||||||
|
for server in servers:
|
||||||
threading.Thread(target=serve_forever, args=(server,)).start()
|
threading.Thread(target=serve_forever, args=(server,)).start()
|
||||||
|
|
||||||
# Last server, no more thread
|
# mainloop: wait until all servers are exited
|
||||||
serve_forever(servers[-1])
|
# we must do the busy-waiting here, as all ".join()"-calls
|
||||||
|
# completly block the thread, such that signals are not received
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
# the number is irrelevant -- the only thing that matters, is that it is
|
||||||
|
# larger than 0.05
|
||||||
|
# this is due to python implementing its own busy-waiting logic
|
||||||
|
server_exited.wait(10.0)
|
||||||
|
if server_exited.is_set():
|
||||||
|
break
|
||||||
|
finally:
|
||||||
|
#
|
||||||
|
# Cleanly shutdown server
|
||||||
|
#
|
||||||
|
|
||||||
|
# ignore signals, s.t. they cannot interfere
|
||||||
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||||
|
signal.signal(signal.SIGTERM, signal.SIG_IGN)
|
||||||
|
|
||||||
|
for server in servers:
|
||||||
|
server.shutdown()
|
||||||
|
Loading…
Reference in New Issue
Block a user