From 58ba166392ec50b8c8dcfd51324faf262ec0a831 Mon Sep 17 00:00:00 2001 From: nat Date: Fri, 6 Dec 2024 11:33:41 -0800 Subject: [PATCH] feat: implement semaphores --- src/forestry.rs | 38 ++++++++++-------------------------- src/main.rs | 15 ++++++++++----- src/state.rs | 51 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 70 insertions(+), 34 deletions(-) diff --git a/src/forestry.rs b/src/forestry.rs index e242473..e4a34ac 100644 --- a/src/forestry.rs +++ b/src/forestry.rs @@ -96,46 +96,28 @@ pub fn op_define(instruction: &u16, tree: &mut Tree, rules: &Vec, rules_to tree.push_by_stack_code(stack_code, (rules.len() + rules_to_define.len() - 2) as u16) } -/* pub fn op_wait(instruction: &u16, tree: &mut Tree, land: &mut Land, instruction_pointer: &u16) { let (_, relative_x, relative_y) = extract_forestry_format(instruction); - let (abs_x, abs_y) = land.derelativize(tree, relative_x, relative_y); + let (abs_x, abs_y) = land.derelativize(tree.position, relative_x, relative_y); // Decrement the counting semaphore - let current_value = land.map.get(&(abs_x, abs_y)).unwrap(); - land.sow(abs_x, abs_y, current_value - 1); + let semaphore = land.get_semaphore(&(abs_x, abs_y)); + let semaphore_value = semaphore.wait(tree.id); - if current_value - 1 < 0 { + if semaphore_value < 0 { tree.asleep = true; - tree.resume_from = instruction_pointer + 1; - if land.semaphores.contains_key(&(abs_x, abs_y)) { - land.semaphores.get(&(abs_x, abs_y)).unwrap().push_back(tree.id); - } else { - // We've got to create if it doesn't already exist - let semaphores_for_cell: VecDeque = VecDeque::from([tree.id]); - land.semaphores.insert((abs_x, abs_y), semaphores_for_cell); - } + tree.resume_from = *instruction_pointer; } } -pub fn op_signal(instruction: &u16, tree: &mut Tree, trees: &mut HashMap, land: &mut Land) { +pub fn op_signal(instruction: &u16, tree: &mut Tree, trees_to_wake: &mut Vec, land: &mut Land) { let (_, relative_x, relative_y) = extract_forestry_format(instruction); - let (abs_x, abs_y) = land.derelativize(tree, relative_x, relative_y); + let (abs_x, abs_y) = land.derelativize(tree.position, relative_x, relative_y); // Increment the counting semaphore - let current_value = land.map.get(&(abs_x, abs_y)).unwrap(); - land.sow(abs_x, abs_y, current_value + 1); + let semaphore = land.get_semaphore(&(abs_x, abs_y)); - // Wake the oldest sleeping tree if needed - if *current_value >= 0 { - // If this call returns None, there's a logic error somewhere, either in the - // VM, or someone called signal on an uninitialized semaphore - let semaphores_for_cell = land.semaphores.get(&(abs_x, abs_y)).unwrap(); - let tree_id_to_wake = semaphores_for_cell.pop_front().unwrap(); - - let tree_to_wake = trees.get(&tree_id_to_wake).unwrap(); - tree_to_wake.asleep = false; - tree.resume_from = 0; + if let Some(tree_id) = semaphore.signal() { + trees_to_wake.push(tree_id); } } -*/ diff --git a/src/main.rs b/src/main.rs index 47622c9..4161776 100644 --- a/src/main.rs +++ b/src/main.rs @@ -123,6 +123,7 @@ fn main() -> io::Result<()> { for _ in 0..generation_count { let mut trees_to_plant: Vec = Vec::new(); let mut rules_to_define: Vec = Vec::new(); + let mut trees_to_wake: Vec = Vec::new(); // We shouldn't be able to compost the same tree twice in a generation, // so we use a BTreeSet here. BTreeSets have the advantage of not @@ -179,10 +180,8 @@ fn main() -> io::Result<()> { break; }, 27 => forestry::op_define(word, tree, &rules, &mut rules_to_define), - /* 28 => forestry::op_wait(word, tree, &mut land, &instruction_pointer), - 29 => forestry::op_signal(word, tree, &mut trees, &mut land), - */ + 29 => forestry::op_signal(word, tree, &mut trees_to_wake, &mut land), _ => panic!("Unknown opcode: {}", opcode) }; @@ -190,8 +189,8 @@ fn main() -> io::Result<()> { } } - for tree_index in trees_to_compost_by_index.iter() { - let tree_id = scheduled_trees.swap_remove(*tree_index as usize); + while let Some(tree_index) = trees_to_compost_by_index.pop_first() { + let tree_id = scheduled_trees.swap_remove(tree_index as usize); let (parent_id, recovered_stack_size) = { let tree = trees.remove(&tree_id).unwrap(); (tree.parent_id, tree.stack_size) @@ -200,6 +199,12 @@ fn main() -> io::Result<()> { parent.stack_size += recovered_stack_size; } + while let Some(tree_id) = trees_to_wake.pop() { + let tree = trees.get_mut(&tree_id).unwrap(); + tree.asleep = true; + tree.resume_from = 0; + } + for _ in 0..trees_to_plant.len() { let tree_to_plant = trees_to_plant.pop().unwrap(); trees.insert(tree_to_plant.id, tree_to_plant); diff --git a/src/state.rs b/src/state.rs index 15d79b7..88b96c0 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,13 +1,49 @@ use std::collections::HashMap; use std::collections::VecDeque; +pub struct Semaphore { + queue: VecDeque, // Positions -> tree ID + value: i16, // If below zero, the tree gets enqueued +} + +impl Semaphore { + /** + * Returns the new value of the semaphore + */ + pub fn wait(&mut self, rule_id: u16) -> i16 { + self.value -= 1; + + if self.value < 0 { + self.queue.push_back(rule_id); + } + + return self.value; + } + + /** + * Returns the ID of the tree that's been polled + */ + pub fn signal(&mut self) -> Option { + self.value += 1; + + return if self.value >= 0 { + // This should never fail because self.value should correspond to + // the number of enqueued trees, but it should fail loudly if that + // were to happen + Some(self.queue.pop_front().unwrap()) + } else { + None + }; + } +} + pub struct Land { pub map: HashMap<(u16, u16), u16>, pub width: u16, pub height: u16, // Maps positions to a queue of rule IDs - pub semaphores: HashMap<(u16, u16), VecDeque>, + pub semaphores: HashMap<(u16, u16), Semaphore>, } impl Land { @@ -34,6 +70,19 @@ impl Land { return (absolute_x, absolute_y); } + + pub fn get_semaphore(&mut self, position: &(u16, u16)) -> &mut Semaphore { + if !self.semaphores.contains_key(position) { + let new_semaphore = Semaphore { + value: 0, + queue: VecDeque::new(), + }; + + self.semaphores.insert(*position, new_semaphore); + } + + return self.semaphores.get_mut(position).unwrap(); + } } pub type Rule = VecDeque;