codekingpro/portable-devtools
114k
1from __future__ import annotations2 3import threading4 5 6class _HTTP2ProbeCache:7 __slots__ = (8 "_lock",9 "_cache_locks",10 "_cache_values",11 )12 13 def __init__(self) -> None:14 self._lock = threading.Lock()15 self._cache_locks: dict[tuple[str, int], threading.RLock] = {}16 self._cache_values: dict[tuple[str, int], bool | None] = {}17 18 def acquire_and_get(self, host: str, port: int) -> bool | None:19 # By the end of this block we know that20 # _cache_[values,locks] is available.21 value = None22 with self._lock:23 key = (host, port)24 try:25 value = self._cache_values[key]26 # If it's a known value we return right away.27 if value is not None:28 return value29 except KeyError:30 self._cache_locks[key] = threading.RLock()31 self._cache_values[key] = None32 33 # If the value is unknown, we acquire the lock to signal34 # to the requesting thread that the probe is in progress35 # or that the current thread needs to return their findings.36 key_lock = self._cache_locks[key]37 key_lock.acquire()38 try:39 # If the by the time we get the lock the value has been40 # updated we want to return the updated value.41 value = self._cache_values[key]42 43 # In case an exception like KeyboardInterrupt is raised here.44 except BaseException as e: # Defensive:45 assert not isinstance(e, KeyError) # KeyError shouldn't be possible.46 key_lock.release()47 raise48 49 return value50 51 def set_and_release(52 self, host: str, port: int, supports_http2: bool | None53 ) -> None:54 key = (host, port)55 key_lock = self._cache_locks[key]56 with key_lock: # Uses an RLock, so can be locked again from same thread.57 if supports_http2 is None and self._cache_values[key] is not None:58 raise ValueError(59 "Cannot reset HTTP/2 support for origin after value has been set."60 ) # Defensive: not expected in normal usage61 62 self._cache_values[key] = supports_http263 key_lock.release()64 65 def _values(self) -> dict[tuple[str, int], bool | None]:66 """This function is for testing purposes only. Gets the current state of the probe cache"""67 with self._lock:68 return {k: v for k, v in self._cache_values.items()}69 70 def _reset(self) -> None:71 """This function is for testing purposes only. Reset the cache values"""72 with self._lock:73 self._cache_locks = {}74 self._cache_values = {}75 76 77_HTTP2_PROBE_CACHE = _HTTP2ProbeCache()78 79set_and_release = _HTTP2_PROBE_CACHE.set_and_release80acquire_and_get = _HTTP2_PROBE_CACHE.acquire_and_get81_values = _HTTP2_PROBE_CACHE._values82_reset = _HTTP2_PROBE_CACHE._reset83 84__all__ = [85 "set_and_release",86 "acquire_and_get",87]88 