import threading from uuid import UUID from repository.sandbox_lock import ProcessLocalSandboxLifecycleLocker CHAT_ID = UUID('77777777-7777-7777-7777-777777777777') class LockRace: def __init__(self, locker: ProcessLocalSandboxLifecycleLocker) -> None: self.locker = locker self.entered_first = threading.Event() self.second_requested = threading.Event() self.second_entered = threading.Event() self.release_first = threading.Event() self.release_second = threading.Event() self.errors: list[Exception] = [] self.order: list[str] = [] self.first_entry: object | None = None def run_first(self) -> None: try: with self.locker.lock(CHAT_ID): self.first_entry = self.locker._locks_by_chat_id[CHAT_ID] self.order.append('first_entered') self.entered_first.set() assert self.release_first.wait(timeout=1) self.order.append('first_releasing') except Exception as exc: self.errors.append(exc) def run_second(self) -> None: try: assert self.entered_first.wait(timeout=1) context = self.locker.lock(CHAT_ID) self.second_requested.set() with context: self.order.append('second_entered') self.second_entered.set() assert self.release_second.wait(timeout=1) self.order.append('second_releasing') except Exception as exc: self.errors.append(exc) def test_process_local_sandbox_lifecycle_locker_evicts_idle_lock() -> None: locker = ProcessLocalSandboxLifecycleLocker() with locker.lock(CHAT_ID): assert CHAT_ID in locker._locks_by_chat_id assert len(locker._locks_by_chat_id) == 1 assert CHAT_ID not in locker._locks_by_chat_id assert len(locker._locks_by_chat_id) == 0 def test_process_local_sandbox_lifecycle_locker_keeps_shared_lock_for_waiters() -> None: locker = ProcessLocalSandboxLifecycleLocker() race = LockRace(locker) first_thread = threading.Thread(target=race.run_first) second_thread = threading.Thread(target=race.run_second) first_thread.start() assert race.entered_first.wait(timeout=1) second_thread.start() assert race.second_requested.wait(timeout=1) assert len(locker._locks_by_chat_id) == 1 assert locker._locks_by_chat_id[CHAT_ID] is race.first_entry assert not race.second_entered.wait(timeout=0.1) race.release_first.set() assert race.second_entered.wait(timeout=1) assert len(locker._locks_by_chat_id) == 1 assert locker._locks_by_chat_id[CHAT_ID] is race.first_entry race.release_second.set() first_thread.join(timeout=1) second_thread.join(timeout=1) assert not first_thread.is_alive() assert not second_thread.is_alive() assert race.errors == [] assert race.order == [ 'first_entered', 'first_releasing', 'second_entered', 'second_releasing', ] assert CHAT_ID not in locker._locks_by_chat_id assert len(locker._locks_by_chat_id) == 0