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 GPU backends**: CUDA, Metal, WebGPU
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 multiple GPU backends:
61//!
62//! - **CPU** - Testing and fallback (always available)
63//! - **CUDA** - NVIDIA GPUs (requires `cuda` feature)
64//! - **Metal** - Apple GPUs (requires `metal` feature, macOS only)
65//! - **WebGPU** - Cross-platform via wgpu (requires `wgpu` feature)
66//!
67//! Enable backends via Cargo features:
68//!
69//! ```toml
70//! [dependencies]
71//! ringkernel = { version = "0.1", features = ["cuda", "wgpu"] }
72//! ```
73//!
74//! ## Architecture
75//!
76//! ```text
77//! ┌─────────────────────────────────────────────────────────┐
78//! │                    Host (CPU)                           │
79//! │  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐  │
80//! │  │ Application │──│   Runtime   │──│  Message Bridge │  │
81//! │  └─────────────┘  └─────────────┘  └─────────────────┘  │
82//! └──────────────────────────┬──────────────────────────────┘
83//!                            │ DMA Transfers
84//! ┌──────────────────────────┴──────────────────────────────┐
85//! │                   Device (GPU)                          │
86//! │  ┌───────────┐  ┌───────────────┐  ┌───────────────┐    │
87//! │  │ Control   │  │ Input Queue   │  │ Output Queue  │    │
88//! │  │ Block     │  │ (lock-free)   │  │ (lock-free)   │    │
89//! │  └───────────┘  └───────────────┘  └───────────────┘    │
90//! │  ┌─────────────────────────────────────────────────┐    │
91//! │  │         Persistent Kernel (your code)          │    │
92//! │  └─────────────────────────────────────────────────┘    │
93//! └─────────────────────────────────────────────────────────┘
94//! ```
95
96#![warn(missing_docs)]
97#![warn(clippy::all)]
98#![allow(hidden_glob_reexports)]
99
100// Re-export core types
101pub use ringkernel_core::*;
102
103// Re-export derive macros
104pub use ringkernel_derive::*;
105
106// Re-export CPU backend (always available)
107pub use ringkernel_cpu::CpuRuntime;
108
109// Conditional re-exports for GPU backends
110#[cfg(feature = "cuda")]
111pub use ringkernel_cuda::CudaRuntime;
112
113#[cfg(feature = "wgpu")]
114pub use ringkernel_wgpu::WgpuRuntime;
115
116#[cfg(feature = "metal")]
117pub use ringkernel_metal::MetalRuntime;
118
119// Re-export codegen
120pub use ringkernel_codegen as codegen;
121
122// Use types from the re-exported ringkernel_core
123use ringkernel_core::error::Result;
124use ringkernel_core::error::RingKernelError;
125use ringkernel_core::runtime::Backend;
126use ringkernel_core::runtime::KernelHandle;
127use ringkernel_core::runtime::KernelId;
128use ringkernel_core::runtime::LaunchOptions;
129use ringkernel_core::runtime::RingKernelRuntime;
130use ringkernel_core::runtime::RuntimeBuilder;
131use ringkernel_core::runtime::RuntimeMetrics;
132
133/// Prelude module for convenient imports.
134pub mod prelude {
135    pub use crate::RingKernel;
136    pub use ringkernel_core::prelude::*;
137    pub use ringkernel_derive::*;
138}
139
140/// Private module for kernel registration (used by proc macros).
141#[doc(hidden)]
142pub mod __private {
143    use ringkernel_core::types::KernelMode;
144
145    /// Kernel registration information.
146    pub struct KernelRegistration {
147        /// Kernel identifier.
148        pub id: &'static str,
149        /// Execution mode.
150        pub mode: KernelMode,
151        /// Grid size.
152        pub grid_size: u32,
153        /// Block size.
154        pub block_size: u32,
155    }
156
157    // Use inventory for kernel registration
158    inventory::collect!(KernelRegistration);
159}
160
161/// Main RingKernel runtime facade.
162///
163/// This struct provides the primary API for creating and managing
164/// GPU-native actor systems.
165pub struct RingKernel {
166    /// Inner runtime implementation.
167    inner: Box<dyn RingKernelRuntime>,
168}
169
170impl RingKernel {
171    /// Create a new runtime builder.
172    pub fn builder() -> RingKernelBuilder {
173        RingKernelBuilder::new()
174    }
175
176    /// Create a new runtime with default settings.
177    pub async fn new() -> Result<Self> {
178        Self::builder().build().await
179    }
180
181    /// Create with a specific backend.
182    pub async fn with_backend(backend: Backend) -> Result<Self> {
183        Self::builder().backend(backend).build().await
184    }
185
186    /// Get the active backend.
187    pub fn backend(&self) -> Backend {
188        self.inner.backend()
189    }
190
191    /// Check if a backend is available.
192    pub fn is_backend_available(&self, backend: Backend) -> bool {
193        self.inner.is_backend_available(backend)
194    }
195
196    /// Launch a kernel.
197    pub async fn launch(&self, kernel_id: &str, options: LaunchOptions) -> Result<KernelHandle> {
198        self.inner.launch(kernel_id, options).await
199    }
200
201    /// Get a handle to an existing kernel.
202    pub fn get_kernel(&self, kernel_id: &KernelId) -> Option<KernelHandle> {
203        self.inner.get_kernel(kernel_id)
204    }
205
206    /// List all kernel IDs.
207    pub fn list_kernels(&self) -> Vec<KernelId> {
208        self.inner.list_kernels()
209    }
210
211    /// Get runtime metrics.
212    pub fn metrics(&self) -> RuntimeMetrics {
213        self.inner.metrics()
214    }
215
216    /// Shutdown the runtime.
217    pub async fn shutdown(self) -> Result<()> {
218        self.inner.shutdown().await
219    }
220}
221
222/// Builder for RingKernel runtime.
223pub struct RingKernelBuilder {
224    inner: RuntimeBuilder,
225}
226
227impl RingKernelBuilder {
228    /// Create a new builder.
229    pub fn new() -> Self {
230        Self {
231            inner: RuntimeBuilder::new(),
232        }
233    }
234
235    /// Set the backend.
236    pub fn backend(mut self, backend: Backend) -> Self {
237        self.inner = self.inner.backend(backend);
238        self
239    }
240
241    /// Set the device index.
242    pub fn device(mut self, index: usize) -> Self {
243        self.inner = self.inner.device(index);
244        self
245    }
246
247    /// Enable debug mode.
248    pub fn debug(mut self, enable: bool) -> Self {
249        self.inner = self.inner.debug(enable);
250        self
251    }
252
253    /// Enable profiling.
254    pub fn profiling(mut self, enable: bool) -> Self {
255        self.inner = self.inner.profiling(enable);
256        self
257    }
258
259    /// Build the runtime.
260    pub async fn build(self) -> Result<RingKernel> {
261        let backend = self.inner.backend;
262
263        let inner: Box<dyn RingKernelRuntime> = match backend {
264            Backend::Auto => self.build_auto().await?,
265            Backend::Cpu => Box::new(CpuRuntime::new().await?),
266            #[cfg(feature = "cuda")]
267            Backend::Cuda => Box::new(ringkernel_cuda::CudaRuntime::new().await?),
268            #[cfg(not(feature = "cuda"))]
269            Backend::Cuda => {
270                return Err(RingKernelError::BackendUnavailable(
271                    "CUDA feature not enabled".to_string(),
272                ))
273            }
274            #[cfg(feature = "metal")]
275            Backend::Metal => Box::new(ringkernel_metal::MetalRuntime::new().await?),
276            #[cfg(not(feature = "metal"))]
277            Backend::Metal => {
278                return Err(RingKernelError::BackendUnavailable(
279                    "Metal feature not enabled".to_string(),
280                ))
281            }
282            #[cfg(feature = "wgpu")]
283            Backend::Wgpu => Box::new(ringkernel_wgpu::WgpuRuntime::new().await?),
284            #[cfg(not(feature = "wgpu"))]
285            Backend::Wgpu => {
286                return Err(RingKernelError::BackendUnavailable(
287                    "WebGPU feature not enabled".to_string(),
288                ))
289            }
290        };
291
292        Ok(RingKernel { inner })
293    }
294
295    /// Auto-select the best available backend.
296    async fn build_auto(self) -> Result<Box<dyn RingKernelRuntime>> {
297        // Try CUDA first (best performance on NVIDIA)
298        #[cfg(feature = "cuda")]
299        if ringkernel_cuda::is_cuda_available() {
300            tracing::info!("Auto-selected CUDA backend");
301            return Ok(Box::new(ringkernel_cuda::CudaRuntime::new().await?));
302        }
303
304        // Try Metal on macOS
305        #[cfg(feature = "metal")]
306        if ringkernel_metal::is_metal_available() {
307            tracing::info!("Auto-selected Metal backend");
308            return Ok(Box::new(ringkernel_metal::MetalRuntime::new().await?));
309        }
310
311        // Try WebGPU
312        #[cfg(feature = "wgpu")]
313        if ringkernel_wgpu::is_wgpu_available() {
314            tracing::info!("Auto-selected WebGPU backend");
315            return Ok(Box::new(ringkernel_wgpu::WgpuRuntime::new().await?));
316        }
317
318        // Fall back to CPU
319        tracing::info!("Auto-selected CPU backend (no GPU available)");
320        Ok(Box::new(CpuRuntime::new().await?))
321    }
322}
323
324impl Default for RingKernelBuilder {
325    fn default() -> Self {
326        Self::new()
327    }
328}
329
330/// Get list of registered kernels from the inventory.
331pub fn registered_kernels() -> Vec<&'static str> {
332    inventory::iter::<__private::KernelRegistration>
333        .into_iter()
334        .map(|r| r.id)
335        .collect()
336}
337
338/// Check availability of backends at runtime.
339pub mod availability {
340    /// Check if CUDA is available.
341    pub fn cuda() -> bool {
342        #[cfg(feature = "cuda")]
343        {
344            ringkernel_cuda::is_cuda_available()
345        }
346        #[cfg(not(feature = "cuda"))]
347        {
348            false
349        }
350    }
351
352    /// Check if Metal is available.
353    pub fn metal() -> bool {
354        #[cfg(feature = "metal")]
355        {
356            ringkernel_metal::is_metal_available()
357        }
358        #[cfg(not(feature = "metal"))]
359        {
360            false
361        }
362    }
363
364    /// Check if WebGPU is available.
365    pub fn wgpu() -> bool {
366        #[cfg(feature = "wgpu")]
367        {
368            ringkernel_wgpu::is_wgpu_available()
369        }
370        #[cfg(not(feature = "wgpu"))]
371        {
372            false
373        }
374    }
375
376    /// Get list of available backends.
377    pub fn available_backends() -> Vec<super::Backend> {
378        let mut backends = vec![super::Backend::Cpu];
379
380        if cuda() {
381            backends.push(super::Backend::Cuda);
382        }
383        if metal() {
384            backends.push(super::Backend::Metal);
385        }
386        if wgpu() {
387            backends.push(super::Backend::Wgpu);
388        }
389
390        backends
391    }
392}
393
394#[cfg(test)]
395mod tests {
396    use super::*;
397
398    #[tokio::test]
399    async fn test_runtime_creation() {
400        let runtime = RingKernel::new().await.unwrap();
401        // Default backend is Auto which selects best available (CUDA > Metal > WebGPU > CPU)
402        // On systems with CUDA, this will select CUDA; otherwise CPU
403        let backend = runtime.backend();
404        assert!(
405            backend == Backend::Cuda || backend == Backend::Cpu,
406            "Expected Cuda or Cpu backend, got {:?}",
407            backend
408        );
409    }
410
411    #[tokio::test]
412    async fn test_builder() {
413        let runtime = RingKernel::builder()
414            .backend(Backend::Cpu)
415            .debug(true)
416            .build()
417            .await
418            .unwrap();
419
420        assert_eq!(runtime.backend(), Backend::Cpu);
421    }
422
423    #[tokio::test]
424    async fn test_launch_kernel() {
425        let runtime = RingKernel::new().await.unwrap();
426
427        let kernel = runtime
428            .launch("test_kernel", LaunchOptions::default())
429            .await
430            .unwrap();
431
432        assert_eq!(kernel.id().as_str(), "test_kernel");
433    }
434
435    #[test]
436    fn test_availability() {
437        // CPU should always be available
438        let backends = availability::available_backends();
439        assert!(backends.contains(&Backend::Cpu));
440    }
441}