Skip to content

Commit

Permalink
Protect _CVar's storage with a thread lock
Browse files Browse the repository at this point in the history
  • Loading branch information
spanezz committed Oct 6, 2024
1 parent 3323e76 commit 4b10930
Showing 1 changed file with 16 additions and 12 deletions.
28 changes: 16 additions & 12 deletions asgiref/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,37 @@ class _CVar:
"""Storage utility for Local."""

def __init__(self) -> None:
self._thread_lock = threading.RLock()
self._data: dict[str, contextvars.ContextVar[Any]] = {}

def __getattr__(self, key: str) -> Any:
try:
var = self._data[key]
except KeyError:
raise AttributeError(f"{self!r} object has no attribute {key!r}")
with self._thread_lock:
try:
var = self._data[key]
except KeyError:
raise AttributeError(f"{self!r} object has no attribute {key!r}")

try:
return var.get()
except LookupError:
raise AttributeError(f"{self!r} object has no attribute {key!r}")

def __setattr__(self, key: str, value: Any) -> None:
if key == "_data":
if key in ("_data", "_thread_lock"):
return super().__setattr__(key, value)

var = self._data.get(key)
if var is None:
self._data[key] = var = contextvars.ContextVar(key)
with self._thread_lock:
var = self._data.get(key)
if var is None:
self._data[key] = var = contextvars.ContextVar(key)
var.set(value)

def __delattr__(self, key: str) -> None:
if key in self._data:
del self._data[key]
else:
raise AttributeError(f"{self!r} object has no attribute {key!r}")
with self._thread_lock:
if key in self._data:
del self._data[key]
else:
raise AttributeError(f"{self!r} object has no attribute {key!r}")


def Local(thread_critical: bool = False) -> threading.local | _CVar:
Expand Down

0 comments on commit 4b10930

Please sign in to comment.