use super::{process::Process, system::ObjectType, thread_manager::SIMULATORSTACKSIZE}; use crate::{simulator::machine::{NUM_INT_REGS, NUM_FP_REGS, STACK_REG}}; const STACK_FENCEPOST: u32 = 0xdeadbeef; /// Polymorphic macro to get thread without passing a name by default #[macro_export] macro_rules! get_new_thread { () => { Thread::new(DEFAULT_THREAD_NAME) }; ($a:literal) => { Thread::new(&$a.to_string()) }; } #[derive(PartialEq, Debug)] pub struct ThreadContext { pub int_registers: [i64; NUM_INT_REGS], pub float_registers: [f32; NUM_FP_REGS], pc: i64, } #[derive(PartialEq, Debug)] pub struct Thread { name: String, pub process: Option, // simulation_context: UContextT, pub thread_context: ThreadContext, pub stack_pointer: i32, object_type: ObjectType } impl Thread { /// Thread constructor pub fn new(name: &str) -> Self { Self { name: String::from(name), process: None, // simulation_context: UContextT::new(), thread_context: ThreadContext { int_registers: [0; NUM_INT_REGS], float_registers: [0f32; NUM_FP_REGS], pc: 0 }, stack_pointer: 0, object_type: ObjectType::ThreadType, } } pub fn init_thread_context(&mut self, initial_pc_reg: i64, initial_sp: i64, arg: i64) { self.thread_context.pc = initial_pc_reg; self.thread_context.int_registers[10] = arg; self.thread_context.int_registers[STACK_REG] = initial_sp; } pub fn init_simulator_context(&self, base_stack_addr: [i8; SIMULATORSTACKSIZE]) { // let res = self.simulation_context.get_context(); // if res != 0 { // panic!("getcontext returns non-zero value {}", res); // } // self.simulation_context.buf.uc_stack.ss_sp = base_stack_addr; // self.simulation_context.buf.uc_stack.ss_size = base_stack_addr.len(); // self.simulation_context.buf.uc_stack.ss_flags = 0; // self.simulation_context.buf.uc_link = UContextT::new().buf; // self.simulation_context.make_context(start_thread_execution, 0); // self.simulation_context.stackBottom = base_stack_addr.to_vec(); // self.simulation_context.stackBottom[0] = STACK_FENCEPOST; } /// Check if a thread has overflowed its stack /// /// This assertion doesn't catch all stack overflow conditions and your program may still crash because of an overflow. /// pub fn check_overflow(&self) { // if self.simulator_context.stackBottom != STACK_FENCEPOST { // panic!("thread {} has overflowed", self.get_name()) // } } pub fn save_simulator_state(&self) { // todo!(); // simulator state will maybe be removed so panic call is remove. See ucontext.rs } pub fn restore_simulator_state(&self) { // todo!(); // simulator state will maybe be removed so panic call is remove. See ucontext.rs } pub fn get_name(&self) -> String { self.name.clone() } } impl Drop for Thread { fn drop(&mut self) { self.object_type = ObjectType::InvalidType; // todo!(); } } fn start_thread_execution() { } #[cfg(test)] mod test { use super::{Thread, ThreadContext, NUM_INT_REGS, NUM_FP_REGS, ObjectType}; const DEFAULT_THREAD_NAME: &str = "test_thread"; /// This macro allows for getting a Thread for which we've ensured proper initial state /// in case a commit further down the line changes the initial state of threads generated /// from Thread::new macro_rules! expected_initial_state { () => { expected_initial_state!(DEFAULT_THREAD_NAME) }; ($a:expr) => { { let mut x = Thread::new($a); x.name = $a.to_string(); x.process = Option::None; x.thread_context = ThreadContext { int_registers: [0; NUM_INT_REGS], float_registers: [0f32; NUM_FP_REGS], pc: 0 }; x.stack_pointer = 0; x.object_type = ObjectType::ThreadType; x } }; } #[test] fn test_macro() { let t = get_new_thread!("hello"); assert_eq!(t.get_name(), "hello"); let t = get_new_thread!(1); assert_eq!(t.get_name(), "1"); } #[test] fn check_init() { let t = get_new_thread!(); let expected_state = expected_initial_state!(); assert_eq!(t, expected_state) } }