Fix server lifecycle (cleanup)

This commit is contained in:
Yeicor
2024-02-06 19:27:56 +01:00
parent 07334a75f5
commit e79b9adc61

View File

@@ -2,6 +2,7 @@ import asyncio
import atexit import atexit
import os import os
import signal import signal
import sys
from threading import Thread from threading import Thread
from typing import Optional from typing import Optional
@@ -20,6 +21,7 @@ class Server:
app = web.Application() app = web.Application()
runner: web.AppRunner runner: web.AppRunner
thread: Optional[Thread] = None thread: Optional[Thread] = None
do_shutdown = asyncio.Event()
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
# --- Routes --- # --- Routes ---
@@ -29,11 +31,12 @@ class Server:
self.app.router.add_get('/{path:(.*/|)}', _index_handler) # Any folder -> index.html self.app.router.add_get('/{path:(.*/|)}', _index_handler) # Any folder -> index.html
self.app.router.add_static('/', path=FRONTEND_BASE_PATH, name='static_frontend') self.app.router.add_static('/', path=FRONTEND_BASE_PATH, name='static_frontend')
# --- Misc --- # --- Misc ---
self.runner = web.AppRunner(self.app) self.loop = asyncio.new_event_loop()
def start(self): def start(self):
"""Starts the web server in the background""" """Starts the web server in the background"""
assert self.thread is None, "Server already started" assert self.thread is None, "Server already started"
# Start the server in a separate daemon thread
self.thread = Thread(target=self.run_server, name='yacv_server', daemon=True) self.thread = Thread(target=self.run_server, name='yacv_server', daemon=True)
signal.signal(signal.SIGINT | signal.SIGTERM, self.stop) signal.signal(signal.SIGINT | signal.SIGTERM, self.stop)
atexit.register(self.stop) atexit.register(self.stop)
@@ -42,23 +45,34 @@ class Server:
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
def stop(self, *args): def stop(self, *args):
"""Stops the web server""" """Stops the web server"""
print('Stopping server...')
if self.thread is None: if self.thread is None:
print('Cannot stop server because it is not running') print('Cannot stop server because it is not running')
return return
asyncio.run(self.runner.shutdown()) # FIXME: Wait for at least one client to confirm ready before stopping in case we are too fast?
asyncio.run(self.app.cleanup()) self.loop.call_soon_threadsafe(lambda *a: self.do_shutdown.set())
self.thread = None # FIXME: Not properly cleaned up (join blocks forever) self.thread.join(timeout=12)
print('Cleanup done') self.thread = None
if len(args) >= 1 and args[0] in (signal.SIGINT, signal.SIGTERM):
sys.exit(0) # Exit with success
def run_server(self): def run_server(self):
"""Runs the web server""" """Runs the web server"""
loop = asyncio.new_event_loop() asyncio.set_event_loop(self.loop)
asyncio.set_event_loop(loop) self.loop.run_until_complete(self.run_server_async())
loop.run_until_complete(self.runner.setup()) self.loop.stop()
site = web.TCPSite(self.runner, os.getenv('YACV_HOST', 'localhost'), int(os.getenv('YACV_PORT', 32323))) self.loop.close()
loop.run_until_complete(site.start())
loop.run_forever() async def run_server_async(self):
"""Runs the web server (async)"""
runner = web.AppRunner(self.app)
await runner.setup()
site = web.TCPSite(runner, os.getenv('YACV_HOST', 'localhost'), int(os.getenv('YACV_PORT', 32323)))
await site.start()
# print(f'Server started at {site.name}')
# Wait for a signal to stop the server while running
await self.do_shutdown.wait()
# print('Shutting down server...')
await runner.cleanup()
def show_object(self, obj: TopoDS_Shape): def show_object(self, obj: TopoDS_Shape):
pass pass