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}