preemptive_threads/thread_new/
builder.rs

1//! Thread builder for configuring thread creation.
2
3use super::{Thread, JoinHandle, ThreadId};
4use crate::mem::{StackPool, StackSizeClass};
5use crate::errors::SpawnError;
6use crate::time::Duration;
7extern crate alloc;
8use alloc::string::String;
9
10/// Builder for configuring and creating new threads.
11///
12/// This provides a comprehensive interface for setting thread parameters
13/// before spawning, with advanced options for scheduling, debugging, and resource management.
14pub struct ThreadBuilder {
15    /// Stack size class to use
16    stack_size_class: Option<StackSizeClass>,
17    /// Custom stack size in bytes
18    custom_stack_size: Option<usize>,
19    /// Thread priority (0-255, higher = more important)
20    priority: u8,
21    /// Thread name (for debugging and profiling)
22    name: Option<String>,
23    /// CPU affinity mask (bitfield of allowed CPUs)
24    cpu_affinity: Option<u64>,
25    /// Thread group ID for resource accounting
26    group_id: Option<u32>,
27    /// Whether to enable stack guard pages
28    stack_guard_pages: bool,
29    /// Whether to enable stack canary protection
30    stack_canary: bool,
31    /// Custom stack canary value
32    custom_canary: Option<u64>,
33    /// Time slice duration override
34    time_slice: Option<Duration>,
35    /// Whether this thread is critical (affects scheduling)
36    critical: bool,
37    /// Whether this thread can be preempted
38    preemptible: bool,
39    /// Thread-local storage size reservation
40    tls_size: Option<usize>,
41    /// Whether to enable detailed debugging info
42    debug_info: bool,
43    /// Custom thread attributes
44    attributes: ThreadAttributes,
45}
46
47/// Custom thread attributes for advanced configuration.
48#[derive(Debug, Clone)]
49pub struct ThreadAttributes {
50    /// Real-time scheduling parameters
51    rt_priority: Option<u8>,
52    /// Nice value for process priority
53    nice_value: i8,
54    /// Whether to inherit parent's signal mask
55    inherit_signal_mask: bool,
56    /// Custom environment variables
57    environment: Option<alloc::collections::BTreeMap<String, String>>,
58    /// Resource limits
59    limits: ResourceLimits,
60}
61
62/// Resource limits for threads.
63#[derive(Debug, Clone)]
64pub struct ResourceLimits {
65    /// Maximum CPU time (in nanoseconds)
66    max_cpu_time: Option<u64>,
67    /// Maximum memory usage (in bytes)
68    max_memory: Option<usize>,
69    /// Maximum number of file descriptors
70    max_files: Option<u32>,
71    /// Maximum number of child threads
72    max_children: Option<u32>,
73}
74
75impl ThreadBuilder {
76    /// Create a new thread builder with default settings.
77    pub fn new() -> Self {
78        Self {
79            stack_size_class: None,
80            custom_stack_size: None,
81            priority: 128, // Normal priority
82            name: None,
83            cpu_affinity: None,
84            group_id: None,
85            stack_guard_pages: cfg!(feature = "mmu"),
86            stack_canary: true,
87            custom_canary: None,
88            time_slice: None,
89            critical: false,
90            preemptible: true,
91            tls_size: None,
92            debug_info: cfg!(debug_assertions),
93            attributes: ThreadAttributes::default(),
94        }
95    }
96    
97    /// Set the stack size class for the thread.
98    pub fn stack_size_class(mut self, size_class: StackSizeClass) -> Self {
99        self.stack_size_class = Some(size_class);
100        self.custom_stack_size = None; // Clear custom size
101        self
102    }
103    
104    /// Set the stack size in bytes.
105    pub fn stack_size(mut self, size: usize) -> Self {
106        self.custom_stack_size = Some(size);
107        self.stack_size_class = None; // Clear size class
108        self
109    }
110    
111    /// Set the thread priority.
112    pub fn priority(mut self, priority: u8) -> Self {
113        self.priority = priority;
114        self
115    }
116    
117    /// Set the thread name for debugging purposes.
118    pub fn name<T: Into<String>>(mut self, name: T) -> Self {
119        self.name = Some(name.into());
120        self
121    }
122    
123    /// Set CPU affinity mask (which CPUs this thread can run on).
124    pub fn cpu_affinity(mut self, mask: u64) -> Self {
125        self.cpu_affinity = Some(mask);
126        self
127    }
128    
129    /// Set thread group ID for resource accounting.
130    pub fn group_id(mut self, group: u32) -> Self {
131        self.group_id = Some(group);
132        self
133    }
134    
135    /// Enable or disable stack guard pages (requires MMU feature).
136    pub fn stack_guard_pages(mut self, enabled: bool) -> Self {
137        self.stack_guard_pages = enabled;
138        self
139    }
140    
141    /// Enable or disable stack canary protection.
142    pub fn stack_canary(mut self, enabled: bool) -> Self {
143        self.stack_canary = enabled;
144        self
145    }
146    
147    /// Set custom stack canary value.
148    pub fn custom_canary(mut self, canary: u64) -> Self {
149        self.custom_canary = Some(canary);
150        self.stack_canary = true;
151        self
152    }
153    
154    /// Set custom time slice duration.
155    pub fn time_slice(mut self, duration: Duration) -> Self {
156        self.time_slice = Some(duration);
157        self
158    }
159    
160    /// Mark this thread as critical (affects scheduling priority).
161    pub fn critical(mut self, critical: bool) -> Self {
162        self.critical = critical;
163        self
164    }
165    
166    /// Set whether this thread can be preempted.
167    pub fn preemptible(mut self, preemptible: bool) -> Self {
168        self.preemptible = preemptible;
169        self
170    }
171    
172    /// Reserve space for thread-local storage.
173    pub fn tls_size(mut self, size: usize) -> Self {
174        self.tls_size = Some(size);
175        self
176    }
177    
178    /// Enable or disable detailed debugging information.
179    pub fn debug_info(mut self, enabled: bool) -> Self {
180        self.debug_info = enabled;
181        self
182    }
183    
184    /// Set custom thread attributes.
185    pub fn attributes(mut self, attributes: ThreadAttributes) -> Self {
186        self.attributes = attributes;
187        self
188    }
189    
190    /// Spawn a new thread with the configured parameters.
191    ///
192    /// # Arguments
193    ///
194    /// * `thread_id` - Unique identifier for the new thread
195    /// * `stack_pool` - Stack pool to allocate from
196    /// * `entry_point` - Function to run in the new thread
197    ///
198    /// # Returns
199    ///
200    /// A tuple of (Thread, JoinHandle) if successful, or an error if
201    /// thread creation fails.
202    pub fn spawn(
203        self,
204        thread_id: ThreadId,
205        stack_pool: &StackPool,
206        entry_point: fn(),
207    ) -> Result<(Thread, JoinHandle), SpawnError> {
208        // Validate configuration parameters
209        if let Some(name) = &self.name {
210            if name.len() > 64 {
211                return Err(SpawnError::InvalidName(name.clone()));
212            }
213        }
214        
215        if let Some(affinity) = self.cpu_affinity {
216            if affinity == 0 {
217                return Err(SpawnError::InvalidAffinity(affinity));
218            }
219        }
220        
221        // Determine stack size
222        let stack_size = if let Some(custom_size) = self.custom_stack_size {
223            if custom_size < 4096 || custom_size > 16 * 1024 * 1024 {
224                return Err(SpawnError::InvalidStackSize(custom_size));
225            }
226            custom_size
227        } else {
228            let size_class = self.stack_size_class.unwrap_or(StackSizeClass::Small);
229            match size_class {
230                StackSizeClass::Small => 16384,
231                StackSizeClass::Medium => 65536,
232                StackSizeClass::Large => 262144,
233                StackSizeClass::ExtraLarge => 1048576, // 1MB
234            }
235        };
236        
237        // Allocate stack based on size class or custom size
238        let stack = if let Some(_custom_size) = self.custom_stack_size {
239            // For custom sizes, we'll still use the size class system but pick the closest match
240            let size_class = if stack_size <= 16384 {
241                StackSizeClass::Small
242            } else if stack_size <= 65536 {
243                StackSizeClass::Medium
244            } else if stack_size <= 262144 {
245                StackSizeClass::Large
246            } else {
247                StackSizeClass::ExtraLarge
248            };
249            stack_pool.allocate(size_class).ok_or(SpawnError::OutOfMemory)?
250        } else {
251            let size_class = self.stack_size_class.unwrap_or(StackSizeClass::Small);
252            stack_pool.allocate(size_class).ok_or(SpawnError::OutOfMemory)?
253        };
254        
255        // Set up stack protection if enabled
256        if self.stack_guard_pages && cfg!(feature = "mmu") {
257            // In a real implementation, we would configure MMU guard pages here
258            // This would involve platform-specific memory protection calls
259        }
260        
261        // Set up stack canary if enabled
262        if self.stack_canary {
263            let canary_value = self.custom_canary.unwrap_or_else(|| {
264                // Generate a random canary value
265                // In a real implementation, we would use a proper CSPRNG
266                0xDEADBEEFCAFEBABE
267            });
268            
269            // Store canary at the bottom of the stack
270            unsafe {
271                let stack_bottom = stack.stack_bottom() as *mut u64;
272                *stack_bottom = canary_value;
273            }
274        }
275        
276        // Create the thread with all configuration
277        let (thread, join_handle) = Thread::new(
278            thread_id,
279            stack,
280            entry_point,
281            self.priority,
282        );
283        
284        // Apply additional configuration
285        if let Some(name) = self.name {
286            thread.set_name(name);
287        }
288        
289        if let Some(affinity) = self.cpu_affinity {
290            thread.set_cpu_affinity(affinity);
291        }
292        
293        if let Some(group_id) = self.group_id {
294            thread.set_group_id(group_id);
295        }
296        
297        if let Some(time_slice) = self.time_slice {
298            thread.set_time_slice(time_slice);
299        }
300        
301        thread.set_critical(self.critical);
302        thread.set_preemptible(self.preemptible);
303        
304        if let Some(tls_size) = self.tls_size {
305            thread.reserve_tls(tls_size);
306        }
307        
308        thread.set_debug_info(self.debug_info);
309        
310        // Apply thread attributes
311        if let Some(rt_priority) = self.attributes.rt_priority {
312            thread.set_realtime_priority(rt_priority);
313        }
314        
315        thread.set_nice_value(self.attributes.nice_value);
316        thread.set_inherit_signal_mask(self.attributes.inherit_signal_mask);
317        
318        if let Some(env) = self.attributes.environment {
319            thread.set_environment(env);
320        }
321        
322        // Apply resource limits
323        let limits = &self.attributes.limits;
324        if let Some(max_cpu_time) = limits.max_cpu_time {
325            thread.set_max_cpu_time(max_cpu_time);
326        }
327        
328        if let Some(max_memory) = limits.max_memory {
329            thread.set_max_memory(max_memory);
330        }
331        
332        if let Some(max_files) = limits.max_files {
333            thread.set_max_files(max_files);
334        }
335        
336        if let Some(max_children) = limits.max_children {
337            thread.set_max_children(max_children);
338        }
339        
340        Ok((thread, join_handle))
341    }
342}
343
344impl Default for ThreadBuilder {
345    fn default() -> Self {
346        Self::new()
347    }
348}
349
350impl Default for ThreadAttributes {
351    fn default() -> Self {
352        Self {
353            rt_priority: None,
354            nice_value: 0,
355            inherit_signal_mask: true,
356            environment: None,
357            limits: ResourceLimits::default(),
358        }
359    }
360}
361
362impl Default for ResourceLimits {
363    fn default() -> Self {
364        Self {
365            max_cpu_time: None,
366            max_memory: None,
367            max_files: None,
368            max_children: None,
369        }
370    }
371}
372
373
374#[cfg(test)]
375mod tests {
376    use super::*;
377    
378    #[cfg(feature = "std-shim")]
379    #[test]
380    fn test_thread_builder() {
381        let pool = StackPool::new();
382        let thread_id = unsafe { ThreadId::new_unchecked(1) };
383        
384        let builder = ThreadBuilder::new()
385            .stack_size_class(StackSizeClass::Medium)
386            .priority(200)
387            .name("test-thread");
388        
389        let result = builder.spawn(thread_id, &pool, || {
390            // Thread code here
391        });
392        
393        assert!(result.is_ok());
394        let (thread, _join_handle) = result.unwrap();
395        
396        assert_eq!(thread.id(), thread_id);
397        assert_eq!(thread.priority(), 200);
398    }
399    
400    #[cfg(feature = "std-shim")]
401    #[test]
402    fn test_thread_builder_stack_size() {
403        let builder1 = ThreadBuilder::new().stack_size(8192);
404        // Should select Medium size class for 8KB request
405        
406        let builder2 = ThreadBuilder::new().stack_size(1024);
407        // Should select Small size class for 1KB request
408        
409        // We can't easily test the internal state without exposing it,
410        // but the important thing is that it compiles and doesn't panic
411        let _ = builder1;
412        let _ = builder2;
413    }
414}