feat: implement semaphores

This commit is contained in:
Nat 2024-12-06 11:33:41 -08:00
parent 06e8e5d293
commit 58ba166392
3 changed files with 70 additions and 34 deletions

View File

@ -96,46 +96,28 @@ pub fn op_define(instruction: &u16, tree: &mut Tree, rules: &Vec<Rule>, 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<u16> = 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<u16, Tree>, land: &mut Land) {
pub fn op_signal(instruction: &u16, tree: &mut Tree, trees_to_wake: &mut Vec<u16>, 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);
}
}
*/

View File

@ -123,6 +123,7 @@ fn main() -> io::Result<()> {
for _ in 0..generation_count {
let mut trees_to_plant: Vec<state::Tree> = Vec::new();
let mut rules_to_define: Vec<state::Rule> = Vec::new();
let mut trees_to_wake: Vec<u16> = 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);

View File

@ -1,13 +1,49 @@
use std::collections::HashMap;
use std::collections::VecDeque;
pub struct Semaphore {
queue: VecDeque<u16>, // 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<u16> {
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<u16>>,
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<u16>;