Skip to content

Commit 9569417

Browse files
Andy-Jostclaude
andcommitted
test(cuda.core): keep the cycle collector out of the latch window in test_error_timing_incomplete
The test failed with "DID NOT RAISE" after about 60 s, the latch timeout, on one CI row. The likely cause is a finalizer that the cycle collector runs while the latch holds the stream: a free of memory synchronizes the context and waits for the latch. Collect before the launch and disable the collector until the latch is released. Create the end event before the launch. Use a 10 s latch and fail with the blocked time when the record call stalls. Closes #3296. Co-Authored-By: Claude Opus 5.5 <[email protected]>
1 parent 80b3359 commit 9569417

1 file changed

Lines changed: 34 additions & 7 deletions

File tree

‎cuda_core/tests/test_event.py‎

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
# SPDX-License-Identifier: Apache-2.0
33

44

5+
import gc
56
import math
7+
import time
68

79
import pytest
810
from helpers.latch import LatchKernel
@@ -15,6 +17,10 @@
1517
EventOptions,
1618
)
1719

20+
# Short enough that a stalled host call fails fast; long enough that the
21+
# latch cannot time out on its own during the few calls it guards.
22+
_LATCH_TIMEOUT_SEC = 10
23+
1824

1925
def test_event_init_disabled():
2026
with pytest.raises(RuntimeError, match=r"^Event objects cannot be instantiated directly\."):
@@ -120,22 +126,43 @@ def test_error_timing_recorded():
120126

121127
@pytest.mark.skipif(Device().compute_capability.major < 7, reason="__nanosleep is only available starting Volta (sm70)")
122128
@pytest.mark.thread_unsafe(reason="requires a barrier wait to avoid overlapping pinned latch allocations")
129+
@pytest.mark.agent_authored(model="claude-opus-5-5")
123130
def test_error_timing_incomplete():
124131
device = Device()
125132
device.set_current()
126-
latch = LatchKernel(device)
133+
latch = LatchKernel(device, timeout_sec=_LATCH_TIMEOUT_SEC)
127134
enabled = EventOptions(timing_enabled=True)
128135
stream = device.create_stream()
129136

130137
event1 = stream.record(options=enabled)
131-
latch.launch(stream)
132-
event3 = stream.record(options=enabled)
138+
# Create event3 before the latch holds the stream, so that no event
139+
# creation runs while the stream is held.
140+
event3 = device.create_event(options=enabled)
133141

134-
# event3 will never complete because the latch has not been released
135-
with pytest.raises(RuntimeError, match="^One or both events have not completed."):
136-
event3 - event1
142+
# A finalizer that frees memory synchronizes the context. If the cycle
143+
# collector runs one while the latch holds the stream, the host blocks
144+
# until the latch times out, and event3 completes (#3296). Collect now
145+
# and keep the collector off until the latch is released.
146+
gc.collect()
147+
gc_enabled = gc.isenabled()
148+
gc.disable()
149+
try:
150+
latch.launch(stream)
151+
start = time.monotonic()
152+
stream.record(event3)
153+
elapsed = time.monotonic() - start
154+
assert elapsed < _LATCH_TIMEOUT_SEC / 2, (
155+
f"a host call blocked for {elapsed:.1f} s while the latch held the stream "
156+
f"(latch timeout {_LATCH_TIMEOUT_SEC} s)"
157+
)
158+
# event3 cannot complete while the latch holds the stream
159+
with pytest.raises(RuntimeError, match="^One or both events have not completed."):
160+
event3 - event1
161+
finally:
162+
latch.release()
163+
if gc_enabled:
164+
gc.enable()
137165

138-
latch.release()
139166
event3.sync()
140167
event3 - event1 # this should work
141168

0 commit comments

Comments
 (0)