diff --git a/yacv_server/server.py b/yacv_server/server.py index 8b9c26a..078a306 100644 --- a/yacv_server/server.py +++ b/yacv_server/server.py @@ -2,6 +2,7 @@ import asyncio import atexit import os import signal +import sys from threading import Thread from typing import Optional @@ -20,6 +21,7 @@ class Server: app = web.Application() runner: web.AppRunner thread: Optional[Thread] = None + do_shutdown = asyncio.Event() def __init__(self, *args, **kwargs): # --- Routes --- @@ -29,11 +31,12 @@ class Server: self.app.router.add_get('/{path:(.*/|)}', _index_handler) # Any folder -> index.html self.app.router.add_static('/', path=FRONTEND_BASE_PATH, name='static_frontend') # --- Misc --- - self.runner = web.AppRunner(self.app) + self.loop = asyncio.new_event_loop() def start(self): """Starts the web server in the background""" 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) signal.signal(signal.SIGINT | signal.SIGTERM, self.stop) atexit.register(self.stop) @@ -42,23 +45,34 @@ class Server: # noinspection PyUnusedLocal def stop(self, *args): """Stops the web server""" - print('Stopping server...') if self.thread is None: print('Cannot stop server because it is not running') return - asyncio.run(self.runner.shutdown()) - asyncio.run(self.app.cleanup()) - self.thread = None # FIXME: Not properly cleaned up (join blocks forever) - print('Cleanup done') + # FIXME: Wait for at least one client to confirm ready before stopping in case we are too fast? + self.loop.call_soon_threadsafe(lambda *a: self.do_shutdown.set()) + self.thread.join(timeout=12) + 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): """Runs the web server""" - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - loop.run_until_complete(self.runner.setup()) - site = web.TCPSite(self.runner, os.getenv('YACV_HOST', 'localhost'), int(os.getenv('YACV_PORT', 32323))) - loop.run_until_complete(site.start()) - loop.run_forever() + asyncio.set_event_loop(self.loop) + self.loop.run_until_complete(self.run_server_async()) + self.loop.stop() + self.loop.close() + + 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): pass