瀏覽代碼

make sure to remove elements of "unknown" client and to keep pruning loops running

Falko Schindler 1 年之前
父節點
當前提交
9739cedfa2
共有 1 個文件被更改,包括 36 次插入22 次删除
  1. 36 22
      nicegui/nicegui.py

+ 36 - 22
nicegui/nicegui.py

@@ -171,7 +171,7 @@ async def handle_disconnect(client: Client) -> None:
     delay = client.page.reconnect_timeout if client.page.reconnect_timeout is not None else globals.reconnect_timeout
     await asyncio.sleep(delay)
     if not client.shared:
-        delete_client(client.id)
+        delete_client(client)
     for t in client.disconnect_handlers:
         safe_invoke(t, client)
     for t in globals.disconnect_handlers:
@@ -210,32 +210,46 @@ def handle_javascript_response(client: Client, msg: Dict) -> None:
 
 async def prune_clients() -> None:
     while True:
-        stale_clients = [
-            id
-            for id, client in globals.clients.items()
-            if not client.shared and not client.has_socket_connection and client.created < time.time() - 60.0
-        ]
-        for client_id in stale_clients:
-            delete_client(client_id)
+        try:
+            stale_clients = [
+                client
+                for client in globals.clients.values()
+                if not client.shared and not client.has_socket_connection and client.created < time.time() - 60.0
+            ]
+            for client in stale_clients:
+                delete_client(client)
+        except Exception:
+            # NOTE: make sure the loop doesn't crash
+            globals.log.exception('Error while pruning clients')
         await asyncio.sleep(10)
 
 
 async def prune_slot_stacks() -> None:
     while True:
-        running = [
-            id(task)
-            for task in asyncio.tasks.all_tasks()
-            if not task.done() and not task.cancelled()
-        ]
-        stale = [
-            id_
-            for id_ in globals.slot_stacks
-            if id_ not in running
-        ]
-        for id_ in stale:
-            del globals.slot_stacks[id_]
+        try:
+            running = [
+                id(task)
+                for task in asyncio.tasks.all_tasks()
+                if not task.done() and not task.cancelled()
+            ]
+            stale = [
+                id_
+                for id_ in globals.slot_stacks
+                if id_ not in running
+            ]
+            for id_ in stale:
+                del globals.slot_stacks[id_]
+        except Exception:
+            # NOTE: make sure the loop doesn't crash
+            globals.log.exception('Error while pruning slot stacks')
         await asyncio.sleep(10)
 
 
-def delete_client(client_id: str) -> None:
-    globals.clients.pop(client_id).remove_all_elements()
+def delete_client(client: Client) -> None:
+    """Delete a client and all its elements.
+
+    If the global clients dictionary does not contain the client, its elements are still removed and a KeyError is raised.
+    Normally this should never happen, but has been observed (see #1826).
+    """
+    client.remove_all_elements()
+    del globals.clients[client.id]