Skip to main content

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    #[inline]
216    pub const fn from_u32(value: u32) -> Self {
217        match value {
218            0 => Self::None,
219            1 => Self::InputOverflow,
220            2 => Self::OutputOverflow,
221            3 => Self::InvalidMessage,
222            4 => Self::AllocationFailed,
223            5 => Self::SerializationError,
224            6 => Self::Timeout,
225            _ => Self::InternalError,
226        }
227    }
228}
229
230#[cfg(test)]
231mod tests {
232    use super::*;
233
234    #[test]
235    fn test_control_block_size() {
236        assert_eq!(std::mem::size_of::<ControlBlock>(), 128);
237    }
238
239    #[test]
240    fn test_control_block_alignment() {
241        assert_eq!(std::mem::align_of::<ControlBlock>(), 128);
242    }
243
244    #[test]
245    fn test_queue_size_calculation() {
246        let mut cb = ControlBlock::with_capacities(1024, 1024);
247
248        cb.input_head = 10;
249        cb.input_tail = 5;
250        assert_eq!(cb.input_queue_size(), 5);
251
252        // Test wraparound
253        cb.input_head = 2;
254        cb.input_tail = u64::MAX - 3;
255        assert_eq!(cb.input_queue_size(), 6);
256    }
257
258    #[test]
259    fn test_queue_full_empty() {
260        let mut cb = ControlBlock::with_capacities(16, 16);
261
262        assert!(cb.input_queue_empty());
263        assert!(!cb.input_queue_full());
264
265        cb.input_head = 16;
266        cb.input_tail = 0;
267        assert!(!cb.input_queue_empty());
268        assert!(cb.input_queue_full());
269    }
270
271    #[test]
272    fn test_lifecycle_flags() {
273        let mut cb = ControlBlock::new();
274
275        assert!(!cb.is_active());
276        assert!(!cb.should_terminate());
277        assert!(!cb.has_terminated());
278
279        cb.is_active = 1;
280        assert!(cb.is_active());
281
282        cb.should_terminate = 1;
283        assert!(cb.should_terminate());
284
285        cb.has_terminated = 1;
286        assert!(cb.has_terminated());
287    }
288}