From 34f3d04036565dcae473c216170aecea1d0d9be0 Mon Sep 17 00:00:00 2001 From: cirospaciari Date: Tue, 27 Jun 2023 13:55:10 -0300 Subject: [PATCH] expose block option and also close function on WSGI and ASGI, also avoids exceptions on __del__ --- src/socketify/asgi.py | 46 ++++++++++++++++++++++++++++---------- src/socketify/socketify.py | 15 ++++++++----- src/socketify/wsgi.py | 45 ++++++++++++++++++++++++++++--------- 3 files changed, 77 insertions(+), 29 deletions(-) diff --git a/src/socketify/asgi.py b/src/socketify/asgi.py index 2ed0c3c..8fee5e2 100644 --- a/src/socketify/asgi.py +++ b/src/socketify/asgi.py @@ -773,10 +773,13 @@ class _ASGI: return self def __del__(self): - if self.asgi_http_info: - lib.socketify_destroy_asgi_app_info(self.asgi_http_info) - if self.asgi_ws_info: - lib.socketify_destroy_asgi_ws_app_info(self.asgi_ws_info) + try: + if self.asgi_http_info: + lib.socketify_destroy_asgi_app_info(self.asgi_http_info) + if self.asgi_ws_info: + lib.socketify_destroy_asgi_ws_app_info(self.asgi_ws_info) + except: + pass # "Public" ASGI interface to allow easy forks/workers @@ -797,12 +800,26 @@ class ASGI: self.listen_options = None self.task_factory_max_items = task_factory_max_items self.lifespan = lifespan + self.server = None + self.pid_list = None def listen(self, port_or_options, handler=None): self.listen_options = (port_or_options, handler) return self - def run(self, workers=1): + def close(self): + # always wait a sec so forks can start properly if close is called too fast + import time + time.sleep(1) + + if self.server is not None: + self.server.close() + if self.pid_list is not None: + import signal + for pid in self.pid_list: + os.kill(pid, signal.SIGINT) + + def run(self, workers=1, block=True): def run_task(): server = _ASGI( self.app, @@ -815,22 +832,27 @@ class ASGI: if self.listen_options: (port_or_options, handler) = self.listen_options server.listen(port_or_options, handler) - server.run() + self.server = server + server.run() pid_list = [] + start = 1 if block else 0 # fork limiting the cpu count - 1 - for _ in range(1, workers): + for _ in range(block, workers): pid = os.fork() # n greater than 0 means parent process if not pid > 0: run_task() break pid_list.append(pid) + + self.pid_list = pid_list - run_task() # run app on the main process too :) + if block: + run_task() # run app on the main process too :) + # sigint everything to gracefull shutdown + import signal + for pid in pid_list: + os.kill(pid, signal.SIGINT) - # sigint everything to gracefull shutdown - import signal - for pid in pid_list: - os.kill(pid, signal.SIGINT) return self diff --git a/src/socketify/socketify.py b/src/socketify/socketify.py index c0ac643..b9fec10 100644 --- a/src/socketify/socketify.py +++ b/src/socketify/socketify.py @@ -3449,10 +3449,13 @@ class App: self.loop = None def __del__(self): - if self.app: # only destroy if exists - self.close() - lib.uws_app_destroy(self.SSL, self.app) - if self.loop: - self.loop.dispose() - self.loop = None + try: + if self.app: # only destroy if exists + self.close() + lib.uws_app_destroy(self.SSL, self.app) + if self.loop: + self.loop.dispose() + self.loop = None + except: + pass diff --git a/src/socketify/wsgi.py b/src/socketify/wsgi.py index 41779c9..179dd5c 100644 --- a/src/socketify/wsgi.py +++ b/src/socketify/wsgi.py @@ -691,10 +691,13 @@ class _WSGI: return self def __del__(self): - if self.asgi_http_info: - lib.socketify_destroy_asgi_app_info(self.asgi_http_info) - if self.asgi_ws_info: - lib.socketify_destroy_asgi_ws_app_info(self.asgi_ws_info) + try: + if self.asgi_http_info: + lib.socketify_destroy_asgi_app_info(self.asgi_http_info) + if self.asgi_ws_info: + lib.socketify_destroy_asgi_ws_app_info(self.asgi_ws_info) + except: + pass # "Public" WSGI interface to allow easy forks/workers @@ -714,13 +717,27 @@ class WSGI: self.websocket_options = websocket_options self.listen_options = None self.task_factory_max_items = task_factory_max_items + self.server = None + self.pid_list = None # lifespan is not supported in WSGI def listen(self, port_or_options, handler=None): self.listen_options = (port_or_options, handler) return self - def run(self, workers=1): + def close(self): + # always wait a sec so forks can start properly if close is called too fast + import time + time.sleep(1) + + if self.server is not None: + self.server.close() + if self.pid_list is not None: + import signal + for pid in self.pid_list: + os.kill(pid, signal.SIGINT) + + def run(self, workers=1, block=True): def run_task(): server = _WSGI( self.app, @@ -732,11 +749,14 @@ class WSGI: if self.listen_options: (port_or_options, handler) = self.listen_options server.listen(port_or_options, handler) + self.server = server server.run() pid_list = [] + + start = 1 if block else 0 # fork limiting the cpu count - 1 - for _ in range(1, workers): + for _ in range(start, workers): pid = os.fork() # n greater than 0 means parent process if not pid > 0: @@ -744,10 +764,13 @@ class WSGI: break pid_list.append(pid) - run_task() # run app on the main process too :) + self.pid_list = pid_list + + if block: + run_task() # run app on the main process too :) + # sigint everything to gracefull shutdown + import signal + for pid in pid_list: + os.kill(pid, signal.SIGINT) - # sigint everything to gracefull shutdown - import signal - for pid in pid_list: - os.kill(pid, signal.SIGINT) return self