use signals to shut down plugins instead of sending a socket message

should reduce or outright prevent shutdown stalls
This commit is contained in:
AAGaming
2024-09-11 20:35:24 -04:00
parent ef4ca204bd
commit 8f049f6669
2 changed files with 20 additions and 16 deletions
+15 -12
View File
@@ -120,18 +120,25 @@ class PluginWrapper:
start_time = time() start_time = time()
if self.passive: if self.passive:
return return
self.log.info(f"Shutting down {self.name}")
_, pending = await wait([ pending: set[Task[None]] | None = None;
create_task(self._socket.write_single_line(dumps({ "stop": True, "uninstall": uninstall }, ensure_ascii=False)))
], timeout=1) if uninstall:
_, pending = await wait([
create_task(self._socket.write_single_line(dumps({ "uninstall": uninstall }, ensure_ascii=False)))
], timeout=1)
self.terminate() # the plugin process will handle SIGTERM and shut down cleanly without a socket message
if hasattr(self, "_listener_task"): if hasattr(self, "_listener_task"):
self._listener_task.cancel() self._listener_task.cancel()
await self.kill_if_still_running() await self.kill_if_still_running()
for pending_task in pending: if pending:
pending_task.cancel() for pending_task in pending:
pending_task.cancel()
self.log.info(f"Plugin {self.name} has been stopped in {time() - start_time:.1f}s") self.log.info(f"Plugin {self.name} has been stopped in {time() - start_time:.1f}s")
except Exception as e: except Exception as e:
@@ -139,18 +146,14 @@ class PluginWrapper:
async def kill_if_still_running(self): async def kill_if_still_running(self):
start_time = time() start_time = time()
sigtermed = False
while self.proc and self.proc.is_alive(): while self.proc and self.proc.is_alive():
elapsed_time = time() - start_time elapsed_time = time() - start_time
if elapsed_time >= 5 and not sigtermed: if elapsed_time >= 5:
sigtermed = True self.log.warning(f"Plugin {self.name} still alive 5 seconds after stop request! Sending SIGKILL!")
self.log.warning(f"Plugin {self.name} still alive 5 seconds after stop request! Sending SIGTERM!")
self.terminate()
elif elapsed_time >= 10:
self.log.warning(f"Plugin {self.name} still alive 10 seconds after stop request! Sending SIGKILL!")
self.terminate(True) self.terminate(True)
await sleep(0.1) await sleep(0.1)
def terminate(self, kill: bool = False): def terminate(self, kill: bool = False):
if self.proc and self.proc.is_alive(): if self.proc and self.proc.is_alive():
if kill: if kill:
@@ -40,6 +40,7 @@ class SandboxedPlugin:
self.author = author self.author = author
self.api_version = api_version self.api_version = api_version
self.shutdown_running = False self.shutdown_running = False
self.uninstalling = False
self.log = getLogger("sandboxed_plugin") self.log = getLogger("sandboxed_plugin")
@@ -161,13 +162,13 @@ class SandboxedPlugin:
self.log.error("Failed to uninstall " + self.name + "!\n" + format_exc()) self.log.error("Failed to uninstall " + self.name + "!\n" + format_exc())
pass pass
async def shutdown(self, uninstall: bool = False): async def shutdown(self):
if not self.shutdown_running: if not self.shutdown_running:
self.shutdown_running = True self.shutdown_running = True
self.log.info(f"Calling Loader unload function for {self.name}.") self.log.info(f"Calling Loader unload function for {self.name}.")
await self._unload() await self._unload()
if uninstall: if self.uninstalling:
self.log.info("Calling Loader uninstall function.") self.log.info("Calling Loader uninstall function.")
await self._uninstall() await self._uninstall()
@@ -180,8 +181,8 @@ class SandboxedPlugin:
async def on_new_message(self, message : str) -> str|None: async def on_new_message(self, message : str) -> str|None:
data = loads(message) data = loads(message)
if "stop" in data: if "uninstall" in data:
await self.shutdown(data.get('uninstall')) self.uninstalling = data.get("uninstall")
d: SocketResponseDict = {"type": SocketMessageType.RESPONSE, "res": None, "success": True, "id": data["id"]} d: SocketResponseDict = {"type": SocketMessageType.RESPONSE, "res": None, "success": True, "id": data["id"]}
try: try: