Skip to main content

hrx/
lib.rs

1//! GPU execution and Loom compilation without a build-time native dependency.
2//!
3//! Native libraries are verified and loaded on first use. See [`bundle`] for
4//! offline setup.
5mod access_graph;
6pub mod artifacts;
7#[path = "benchmark_statistics.rs"]
8pub mod benchmark;
9mod buffer_pool;
10pub mod bundle;
11mod dependency_frontier;
12pub mod fabric;
13pub mod image;
14pub mod inference;
15pub mod jobs;
16pub mod plan_cache;
17pub mod residency;
18mod runtime;
19#[cfg(target_os = "linux")]
20pub mod storage;
21mod target;
22pub mod tensor;
23pub use access_graph::{AccessGraph, AccessView};
24pub use buffer_pool::{BufferPool, PooledBuffer};
25pub use runtime::*;
26/// Coordinated GPU/NPU execution with inferred memory dependencies.
27pub mod execution;
28#[path = "loom/model.rs"]
29pub mod model;
30pub use execution::{Access, Completion, Runtime};
31/// Low-level GPU execution. Kernel dispatch and external memory access require
32/// the caller to establish their safety and synchronization contracts.
33pub mod gpu {
34    pub use crate::runtime::*;
35}
36pub use target::{TARGET_KEY, Target};
37pub mod loom;
38#[cfg(feature = "npu")]
39pub mod npu;
40
41/// The README's example is compiled with the crate, so an API change that would
42/// invalidate it fails the build instead of reaching a reader.
43#[cfg(doctest)]
44#[doc = include_str!("../README.md")]
45pub struct Readme;
46
47/// A runtime, validation, provisioning or compiler failure.
48#[derive(Debug, thiserror::Error)]
49#[non_exhaustive]
50pub enum Error {
51    /// A mapped allocation or exhausted prepared run capacity prevents submission.
52    #[error("resource busy: {0}")]
53    Busy(String),
54    /// Hardware or the selected native runtime cannot provide a requested capability.
55    #[error("unsupported capability: {0}")]
56    Unsupported(String),
57    /// A device failure left memory contents or completion uncertain.
58    #[error("device execution failed: {0}")]
59    DeviceLost(String),
60    /// A native backend failure with its original status code.
61    #[error("{backend} {operation} failed ({code}): {message}")]
62    Backend {
63        /// Native backend name.
64        backend: &'static str,
65        /// Operation that failed.
66        operation: &'static str,
67        /// Native status or command state.
68        code: i32,
69        /// Native diagnostic text. A boxed slice keeps the success-path Result
70        /// layout within the pre-existing native error size.
71        message: Box<str>,
72    },
73    /// A retained asynchronous failure, shared by completion observers.
74    #[error("{source}")]
75    Execution {
76        /// Original structured failure, including backend and node context.
77        #[source]
78        source: std::sync::Arc<Error>,
79    },
80    /// Work was cancelled before completion.
81    #[error("execution cancelled")]
82    Cancelled,
83    /// Invalid input or a contextual diagnostic.
84    #[error("{0}")]
85    Message(String),
86    /// Structured diagnostics from a failed Loom compiler invocation.
87    #[error("Loom compilation failed: {message}")]
88    Compile {
89        /// Rendered diagnostic summary.
90        message: String,
91        /// Complete structured diagnostics.
92        diagnostics: Vec<loom::Diagnostic>,
93        /// Compiler report produced before failure, when requested and available.
94        report: Option<Box<serde_json::Value>>,
95    },
96    /// An operating-system failure, preserving its error kind and source.
97    #[error("{0}")]
98    Io(#[from] std::io::Error),
99    /// Invalid JSON, preserving the parser's location and source.
100    #[error("{0}")]
101    Json(#[from] serde_json::Error),
102    /// A dynamic library or symbol could not be loaded.
103    #[error("{0}")]
104    Library(#[from] libloading::Error),
105    /// A network provisioning failure.
106    #[error("{0}")]
107    Download(#[source] Box<ureq::Error>),
108    /// Legacy native status retained for source compatibility.
109    /// The native 0.8 runtime reports structured errors through other variants.
110    #[error("{context}: {message}")]
111    Runtime {
112        /// Operation that failed.
113        context: String,
114        /// Native HRX status code.
115        code: i32,
116        /// Owned native status message.
117        message: String,
118    },
119    /// Adds context while preserving an underlying failure.
120    #[error("{context}: {source}")]
121    Context {
122        /// Operation that failed.
123        context: String,
124        #[source]
125        /// Underlying failure.
126        source: Box<Error>,
127    },
128}
129impl Error {
130    /// Structured compiler diagnostics through any contextual wrappers.
131    pub fn compiler_diagnostics(&self) -> Option<&[loom::Diagnostic]> {
132        match self {
133            Self::Compile { diagnostics, .. } => Some(diagnostics),
134            Self::Context { source, .. } => source.compiler_diagnostics(),
135            Self::Execution { source } => source.compiler_diagnostics(),
136            _ => None,
137        }
138    }
139    /// Compiler report retained from a failed invocation, when requested.
140    pub fn compiler_report(&self) -> Option<&serde_json::Value> {
141        match self {
142            Self::Compile { report, .. } => report.as_deref(),
143            Self::Context { source, .. } => source.compiler_report(),
144            Self::Execution { source } => source.compiler_report(),
145            _ => None,
146        }
147    }
148    /// Attach context without flattening the underlying error.
149    pub fn context(self, context: impl Into<String>) -> Self {
150        Self::Context {
151            context: context.into(),
152            source: Box::new(self),
153        }
154    }
155}
156/// The result of an HRX operation.
157pub type Result<T> = std::result::Result<T, Error>;
158
159pub mod runner;
160
161// Initialization failures remain retryable; only a successful value is memoized.
162pub(crate) fn cached_init<T: Clone>(
163    cache: &std::sync::Mutex<Option<T>>,
164    initialize: impl FnOnce() -> Result<T>,
165) -> Result<T> {
166    let mut cached = cache
167        .lock()
168        .map_err(|_| Error::Message("initialization cache poisoned".into()))?;
169    if let Some(value) = cached.as_ref() {
170        return Ok(value.clone());
171    }
172    let value = initialize()?;
173    *cached = Some(value.clone());
174    Ok(value)
175}
176
177#[cfg(test)]
178mod initialization_tests {
179    #[test]
180    fn failures_retry_and_concurrent_success_initializes_once() {
181        use std::sync::{
182            Mutex,
183            atomic::{AtomicUsize, Ordering},
184        };
185        let cache = Mutex::new(None);
186        assert!(
187            super::cached_init(&cache, || Err::<usize, _>(super::Error::Message(
188                "transient".into()
189            )))
190            .is_err()
191        );
192        let calls = AtomicUsize::new(0);
193        std::thread::scope(|scope| {
194            for _ in 0..8 {
195                scope.spawn(|| {
196                    assert_eq!(
197                        super::cached_init(&cache, || {
198                            calls.fetch_add(1, Ordering::Relaxed);
199                            Ok(42)
200                        })
201                        .unwrap(),
202                        42
203                    );
204                });
205            }
206        });
207        assert_eq!(calls.load(Ordering::Relaxed), 1);
208    }
209}