tensor-grep 1.76.2__tar.gz → 1.76.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/PKG-INFO +1 -1
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/pyproject.toml +3 -1
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/Cargo.lock +1 -1
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/Cargo.toml +3 -1
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/src/gpu_native.rs +187 -4
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/src/main.rs +14 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_gpu_native_graphs.rs +2 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_gpu_native_integration.rs +3 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_gpu_native_long_lines.rs +5 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_gpu_native_streams.rs +4 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/session_daemon.py +87 -4
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/LICENSE +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/NOTICE +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/README.md +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/deny.toml +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/rust-toolchain.toml +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/src/backend_ast.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/src/backend_ast_workflow.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/src/backend_cpu.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/src/bin/tg_search_fast.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/src/cli.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/src/crossover.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/src/editor_plane.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/src/index.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/src/index_lock.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/src/lib.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/src/mmap_arrow.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/src/native_search.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/src/python_sidecar.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/src/rg_passthrough.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/src/routing.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/src/runtime_paths.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_ast_backend.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_ast_cache.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_ast_grep_embed.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_ast_rewrite.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_audit_manifest_symlink_unix.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_gpu_native.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_gpu_native_error_handling.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_index.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_index_lock_concurrency.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_mmap_arrow.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_native_search.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_output.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_public_native_cli_parity.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_python_bootstrap.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_release_profile.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_replace.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_resident_worker.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_routing.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_runtime_path_resolution.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_schema_compat.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_search.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_search_golden.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_sidecar_ipc.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_smart_routing.rs +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/__init__.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/__main__.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/backends/__init__.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/backends/ast_backend.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/backends/ast_wrapper_backend.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/backends/base.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/backends/cpu_backend.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/backends/cudf_backend.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/backends/cybert_backend.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/backends/ripgrep_backend.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/backends/rust_backend.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/backends/stringzilla_backend.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/backends/torch_backend.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/__init__.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/_index_lock.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/agent_capsule.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/apply_policy.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/ast_workflows.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/audit_manifest.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/bootstrap.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/checkpoint_store.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/codemap.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/commands.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/docs_coverage.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/dogfood.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/evidence_receipt.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/evidence_signing.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/formatters/__init__.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/formatters/base.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/formatters/csv_fmt.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/formatters/json_fmt.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/formatters/ripgrep_fmt.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/formatters/table_fmt.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/inventory.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/lang_go.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/lang_registry.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/lsp_external_provider.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/lsp_provider_setup.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/lsp_server.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/main.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/mcp_server.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/orient_capsule.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/progress.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/repo_map.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/rg_contract.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/rule_packs.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/runtime_paths.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/scan_guardrails.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/session_store.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/cli/subprocess_policy.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/__init__.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/config.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/hardware/__init__.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/hardware/device_detect.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/hardware/device_inventory.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/hardware/memory_manager.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/observability.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/pipeline.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/query_analyzer.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/registration_check.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/reranker.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/result.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/retrieval_bm25.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/retrieval_chunker.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/retrieval_dense.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/retrieval_fusion.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/retrieval_late.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/retrieval_lexical.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/retrieval_scoring.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/core/semantic_index.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/io/__init__.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/io/base.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/io/directory_scanner.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/io/reader_cudf.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/io/reader_dstorage.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/io/reader_fallback.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/io/reader_kvikio.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/perf_guard.py +0 -0
- {tensor_grep-1.76.2 → tensor_grep-1.76.4}/src/tensor_grep/sidecar.py +0 -0
|
@@ -530,12 +530,14 @@ version_variables = [
|
|
|
530
530
|
|
|
531
531
|
|
|
532
532
|
|
|
533
|
+
|
|
534
|
+
|
|
533
535
|
|
|
534
536
|
|
|
535
537
|
|
|
536
538
|
[project]
|
|
537
539
|
name = "tensor-grep"
|
|
538
|
-
version = "1.76.
|
|
540
|
+
version = "1.76.4"
|
|
539
541
|
description = "High-performance grep-compatible CLI with Rust, GPU, and AST/NLP routing"
|
|
540
542
|
readme = { file = "rust_core/README.md", content-type = "text/markdown" }
|
|
541
543
|
requires-python = ">=3.11"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "tensor_grep_rs"
|
|
3
|
-
version = "1.76.
|
|
3
|
+
version = "1.76.4"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
description = "Rust core engine for tensor-grep"
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -438,6 +438,8 @@ path = "src/bin/tg_search_fast.rs"
|
|
|
438
438
|
|
|
439
439
|
|
|
440
440
|
|
|
441
|
+
|
|
442
|
+
|
|
441
443
|
|
|
442
444
|
|
|
443
445
|
|
|
@@ -361,6 +361,16 @@ pub struct GpuNativeSearchConfig {
|
|
|
361
361
|
pub hidden: bool,
|
|
362
362
|
pub max_depth: Option<usize>,
|
|
363
363
|
pub max_batch_bytes: Option<usize>,
|
|
364
|
+
/// Whether the caller omitted an explicit PATH (the search root defaulted to `.` rather than
|
|
365
|
+
/// a user-supplied path). Gates `check_gpu_native_implicit_walk_ceiling`, this engine's own
|
|
366
|
+
/// refuse-before-enumerate guard (audit #109 -- the cuda-native-GPU sibling of
|
|
367
|
+
/// `NativeSearchConfig::path_was_implicit`, audit #105, itself the native-CPU sibling of
|
|
368
|
+
/// `RipgrepSearchArgs::path_was_implicit`, audit #100). An explicit, deliberately-scoped PATH
|
|
369
|
+
/// must never be refused regardless of its size. `Default`'s `false` is NOT a safe fallback for
|
|
370
|
+
/// the walk guard itself (it means "never refuse"); it only exists so ad hoc test fixtures and
|
|
371
|
+
/// the always-explicit-`--path` diagnostic commands (`gpu-native-stats`, cuda-graph benchmark)
|
|
372
|
+
/// get deterministic, non-refusing behavior, mirroring `NativeSearchConfig`'s convention.
|
|
373
|
+
pub path_was_implicit: bool,
|
|
364
374
|
}
|
|
365
375
|
|
|
366
376
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
|
@@ -3784,6 +3794,47 @@ fn ensure_capacity(capacity: usize, required: usize, path: &Path) -> Result<()>
|
|
|
3784
3794
|
))
|
|
3785
3795
|
}
|
|
3786
3796
|
|
|
3797
|
+
/// Bounded refuse-before-enumerate gate for the GPU-native engine's own root walk -- the
|
|
3798
|
+
/// cuda-native sibling of `native_search::check_native_implicit_walk_ceiling` (audit #105) and
|
|
3799
|
+
/// `rg_passthrough::check_implicit_walk_ceiling` (audit #100). Audit #109 found the GPU-native
|
|
3800
|
+
/// engine had NO ceiling at all: `GpuSearchParams::path_was_implicit` (main.rs) was threaded into
|
|
3801
|
+
/// the CPU-fallback `RipgrepSearchArgs`/`NativeSearchConfig` redirects (`execute_gpu_native_route`'s
|
|
3802
|
+
/// unavailable-GPU fallback path) but never into `GpuNativeSearchConfig` itself, so a bare
|
|
3803
|
+
/// implicit-path `tg search PAT --gpu-device-ids 0` on a huge root walked unbounded through this
|
|
3804
|
+
/// engine even though the CPU engine (#105) and rg-passthrough engine (#100) were both already
|
|
3805
|
+
/// bounded for the exact same shape of request.
|
|
3806
|
+
///
|
|
3807
|
+
/// Only meaningful when `config.path_was_implicit` -- an explicit, deliberately-scoped PATH is
|
|
3808
|
+
/// never refused regardless of size. Called as the FIRST statement of `collect_walked_files`, the
|
|
3809
|
+
/// only function in this module that ever hands a root to `WalkBuilder`.
|
|
3810
|
+
fn check_gpu_native_implicit_walk_ceiling(
|
|
3811
|
+
config: &GpuNativeSearchConfig,
|
|
3812
|
+
roots: &[PathBuf],
|
|
3813
|
+
) -> Option<String> {
|
|
3814
|
+
if !config.path_was_implicit {
|
|
3815
|
+
return None;
|
|
3816
|
+
}
|
|
3817
|
+
let probe_roots: Vec<String> = roots
|
|
3818
|
+
.iter()
|
|
3819
|
+
.map(|root| root.to_string_lossy().into_owned())
|
|
3820
|
+
.collect();
|
|
3821
|
+
if crate::rg_passthrough::implicit_search_walk_exceeds_ceiling(
|
|
3822
|
+
&probe_roots,
|
|
3823
|
+
config.max_depth,
|
|
3824
|
+
config.no_ignore,
|
|
3825
|
+
config.hidden,
|
|
3826
|
+
crate::rg_passthrough::IMPLICIT_SEARCH_WALK_FILE_CEILING,
|
|
3827
|
+
) {
|
|
3828
|
+
Some(
|
|
3829
|
+
crate::rg_passthrough::format_unbounded_implicit_search_walk_error(
|
|
3830
|
+
crate::rg_passthrough::IMPLICIT_SEARCH_WALK_FILE_CEILING,
|
|
3831
|
+
),
|
|
3832
|
+
)
|
|
3833
|
+
} else {
|
|
3834
|
+
None
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
|
|
3787
3838
|
fn collect_search_files(config: &GpuNativeSearchConfig) -> Result<Vec<PathBuf>> {
|
|
3788
3839
|
let mut files = Vec::new();
|
|
3789
3840
|
let mut roots = Vec::new();
|
|
@@ -3812,6 +3863,9 @@ fn collect_search_files(config: &GpuNativeSearchConfig) -> Result<Vec<PathBuf>>
|
|
|
3812
3863
|
}
|
|
3813
3864
|
|
|
3814
3865
|
fn collect_walked_files(config: &GpuNativeSearchConfig, roots: &[PathBuf]) -> Result<Vec<PathBuf>> {
|
|
3866
|
+
if let Some(refusal) = check_gpu_native_implicit_walk_ceiling(config, roots) {
|
|
3867
|
+
return Err(anyhow!(refusal));
|
|
3868
|
+
}
|
|
3815
3869
|
let builder = build_walk_builder(config, roots)?;
|
|
3816
3870
|
let walked_files = Arc::new(std::sync::Mutex::new(Vec::new()));
|
|
3817
3871
|
let shared_files = Arc::clone(&walked_files);
|
|
@@ -4288,13 +4342,142 @@ fn ensure_cuda_library_path() {
|
|
|
4288
4342
|
#[cfg(test)]
|
|
4289
4343
|
mod tests {
|
|
4290
4344
|
use super::{
|
|
4291
|
-
cached_search_kernel_ptx_path,
|
|
4292
|
-
|
|
4345
|
+
cached_search_kernel_ptx_path, check_gpu_native_implicit_walk_ceiling,
|
|
4346
|
+
collect_search_files, kernel_compile_options_for_compute_capability, line_matches_for_file,
|
|
4347
|
+
load_file_batch_into_pinned_slice, resolve_adaptive_match_capacity,
|
|
4293
4348
|
resolve_max_match_capacity, validate_requested_cuda_device_ids, BatchedFile, FileBatchPlan,
|
|
4294
|
-
LineDescriptor, PatternMatchPosition,
|
|
4349
|
+
GpuNativeSearchConfig, LineDescriptor, PatternMatchPosition,
|
|
4295
4350
|
};
|
|
4296
4351
|
use std::fs;
|
|
4297
|
-
use std::path::PathBuf;
|
|
4352
|
+
use std::path::{Path, PathBuf};
|
|
4353
|
+
|
|
4354
|
+
// --- Audit #109: GPU-native implicit-walk-ceiling gate --------------------------------
|
|
4355
|
+
// Mirrors native_search.rs's audit #105 test suite for `check_native_implicit_walk_ceiling`.
|
|
4356
|
+
// #105 hoisted a walk-ceiling gate into the native-CPU engine but left the GPU-native engine
|
|
4357
|
+
// (this module) with NO ceiling at all -- `GpuNativeSearchConfig` did not even have a
|
|
4358
|
+
// `path_was_implicit` field, so a bare implicit-path `--gpu-device-ids` search on a huge root
|
|
4359
|
+
// walked unbounded through `collect_walked_files`.
|
|
4360
|
+
|
|
4361
|
+
fn make_stub_file_dir(dir: &Path, file_count: usize) {
|
|
4362
|
+
for index in 0..file_count {
|
|
4363
|
+
fs::write(
|
|
4364
|
+
dir.join(format!("stub_{index}.py")),
|
|
4365
|
+
"nothing interesting\n",
|
|
4366
|
+
)
|
|
4367
|
+
.unwrap();
|
|
4368
|
+
}
|
|
4369
|
+
}
|
|
4370
|
+
|
|
4371
|
+
fn config_with_paths(paths: Vec<PathBuf>, path_was_implicit: bool) -> GpuNativeSearchConfig {
|
|
4372
|
+
GpuNativeSearchConfig {
|
|
4373
|
+
patterns: vec!["TODO".to_string()],
|
|
4374
|
+
paths,
|
|
4375
|
+
path_was_implicit,
|
|
4376
|
+
..GpuNativeSearchConfig::default()
|
|
4377
|
+
}
|
|
4378
|
+
}
|
|
4379
|
+
|
|
4380
|
+
#[test]
|
|
4381
|
+
fn check_gpu_native_implicit_walk_ceiling_refuses_oversized_implicit_walk() {
|
|
4382
|
+
// RED-before-fix: this is the exact shape of the #109 bypass -- an implicit-path search
|
|
4383
|
+
// (no explicit PATH positional) on a root over the 1500-file ceiling.
|
|
4384
|
+
let dir = tempfile::tempdir().unwrap();
|
|
4385
|
+
make_stub_file_dir(dir.path(), 1600);
|
|
4386
|
+
let roots = vec![dir.path().to_path_buf()];
|
|
4387
|
+
let config = config_with_paths(roots.clone(), true);
|
|
4388
|
+
|
|
4389
|
+
let refusal = check_gpu_native_implicit_walk_ceiling(&config, &roots);
|
|
4390
|
+
|
|
4391
|
+
assert!(
|
|
4392
|
+
refusal.is_some(),
|
|
4393
|
+
"an oversized implicit-path walk must be refused"
|
|
4394
|
+
);
|
|
4395
|
+
}
|
|
4396
|
+
|
|
4397
|
+
#[test]
|
|
4398
|
+
fn check_gpu_native_implicit_walk_ceiling_allows_explicit_path_even_when_oversized() {
|
|
4399
|
+
// Non-regression (Trap #3 parity, mirrors native_search.rs/rg_passthrough.rs): an
|
|
4400
|
+
// EXPLICIT, deliberately-scoped PATH must never be refused regardless of size.
|
|
4401
|
+
let dir = tempfile::tempdir().unwrap();
|
|
4402
|
+
make_stub_file_dir(dir.path(), 1600);
|
|
4403
|
+
let roots = vec![dir.path().to_path_buf()];
|
|
4404
|
+
let config = config_with_paths(roots.clone(), false);
|
|
4405
|
+
|
|
4406
|
+
let refusal = check_gpu_native_implicit_walk_ceiling(&config, &roots);
|
|
4407
|
+
|
|
4408
|
+
assert!(
|
|
4409
|
+
refusal.is_none(),
|
|
4410
|
+
"an explicit path must run uninhibited even when the walk exceeds the ceiling"
|
|
4411
|
+
);
|
|
4412
|
+
}
|
|
4413
|
+
|
|
4414
|
+
#[test]
|
|
4415
|
+
fn check_gpu_native_implicit_walk_ceiling_allows_implicit_path_under_ceiling() {
|
|
4416
|
+
// Normal-case non-regression: an implicit path under the ceiling is unaffected -- a
|
|
4417
|
+
// typical repo must never be refused.
|
|
4418
|
+
let dir = tempfile::tempdir().unwrap();
|
|
4419
|
+
make_stub_file_dir(dir.path(), 50);
|
|
4420
|
+
let roots = vec![dir.path().to_path_buf()];
|
|
4421
|
+
let config = config_with_paths(roots.clone(), true);
|
|
4422
|
+
|
|
4423
|
+
let refusal = check_gpu_native_implicit_walk_ceiling(&config, &roots);
|
|
4424
|
+
|
|
4425
|
+
assert!(
|
|
4426
|
+
refusal.is_none(),
|
|
4427
|
+
"a 50-file implicit root must not be refused"
|
|
4428
|
+
);
|
|
4429
|
+
}
|
|
4430
|
+
|
|
4431
|
+
#[test]
|
|
4432
|
+
fn collect_search_files_refuses_oversized_implicit_walk_before_enumerating() {
|
|
4433
|
+
// Hermetic end-to-end test of the actual `collect_search_files` production entry point
|
|
4434
|
+
// `gpu_native_search_paths_multi_with_options` calls before ever touching a CUDA device
|
|
4435
|
+
// (see that function: `collect_search_files(config)?` runs BEFORE `resolve_cuda_devices`),
|
|
4436
|
+
// so this test exercises the real bug with no GPU/CUDA runtime involved. Bounded per
|
|
4437
|
+
// anti-hang-test-protocol: run on a joined worker thread with an explicit timeout so a
|
|
4438
|
+
// regression (the gate silently stops firing) that falls through to the unbounded
|
|
4439
|
+
// parallel walk cannot hang the test runner -- it fails fast with a clear panic instead.
|
|
4440
|
+
let dir = tempfile::tempdir().unwrap();
|
|
4441
|
+
make_stub_file_dir(dir.path(), 1600);
|
|
4442
|
+
let config = config_with_paths(vec![dir.path().to_path_buf()], true);
|
|
4443
|
+
|
|
4444
|
+
let (tx, rx) = std::sync::mpsc::channel();
|
|
4445
|
+
std::thread::spawn(move || {
|
|
4446
|
+
let result = collect_search_files(&config).map_err(|error| error.to_string());
|
|
4447
|
+
let _ = tx.send(result);
|
|
4448
|
+
});
|
|
4449
|
+
let result = rx.recv_timeout(std::time::Duration::from_secs(10)).expect(
|
|
4450
|
+
"collect_search_files must return well within 10s -- a hang here means the \
|
|
4451
|
+
walk-ceiling gate did not fire before an unbounded parallel walk",
|
|
4452
|
+
);
|
|
4453
|
+
|
|
4454
|
+
let err = result.expect_err("an oversized implicit-path walk must be refused, not Ok");
|
|
4455
|
+
assert!(
|
|
4456
|
+
crate::rg_passthrough::is_unbounded_implicit_search_walk_refusal(&err),
|
|
4457
|
+
"unexpected error (expected the walk-ceiling refusal): {err}"
|
|
4458
|
+
);
|
|
4459
|
+
}
|
|
4460
|
+
|
|
4461
|
+
#[test]
|
|
4462
|
+
fn collect_search_files_does_not_refuse_explicit_oversized_path() {
|
|
4463
|
+
// Non-regression: an explicit PATH (even oversized) must complete normally, not be
|
|
4464
|
+
// refused -- fail-open for explicit scoping is the whole point of the guard (Trap #3
|
|
4465
|
+
// parity). Bounded per anti-hang-test-protocol.
|
|
4466
|
+
let dir = tempfile::tempdir().unwrap();
|
|
4467
|
+
make_stub_file_dir(dir.path(), 1600);
|
|
4468
|
+
let config = config_with_paths(vec![dir.path().to_path_buf()], false);
|
|
4469
|
+
|
|
4470
|
+
let (tx, rx) = std::sync::mpsc::channel();
|
|
4471
|
+
std::thread::spawn(move || {
|
|
4472
|
+
let result = collect_search_files(&config).map_err(|error| error.to_string());
|
|
4473
|
+
let _ = tx.send(result);
|
|
4474
|
+
});
|
|
4475
|
+
let result = rx
|
|
4476
|
+
.recv_timeout(std::time::Duration::from_secs(20))
|
|
4477
|
+
.expect("collect_search_files must return well within 20s for an explicit path");
|
|
4478
|
+
|
|
4479
|
+
result.expect("an explicit oversized path must not be refused");
|
|
4480
|
+
}
|
|
4298
4481
|
|
|
4299
4482
|
#[test]
|
|
4300
4483
|
fn validate_requested_cuda_device_ids_preserves_selected_subset_without_expanding() {
|
|
@@ -10663,6 +10663,10 @@ fn gpu_native_config_from_internal_args(args: &GpuNativeStatsArgs) -> GpuNativeS
|
|
|
10663
10663
|
hidden: false,
|
|
10664
10664
|
max_depth: None,
|
|
10665
10665
|
max_batch_bytes: args.max_batch_bytes,
|
|
10666
|
+
// `GpuNativeStatsArgs::path` is a required `#[arg(long)]` (no default_value) -- this
|
|
10667
|
+
// diagnostic command always has an explicit, deliberately-scoped PATH, never an implicit
|
|
10668
|
+
// one, so the #109 ceiling must never fire here regardless of tree size.
|
|
10669
|
+
path_was_implicit: false,
|
|
10666
10670
|
}
|
|
10667
10671
|
}
|
|
10668
10672
|
|
|
@@ -10676,6 +10680,10 @@ fn gpu_native_config_from_graph_args(args: &GpuCudaGraphArgs) -> GpuNativeSearch
|
|
|
10676
10680
|
hidden: false,
|
|
10677
10681
|
max_depth: None,
|
|
10678
10682
|
max_batch_bytes: args.max_batch_bytes,
|
|
10683
|
+
// `GpuCudaGraphArgs::path` is a required `#[arg(long)]` (no default_value) -- this
|
|
10684
|
+
// benchmark command always has an explicit, deliberately-scoped PATH, never an implicit
|
|
10685
|
+
// one, so the #109 ceiling must never fire here regardless of tree size.
|
|
10686
|
+
path_was_implicit: false,
|
|
10679
10687
|
}
|
|
10680
10688
|
}
|
|
10681
10689
|
|
|
@@ -10764,6 +10772,12 @@ fn execute_gpu_native_route(
|
|
|
10764
10772
|
hidden: params.hidden,
|
|
10765
10773
|
max_depth: params.max_depth,
|
|
10766
10774
|
max_batch_bytes: None,
|
|
10775
|
+
// Audit #109 fix: this field did not exist on `GpuNativeSearchConfig` at all, so the
|
|
10776
|
+
// GPU-native engine had no equivalent of the #105 native-CPU implicit-walk ceiling --
|
|
10777
|
+
// `params.path_was_implicit` was already threaded into the CPU-fallback redirects
|
|
10778
|
+
// (`ripgrep_args_for_gpu_params`, `native_search_config_for_gpu_params`) but silently
|
|
10779
|
+
// dropped here, the one construction site that feeds the actual cuda kernel walk.
|
|
10780
|
+
path_was_implicit: params.path_was_implicit,
|
|
10767
10781
|
};
|
|
10768
10782
|
|
|
10769
10783
|
let stats = gpu_native_search_paths_multi(&config, device_ids)?;
|
|
@@ -46,6 +46,7 @@ fn test_gpu_native_replays_captured_cuda_graphs_without_changing_results() {
|
|
|
46
46
|
hidden: false,
|
|
47
47
|
max_depth: None,
|
|
48
48
|
max_batch_bytes: Some(4 * 1024),
|
|
49
|
+
path_was_implicit: false,
|
|
49
50
|
};
|
|
50
51
|
|
|
51
52
|
let benchmark = benchmark_cuda_graph_search_paths(&config, device_id).unwrap();
|
|
@@ -83,6 +84,7 @@ fn test_gpu_native_cuda_graphs_reduce_batch_overhead_by_ten_percent() {
|
|
|
83
84
|
hidden: false,
|
|
84
85
|
max_depth: None,
|
|
85
86
|
max_batch_bytes: Some(4 * 1024),
|
|
87
|
+
path_was_implicit: false,
|
|
86
88
|
};
|
|
87
89
|
|
|
88
90
|
let benchmark = benchmark_cuda_graph_search_paths(&config, device_id).unwrap();
|
|
@@ -777,6 +777,7 @@ fn test_gpu_native_multi_pattern_falls_back_to_batched_passes_for_large_pattern_
|
|
|
777
777
|
hidden: false,
|
|
778
778
|
max_depth: None,
|
|
779
779
|
max_batch_bytes: Some(8 * 1024),
|
|
780
|
+
path_was_implicit: false,
|
|
780
781
|
};
|
|
781
782
|
|
|
782
783
|
let stats = gpu_native_search_paths(&config, device_id).unwrap();
|
|
@@ -821,6 +822,7 @@ fn test_gpu_native_three_pattern_dispatch_is_below_two_times_single_pattern() {
|
|
|
821
822
|
hidden: false,
|
|
822
823
|
max_depth: None,
|
|
823
824
|
max_batch_bytes: None,
|
|
825
|
+
path_was_implicit: false,
|
|
824
826
|
};
|
|
825
827
|
let multi_config = GpuNativeSearchConfig {
|
|
826
828
|
patterns: vec![
|
|
@@ -834,6 +836,7 @@ fn test_gpu_native_three_pattern_dispatch_is_below_two_times_single_pattern() {
|
|
|
834
836
|
hidden: false,
|
|
835
837
|
max_depth: None,
|
|
836
838
|
max_batch_bytes: None,
|
|
839
|
+
path_was_implicit: false,
|
|
837
840
|
};
|
|
838
841
|
|
|
839
842
|
let mut single_samples = Vec::new();
|
|
@@ -119,6 +119,7 @@ fn test_gpu_native_adaptive_dispatch_matches_cpu_for_ten_kib_lines() {
|
|
|
119
119
|
hidden: false,
|
|
120
120
|
max_depth: None,
|
|
121
121
|
max_batch_bytes: Some(256 * 1024),
|
|
122
|
+
path_was_implicit: false,
|
|
122
123
|
};
|
|
123
124
|
|
|
124
125
|
let stats = gpu_native_search_paths(&config, device_id).unwrap();
|
|
@@ -161,6 +162,7 @@ fn test_gpu_native_adaptive_dispatch_matches_cpu_for_hundred_kib_lines() {
|
|
|
161
162
|
hidden: false,
|
|
162
163
|
max_depth: None,
|
|
163
164
|
max_batch_bytes: Some(512 * 1024),
|
|
165
|
+
path_was_implicit: false,
|
|
164
166
|
};
|
|
165
167
|
|
|
166
168
|
let stats = gpu_native_search_paths(&config, device_id).unwrap();
|
|
@@ -202,6 +204,7 @@ fn test_gpu_native_adaptive_dispatch_classifies_mixed_short_medium_and_long_line
|
|
|
202
204
|
hidden: false,
|
|
203
205
|
max_depth: None,
|
|
204
206
|
max_batch_bytes: Some(256 * 1024),
|
|
207
|
+
path_was_implicit: false,
|
|
205
208
|
};
|
|
206
209
|
|
|
207
210
|
let stats = gpu_native_search_paths(&config, device_id).unwrap();
|
|
@@ -263,6 +266,7 @@ fn test_gpu_native_long_line_corpus_is_not_slower_than_short_line_corpus() {
|
|
|
263
266
|
hidden: false,
|
|
264
267
|
max_depth: None,
|
|
265
268
|
max_batch_bytes: Some(2 * 1024 * 1024),
|
|
269
|
+
path_was_implicit: false,
|
|
266
270
|
};
|
|
267
271
|
let long_config = GpuNativeSearchConfig {
|
|
268
272
|
patterns: vec![pattern.to_string()],
|
|
@@ -272,6 +276,7 @@ fn test_gpu_native_long_line_corpus_is_not_slower_than_short_line_corpus() {
|
|
|
272
276
|
hidden: false,
|
|
273
277
|
max_depth: None,
|
|
274
278
|
max_batch_bytes: Some(2 * 1024 * 1024),
|
|
279
|
+
path_was_implicit: false,
|
|
275
280
|
};
|
|
276
281
|
|
|
277
282
|
let mut short_samples = Vec::new();
|
|
@@ -80,6 +80,7 @@ fn test_gpu_native_search_reports_pinned_double_buffered_stream_pipeline() {
|
|
|
80
80
|
hidden: false,
|
|
81
81
|
max_depth: None,
|
|
82
82
|
max_batch_bytes: Some(64 * 1024),
|
|
83
|
+
path_was_implicit: false,
|
|
83
84
|
};
|
|
84
85
|
|
|
85
86
|
let stats = gpu_native_search_paths(&config, device_id).unwrap();
|
|
@@ -107,6 +108,7 @@ fn test_gpu_native_overlap_matches_expected_results_without_races() {
|
|
|
107
108
|
hidden: false,
|
|
108
109
|
max_depth: None,
|
|
109
110
|
max_batch_bytes: Some(64 * 1024),
|
|
111
|
+
path_was_implicit: false,
|
|
110
112
|
};
|
|
111
113
|
|
|
112
114
|
let mut actual = gpu_native_search_paths(&config, device_id)
|
|
@@ -138,6 +140,7 @@ fn test_gpu_native_overlap_metrics_show_transfer_compute_overlap() {
|
|
|
138
140
|
hidden: false,
|
|
139
141
|
max_depth: None,
|
|
140
142
|
max_batch_bytes: Some(64 * 1024),
|
|
143
|
+
path_was_implicit: false,
|
|
141
144
|
};
|
|
142
145
|
|
|
143
146
|
let stats = gpu_native_search_paths(&config, device_id).unwrap();
|
|
@@ -189,6 +192,7 @@ fn test_gpu_native_multi_gpu_balances_distribution_and_matches_single_gpu_result
|
|
|
189
192
|
hidden: false,
|
|
190
193
|
max_depth: None,
|
|
191
194
|
max_batch_bytes: Some(128 * 1024),
|
|
195
|
+
path_was_implicit: false,
|
|
192
196
|
};
|
|
193
197
|
|
|
194
198
|
let single = gpu_native_search_paths(&config, devices[0].device_id).unwrap();
|
|
@@ -372,14 +372,71 @@ def _release_daemon_start_lock(root: Path) -> None:
|
|
|
372
372
|
pass
|
|
373
373
|
|
|
374
374
|
|
|
375
|
-
def _remove_daemon_metadata(
|
|
375
|
+
def _remove_daemon_metadata(
|
|
376
|
+
root: Path, *, expected_pid: int | None = None, expected_port: int | None = None
|
|
377
|
+
) -> None:
|
|
378
|
+
"""Remove ``daemon.json`` for ``root``, guarded against deleting a REPLACEMENT daemon's metadata.
|
|
379
|
+
|
|
380
|
+
Task #143a-a (stale-daemon-metadata race): when ``_probe_daemon`` rejects a daemon over a
|
|
381
|
+
``package_version`` mismatch (see the skew check in ``_probe_daemon`` below), the client spawns
|
|
382
|
+
a replacement daemon (call it B) while the old daemon (A) keeps running until its own
|
|
383
|
+
idle/max-uptime self-shutdown fires. If A's shutdown path (or ``stop_session_daemon``'s
|
|
384
|
+
PID-kill fallback) unlinked ``daemon.json`` unconditionally, it would delete WHATEVER metadata
|
|
385
|
+
currently exists -- including B's, the healthy replacement -- orphaning B (it keeps serving,
|
|
386
|
+
but no client can discover it) and silently piling up orphaned daemon processes over time.
|
|
387
|
+
|
|
388
|
+
Passing ``expected_pid``/``expected_port`` re-reads the CURRENT on-disk metadata immediately
|
|
389
|
+
before unlinking and only proceeds if its ``pid``/``port`` fields still match -- i.e. the file
|
|
390
|
+
still identifies the exact instance the caller intends to remove. A mismatch (a replacement has
|
|
391
|
+
since published its own metadata), a missing file, or an unparseable/corrupt file are all
|
|
392
|
+
treated as "not mine" and left untouched -- the fail-safe choice, since we cannot prove the file
|
|
393
|
+
is safe to delete, and a corrupt file self-heals the next time a daemon is (re)started via
|
|
394
|
+
``_spawn_daemon_subprocess``'s unconditional clear below.
|
|
395
|
+
|
|
396
|
+
When BOTH ``expected_pid`` and ``expected_port`` are omitted (the default), removal is
|
|
397
|
+
unconditional -- this mode exists for exactly one legitimate caller, ``_spawn_daemon_subprocess``,
|
|
398
|
+
which holds the daemon-start lock (excluding any concurrent spawn for this root) and is clearing
|
|
399
|
+
the slate for a brand-new instance it is about to launch, so there is no prior "self" identity to
|
|
400
|
+
compare against yet. Every other caller must supply at least one ``expected_*`` value, or skip
|
|
401
|
+
the call entirely when it cannot establish one (see ``stop_session_daemon``).
|
|
402
|
+
"""
|
|
376
403
|
metadata_path = _daemon_metadata_path(root)
|
|
404
|
+
if expected_pid is not None or expected_port is not None:
|
|
405
|
+
current = _read_daemon_metadata(root)
|
|
406
|
+
if current is None:
|
|
407
|
+
return
|
|
408
|
+
if expected_pid is not None and current.get("pid") != expected_pid:
|
|
409
|
+
return
|
|
410
|
+
if expected_port is not None and current.get("port") != expected_port:
|
|
411
|
+
return
|
|
377
412
|
try:
|
|
378
413
|
metadata_path.unlink()
|
|
379
414
|
except OSError:
|
|
380
415
|
pass
|
|
381
416
|
|
|
382
417
|
|
|
418
|
+
def _daemon_identity(metadata: dict[str, Any] | None) -> tuple[int | None, int | None]:
|
|
419
|
+
"""Best-effort ``(pid, port)`` extraction from daemon metadata (task #143a-a).
|
|
420
|
+
|
|
421
|
+
Used by callers of the guarded ``_remove_daemon_metadata`` above to compute the specific
|
|
422
|
+
instance they intend to remove. Missing or non-numeric fields resolve to ``None`` rather than
|
|
423
|
+
raising, so a malformed or partial metadata dict never turns into a removal call the guard
|
|
424
|
+
cannot safely reason about -- the caller is expected to skip the removal when either half of
|
|
425
|
+
the pair it needs comes back ``None`` (see ``stop_session_daemon``).
|
|
426
|
+
"""
|
|
427
|
+
if not metadata:
|
|
428
|
+
return None, None
|
|
429
|
+
try:
|
|
430
|
+
pid: int | None = int(metadata["pid"])
|
|
431
|
+
except (KeyError, TypeError, ValueError):
|
|
432
|
+
pid = None
|
|
433
|
+
try:
|
|
434
|
+
port: int | None = int(metadata["port"])
|
|
435
|
+
except (KeyError, TypeError, ValueError):
|
|
436
|
+
port = None
|
|
437
|
+
return pid, port
|
|
438
|
+
|
|
439
|
+
|
|
383
440
|
def _pid_looks_like_tg_daemon(pid: int) -> bool:
|
|
384
441
|
"""Best-effort check that ``pid`` is a tensor-grep session daemon (audit I7).
|
|
385
442
|
|
|
@@ -630,6 +687,12 @@ def _spawn_daemon_subprocess(root: Path) -> None:
|
|
|
630
687
|
``daemon.json`` to appear and does not touch the start-lock -- the caller is responsible for
|
|
631
688
|
both (holding the lock across this call, and deciding whether/how long to wait afterward).
|
|
632
689
|
"""
|
|
690
|
+
# Task #143a-a: unconditional (force) removal is intentional and safe here -- every caller
|
|
691
|
+
# holds the daemon-start lock for `root` (excluding any concurrent spawn), so nothing else can
|
|
692
|
+
# be publishing fresh metadata for this root while we hold it, and we are about to Popen a
|
|
693
|
+
# brand-new instance that will publish its OWN metadata momentarily. There is no prior "self"
|
|
694
|
+
# identity to compare against yet, unlike the guarded callers in stop_session_daemon and
|
|
695
|
+
# run_session_daemon_server's shutdown path below.
|
|
633
696
|
_remove_daemon_metadata(root)
|
|
634
697
|
creationflags = 0
|
|
635
698
|
env = os.environ.copy()
|
|
@@ -772,7 +835,16 @@ def stop_session_daemon(path: str = ".") -> dict[str, Any]:
|
|
|
772
835
|
# socket is wedged). Fall back to terminating the recorded pid if it validates.
|
|
773
836
|
stale_metadata = _read_daemon_metadata(root)
|
|
774
837
|
killed = _terminate_daemon_by_pid(stale_metadata)
|
|
775
|
-
|
|
838
|
+
# Task #143a-a: only remove the metadata that still identifies the STALE daemon we just
|
|
839
|
+
# targeted -- never whatever happens to be on disk by the time we get here. A concurrent
|
|
840
|
+
# autostart elsewhere may have already spawned and published a healthy replacement's
|
|
841
|
+
# daemon.json in the window between the failed probe above and this cleanup; an
|
|
842
|
+
# unconditional unlink would delete that replacement's metadata too. When the stale
|
|
843
|
+
# metadata itself cannot be identified (missing/corrupt), there is nothing safe to
|
|
844
|
+
# remove -- skip rather than guess.
|
|
845
|
+
stale_pid, stale_port = _daemon_identity(stale_metadata)
|
|
846
|
+
if stale_pid is not None:
|
|
847
|
+
_remove_daemon_metadata(root, expected_pid=stale_pid, expected_port=stale_port)
|
|
776
848
|
return {
|
|
777
849
|
"version": _SESSION_VERSION,
|
|
778
850
|
"root": str(root),
|
|
@@ -802,7 +874,12 @@ def stop_session_daemon(path: str = ".") -> dict[str, Any]:
|
|
|
802
874
|
# validated pid terminate so a wedged daemon is not left running.
|
|
803
875
|
if _terminate_daemon_by_pid(metadata):
|
|
804
876
|
stop_method = "pid"
|
|
805
|
-
|
|
877
|
+
# Task #143a-a: only remove daemon.json if it still identifies the SAME daemon this call
|
|
878
|
+
# targeted (captured in `metadata` above) -- a replacement may have spawned and published its
|
|
879
|
+
# own metadata in the window since. See _remove_daemon_metadata's docstring for the full race.
|
|
880
|
+
target_pid, target_port = _daemon_identity(metadata)
|
|
881
|
+
if target_pid is not None:
|
|
882
|
+
_remove_daemon_metadata(root, expected_pid=target_pid, expected_port=target_port)
|
|
806
883
|
response["running"] = False
|
|
807
884
|
response["root"] = str(root)
|
|
808
885
|
response["stopped"] = True
|
|
@@ -1965,7 +2042,13 @@ def run_session_daemon_server(path: str = ".") -> None:
|
|
|
1965
2042
|
finally:
|
|
1966
2043
|
stop_event.set()
|
|
1967
2044
|
_flush_demand_metrics_if_dirty(server, force=True)
|
|
1968
|
-
|
|
2045
|
+
# Task #143a-a: this daemon must only ever remove ITS OWN daemon.json on shutdown
|
|
2046
|
+
# (idle timeout, max-uptime, or a cooperative "stop" command). If a client already
|
|
2047
|
+
# rejected this daemon over a package_version mismatch and spawned a healthy
|
|
2048
|
+
# replacement, that replacement's metadata is what is on disk by the time THIS daemon
|
|
2049
|
+
# finally shuts itself down -- an unconditional unlink here would delete it out from
|
|
2050
|
+
# under the replacement, orphaning it with no discoverable daemon.json.
|
|
2051
|
+
_remove_daemon_metadata(root, expected_pid=os.getpid(), expected_port=int(port))
|
|
1969
2052
|
|
|
1970
2053
|
|
|
1971
2054
|
def _parse_args(argv: list[str]) -> argparse.Namespace:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tensor_grep-1.76.2 → tensor_grep-1.76.4}/rust_core/tests/test_audit_manifest_symlink_unix.rs
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|