rudus/src/world.rs

401 lines
12 KiB
Rust
Raw Normal View History

2025-06-27 00:30:40 +00:00
use crate::chunk::Chunk;
2025-06-26 05:28:33 +00:00
use crate::value::Value;
use crate::vm::{Creature, Panic};
use ran::ran_u8;
2025-06-26 20:11:35 +00:00
use std::cell::RefCell;
use std::collections::HashMap;
use std::mem::swap;
2025-06-26 20:11:35 +00:00
use std::rc::Rc;
use std::time::{Duration, Instant};
2025-06-26 05:28:33 +00:00
const ANIMALS: [&str; 24] = [
"tortoise",
"hare",
"squirrel",
"hawk",
"woodpecker",
"cardinal",
"coyote",
"raccoon",
"rat",
"axolotl",
"cormorant",
"duck",
"orca",
"humbpack",
"tern",
"quokka",
"koala",
"kangaroo",
"zebra",
"hyena",
"giraffe",
"leopard",
"lion",
2025-06-26 20:11:35 +00:00
"hippopotamus",
2025-06-26 05:28:33 +00:00
];
#[derive(Debug, Clone, PartialEq)]
enum Status {
Empty,
Borrowed,
Nested(Creature),
}
2025-06-27 00:30:40 +00:00
impl std::fmt::Display for Status {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Status::Empty => write!(f, "empty"),
Status::Borrowed => write!(f, "borrowed"),
Status::Nested(creature) => write!(f, "nested {creature}"),
}
}
}
impl Status {
pub fn receive(&mut self, msg: Value) {
match self {
Status::Nested(creature) => creature.receive(msg),
Status::Borrowed => println!("sending a message to a borrowed process"),
Status::Empty => println!("sending a message to a dead process"),
}
}
}
2025-06-26 05:28:33 +00:00
#[derive(Debug, Clone, PartialEq)]
2025-06-27 00:30:40 +00:00
pub struct Zoo {
2025-06-26 05:28:33 +00:00
procs: Vec<Status>,
empty: Vec<usize>,
ids: HashMap<&'static str, usize>,
dead: Vec<&'static str>,
2025-06-27 00:30:40 +00:00
kill_list: Vec<&'static str>,
sleeping: HashMap<&'static str, (Instant, Duration)>,
active_idx: usize,
active_id: &'static str,
2025-06-26 05:28:33 +00:00
}
impl Zoo {
pub fn new() -> Zoo {
Zoo {
procs: vec![],
empty: vec![],
ids: HashMap::new(),
2025-06-27 00:30:40 +00:00
kill_list: vec![],
2025-06-26 05:28:33 +00:00
dead: vec![],
sleeping: HashMap::new(),
active_idx: 0,
active_id: "",
2025-06-26 05:28:33 +00:00
}
}
2025-06-26 20:11:35 +00:00
fn random_id(&self) -> String {
let rand = ran_u8() as usize % 24;
let idx = self.procs.len();
format!("{}_{idx}", ANIMALS[rand])
}
fn new_id(&self) -> &'static str {
let mut new = self.random_id();
while self.dead.iter().any(|old| *old == new) {
new = self.random_id();
}
new.leak()
}
2025-06-26 05:28:33 +00:00
pub fn put(&mut self, mut proc: Creature) -> &'static str {
if self.empty.is_empty() {
2025-06-26 20:11:35 +00:00
let id = self.new_id();
2025-06-26 05:28:33 +00:00
let idx = self.procs.len();
proc.id = id;
self.procs.push(Status::Nested(proc));
self.ids.insert(id, idx);
id
} else {
let idx = self.empty.pop().unwrap();
let rand = ran_u8() as usize % 24;
let id = format!("{}_{idx}", ANIMALS[rand]).leak();
proc.id = id;
self.ids.insert(id, idx);
self.procs[idx] = Status::Nested(proc);
id
}
}
pub fn kill(&mut self, id: &'static str) {
2025-06-27 00:30:40 +00:00
self.kill_list.push(id);
}
pub fn sleep(&mut self, id: &'static str, ms: usize) {
self.sleeping
.insert(id, (Instant::now(), Duration::from_millis(ms as u64)));
}
pub fn is_alive(&self, id: &'static str) -> bool {
if self.kill_list.contains(&id) {
return false;
}
let idx = self.ids.get(id);
match idx {
Some(idx) => match self.procs.get(*idx) {
Some(proc) => match proc {
Status::Empty => false,
Status::Borrowed => true,
Status::Nested(_) => true,
},
None => false,
},
None => false,
}
}
2025-06-27 00:30:40 +00:00
pub fn clean_up(&mut self) {
while let Some(id) = self.kill_list.pop() {
if let Some(idx) = self.ids.get(id) {
println!("buried process {id}");
2025-06-27 00:30:40 +00:00
self.procs[*idx] = Status::Empty;
self.empty.push(*idx);
self.ids.remove(id);
self.dead.push(id);
}
2025-06-26 05:28:33 +00:00
}
self.sleeping
.retain(|_, (instant, duration)| instant.elapsed() < *duration);
println!(
"currently sleeping processes: {}",
self.sleeping
.keys()
.map(|id| id.to_string())
.collect::<Vec<_>>()
.join(" | ")
);
2025-06-26 05:28:33 +00:00
}
pub fn catch(&mut self, id: &'static str) -> Creature {
if let Some(idx) = self.ids.get(id) {
let mut proc = Status::Borrowed;
swap(&mut proc, &mut self.procs[*idx]);
2025-06-26 05:28:33 +00:00
let Status::Nested(proc) = proc else {
unreachable!("tried to borrow an empty or already-borrowed process {id}");
2025-06-26 05:28:33 +00:00
};
proc
} else {
unreachable!("tried to borrow a non-existent process {id}");
2025-06-26 05:28:33 +00:00
}
}
pub fn release(&mut self, proc: Creature) {
let id = proc.id;
if let Some(idx) = self.ids.get(id) {
let mut proc = Status::Nested(proc);
swap(&mut proc, &mut self.procs[*idx]);
2025-06-26 05:28:33 +00:00
}
// Removed because, well, we shouldn't have creatures we don't know about
// And since zoo.next now cleans (and thus kills) before the world releases its active process
// We'll die if we execute this check
// else {
// unreachable!("tried to return a process the world doesn't know about");
// }
2025-06-26 05:28:33 +00:00
}
2025-06-26 20:11:35 +00:00
pub fn is_available(&self) -> bool {
match &self.procs[self.active_idx] {
Status::Empty => false,
Status::Borrowed => false,
Status::Nested(proc) => !self.sleeping.contains_key(proc.id),
2025-06-26 20:11:35 +00:00
}
}
pub fn next(&mut self) -> &'static str {
self.active_idx = (self.active_idx + 1) % self.procs.len();
while !self.is_available() {
self.clean_up();
self.active_idx = (self.active_idx + 1) % self.procs.len();
println!(
"testing process availability: {}",
self.procs[self.active_idx]
);
2025-06-26 20:11:35 +00:00
}
match &self.procs[self.active_idx] {
Status::Empty | Status::Borrowed => unreachable!(),
2025-06-26 20:11:35 +00:00
Status::Nested(proc) => proc.id,
}
}
2025-06-26 05:28:33 +00:00
2025-06-27 00:30:40 +00:00
pub fn send_msg(&mut self, id: &'static str, msg: Value) {
let Some(idx) = self.ids.get(id) else {
return;
};
self.procs[*idx].receive(msg);
}
}
2025-06-26 20:11:35 +00:00
#[derive(Debug, Clone, PartialEq)]
2025-06-26 05:28:33 +00:00
pub struct World {
2025-06-27 00:30:40 +00:00
zoo: Rc<RefCell<Zoo>>,
2025-06-26 20:11:35 +00:00
active: Option<Creature>,
2025-06-26 05:28:33 +00:00
main: &'static str,
2025-06-26 20:11:35 +00:00
pub result: Option<Result<Value, Panic>>,
2025-06-26 05:28:33 +00:00
}
impl World {
2025-06-27 00:30:40 +00:00
pub fn new(chunk: Chunk, debug: bool) -> World {
let zoo = Rc::new(RefCell::new(Zoo::new()));
let main = Creature::new(chunk, zoo.clone(), debug);
let id = zoo.as_ref().borrow_mut().put(main);
World {
2025-06-26 20:11:35 +00:00
zoo,
active: None,
2025-06-26 05:28:33 +00:00
main: id,
2025-06-26 20:11:35 +00:00
result: None,
2025-06-27 00:30:40 +00:00
}
2025-06-26 05:28:33 +00:00
}
2025-06-27 00:30:40 +00:00
// pub fn spawn(&mut self, proc: Creature) -> Value {
// let id = self.zoo.put(proc);
// Value::Keyword(id)
// }
2025-06-26 05:28:33 +00:00
2025-06-27 00:30:40 +00:00
// pub fn send_msg(&mut self, id: &'static str, msg: Value) {
// let mut proc = self.zoo.catch(id);
// proc.receive(msg);
// self.zoo.release(proc);
// }
//
2025-06-26 20:11:35 +00:00
fn next(&mut self) {
let mut active = None;
swap(&mut active, &mut self.active);
let mut zoo = self.zoo.as_ref().borrow_mut();
zoo.release(active.unwrap());
// at the moment, active is None, process is released.
// zoo should NOT need an id--it has a representation of the current active process
// world has an active process for memory reasons
// not for state-keeping reasons
let new_active_id = zoo.next();
let mut new_active_proc = zoo.catch(new_active_id);
new_active_proc.reset_reductions();
let mut new_active_opt = Some(new_active_proc);
swap(&mut new_active_opt, &mut self.active);
2025-06-26 05:28:33 +00:00
}
// fn old_next(&mut self) {
// let id = self
// .zoo
// .as_ref()
// .borrow_mut()
// .next(self.active.as_ref().unwrap().id);
// println!("next id is {id}");
// let mut active = None;
// std::mem::swap(&mut active, &mut self.active);
// let mut holding_pen = self.zoo.as_ref().borrow_mut().catch(id);
// let mut active = active.unwrap();
// std::mem::swap(&mut active, &mut holding_pen);
// println!("now in the holding pen: {}", holding_pen.id);
// holding_pen.reset_reductions();
// self.zoo.as_ref().borrow_mut().release(holding_pen);
// let mut active = Some(active);
// std::mem::swap(&mut active, &mut self.active);
// }
2025-06-26 20:11:35 +00:00
// pub fn sleep(&mut self, id: &'static str) {
// // check if the id is the actually active process
// if self.active.id != id {
// panic!("attempted to sleep a process from outside that process: active = {}; to sleep: = {id}", self.active.id);
// }
// self.next(id);
// }
2025-06-26 05:28:33 +00:00
2025-06-26 20:11:35 +00:00
// pub fn panic(&mut self, id: &'static str, panic: Panic) {
// // TODO: devise some way of linking processes (study the BEAM on this)
// // check if the id is active
// if self.active.id != id {
// panic!("attempted to panic from a process from outside that process: active = {}; panicking = {id}; panic = {panic}", self.active.id);
// }
// // check if the process is `main`, and crash the program if it is
// if self.main == id {
// self.result = self.active.result.clone();
// }
// // kill the process
// self.zoo.kill(id);
// self.next(id);
// }
2025-06-26 05:28:33 +00:00
2025-06-26 20:11:35 +00:00
// pub fn complete(&mut self) {
// if self.main == self.active.id {
// self.result = self.active.result.clone();
// }
// self.next(id);
// }
2025-06-26 05:28:33 +00:00
2025-06-27 00:30:40 +00:00
pub fn activate_main(&mut self) {
let main = self.zoo.as_ref().borrow_mut().catch(self.main);
self.active = Some(main);
2025-06-27 00:30:40 +00:00
}
pub fn active_id(&mut self) -> &'static str {
self.active.as_ref().unwrap().id
}
pub fn kill_active(&mut self) {
let id = self.active_id();
self.zoo.as_ref().borrow_mut().kill(id);
}
pub fn active_result(&mut self) -> &Option<Result<Value, Panic>> {
&self.active.as_ref().unwrap().result
}
pub fn run(&mut self) {
self.activate_main();
2025-06-26 20:11:35 +00:00
loop {
println!(
"entering world loop; active process is {}",
self.active_id()
);
2025-06-26 20:11:35 +00:00
self.active.as_mut().unwrap().interpret();
println!("yielded from {}", self.active_id());
2025-06-27 00:30:40 +00:00
match self.active_result() {
2025-06-26 20:11:35 +00:00
None => (),
Some(_) => {
2025-06-27 00:30:40 +00:00
if self.active_id() == self.main {
self.result = self.active_result().clone();
2025-06-26 20:11:35 +00:00
return;
}
println!("process died: {}", self.active_id());
2025-06-27 00:30:40 +00:00
self.kill_active();
2025-06-26 20:11:35 +00:00
}
}
2025-06-27 00:30:40 +00:00
println!("getting next process");
2025-06-26 20:11:35 +00:00
self.next();
// self.clean_up();
2025-06-26 20:11:35 +00:00
}
2025-06-26 05:28:33 +00:00
}
// TODO:
// * [ ] Maybe I need to write this from the bottom up?
// What do processes need to do?
// - [ ] send a message to another process
// - [ ] tell the world to spawn a new process, get the pid back
// - [ ] receive its messages (always until something matches, or sleep if nothing matches)
// - [ ] delete a message from the mbx if it's a match (by idx)
// - [ ] yield
// - [ ] panic
// - [ ] complete
// Thus the other side of this looks like:
// * [x] Spawn a process
// * [x]
}
// Okay, some more thinking
// The world and process can't have mutable references to one another
// They will each need an Rc<RefCell<PostOffice>>
// All the message passing and world/proc communication will happen through there
// And ownership goes World -> Process A -> World -> Process B
// Both the world and a process will have an endless `loop`.
// But I already have three terms: Zoo, Creature, and World
// That should be enough indirection?
// To solve tomorrow.