Skip to content

Commit ef3a763

Browse files
rwgkcursoragent
andcommitted
fix(pathfinder): fail fast on canary probe child errors
Treat only a missing canary library as a recoverable probe miss and surface all other child-process failures immediately. This prevents development bugs from being silently masked as normal CTK canary fallback behavior. Co-authored-by: Cursor <[email protected]>
1 parent fbdfc5a commit ef3a763

3 files changed

Lines changed: 71 additions & 15 deletions

File tree

‎cuda_pathfinder/cuda/pathfinder/_dynamic_libs/canary_probe_subprocess.py‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import json
66
import sys
77

8-
from cuda.pathfinder._dynamic_libs.load_dl_common import LoadedDL
8+
from cuda.pathfinder._dynamic_libs.load_dl_common import DynamicLibNotFoundError, LoadedDL
99
from cuda.pathfinder._utils.platform_aware import IS_WINDOWS
1010

1111
if IS_WINDOWS:
@@ -15,7 +15,10 @@
1515

1616

1717
def _probe_canary_abs_path(libname: str) -> str | None:
18-
loaded: LoadedDL | None = load_with_system_search(libname)
18+
try:
19+
loaded: LoadedDL | None = load_with_system_search(libname)
20+
except DynamicLibNotFoundError:
21+
return None
1922
if loaded is None:
2023
return None
2124
abs_path = loaded.abs_path

‎cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -78,28 +78,28 @@ def _resolve_system_loaded_abs_path_in_subprocess(libname: str) -> str | None:
7878
This keeps any side-effects of loading the canary library scoped to the
7979
child process instead of polluting the current process.
8080
"""
81-
try:
82-
result = run_in_spawned_child_process(
83-
probe_canary_abs_path_and_print_json,
84-
args=(libname,),
85-
timeout=10.0,
86-
)
87-
except (OSError, RuntimeError):
88-
return None
89-
if result.returncode != 0:
90-
return None
81+
result = run_in_spawned_child_process(
82+
probe_canary_abs_path_and_print_json,
83+
args=(libname,),
84+
timeout=10.0,
85+
rethrow=True,
86+
)
9187

9288
# Read the final non-empty stdout line in case earlier lines are emitted.
9389
lines = [line for line in result.stdout.splitlines() if line.strip()]
9490
if not lines:
95-
return None
91+
raise RuntimeError(f"Canary probe child process produced no stdout payload for {libname!r}")
9692
try:
9793
payload = json.loads(lines[-1])
9894
except json.JSONDecodeError:
99-
return None
95+
raise RuntimeError(
96+
f"Canary probe child process emitted invalid JSON payload for {libname!r}: {lines[-1]!r}"
97+
) from None
10098
if isinstance(payload, str):
10199
return payload
102-
return None
100+
if payload is None:
101+
return None
102+
raise RuntimeError(f"Canary probe child process emitted unexpected payload for {libname!r}: {payload!r}")
103103

104104

105105
def _try_ctk_root_canary(finder: _FindNvidiaDynamicLib) -> str | None:

‎cuda_pathfinder/tests/test_ctk_root_discovery.py‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from cuda.pathfinder._dynamic_libs.load_dl_common import LoadedDL
1414
from cuda.pathfinder._dynamic_libs.load_nvidia_dynamic_lib import (
1515
_load_lib_no_cache,
16+
_resolve_system_loaded_abs_path_in_subprocess,
1617
_try_ctk_root_canary,
1718
)
1819
from cuda.pathfinder._utils.platform_aware import IS_WINDOWS
@@ -140,6 +141,58 @@ def test_try_via_ctk_root_regular_lib(tmp_path):
140141
assert _FindNvidiaDynamicLib("cudart").try_via_ctk_root(str(ctk_root)) == str(cudart_lib)
141142

142143

144+
# ---------------------------------------------------------------------------
145+
# _resolve_system_loaded_abs_path_in_subprocess
146+
# ---------------------------------------------------------------------------
147+
148+
149+
def test_subprocess_probe_returns_abs_path_on_string_payload(mocker):
150+
result = mocker.Mock(stdout='"/usr/local/cuda/lib64/libcudart.so.13"\n')
151+
mocker.patch(f"{_MODULE}.run_in_spawned_child_process", return_value=result)
152+
153+
assert _resolve_system_loaded_abs_path_in_subprocess("cudart") == "/usr/local/cuda/lib64/libcudart.so.13"
154+
155+
156+
def test_subprocess_probe_returns_none_on_null_payload(mocker):
157+
result = mocker.Mock(stdout="null\n")
158+
mocker.patch(f"{_MODULE}.run_in_spawned_child_process", return_value=result)
159+
160+
assert _resolve_system_loaded_abs_path_in_subprocess("cudart") is None
161+
162+
163+
def test_subprocess_probe_raises_on_child_failure(mocker):
164+
mocker.patch(
165+
f"{_MODULE}.run_in_spawned_child_process",
166+
side_effect=ChildProcessError("child failed"),
167+
)
168+
with pytest.raises(ChildProcessError, match="child failed"):
169+
_resolve_system_loaded_abs_path_in_subprocess("cudart")
170+
171+
172+
def test_subprocess_probe_raises_on_empty_stdout(mocker):
173+
result = mocker.Mock(stdout=" \n \n")
174+
mocker.patch(f"{_MODULE}.run_in_spawned_child_process", return_value=result)
175+
176+
with pytest.raises(RuntimeError, match="produced no stdout payload"):
177+
_resolve_system_loaded_abs_path_in_subprocess("cudart")
178+
179+
180+
def test_subprocess_probe_raises_on_invalid_json_payload(mocker):
181+
result = mocker.Mock(stdout="not-json\n")
182+
mocker.patch(f"{_MODULE}.run_in_spawned_child_process", return_value=result)
183+
184+
with pytest.raises(RuntimeError, match="invalid JSON payload"):
185+
_resolve_system_loaded_abs_path_in_subprocess("cudart")
186+
187+
188+
def test_subprocess_probe_raises_on_unexpected_json_payload(mocker):
189+
result = mocker.Mock(stdout='{"path": "/usr/local/cuda/lib64/libcudart.so.13"}\n')
190+
mocker.patch(f"{_MODULE}.run_in_spawned_child_process", return_value=result)
191+
192+
with pytest.raises(RuntimeError, match="unexpected payload"):
193+
_resolve_system_loaded_abs_path_in_subprocess("cudart")
194+
195+
143196
# ---------------------------------------------------------------------------
144197
# _try_ctk_root_canary
145198
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)