using deque in a few places for efficiency

This commit is contained in:
Eric 2023-01-19 13:29:34 -08:00
parent 154e5f72b9
commit f61539275f
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
10 changed files with 1716 additions and 1632 deletions

View File

@ -4008,50 +4008,50 @@
"assets/src/ba_data/python/ba/_generated/__init__.py": "https://files.ballistica.net/cache/ba1/ee/e8/cad05aa531c7faf7ff7b96db7f6e", "assets/src/ba_data/python/ba/_generated/__init__.py": "https://files.ballistica.net/cache/ba1/ee/e8/cad05aa531c7faf7ff7b96db7f6e",
"assets/src/ba_data/python/ba/_generated/enums.py": "https://files.ballistica.net/cache/ba1/1c/77/ac670a5118abdf8a7687af0e159b", "assets/src/ba_data/python/ba/_generated/enums.py": "https://files.ballistica.net/cache/ba1/1c/77/ac670a5118abdf8a7687af0e159b",
"ballisticacore-windows/Generic/BallisticaCore.ico": "https://files.ballistica.net/cache/ba1/89/c0/e32c7d2a35dc9aef57cc73b0911a", "ballisticacore-windows/Generic/BallisticaCore.ico": "https://files.ballistica.net/cache/ba1/89/c0/e32c7d2a35dc9aef57cc73b0911a",
"build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/2b/e0/d62298f03362b29b1d7e15319f9f", "build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/82/d3/3715dc8f54d1353bab3493b785bc",
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/d0/db/4e877084f27207c687cb965c26f2", "build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/21/a3/3d39047fbe2d7168fde9323461a3",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/3e/e0/8f1b50fbd66f92c52984511f3663", "build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/fa/ae/0b8e3ea76ead98bcb1f91154adb4",
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e0/18/86760bcace92207da35a3776d9f1", "build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/9a/de/2d3ea7b4b6b8d96fee70bc6ee51e",
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/d7/62/c324bceefb32847929fc7acf463c", "build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/63/73/49be93777a8e46dd3379897eaee0",
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/4f/c0/3c4c12ae0e6c4cae936f70518490", "build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/bc/81/80bb7ab35ed92ce9e904874f38b9",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/3e/8a/76fb94e739dc1abd304c7167e21d", "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/46/1d/18f4916b42ec3af78ca83e132be4",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f2/53/14f99c17acec8ec729a1e745c79c", "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/9f/38/5a8b30c0b79cecb834eb5348ff8d",
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/75/6b/4554a4cea11610211f913d223619", "build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/3e/76/8c1cea0d914d49a64bf721e49070",
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/07/51/13a07fe150afd562218a8006ce76", "build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/a4/a3/189278b9cd02c7c4255f96bc5bc2",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/52/33/2d9f49b297b8ff594dcc17d0f32f", "build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/60/2a/8f637d1dcf6e3b832c4770ed0c16",
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/10/00/374c0b00b7dd3d5c21b282d46902", "build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/01/28/20484d98b6b88ce8b56e28efcc80",
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/26/11/c4d90c60ca4f2cf15151a939124a", "build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/50/a2/b54a80f3881e94c7fabeaaec7ad4",
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/29/64/746de9f0ac84f842222801c362a2", "build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/b0/ab/7472dd6f3ae5bb06753f5df250ff",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/86/98/43faaadafd9a2d7f268fbb3e9d49", "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/8c/0a/35c89966441a7dd16f6543d540f3",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/1b/01/d3357b85faef1469067a2eba4ee6", "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e5/9c/43a7f214bacd6eab04ec30ebcafd",
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/17/82/7bc7a9b23dece50f91581da61a10", "build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/71/c7/781eae3a95730651fc8fef6e6346",
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/10/33/80e50c50f6e6081ee729018c088d", "build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/b2/a9/7bc2e2f656ddb8505f9f380033af",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/a2/5b/1a508cfac64802bc5359647f593e", "build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/c4/7c/6f8e20e6ec4788047a228a5d896f",
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/5b/c3/3e2944f02aab59db9d6818a58066", "build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/3b/2e/48440d1ace2bc05e48eb9f56f63b",
"build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/58/9d/6bfac8dfa89a40303d8ed3fbd979", "build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/34/66/0a1e0fece6b9ad87ac315a9ba9f3",
"build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/48/d3/762370803cf51dc78281610925b0", "build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/6d/a4/4a1357b21f0bc128c9b5948061da",
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/55/1c/4a5e5dcf1060965a6268d75ca85d", "build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/c1/23/405f68ec8c229549de5b0dad41eb",
"build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e2/ff/11cc9bb55cdef26f5c8bfe3e48b5", "build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/aa/a5/c088252e71a395765f8bc0cdc028",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/70/a6/9571c5f278487a043230cac2e7b7", "build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/0f/ba/196bbcd459b59e32e93c15a7c128",
"build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ce/bf/e8d377d882414498c904ef0d7196", "build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/39/d2/706b2ca75bae629545cc7eb0a8d5",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/53/0a/cd2800c0f21e0841ecd2e22e24f3", "build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/89/56/24c6be441e8b120eef698c467ce6",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/01/bc/dba89ca8070a1b7ecb34352d737d", "build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/6b/11/b1c5fc90854821d7e63fafd6f664",
"build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/34/79/44a55d426a00311c834db41e8246", "build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/3c/a7/308c08a0ebd82d100505666b85d5",
"build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/20/aa/80da4a9785bcf06416b2d8a338e8", "build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2e/83/abff53b7982976aa9c6edb39d22b",
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f2/03/dbb9b61aa3a5488cba6d8eb628e8", "build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/01/d2/b005ef960a6c9ba96c3d77fd005f",
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/60/f4/7e766850a0f24e099a641c1dd369", "build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/dc/53/af455ca35433fba5018fb0a4ea10",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/04/34/78bed4b7e83c48e370122c22108e", "build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e7/91/4c135c16252f54a325ca375a7f79",
"build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/1b/79/b617b139fcb528eaa7d56730e4c3", "build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/65/95/85ac4196233bb9a6fbef9e10f431",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/da/b7/5041c7ea7b951ddfed1cabc599ff", "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ac/0d/82b7132a360c91e386b070c38fa2",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/c1/9c/06d199252fb5ce1d21db06488cc9", "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7d/43/7112b8b9467213a48880352a4ba2",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/57/e8/5c589728e4f3b0685595dfd9a732", "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/4c/8a/bfeae3274074681c9fcc50625029",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/f9/9c/d9132f91b3f02daeb581cdc6144b", "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/0e/46/75aff046dbc4ac23208ab9b4774e",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/c7/dc/16cbdf63b6fbae771c97070d32b4", "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/81/5d/66dfda3a7d4428b8c7a3ed1ad17f",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/78/c9/df88a990bb9587d297f7b720e936", "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/41/79/5be0ca4e66ab63ccfdb4f129ddcb",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/7b/ac/8f61cb2620f6c8646aebb8746392", "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/f2/0a/e8432743590dd423b723eb3f262f",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/ee/31/7e1f0d67ad3004cc2e4e4cc18570", "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/06/13/fc1c3c4b3acb2677a48e28ffae63",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/99/60/92ce6b353fef56eb7040d7bbee54", "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/58/95/3a1c4831cd80b6516ad95301bb7d",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/af/e5/95ff75798915d83d20fdb453faf1", "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/f3/08/8936bdf7db0abe3c438a1db84445",
"src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/23/ce/68396b1b7ec6d2f8425902148140", "src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/23/ce/68396b1b7ec6d2f8425902148140",
"src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/2d/4f/f4fe67827f36cd59cd5193333a02", "src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/2d/4f/f4fe67827f36cd59cd5193333a02",
"src/ballistica/generated/python_embedded/bootstrap_monolithic.inc": "https://files.ballistica.net/cache/ba1/ef/c1/aa5f1aa10af89f5c0b1e616355fd" "src/ballistica/generated/python_embedded/bootstrap_monolithic.inc": "https://files.ballistica.net/cache/ba1/ef/c1/aa5f1aa10af89f5c0b1e616355fd"

View File

@ -1,3 +1,5 @@
### 1.7.20 (build 20999, api 7, 2023-01-19)
### 1.7.19 (build 20997, api 7, 2023-01-19) ### 1.7.19 (build 20997, api 7, 2023-01-19)
- Fixes an issue where repeated curses could use incorrect countdown times (Thanks EraOSBeta!). - Fixes an issue where repeated curses could use incorrect countdown times (Thanks EraOSBeta!).
- Last manual party connect port is now saved. Previously, it always assumed the port to be 43210 (Thanks ritiek!). - Last manual party connect port is now saved. Previously, it always assumed the port to be 43210 (Thanks ritiek!).

View File

@ -47,7 +47,7 @@ def bootstrap() -> None:
# Give a soft warning if we're being used with a different binary # Give a soft warning if we're being used with a different binary
# version than we expect. # version than we expect.
expected_build = 20997 expected_build = 20999
running_build: int = env['build_number'] running_build: int = env['build_number']
if running_build != expected_build: if running_build != expected_build:
print( print(

View File

@ -4,6 +4,7 @@
from __future__ import annotations from __future__ import annotations
import threading import threading
from collections import deque
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import _ba import _ba
@ -68,7 +69,7 @@ class _MacMusicAppThread(threading.Thread):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
self._commands_available = threading.Event() self._commands_available = threading.Event()
self._commands: list[list] = [] self._commands = deque[list]()
self._volume = 1.0 self._volume = 1.0
self._current_playlist: str | None = None self._current_playlist: str | None = None
self._orig_volume: int | None = None self._orig_volume: int | None = None
@ -109,7 +110,7 @@ class _MacMusicAppThread(threading.Thread):
# We're not protecting this list with a mutex but we're # We're not protecting this list with a mutex but we're
# just using it as a simple queue so it should be fine. # just using it as a simple queue so it should be fine.
while self._commands: while self._commands:
cmd = self._commands.pop(0) cmd = self._commands.popleft()
if cmd[0] == 'DIE': if cmd[0] == 'DIE':
self._handle_die_command() self._handle_die_command()
done = True done = True

File diff suppressed because it is too large Load Diff

View File

@ -211,9 +211,8 @@ class PartyWindow(ba.Window):
flatness=1.0, flatness=1.0,
) )
self._chat_texts.append(txt) self._chat_texts.append(txt)
if len(self._chat_texts) > 40: while len(self._chat_texts) > 40:
first = self._chat_texts.pop(0) self._chat_texts.pop(0).delete()
first.delete()
ba.containerwidget(edit=self._columnwidget, visible_child=txt) ba.containerwidget(edit=self._columnwidget, visible_child=txt)
def _on_menu_button_press(self) -> None: def _on_menu_button_press(self) -> None:

View File

@ -32,8 +32,8 @@
namespace ballistica { namespace ballistica {
// These are set automatically via script; don't modify them here. // These are set automatically via script; don't modify them here.
const int kAppBuildNumber = 20997; const int kAppBuildNumber = 20999;
const char* kAppVersion = "1.7.19"; const char* kAppVersion = "1.7.20";
// Our standalone globals. // Our standalone globals.
// These are separated out for easy access. // These are separated out for easy access.

View File

@ -8,7 +8,9 @@ import time
import asyncio import asyncio
import logging import logging
import datetime import datetime
import itertools
from enum import Enum from enum import Enum
from collections import deque
from dataclasses import dataclass from dataclasses import dataclass
from typing import TYPE_CHECKING, Annotated from typing import TYPE_CHECKING, Annotated
from threading import Thread, current_thread, Lock from threading import Thread, current_thread, Lock
@ -143,7 +145,7 @@ class LogHandler(logging.Handler):
assert cache_size_limit >= 0 assert cache_size_limit >= 0
self._cache_size_limit = cache_size_limit self._cache_size_limit = cache_size_limit
self._cache_time_limit = cache_time_limit self._cache_time_limit = cache_time_limit
self._cache: list[tuple[int, LogEntry]] = [] self._cache = deque[tuple[int, LogEntry]]()
self._cache_index_offset = 0 self._cache_index_offset = 0
self._cache_lock = Lock() self._cache_lock = Lock()
self._printed_callback_error = False self._printed_callback_error = False
@ -200,7 +202,7 @@ class LogHandler(logging.Handler):
self._cache self._cache
and (now - self._cache[0][1].time) >= self._cache_time_limit and (now - self._cache[0][1].time) >= self._cache_time_limit
): ):
popped = self._cache.pop(0) popped = self._cache.popleft()
self._cache_size -= popped[0] self._cache_size -= popped[0]
self._cache_index_offset += 1 self._cache_index_offset += 1
@ -236,22 +238,40 @@ class LogHandler(logging.Handler):
return LogArchive( return LogArchive(
log_size=self._cache_index_offset + len(self._cache), log_size=self._cache_index_offset + len(self._cache),
start_index=start_index + self._cache_index_offset, start_index=start_index + self._cache_index_offset,
entries=[e[1] for e in self._cache[start_index:end_index]], entries=self._cache_slice(start_index, end_index),
) )
def _cache_slice(
self, start: int, end: int, step: int = 1
) -> list[LogEntry]:
# Deque doesn't natively support slicing but we can do it manually.
# It sounds like rotating the deque and pulling from the beginning
# is the most efficient way to do this. The downside is the deque
# gets temporarily modified in the process so we need to make sure
# we're holding the lock.
assert self._cache_lock.locked()
cache = self._cache
cache.rotate(-start)
slc = [e[1] for e in itertools.islice(cache, 0, end - start, step)]
cache.rotate(start)
return slc
@classmethod
def _is_immutable_log_data(cls, data: Any) -> bool:
if isinstance(data, (str, bool, int, float, bytes)):
return True
if isinstance(data, tuple):
return all(cls._is_immutable_log_data(x) for x in data)
return False
def emit(self, record: logging.LogRecord) -> None: def emit(self, record: logging.LogRecord) -> None:
if __debug__: if __debug__:
starttime = time.monotonic() starttime = time.monotonic()
# Called by logging to send us records. # Called by logging to send us records.
# We simply package them up and ship them to our thread.
# UPDATE: turns out we CAN get log messages from this thread
# (the C++ layer can spit out some performance metrics when
# calls take too long/etc.)
# assert current_thread() is not self._thread
# Special case - filter out this common extra-chatty category. # Special case: filter out this common extra-chatty category.
# TODO - should use a standard logging.Filter for this. # TODO - perhaps should use a standard logging.Filter for this.
if ( if (
self._suppress_non_root_debug self._suppress_non_root_debug
and record.name != 'root' and record.name != 'root'
@ -259,40 +279,60 @@ class LogHandler(logging.Handler):
): ):
return return
# We want to forward as much as we can along without processing it # Optimization: if our log args are all simple immutable values,
# (better to do so in a bg thread). # we can just kick the whole thing over to our background thread to
# However its probably best to flatten the message string here since # be formatted there at our leisure. If anything is mutable and
# it could cause problems stringifying things in threads where they # thus could possibly change between now and then or if we want
# didn't expect to be stringified. # to do immediate file echoing then we need to bite the bullet
msg = self.format(record) # and do that stuff here at the call site.
fast_path = self._echofile is None and self._is_immutable_log_data(
if __debug__: record.args
formattime = time.monotonic()
# Also immediately print pretty colored output to our echo file
# (generally stderr). We do this part here instead of in our bg
# thread because the delay can throw off command line prompts or
# make tight debugging harder.
if self._echofile is not None:
ends = LEVELNO_COLOR_CODES.get(record.levelno)
if ends is not None:
self._echofile.write(f'{ends[0]}{msg}{ends[1]}\n')
else:
self._echofile.write(f'{msg}\n')
if __debug__:
echotime = time.monotonic()
self._event_loop.call_soon_threadsafe(
tpartial(
self._emit_in_thread,
record.name,
record.levelno,
record.created,
msg,
)
) )
if fast_path:
if __debug__:
formattime = echotime = time.monotonic()
self._event_loop.call_soon_threadsafe(
tpartial(
self._emit_in_thread,
record.name,
record.levelno,
record.created,
record,
)
)
else:
# Slow case; do formatting and echoing here at the log call
# site.
msg = self.format(record)
if __debug__:
formattime = time.monotonic()
# Also immediately print pretty colored output to our echo file
# (generally stderr). We do this part here instead of in our bg
# thread because the delay can throw off command line prompts or
# make tight debugging harder.
if self._echofile is not None:
ends = LEVELNO_COLOR_CODES.get(record.levelno)
if ends is not None:
self._echofile.write(f'{ends[0]}{msg}{ends[1]}\n')
else:
self._echofile.write(f'{msg}\n')
if __debug__:
echotime = time.monotonic()
self._event_loop.call_soon_threadsafe(
tpartial(
self._emit_in_thread,
record.name,
record.levelno,
record.created,
msg,
)
)
if __debug__: if __debug__:
# Make noise if we're taking a significant amount of time here. # Make noise if we're taking a significant amount of time here.
# Limit the noise to once every so often though; otherwise we # Limit the noise to once every so often though; otherwise we
@ -317,17 +357,28 @@ class LogHandler(logging.Handler):
tpartial( tpartial(
logging.warning, logging.warning,
'efro.log.LogHandler emit took too long' 'efro.log.LogHandler emit took too long'
' (%.2fs total; %.2fs format, %.2fs echo).', ' (%.2fs total; %.2fs format, %.2fs echo,'
' fast_path=%s).',
duration, duration,
format_duration, format_duration,
echo_duration, echo_duration,
fast_path,
) )
) )
def _emit_in_thread( def _emit_in_thread(
self, name: str, levelno: int, created: float, message: str self,
name: str,
levelno: int,
created: float,
message: str | logging.LogRecord,
) -> None: ) -> None:
try: try:
# If they passed a raw record here, bake it down to a string.
if isinstance(message, logging.LogRecord):
message = self.format(message)
self._emit_entry( self._emit_entry(
LogEntry( LogEntry(
name=name, name=name,
@ -446,7 +497,7 @@ class LogHandler(logging.Handler):
# Prune old until we are back at or under our limit. # Prune old until we are back at or under our limit.
while self._cache_size > self._cache_size_limit: while self._cache_size > self._cache_size_limit:
popped = self._cache.pop(0) popped = self._cache.popleft()
self._cache_size -= popped[0] self._cache_size -= popped[0]
self._cache_index_offset += 1 self._cache_index_offset += 1
@ -506,6 +557,7 @@ def setup_logging(
level: LogLevel, level: LogLevel,
suppress_non_root_debug: bool = False, suppress_non_root_debug: bool = False,
log_stdout_stderr: bool = False, log_stdout_stderr: bool = False,
echo_to_stderr: bool = True,
cache_size_limit: int = 0, cache_size_limit: int = 0,
cache_time_limit: datetime.timedelta | None = None, cache_time_limit: datetime.timedelta | None = None,
) -> LogHandler: ) -> LogHandler:
@ -536,8 +588,7 @@ def setup_logging(
# which would create an infinite loop. # which would create an infinite loop.
loghandler = LogHandler( loghandler = LogHandler(
log_path, log_path,
# echofile=sys.stderr if sys.stderr.isatty() else None, echofile=sys.stderr if echo_to_stderr else None,
echofile=sys.stderr,
suppress_non_root_debug=suppress_non_root_debug, suppress_non_root_debug=suppress_non_root_debug,
cache_size_limit=cache_size_limit, cache_size_limit=cache_size_limit,
cache_time_limit=cache_time_limit, cache_time_limit=cache_time_limit,

View File

@ -9,6 +9,7 @@ import asyncio
import logging import logging
import weakref import weakref
from enum import Enum from enum import Enum
from collections import deque
from dataclasses import dataclass from dataclasses import dataclass
from threading import current_thread from threading import current_thread
from typing import TYPE_CHECKING, Annotated from typing import TYPE_CHECKING, Annotated
@ -201,7 +202,7 @@ class RPCEndpoint:
self._closing = False self._closing = False
self._did_wait_closed = False self._did_wait_closed = False
self._event_loop = asyncio.get_running_loop() self._event_loop = asyncio.get_running_loop()
self._out_packets: list[bytes] = [] self._out_packets = deque[bytes]()
self._have_out_packets = asyncio.Event() self._have_out_packets = asyncio.Event()
self._run_called = False self._run_called = False
self._peer_info: _PeerInfo | None = None self._peer_info: _PeerInfo | None = None
@ -758,7 +759,7 @@ class RPCEndpoint:
await self._have_out_packets.wait() await self._have_out_packets.wait()
assert self._out_packets assert self._out_packets
data = self._out_packets.pop(0) data = self._out_packets.popleft()
# Important: only clear this once all packets are sent. # Important: only clear this once all packets are sent.
if not self._out_packets: if not self._out_packets:

View File

@ -5,6 +5,7 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
from collections import deque
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from threading import Condition, Thread from threading import Condition, Thread
import os import os
@ -23,7 +24,7 @@ class _FileBatchesRun:
) -> None: ) -> None:
self.condition = Condition() self.condition = Condition()
self.paths = paths self.paths = paths
self.batches: list[list[str]] = [] self.batches = deque[list[str]]()
self.batch_size = batch_size self.batch_size = batch_size
self.done = False self.done = False
self.errored = False self.errored = False
@ -141,7 +142,7 @@ def file_batches(
if run.errored: if run.errored:
raise RuntimeError('BG batch run errored.') raise RuntimeError('BG batch run errored.')
while run.batches: while run.batches:
yield run.batches.pop(0) yield run.batches.popleft()
if run.done: if run.done:
break break
except GeneratorExit: except GeneratorExit: