ringkernel_core/
control.rs

1//! Control block for kernel state management.
2//!
3//! The control block is a fixed-size structure in GPU memory that manages
4//! kernel lifecycle, message queue pointers, and synchronization state.
5
6use crate::hlc::HlcState;
7
8/// Kernel control block (128 bytes, cache-line aligned).
9///
10/// This structure resides in GPU global memory and is accessed atomically
11/// by both host and device code for kernel lifecycle management.
12///
13/// ## Memory Layout
14///
15/// The structure is carefully designed to minimize false sharing:
16/// - Frequently written fields are grouped together
17/// - Read-only fields are separated
18/// - Padding ensures proper alignment
19#[derive(Debug, Clone, Copy)]
20#[repr(C, align(128))]
21pub struct ControlBlock {
22    // === Lifecycle State (frequently accessed) ===
23    /// Kernel is actively processing messages (atomic bool).
24    pub is_active: u32,
25    /// Signal to terminate the kernel (atomic bool).
26    pub should_terminate: u32,
27    /// Kernel has completed termination (atomic bool).
28    pub has_terminated: u32,
29    /// Reserved for alignment.
30    pub _pad1: u32,
31
32    // === Counters (frequently updated) ===
33    /// Total messages processed.
34    pub messages_processed: u64,
35    /// Messages currently being processed.
36    pub messages_in_flight: u64,
37
38    // === Queue Pointers ===
39    /// Input queue head pointer (producer writes).
40    pub input_head: u64,
41    /// Input queue tail pointer (consumer reads).
42    pub input_tail: u64,
43    /// Output queue head pointer (producer writes).
44    pub output_head: u64,
45    /// Output queue tail pointer (consumer reads).
46    pub output_tail: u64,
47
48    // === Queue Metadata (read-mostly) ===
49    /// Input queue capacity (power of 2).
50    pub input_capacity: u32,
51    /// Output queue capacity (power of 2).
52    pub output_capacity: u32,
53    /// Input queue mask (capacity - 1).
54    pub input_mask: u32,
55    /// Output queue mask (capacity - 1).
56    pub output_mask: u32,
57
58    // === Timing ===
59    /// HLC state for this kernel.
60    pub hlc_state: HlcState,
61
62    // === Error State ===
63    /// Last error code (0 = no error).
64    pub last_error: u32,
65    /// Error count.
66    pub error_count: u32,
67
68    // === Reserved (pad to 128 bytes) ===
69    /// Reserved for future use.
70    pub _reserved: [u8; 24],
71}
72
73// Verify size at compile time
74const _: () = assert!(std::mem::size_of::<ControlBlock>() == 128);
75
76impl ControlBlock {
77    /// Create a new control block with default values.
78    pub const fn new() -> Self {
79        Self {
80            is_active: 0,
81            should_terminate: 0,
82            has_terminated: 0,
83            _pad1: 0,
84            messages_processed: 0,
85            messages_in_flight: 0,
86            input_head: 0,
87            input_tail: 0,
88            output_head: 0,
89            output_tail: 0,
90            input_capacity: 0,
91            output_capacity: 0,
92            input_mask: 0,
93            output_mask: 0,
94            hlc_state: HlcState::new(0, 0),
95            last_error: 0,
96            error_count: 0,
97            _reserved: [0; 24],
98        }
99    }
100
101    /// Create with specified queue capacities.
102    ///
103    /// Capacities must be powers of 2.
104    pub fn with_capacities(input_capacity: u32, output_capacity: u32) -> Self {
105        debug_assert!(input_capacity.is_power_of_two());
106        debug_assert!(output_capacity.is_power_of_two());
107
108        Self {
109            is_active: 0,
110            should_terminate: 0,
111            has_terminated: 0,
112            _pad1: 0,
113            messages_processed: 0,
114            messages_in_flight: 0,
115            input_head: 0,
116            input_tail: 0,
117            output_head: 0,
118            output_tail: 0,
119            input_capacity,
120            output_capacity,
121            input_mask: input_capacity.saturating_sub(1),
122            output_mask: output_capacity.saturating_sub(1),
123            hlc_state: HlcState::new(0, 0),
124            last_error: 0,
125            error_count: 0,
126            _reserved: [0; 24],
127        }
128    }
129
130    /// Check if the kernel is active.
131    #[inline]
132    pub fn is_active(&self) -> bool {
133        self.is_active != 0
134    }
135
136    /// Check if termination was requested.
137    #[inline]
138    pub fn should_terminate(&self) -> bool {
139        self.should_terminate != 0
140    }
141
142    /// Check if the kernel has terminated.
143    #[inline]
144    pub fn has_terminated(&self) -> bool {
145        self.has_terminated != 0
146    }
147
148    /// Get input queue size.
149    #[inline]
150    pub fn input_queue_size(&self) -> u64 {
151        self.input_head.wrapping_sub(self.input_tail)
152    }
153
154    /// Get output queue size.
155    #[inline]
156    pub fn output_queue_size(&self) -> u64 {
157        self.output_head.wrapping_sub(self.output_tail)
158    }
159
160    /// Check if input queue is empty.
161    #[inline]
162    pub fn input_queue_empty(&self) -> bool {
163        self.input_head == self.input_tail
164    }
165
166    /// Check if output queue is empty.
167    #[inline]
168    pub fn output_queue_empty(&self) -> bool {
169        self.output_head == self.output_tail
170    }
171
172    /// Check if input queue is full.
173    #[inline]
174    pub fn input_queue_full(&self) -> bool {
175        self.input_queue_size() >= self.input_capacity as u64
176    }
177
178    /// Check if output queue is full.
179    #[inline]
180    pub fn output_queue_full(&self) -> bool {
181        self.output_queue_size() >= self.output_capacity as u64
182    }
183}
184
185impl Default for ControlBlock {
186    fn default() -> Self {
187        Self::new()
188    }
189}
190
191/// Error codes for control block.
192#[derive(Debug, Clone, Copy, PartialEq, Eq)]
193#[repr(u32)]
194pub enum ControlError {
195    /// No error.
196    None = 0,
197    /// Input queue overflow.
198    InputOverflow = 1,
199    /// Output queue overflow.
200    OutputOverflow = 2,
201    /// Invalid message.
202    InvalidMessage = 3,
203    /// Memory allocation failed.
204    AllocationFailed = 4,
205    /// Serialization error.
206    SerializationError = 5,
207    /// Timeout waiting for message.
208    Timeout = 6,
209    /// Internal kernel error.
210    InternalError = 7,
211}
212
213impl ControlError {
214    /// Convert from u32.
215    pub const fn from_u32(value: u32) -> Self {
216        match value {
217            0 => Self::None,
218            1 => Self::InputOverflow,
219            2 => Self::OutputOverflow,
220            3 => Self::InvalidMessage,
221            4 => Self::AllocationFailed,
222            5 => Self::SerializationError,
223            6 => Self::Timeout,
224            _ => Self::InternalError,
225        }
226    }
227}
228
229#[cfg(test)]
230mod tests {
231    use super::*;
232
233    #[test]
234    fn test_control_block_size() {
235        assert_eq!(std::mem::size_of::<ControlBlock>(), 128);
236    }
237
238    #[test]
239    fn test_control_block_alignment() {
240        assert_eq!(std::mem::align_of::<ControlBlock>(), 128);
241    }
242
243    #[test]
244    fn test_queue_size_calculation() {
245        let mut cb = ControlBlock::with_capacities(1024, 1024);
246
247        cb.input_head = 10;
248        cb.input_tail = 5;
249        assert_eq!(cb.input_queue_size(), 5);
250
251        // Test wraparound
252        cb.input_head = 2;
253        cb.input_tail = u64::MAX - 3;
254        assert_eq!(cb.input_queue_size(), 6);
255    }
256
257    #[test]
258    fn test_queue_full_empty() {
259        let mut cb = ControlBlock::with_capacities(16, 16);
260
261        assert!(cb.input_queue_empty());
262        assert!(!cb.input_queue_full());
263
264        cb.input_head = 16;
265        cb.input_tail = 0;
266        assert!(!cb.input_queue_empty());
267        assert!(cb.input_queue_full());
268    }
269
270    #[test]
271    fn test_lifecycle_flags() {
272        let mut cb = ControlBlock::new();
273
274        assert!(!cb.is_active());
275        assert!(!cb.should_terminate());
276        assert!(!cb.has_terminated());
277
278        cb.is_active = 1;
279        assert!(cb.is_active());
280
281        cb.should_terminate = 1;
282        assert!(cb.should_terminate());
283
284        cb.has_terminated = 1;
285        assert!(cb.has_terminated());
286    }
287}