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