65 lines
2.1 KiB
Python
65 lines
2.1 KiB
Python
"""One owned, immutable latest result, mirroring the 0920 LatestResult contract."""
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Mapping
|
|
import threading
|
|
from types import MappingProxyType
|
|
|
|
|
|
def freeze(value):
|
|
if isinstance(value, Mapping):
|
|
return MappingProxyType({key: freeze(item) for key, item in value.items()})
|
|
if isinstance(value, (list, tuple)):
|
|
return tuple(freeze(item) for item in value)
|
|
return value
|
|
|
|
|
|
def result_to_dict(value):
|
|
"""Return a mutable JSON-compatible copy when a consumer needs serialization."""
|
|
if isinstance(value, Mapping):
|
|
return {key: result_to_dict(item) for key, item in value.items()}
|
|
if isinstance(value, tuple):
|
|
return [result_to_dict(item) for item in value]
|
|
return value
|
|
|
|
|
|
class LatestResult:
|
|
"""Readers share a stable snapshot; reading never removes or acknowledges it.
|
|
|
|
Only the latest result is retained here. Slow readers can skip frames, and
|
|
references they already hold remain valid after subsequent publications.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self._condition = threading.Condition()
|
|
self._value = None
|
|
self._closed = False
|
|
|
|
def publish(self, result):
|
|
if result is None:
|
|
raise ValueError("Cannot publish a null result")
|
|
snapshot = freeze(result)
|
|
with self._condition:
|
|
if self._closed:
|
|
raise RuntimeError("Result publisher is closed")
|
|
self._value = snapshot
|
|
self._condition.notify_all()
|
|
|
|
def load(self):
|
|
with self._condition:
|
|
return self._value
|
|
|
|
def wait(self, after_frame_index=-1, timeout=None):
|
|
"""Wait for a newer result; return None on timeout/end without a new frame."""
|
|
with self._condition:
|
|
def has_new_frame():
|
|
return self._value is not None and self._value['frame_index'] > after_frame_index
|
|
|
|
self._condition.wait_for(lambda: has_new_frame() or self._closed, timeout)
|
|
return self._value if has_new_frame() else None
|
|
|
|
def close(self):
|
|
with self._condition:
|
|
self._closed = True
|
|
self._condition.notify_all()
|