Skip to content

Commit 0625eb8

Browse files
committed
fixup! feat(core.utils): retry os.replace on Windows ERROR_ACCESS_DENIED (winerror 5)
1 parent 106bf74 commit 0625eb8

File tree

2 files changed

+13
-6
lines changed

2 files changed

+13
-6
lines changed

cuda_core/cuda/core/utils/_program_cache.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1067,7 +1067,7 @@ def _enforce_size_cap(self) -> None:
10671067
_TMP_STALE_AGE_SECONDS = 3600
10681068

10691069

1070-
_SHARING_VIOLATION_WINERRORS = (32, 33) # ERROR_SHARING_VIOLATION, ERROR_LOCK_VIOLATION
1070+
_SHARING_VIOLATION_WINERRORS = (5, 32, 33) # ERROR_ACCESS_DENIED, ERROR_SHARING_VIOLATION, ERROR_LOCK_VIOLATION
10711071
_REPLACE_RETRY_DELAYS = (0.0, 0.005, 0.010, 0.020, 0.050, 0.100) # ~185ms budget
10721072

10731073

@@ -1078,6 +1078,11 @@ def _replace_with_sharing_retry(tmp_path: Path, target: Path) -> bool:
10781078
exhausted on Windows with a genuine sharing violation -- the caller then
10791079
treats the cache write as dropped. Any other ``PermissionError`` (ACLs,
10801080
read-only dir, unexpected winerror, or any POSIX failure) propagates.
1081+
1082+
``ERROR_ACCESS_DENIED`` (winerror 5) is treated as a sharing violation
1083+
because Windows surfaces it when a file is held open without
1084+
``FILE_SHARE_WRITE`` (Python's default for ``open(p, "wb")``) or while
1085+
a previous unlink is in ``PENDING_DELETE`` -- both are transient.
10811086
"""
10821087
for i, delay in enumerate(_REPLACE_RETRY_DELAYS):
10831088
if delay:

cuda_core/tests/test_program_cache.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1547,17 +1547,19 @@ def _denied(*args, **kwargs):
15471547
@pytest.mark.parametrize(
15481548
"winerror, should_raise",
15491549
[
1550+
pytest.param(5, False, id="access_denied_swallowed"),
15501551
pytest.param(32, False, id="sharing_violation_swallowed"),
15511552
pytest.param(33, False, id="lock_violation_swallowed"),
1552-
pytest.param(5, True, id="access_denied_propagates"),
1553+
pytest.param(1, True, id="other_winerror_propagates"),
15531554
pytest.param(None, True, id="no_winerror_propagates"),
15541555
],
15551556
)
15561557
def test_filestream_cache_permission_error_windows_is_narrowed(tmp_path, monkeypatch, winerror, should_raise):
1557-
"""On Windows, only ERROR_SHARING_VIOLATION (32) and ERROR_LOCK_VIOLATION
1558-
(33) count as the "target held open by another process" case worth
1559-
swallowing. Any other PermissionError -- ACL issues, read-only dirs,
1560-
missing winerror attribute, etc. -- is a real problem and must propagate."""
1558+
"""On Windows, ERROR_ACCESS_DENIED (5), ERROR_SHARING_VIOLATION (32) and
1559+
ERROR_LOCK_VIOLATION (33) are all transient "target held open by another
1560+
process / pending delete" cases worth swallowing after the bounded retry.
1561+
Any other PermissionError -- unrelated winerrors, missing winerror
1562+
attribute, etc. -- is a real problem and must propagate."""
15611563
import os as _os
15621564

15631565
from cuda.core.utils import FileStreamProgramCache, _program_cache

0 commit comments

Comments
 (0)