Compare commits

...

4 Commits

Author SHA1 Message Date
Fabian Dill
7ae486ad04 Update BaseClasses.py
Co-authored-by: Doug Hoskisson <beauxq@users.noreply.github.com>
2024-04-18 18:35:46 +02:00
Fabian Dill
25a5e37cc6 Core: switch to singleton pattern 2024-04-17 08:20:57 +02:00
Fabian Dill
860df6e658 Core: clean up observer thread 2024-04-17 03:37:43 +02:00
Fabian Dill
16525c91b8 Core: observe and report currently long-running function 2024-04-17 02:45:00 +02:00
2 changed files with 44 additions and 2 deletions

View File

@@ -7,6 +7,8 @@ import logging
import random
import secrets
import typing # this can go away when Python 3.8 support is dropped
import threading
import time
from argparse import Namespace
from collections import Counter, deque
from collections.abc import Collection, MutableSequence
@@ -95,6 +97,42 @@ class MultiWorld():
def __getitem__(self, player) -> bool:
return self.rule(player)
class Observer(threading.Thread):
current_function: str
entered: float
shutdown: bool = False
def __init__(self):
self.current_function = ""
self.entered = 0.0
super().__init__(name="Observer", daemon=True)
def __call__(self, function: typing.Callable, entered: float):
# use str of function to avoid having a reference to a bound method
self.current_function = str(function)
self.entered = entered
return self
def __enter__(self):
assert self.current_function, "Entered Observer Context without current method."
def __exit__(self, exc_type, exc_val, exc_tb):
self.current_function = ""
def run(self):
while not self.shutdown:
time.sleep(1)
if self.current_function:
now = time.perf_counter()
elapsed = now - self.entered
if elapsed > 60:
logging.info(f"Generation stalling in {self.current_function}, "
f"running since {elapsed:.0f} seconds ago.")
self.current_function = ""
observer = Observer()
observer.start()
class RegionManager:
region_cache: Dict[int, Dict[str, Region]]
entrance_cache: Dict[int, Dict[str, Entrance]]

View File

@@ -121,7 +121,11 @@ class AutoLogicRegister(type):
def _timed_call(method: Callable[..., Any], *args: Any,
multiworld: Optional["MultiWorld"] = None, player: Optional[int] = None) -> Any:
start = time.perf_counter()
ret = method(*args)
if multiworld:
with multiworld.observer(method, start):
ret = method(*args)
else:
ret = method(*args)
taken = time.perf_counter() - start
if taken > 1.0:
if player and multiworld:
@@ -169,7 +173,7 @@ def call_stage(multiworld: "MultiWorld", method_name: str, *args: Any) -> None:
for world_type in sorted(world_types, key=lambda world: world.__name__):
stage_callable = getattr(world_type, f"stage_{method_name}", None)
if stage_callable:
_timed_call(stage_callable, multiworld, *args)
_timed_call(stage_callable, multiworld, *args, multiworld=multiworld)
class WebWorld: