Skip to content

Commit 4b10930

Browse files
committed
Protect _CVar's storage with a thread lock
1 parent 3323e76 commit 4b10930

File tree

1 file changed

+16
-12
lines changed

1 file changed

+16
-12
lines changed

asgiref/local.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,37 @@ class _CVar:
99
"""Storage utility for Local."""
1010

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

1415
def __getattr__(self, key: str) -> Any:
15-
try:
16-
var = self._data[key]
17-
except KeyError:
18-
raise AttributeError(f"{self!r} object has no attribute {key!r}")
16+
with self._thread_lock:
17+
try:
18+
var = self._data[key]
19+
except KeyError:
20+
raise AttributeError(f"{self!r} object has no attribute {key!r}")
1921

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

2527
def __setattr__(self, key: str, value: Any) -> None:
26-
if key == "_data":
28+
if key in ("_data", "_thread_lock"):
2729
return super().__setattr__(key, value)
2830

29-
var = self._data.get(key)
30-
if var is None:
31-
self._data[key] = var = contextvars.ContextVar(key)
31+
with self._thread_lock:
32+
var = self._data.get(key)
33+
if var is None:
34+
self._data[key] = var = contextvars.ContextVar(key)
3235
var.set(value)
3336

3437
def __delattr__(self, key: str) -> None:
35-
if key in self._data:
36-
del self._data[key]
37-
else:
38-
raise AttributeError(f"{self!r} object has no attribute {key!r}")
38+
with self._thread_lock:
39+
if key in self._data:
40+
del self._data[key]
41+
else:
42+
raise AttributeError(f"{self!r} object has no attribute {key!r}")
3943

4044

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

0 commit comments

Comments
 (0)