Skip to main content

ringkernel/
lib.rs

1//! # RingKernel
2//!
3//! GPU-native persistent actor model framework for Rust.
4//!
5//! RingKernel is a Rust port of DotCompute's Ring Kernel system, enabling
6//! GPU-accelerated actor systems with persistent kernels, lock-free message
7//! passing, and hybrid logical clocks for causal ordering.
8//!
9//! ## Features
10//!
11//! - **Persistent GPU-resident state** across kernel invocations
12//! - **Lock-free message passing** between kernels (K2K messaging)
13//! - **Hybrid Logical Clocks (HLC)** for temporal ordering
14//! - **Multiple backends**: CUDA (NVIDIA GPUs), CPU (testing/fallback)
15//! - **Type-safe serialization** via rkyv/zerocopy
16//!
17//! ## Quick Start
18//!
19//! ```ignore
20//! use ringkernel::prelude::*;
21//!
22//! #[derive(RingMessage)]
23//! struct AddRequest {
24//!     #[message(id)]
25//!     id: MessageId,
26//!     a: f32,
27//!     b: f32,
28//! }
29//!
30//! #[tokio::main]
31//! async fn main() -> Result<()> {
32//!     // Create runtime with auto-detected backend
33//!     let runtime = RingKernel::builder()
34//!         .backend(Backend::Auto)
35//!         .build()
36//!         .await?;
37//!
38//!     // Launch a kernel
39//!     let kernel = runtime.launch("adder", LaunchOptions::default()).await?;
40//!     kernel.activate().await?;
41//!
42//!     // Send a message
43//!     kernel.send(AddRequest {
44//!         id: MessageId::generate(),
45//!         a: 1.0,
46//!         b: 2.0,
47//!     }).await?;
48//!
49//!     // Receive response
50//!     let response = kernel.receive().await?;
51//!     println!("Result: {:?}", response);
52//!
53//!     kernel.terminate().await?;
54//!     Ok(())
55//! }
56//! ```
57//!
58//! ## Backends
59//!
60//! RingKernel supports the following backends:
61//!
62//! - **CPU** - Testing and fallback (always available)
63//! - **CUDA** - NVIDIA GPUs (requires `cuda` feature)
64//!
65//! Enable the CUDA backend via Cargo features:
66//!
67//! ```toml
68//! [dependencies]
69//! ringkernel = { version = "1.0", features = ["cuda"] }
70//! ```
71//!
72//! ## Architecture
73//!
74//! ```text
75//! ┌─────────────────────────────────────────────────────────┐
76//! │                    Host (CPU)                           │
77//! │  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐  │
78//! │  │ Application │──│   Runtime   │──│  Message Bridge │  │
79//! │  └─────────────┘  └─────────────┘  └─────────────────┘  │
80//! └──────────────────────────┬──────────────────────────────┘
81//!                            │ DMA Transfers
82//! ┌──────────────────────────┴──────────────────────────────┐
83//! │                   Device (GPU)                          │
84//! │  ┌───────────┐  ┌───────────────┐  ┌───────────────┐    │
85//! │  │ Control   │  │ Input Queue   │  │ Output Queue  │    │
86//! │  │ Block     │  │ (lock-free)   │  │ (lock-free)   │    │
87//! │  └───────────┘  └───────────────┘  └───────────────┘    │
88//! │  ┌─────────────────────────────────────────────────┐    │
89//! │  │         Persistent Kernel (your code)          │    │
90//! │  └─────────────────────────────────────────────────┘    │
91//! └─────────────────────────────────────────────────────────┘
92//! ```
93
94#![warn(missing_docs)]
95#![warn(clippy::all)]
96#![allow(hidden_glob_reexports)]
97
98// Re-export core types
99pub use ringkernel_core::*;
100
101// Re-export derive macros
102pub use ringkernel_derive::*;
103
104// Re-export CPU backend (always available)
105pub use ringkernel_cpu::CpuRuntime;
106
107// Conditional re-exports for GPU backends
108#[cfg(feature = "cuda")]
109pub use ringkernel_cuda::CudaRuntime;
110
111// Re-export codegen
112pub use ringkernel_codegen as codegen;
113
114// Use types from the re-exported ringkernel_core
115use ringkernel_core::error::Result;
116use ringkernel_core::error::RingKernelError;
117use ringkernel_core::runtime::Backend;
118use ringkernel_core::runtime::KernelHandle;
119use ringkernel_core::runtime::KernelId;
120use ringkernel_core::runtime::LaunchOptions;
121use ringkernel_core::runtime::RingKernelRuntime;
122use ringkernel_core::runtime::RuntimeBuilder;
123use ringkernel_core::runtime::RuntimeMetrics;
124
125/// Prelude module for convenient imports.
126pub mod prelude {
127    pub use crate::RingKernel;
128    pub use ringkernel_core::prelude::*;
129    pub use ringkernel_derive::*;
130}
131
132/// Private module for kernel registration (used by proc macros).
133#[doc(hidden)]
134pub mod __private {
135    use ringkernel_core::types::KernelMode;
136
137    /// Kernel registration information.
138    pub struct KernelRegistration {
139        /// Kernel identifier.
140        pub id: &'static str,
141        /// Execution mode.
142        pub mode: KernelMode,
143        /// Grid size.
144        pub grid_size: u32,
145        /// Block size.
146        pub block_size: u32,
147    }
148
149    // Use inventory for kernel registration
150    inventory::collect!(KernelRegistration);
151}
152
153/// Main RingKernel runtime facade.
154///
155/// This struct provides the primary API for creating and managing
156/// GPU-native actor systems.
157pub struct RingKernel {
158    /// Inner runtime implementation.
159    inner: Box<dyn RingKernelRuntime>,
160}
161
162impl RingKernel {
163    /// Create a new runtime builder.
164    pub fn builder() -> RingKernelBuilder {
165        RingKernelBuilder::new()
166    }
167
168    /// Create a new runtime with default settings.
169    pub async fn new() -> Result<Self> {
170        Self::builder().build().await
171    }
172
173    /// Create with a specific backend.
174    pub async fn with_backend(backend: Backend) -> Result<Self> {
175        Self::builder().backend(backend).build().await
176    }
177
178    /// Get the active backend.
179    pub fn backend(&self) -> Backend {
180        self.inner.backend()
181    }
182
183    /// Check if a backend is available.
184    pub fn is_backend_available(&self, backend: Backend) -> bool {
185        self.inner.is_backend_available(backend)
186    }
187
188    /// Launch a kernel.
189    pub async fn launch(&self, kernel_id: &str, options: LaunchOptions) -> Result<KernelHandle> {
190        self.inner.launch(kernel_id, options).await
191    }
192
193    /// Get a handle to an existing kernel.
194    pub fn get_kernel(&self, kernel_id: &KernelId) -> Option<KernelHandle> {
195        self.inner.get_kernel(kernel_id)
196    }
197
198    /// List all kernel IDs.
199    pub fn list_kernels(&self) -> Vec<KernelId> {
200        self.inner.list_kernels()
201    }
202
203    /// Get runtime metrics.
204    pub fn metrics(&self) -> RuntimeMetrics {
205        self.inner.metrics()
206    }
207
208    /// Shutdown the runtime.
209    pub async fn shutdown(self) -> Result<()> {
210        self.inner.shutdown().await
211    }
212}
213
214/// Builder for RingKernel runtime.
215pub struct RingKernelBuilder {
216    inner: RuntimeBuilder,
217}
218
219impl RingKernelBuilder {
220    /// Create a new builder.
221    pub fn new() -> Self {
222        Self {
223            inner: RuntimeBuilder::new(),
224        }
225    }
226
227    /// Set the backend.
228    pub fn backend(mut self, backend: Backend) -> Self {
229        self.inner = self.inner.backend(backend);
230        self
231    }
232
233    /// Set the device index.
234    pub fn device(mut self, index: usize) -> Self {
235        self.inner = self.inner.device(index);
236        self
237    }
238
239    /// Enable debug mode.
240    pub fn debug(mut self, enable: bool) -> Self {
241        self.inner = self.inner.debug(enable);
242        self
243    }
244
245    /// Enable profiling.
246    pub fn profiling(mut self, enable: bool) -> Self {
247        self.inner = self.inner.profiling(enable);
248        self
249    }
250
251    /// Build the runtime.
252    pub async fn build(self) -> Result<RingKernel> {
253        let backend = self.inner.backend;
254
255        let inner: Box<dyn RingKernelRuntime> = match backend {
256            Backend::Auto => self.build_auto().await?,
257            Backend::Cpu => Box::new(CpuRuntime::new().await?),
258            #[cfg(feature = "cuda")]
259            Backend::Cuda => Box::new(ringkernel_cuda::CudaRuntime::new().await?),
260            #[cfg(not(feature = "cuda"))]
261            Backend::Cuda => {
262                return Err(RingKernelError::BackendUnavailable(
263                    "CUDA feature not enabled".to_string(),
264                ))
265            }
266            Backend::Metal => {
267                return Err(RingKernelError::BackendUnavailable(
268                    "Metal backend has been removed".to_string(),
269                ))
270            }
271            Backend::Wgpu => {
272                return Err(RingKernelError::BackendUnavailable(
273                    "WebGPU backend has been removed".to_string(),
274                ))
275            }
276        };
277
278        Ok(RingKernel { inner })
279    }
280
281    /// Auto-select the best available backend.
282    async fn build_auto(self) -> Result<Box<dyn RingKernelRuntime>> {
283        // Try CUDA first (best performance on NVIDIA)
284        #[cfg(feature = "cuda")]
285        if ringkernel_cuda::is_cuda_available() {
286            tracing::info!("Auto-selected CUDA backend");
287            return Ok(Box::new(ringkernel_cuda::CudaRuntime::new().await?));
288        }
289
290        // Fall back to CPU
291        tracing::info!("Auto-selected CPU backend (no GPU available)");
292        Ok(Box::new(CpuRuntime::new().await?))
293    }
294}
295
296impl Default for RingKernelBuilder {
297    fn default() -> Self {
298        Self::new()
299    }
300}
301
302/// Get list of registered kernels from the inventory.
303pub fn registered_kernels() -> Vec<&'static str> {
304    inventory::iter::<__private::KernelRegistration>
305        .into_iter()
306        .map(|r| r.id)
307        .collect()
308}
309
310/// Check availability of backends at runtime.
311pub mod availability {
312    /// Check if CUDA is available.
313    pub fn cuda() -> bool {
314        #[cfg(feature = "cuda")]
315        {
316            ringkernel_cuda::is_cuda_available()
317        }
318        #[cfg(not(feature = "cuda"))]
319        {
320            false
321        }
322    }
323
324    /// Get list of available backends.
325    pub fn available_backends() -> Vec<super::Backend> {
326        let mut backends = vec![super::Backend::Cpu];
327
328        if cuda() {
329            backends.push(super::Backend::Cuda);
330        }
331
332        backends
333    }
334}
335
336#[cfg(test)]
337mod tests {
338    use super::*;
339
340    #[tokio::test]
341    async fn test_runtime_creation() {
342        let runtime = RingKernel::new().await.unwrap();
343        // Default backend is Auto which selects best available (CUDA > CPU)
344        let backend = runtime.backend();
345        assert!(
346            backend == Backend::Cuda || backend == Backend::Cpu,
347            "Expected Cuda or Cpu backend, got {:?}",
348            backend
349        );
350    }
351
352    #[tokio::test]
353    async fn test_builder() {
354        let runtime = RingKernel::builder()
355            .backend(Backend::Cpu)
356            .debug(true)
357            .build()
358            .await
359            .unwrap();
360
361        assert_eq!(runtime.backend(), Backend::Cpu);
362    }
363
364    #[tokio::test]
365    async fn test_launch_kernel() {
366        let runtime = RingKernel::new().await.unwrap();
367
368        let kernel = runtime
369            .launch("test_kernel", LaunchOptions::default())
370            .await
371            .unwrap();
372
373        assert_eq!(kernel.id().as_str(), "test_kernel");
374    }
375
376    #[test]
377    fn test_availability() {
378        // CPU should always be available
379        let backends = availability::available_backends();
380        assert!(backends.contains(&Backend::Cpu));
381    }
382}