Skip to content

Commit af3172b

Browse files
committed
fix: enforce thread safety in sysmon.py
Testing on nogil failed with: ``` 2024-06-13T07:28:52.5961120Z @panopticon() 2024-06-13T07:28:52.5962049Z def stop(self) -> None: 2024-06-13T07:28:52.5963007Z """Stop this Tracer.""" 2024-06-13T07:28:52.5963993Z if not self.sysmon_on: 2024-06-13T07:28:52.5964998Z # In forking situations, we might try to stop when we are not 2024-06-13T07:28:52.5966053Z # started. Do nothing in that case. 2024-06-13T07:28:52.5966980Z return 2024-06-13T07:28:52.5967980Z assert sys_monitoring is not None 2024-06-13T07:28:52.5968885Z sys_monitoring.set_events(self.myid, 0) 2024-06-13T07:28:52.5969793Z self.sysmon_on = False 2024-06-13T07:28:52.5970648Z > for code in self.local_event_codes.values(): 2024-06-13T07:28:52.5971663Z E RuntimeError: dictionary changed size during iteration 2024-06-13T07:28:52.5972502Z 2024-06-13T07:28:52.5972851Z coverage/sysmon.py:252: RuntimeError ```
1 parent 562e759 commit af3172b

File tree

1 file changed

+21
-18
lines changed

1 file changed

+21
-18
lines changed

coverage/sysmon.py

+21-18
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ def __init__(self, tool_id: int) -> None:
200200
# Map id(code_object) -> code_object
201201
self.local_event_codes: dict[int, CodeType] = {}
202202
self.sysmon_on = False
203+
self.lock = threading.Lock()
203204

204205
self.stats = {
205206
"starts": 0,
@@ -248,10 +249,11 @@ def stop(self) -> None:
248249
return
249250
assert sys_monitoring is not None
250251
sys_monitoring.set_events(self.myid, 0)
251-
self.sysmon_on = False
252-
for code in self.local_event_codes.values():
253-
sys_monitoring.set_local_events(self.myid, code, 0)
254-
self.local_event_codes = {}
252+
with self.lock:
253+
self.sysmon_on = False
254+
for code in self.local_event_codes.values():
255+
sys_monitoring.set_local_events(self.myid, code, 0)
256+
self.local_event_codes = {}
255257
sys_monitoring.free_tool_id(self.myid)
256258

257259
@panopticon()
@@ -332,20 +334,21 @@ def sysmon_py_start(self, code: CodeType, instruction_offset: int) -> MonitorRet
332334

333335
if tracing_code:
334336
events = sys.monitoring.events
335-
if self.sysmon_on:
336-
assert sys_monitoring is not None
337-
sys_monitoring.set_local_events(
338-
self.myid,
339-
code,
340-
events.PY_RETURN
341-
#
342-
| events.PY_RESUME
343-
# | events.PY_YIELD
344-
| events.LINE,
345-
# | events.BRANCH
346-
# | events.JUMP
347-
)
348-
self.local_event_codes[id(code)] = code
337+
with self.lock:
338+
if self.sysmon_on:
339+
assert sys_monitoring is not None
340+
sys_monitoring.set_local_events(
341+
self.myid,
342+
code,
343+
events.PY_RETURN
344+
#
345+
| events.PY_RESUME
346+
# | events.PY_YIELD
347+
| events.LINE,
348+
# | events.BRANCH
349+
# | events.JUMP
350+
)
351+
self.local_event_codes[id(code)] = code
349352

350353
if tracing_code and self.trace_arcs:
351354
frame = self.callers_frame()

0 commit comments

Comments
 (0)