preemptive_threads/thread_new/
builder.rs1use 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
10pub struct ThreadBuilder {
15 stack_size_class: Option<StackSizeClass>,
17 custom_stack_size: Option<usize>,
19 priority: u8,
21 name: Option<String>,
23 cpu_affinity: Option<u64>,
25 group_id: Option<u32>,
27 stack_guard_pages: bool,
29 stack_canary: bool,
31 custom_canary: Option<u64>,
33 time_slice: Option<Duration>,
35 critical: bool,
37 preemptible: bool,
39 tls_size: Option<usize>,
41 debug_info: bool,
43 attributes: ThreadAttributes,
45}
46
47#[derive(Debug, Clone)]
49pub struct ThreadAttributes {
50 rt_priority: Option<u8>,
52 nice_value: i8,
54 inherit_signal_mask: bool,
56 environment: Option<alloc::collections::BTreeMap<String, String>>,
58 limits: ResourceLimits,
60}
61
62#[derive(Debug, Clone)]
64pub struct ResourceLimits {
65 max_cpu_time: Option<u64>,
67 max_memory: Option<usize>,
69 max_files: Option<u32>,
71 max_children: Option<u32>,
73}
74
75impl ThreadBuilder {
76 pub fn new() -> Self {
78 Self {
79 stack_size_class: None,
80 custom_stack_size: None,
81 priority: 128, 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 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; self
102 }
103
104 pub fn stack_size(mut self, size: usize) -> Self {
106 self.custom_stack_size = Some(size);
107 self.stack_size_class = None; self
109 }
110
111 pub fn priority(mut self, priority: u8) -> Self {
113 self.priority = priority;
114 self
115 }
116
117 pub fn name<T: Into<String>>(mut self, name: T) -> Self {
119 self.name = Some(name.into());
120 self
121 }
122
123 pub fn cpu_affinity(mut self, mask: u64) -> Self {
125 self.cpu_affinity = Some(mask);
126 self
127 }
128
129 pub fn group_id(mut self, group: u32) -> Self {
131 self.group_id = Some(group);
132 self
133 }
134
135 pub fn stack_guard_pages(mut self, enabled: bool) -> Self {
137 self.stack_guard_pages = enabled;
138 self
139 }
140
141 pub fn stack_canary(mut self, enabled: bool) -> Self {
143 self.stack_canary = enabled;
144 self
145 }
146
147 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 pub fn time_slice(mut self, duration: Duration) -> Self {
156 self.time_slice = Some(duration);
157 self
158 }
159
160 pub fn critical(mut self, critical: bool) -> Self {
162 self.critical = critical;
163 self
164 }
165
166 pub fn preemptible(mut self, preemptible: bool) -> Self {
168 self.preemptible = preemptible;
169 self
170 }
171
172 pub fn tls_size(mut self, size: usize) -> Self {
174 self.tls_size = Some(size);
175 self
176 }
177
178 pub fn debug_info(mut self, enabled: bool) -> Self {
180 self.debug_info = enabled;
181 self
182 }
183
184 pub fn attributes(mut self, attributes: ThreadAttributes) -> Self {
186 self.attributes = attributes;
187 self
188 }
189
190 pub fn spawn(
203 self,
204 thread_id: ThreadId,
205 stack_pool: &StackPool,
206 entry_point: fn(),
207 ) -> Result<(Thread, JoinHandle), SpawnError> {
208 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 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, }
235 };
236
237 let stack = if let Some(_custom_size) = self.custom_stack_size {
239 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 if self.stack_guard_pages && cfg!(feature = "mmu") {
257 }
260
261 if self.stack_canary {
263 let canary_value = self.custom_canary.unwrap_or_else(|| {
264 0xDEADBEEFCAFEBABE
267 });
268
269 unsafe {
271 let stack_bottom = stack.stack_bottom() as *mut u64;
272 *stack_bottom = canary_value;
273 }
274 }
275
276 let (thread, join_handle) = Thread::new(
278 thread_id,
279 stack,
280 entry_point,
281 self.priority,
282 );
283
284 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 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 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 });
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 let builder2 = ThreadBuilder::new().stack_size(1024);
407 let _ = builder1;
412 let _ = builder2;
413 }
414}