ringkernel_core/
runtime.rs

1//! Runtime traits and types for kernel management.
2//!
3//! This module defines the core runtime abstraction that backends implement
4//! to provide kernel lifecycle management, message passing, and monitoring.
5
6use std::future::Future;
7use std::pin::Pin;
8use std::sync::Arc;
9use std::time::Duration;
10
11use async_trait::async_trait;
12
13use crate::error::Result;
14use crate::message::{MessageEnvelope, RingMessage};
15use crate::telemetry::KernelMetrics;
16use crate::types::KernelMode;
17
18/// Unique kernel identifier.
19#[derive(Debug, Clone, PartialEq, Eq, Hash)]
20pub struct KernelId(pub String);
21
22impl KernelId {
23    /// Create a new kernel ID.
24    pub fn new(id: impl Into<String>) -> Self {
25        Self(id.into())
26    }
27
28    /// Get the ID as a string slice.
29    pub fn as_str(&self) -> &str {
30        &self.0
31    }
32}
33
34impl std::fmt::Display for KernelId {
35    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36        write!(f, "{}", self.0)
37    }
38}
39
40impl From<&str> for KernelId {
41    fn from(s: &str) -> Self {
42        Self(s.to_string())
43    }
44}
45
46impl From<String> for KernelId {
47    fn from(s: String) -> Self {
48        Self(s)
49    }
50}
51
52/// Kernel lifecycle state.
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
54pub enum KernelState {
55    /// Kernel is created but not launched.
56    Created,
57    /// Kernel is launched and initializing.
58    Launched,
59    /// Kernel is active and processing messages.
60    Active,
61    /// Kernel is deactivated (paused).
62    Deactivated,
63    /// Kernel is terminating.
64    Terminating,
65    /// Kernel has terminated.
66    Terminated,
67}
68
69impl KernelState {
70    /// Check if kernel can be activated.
71    pub fn can_activate(&self) -> bool {
72        matches!(self, Self::Launched | Self::Deactivated)
73    }
74
75    /// Check if kernel can be deactivated.
76    pub fn can_deactivate(&self) -> bool {
77        matches!(self, Self::Active)
78    }
79
80    /// Check if kernel can be terminated.
81    pub fn can_terminate(&self) -> bool {
82        matches!(self, Self::Active | Self::Deactivated | Self::Launched)
83    }
84
85    /// Check if kernel is running (can process messages).
86    pub fn is_running(&self) -> bool {
87        matches!(self, Self::Active)
88    }
89
90    /// Check if kernel is finished.
91    pub fn is_finished(&self) -> bool {
92        matches!(self, Self::Terminated)
93    }
94}
95
96/// Kernel status including state and metrics.
97#[derive(Debug, Clone)]
98pub struct KernelStatus {
99    /// Kernel identifier.
100    pub id: KernelId,
101    /// Current state.
102    pub state: KernelState,
103    /// Execution mode.
104    pub mode: KernelMode,
105    /// Messages in input queue.
106    pub input_queue_depth: usize,
107    /// Messages in output queue.
108    pub output_queue_depth: usize,
109    /// Total messages processed.
110    pub messages_processed: u64,
111    /// Uptime since launch.
112    pub uptime: Duration,
113}
114
115/// GPU backend type.
116#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
117pub enum Backend {
118    /// CPU backend (for testing).
119    Cpu,
120    /// NVIDIA CUDA backend.
121    Cuda,
122    /// Apple Metal backend.
123    Metal,
124    /// WebGPU cross-platform backend.
125    Wgpu,
126    /// Automatically select best available backend.
127    #[default]
128    Auto,
129}
130
131impl Backend {
132    /// Get display name.
133    pub fn name(&self) -> &'static str {
134        match self {
135            Backend::Cpu => "CPU",
136            Backend::Cuda => "CUDA",
137            Backend::Metal => "Metal",
138            Backend::Wgpu => "WebGPU",
139            Backend::Auto => "Auto",
140        }
141    }
142}
143
144impl std::fmt::Display for Backend {
145    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146        write!(f, "{}", self.name())
147    }
148}
149
150/// Options for launching a kernel.
151#[derive(Debug, Clone)]
152pub struct LaunchOptions {
153    /// Execution mode (persistent or event-driven).
154    pub mode: KernelMode,
155    /// Grid size (number of blocks).
156    pub grid_size: u32,
157    /// Block size (threads per block).
158    pub block_size: u32,
159    /// Input queue capacity.
160    pub input_queue_capacity: usize,
161    /// Output queue capacity.
162    pub output_queue_capacity: usize,
163    /// Shared memory size in bytes.
164    pub shared_memory_size: usize,
165    /// Whether to activate immediately after launch.
166    pub auto_activate: bool,
167    /// Enable cooperative groups for grid-wide synchronization.
168    /// Requires GPU support for cooperative kernel launch.
169    pub cooperative: bool,
170    /// Enable K2K (kernel-to-kernel) messaging.
171    /// Allocates routing table and inbox buffers on GPU.
172    pub enable_k2k: bool,
173}
174
175impl Default for LaunchOptions {
176    fn default() -> Self {
177        Self {
178            mode: KernelMode::Persistent,
179            grid_size: 1,
180            block_size: 256,
181            input_queue_capacity: 1024,
182            output_queue_capacity: 1024,
183            shared_memory_size: 0,
184            auto_activate: true,
185            cooperative: false,
186            enable_k2k: false,
187        }
188    }
189}
190
191impl LaunchOptions {
192    /// Create options for a single-block kernel.
193    pub fn single_block(block_size: u32) -> Self {
194        Self {
195            block_size,
196            ..Default::default()
197        }
198    }
199
200    /// Create options for a multi-block kernel.
201    pub fn multi_block(grid_size: u32, block_size: u32) -> Self {
202        Self {
203            grid_size,
204            block_size,
205            ..Default::default()
206        }
207    }
208
209    /// Set execution mode.
210    pub fn with_mode(mut self, mode: KernelMode) -> Self {
211        self.mode = mode;
212        self
213    }
214
215    /// Set queue capacities.
216    pub fn with_queue_capacity(mut self, capacity: usize) -> Self {
217        self.input_queue_capacity = capacity;
218        self.output_queue_capacity = capacity;
219        self
220    }
221
222    /// Set shared memory size.
223    pub fn with_shared_memory(mut self, size: usize) -> Self {
224        self.shared_memory_size = size;
225        self
226    }
227
228    /// Disable auto-activation.
229    pub fn without_auto_activate(mut self) -> Self {
230        self.auto_activate = false;
231        self
232    }
233
234    /// Set the grid size (number of blocks).
235    pub fn with_grid_size(mut self, grid_size: u32) -> Self {
236        self.grid_size = grid_size;
237        self
238    }
239
240    /// Set the block size (threads per block).
241    pub fn with_block_size(mut self, block_size: u32) -> Self {
242        self.block_size = block_size;
243        self
244    }
245
246    /// Enable cooperative groups for grid-wide synchronization.
247    ///
248    /// When enabled, the kernel will be launched cooperatively, allowing
249    /// all blocks to synchronize via `grid.sync()`. Requires GPU support
250    /// and nvcc at build time.
251    pub fn with_cooperative(mut self, cooperative: bool) -> Self {
252        self.cooperative = cooperative;
253        self
254    }
255
256    /// Enable K2K (kernel-to-kernel) messaging.
257    ///
258    /// When enabled, allocates routing table and inbox buffers on GPU
259    /// for direct kernel-to-kernel communication without host intervention.
260    pub fn with_k2k(mut self, enable: bool) -> Self {
261        self.enable_k2k = enable;
262        self
263    }
264
265    /// Set priority hint for kernel scheduling.
266    ///
267    /// Note: This is a hint for future use - currently ignored by backends.
268    pub fn with_priority(self, _priority: u8) -> Self {
269        // Priority hint stored for future scheduling use
270        self
271    }
272
273    /// Set input queue capacity only.
274    pub fn with_input_queue_capacity(mut self, capacity: usize) -> Self {
275        self.input_queue_capacity = capacity;
276        self
277    }
278
279    /// Set output queue capacity only.
280    pub fn with_output_queue_capacity(mut self, capacity: usize) -> Self {
281        self.output_queue_capacity = capacity;
282        self
283    }
284}
285
286/// Type-erased future for async operations.
287pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
288
289/// Backend-agnostic runtime trait for kernel management.
290///
291/// This trait is implemented by each backend (CPU, CUDA, Metal, WebGPU)
292/// to provide kernel lifecycle management and message passing.
293#[async_trait]
294pub trait RingKernelRuntime: Send + Sync {
295    /// Get the backend type.
296    fn backend(&self) -> Backend;
297
298    /// Check if a specific backend is available.
299    fn is_backend_available(&self, backend: Backend) -> bool;
300
301    /// Launch a kernel.
302    async fn launch(&self, kernel_id: &str, options: LaunchOptions) -> Result<KernelHandle>;
303
304    /// Get a handle to an existing kernel.
305    fn get_kernel(&self, kernel_id: &KernelId) -> Option<KernelHandle>;
306
307    /// List all kernel IDs.
308    fn list_kernels(&self) -> Vec<KernelId>;
309
310    /// Get runtime metrics.
311    fn metrics(&self) -> RuntimeMetrics;
312
313    /// Shutdown the runtime and terminate all kernels.
314    async fn shutdown(&self) -> Result<()>;
315}
316
317/// Handle to a launched kernel.
318///
319/// Provides an ergonomic API for interacting with a kernel.
320#[derive(Clone)]
321pub struct KernelHandle {
322    /// Kernel identifier.
323    id: KernelId,
324    /// Inner implementation.
325    inner: Arc<dyn KernelHandleInner>,
326}
327
328impl KernelHandle {
329    /// Create a new kernel handle.
330    pub fn new(id: KernelId, inner: Arc<dyn KernelHandleInner>) -> Self {
331        Self { id, inner }
332    }
333
334    /// Get the kernel ID.
335    pub fn id(&self) -> &KernelId {
336        &self.id
337    }
338
339    /// Activate the kernel.
340    pub async fn activate(&self) -> Result<()> {
341        self.inner.activate().await
342    }
343
344    /// Deactivate the kernel.
345    pub async fn deactivate(&self) -> Result<()> {
346        self.inner.deactivate().await
347    }
348
349    /// Terminate the kernel.
350    pub async fn terminate(&self) -> Result<()> {
351        self.inner.terminate().await
352    }
353
354    /// Send a message to the kernel.
355    pub async fn send<M: RingMessage>(&self, message: M) -> Result<()> {
356        let envelope = MessageEnvelope::new(
357            &message,
358            0, // Host source
359            self.inner.kernel_id_num(),
360            self.inner.current_timestamp(),
361        );
362        self.inner.send_envelope(envelope).await
363    }
364
365    /// Send a raw envelope.
366    pub async fn send_envelope(&self, envelope: MessageEnvelope) -> Result<()> {
367        self.inner.send_envelope(envelope).await
368    }
369
370    /// Receive a message from the kernel.
371    pub async fn receive(&self) -> Result<MessageEnvelope> {
372        self.inner.receive().await
373    }
374
375    /// Receive a message with timeout.
376    pub async fn receive_timeout(&self, timeout: Duration) -> Result<MessageEnvelope> {
377        self.inner.receive_timeout(timeout).await
378    }
379
380    /// Try to receive a message (non-blocking).
381    pub fn try_receive(&self) -> Result<MessageEnvelope> {
382        self.inner.try_receive()
383    }
384
385    /// Send request and wait for response (call pattern).
386    pub async fn call<M: RingMessage>(
387        &self,
388        message: M,
389        timeout: Duration,
390    ) -> Result<MessageEnvelope> {
391        // Generate correlation ID
392        let correlation = crate::message::CorrelationId::generate();
393
394        // Create envelope with correlation
395        let mut envelope = MessageEnvelope::new(
396            &message,
397            0,
398            self.inner.kernel_id_num(),
399            self.inner.current_timestamp(),
400        );
401        envelope.header.correlation_id = correlation;
402
403        // Send and wait for correlated response
404        self.inner.send_envelope(envelope).await?;
405        self.inner.receive_correlated(correlation, timeout).await
406    }
407
408    /// Get kernel status.
409    pub fn status(&self) -> KernelStatus {
410        self.inner.status()
411    }
412
413    /// Get kernel metrics.
414    pub fn metrics(&self) -> KernelMetrics {
415        self.inner.metrics()
416    }
417
418    /// Wait for kernel to terminate.
419    pub async fn wait(&self) -> Result<()> {
420        self.inner.wait().await
421    }
422
423    /// Get the current kernel state.
424    ///
425    /// This is a convenience method that returns just the state from status().
426    pub fn state(&self) -> KernelState {
427        self.status().state
428    }
429
430    /// Suspend (deactivate) the kernel.
431    ///
432    /// This is an alias for `deactivate()` for more intuitive API usage.
433    pub async fn suspend(&self) -> Result<()> {
434        self.deactivate().await
435    }
436
437    /// Resume (activate) the kernel.
438    ///
439    /// This is an alias for `activate()` for more intuitive API usage.
440    pub async fn resume(&self) -> Result<()> {
441        self.activate().await
442    }
443
444    /// Check if the kernel is currently active.
445    pub fn is_active(&self) -> bool {
446        self.state() == KernelState::Active
447    }
448
449    /// Check if the kernel has terminated.
450    pub fn is_terminated(&self) -> bool {
451        self.state() == KernelState::Terminated
452    }
453}
454
455impl std::fmt::Debug for KernelHandle {
456    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
457        f.debug_struct("KernelHandle")
458            .field("id", &self.id)
459            .finish()
460    }
461}
462
463/// Inner trait for kernel handle implementation.
464///
465/// This is implemented by each backend to provide the actual functionality.
466#[async_trait]
467pub trait KernelHandleInner: Send + Sync {
468    /// Get numeric kernel ID.
469    fn kernel_id_num(&self) -> u64;
470
471    /// Get current timestamp.
472    fn current_timestamp(&self) -> crate::hlc::HlcTimestamp;
473
474    /// Activate kernel.
475    async fn activate(&self) -> Result<()>;
476
477    /// Deactivate kernel.
478    async fn deactivate(&self) -> Result<()>;
479
480    /// Terminate kernel.
481    async fn terminate(&self) -> Result<()>;
482
483    /// Send message envelope.
484    async fn send_envelope(&self, envelope: MessageEnvelope) -> Result<()>;
485
486    /// Receive message.
487    async fn receive(&self) -> Result<MessageEnvelope>;
488
489    /// Receive with timeout.
490    async fn receive_timeout(&self, timeout: Duration) -> Result<MessageEnvelope>;
491
492    /// Try receive (non-blocking).
493    fn try_receive(&self) -> Result<MessageEnvelope>;
494
495    /// Receive correlated response.
496    async fn receive_correlated(
497        &self,
498        correlation: crate::message::CorrelationId,
499        timeout: Duration,
500    ) -> Result<MessageEnvelope>;
501
502    /// Get status.
503    fn status(&self) -> KernelStatus;
504
505    /// Get metrics.
506    fn metrics(&self) -> KernelMetrics;
507
508    /// Wait for termination.
509    async fn wait(&self) -> Result<()>;
510}
511
512/// Runtime-level metrics.
513#[derive(Debug, Clone, Default)]
514pub struct RuntimeMetrics {
515    /// Number of active kernels.
516    pub active_kernels: usize,
517    /// Total kernels launched.
518    pub total_launched: u64,
519    /// Total messages sent.
520    pub messages_sent: u64,
521    /// Total messages received.
522    pub messages_received: u64,
523    /// GPU memory used (bytes).
524    pub gpu_memory_used: u64,
525    /// Host memory used (bytes).
526    pub host_memory_used: u64,
527}
528
529/// Builder for creating a runtime instance.
530#[derive(Debug, Clone)]
531pub struct RuntimeBuilder {
532    /// Selected backend.
533    pub backend: Backend,
534    /// Device index (for multi-GPU).
535    pub device_index: usize,
536    /// Enable debug mode.
537    pub debug: bool,
538    /// Enable profiling.
539    pub profiling: bool,
540}
541
542impl Default for RuntimeBuilder {
543    fn default() -> Self {
544        Self {
545            backend: Backend::Auto,
546            device_index: 0,
547            debug: false,
548            profiling: false,
549        }
550    }
551}
552
553impl RuntimeBuilder {
554    /// Create a new builder.
555    pub fn new() -> Self {
556        Self::default()
557    }
558
559    /// Set the backend.
560    pub fn backend(mut self, backend: Backend) -> Self {
561        self.backend = backend;
562        self
563    }
564
565    /// Set device index.
566    pub fn device(mut self, index: usize) -> Self {
567        self.device_index = index;
568        self
569    }
570
571    /// Enable debug mode.
572    pub fn debug(mut self, enable: bool) -> Self {
573        self.debug = enable;
574        self
575    }
576
577    /// Enable profiling.
578    pub fn profiling(mut self, enable: bool) -> Self {
579        self.profiling = enable;
580        self
581    }
582}
583
584#[cfg(test)]
585mod tests {
586    use super::*;
587
588    #[test]
589    fn test_kernel_state_transitions() {
590        assert!(KernelState::Launched.can_activate());
591        assert!(KernelState::Deactivated.can_activate());
592        assert!(!KernelState::Active.can_activate());
593        assert!(!KernelState::Terminated.can_activate());
594
595        assert!(KernelState::Active.can_deactivate());
596        assert!(!KernelState::Launched.can_deactivate());
597
598        assert!(KernelState::Active.can_terminate());
599        assert!(KernelState::Deactivated.can_terminate());
600        assert!(!KernelState::Terminated.can_terminate());
601    }
602
603    #[test]
604    fn test_launch_options_builder() {
605        let opts = LaunchOptions::multi_block(4, 128)
606            .with_mode(KernelMode::EventDriven)
607            .with_queue_capacity(2048)
608            .with_shared_memory(4096)
609            .without_auto_activate();
610
611        assert_eq!(opts.grid_size, 4);
612        assert_eq!(opts.block_size, 128);
613        assert_eq!(opts.mode, KernelMode::EventDriven);
614        assert_eq!(opts.input_queue_capacity, 2048);
615        assert_eq!(opts.shared_memory_size, 4096);
616        assert!(!opts.auto_activate);
617    }
618
619    #[test]
620    fn test_kernel_id() {
621        let id1 = KernelId::new("test_kernel");
622        let id2: KernelId = "test_kernel".into();
623        assert_eq!(id1, id2);
624        assert_eq!(id1.as_str(), "test_kernel");
625    }
626
627    #[test]
628    fn test_backend_name() {
629        assert_eq!(Backend::Cpu.name(), "CPU");
630        assert_eq!(Backend::Cuda.name(), "CUDA");
631        assert_eq!(Backend::Metal.name(), "Metal");
632        assert_eq!(Backend::Wgpu.name(), "WebGPU");
633    }
634}