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};
|
2025-06-30 16:48:50 +00:00
|
|
|
use crate::io::{MsgOut, MsgIn, do_io};
|
2025-06-26 05:28:33 +00:00
|
|
|
use ran::ran_u8;
|
2025-06-26 20:11:35 +00:00
|
|
|
use std::cell::RefCell;
|
2025-06-29 22:14:06 +00:00
|
|
|
use std::collections::{HashMap, HashSet};
|
2025-06-27 18:27:42 +00:00
|
|
|
use std::mem::swap;
|
2025-06-26 20:11:35 +00:00
|
|
|
use std::rc::Rc;
|
2025-06-27 18:27:42 +00:00
|
|
|
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>,
|
2025-06-29 22:14:06 +00:00
|
|
|
dead: HashSet<&'static str>,
|
2025-06-27 00:30:40 +00:00
|
|
|
kill_list: Vec<&'static str>,
|
2025-06-27 18:27:42 +00:00
|
|
|
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-29 22:14:06 +00:00
|
|
|
dead: HashSet::new(),
|
2025-06-27 18:27:42 +00:00
|
|
|
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();
|
2025-06-27 22:48:27 +00:00
|
|
|
proc.pid = id;
|
2025-06-26 05:28:33 +00:00
|
|
|
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();
|
2025-06-27 22:48:27 +00:00
|
|
|
proc.pid = id;
|
2025-06-26 05:28:33 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2025-06-27 18:27:42 +00:00
|
|
|
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) {
|
2025-06-27 18:27:42 +00:00
|
|
|
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);
|
2025-06-29 22:14:06 +00:00
|
|
|
self.dead.insert(id);
|
2025-06-27 00:30:40 +00:00
|
|
|
}
|
2025-06-26 05:28:33 +00:00
|
|
|
}
|
2025-06-27 18:27:42 +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;
|
2025-06-27 18:27:42 +00:00
|
|
|
swap(&mut proc, &mut self.procs[*idx]);
|
2025-06-26 05:28:33 +00:00
|
|
|
let Status::Nested(proc) = proc else {
|
2025-06-27 18:27:42 +00:00
|
|
|
unreachable!("tried to borrow an empty or already-borrowed process {id}");
|
2025-06-26 05:28:33 +00:00
|
|
|
};
|
|
|
|
proc
|
|
|
|
} else {
|
2025-06-27 18:27:42 +00:00
|
|
|
unreachable!("tried to borrow a non-existent process {id}");
|
2025-06-26 05:28:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn release(&mut self, proc: Creature) {
|
2025-06-27 22:48:27 +00:00
|
|
|
let id = proc.pid;
|
2025-06-26 05:28:33 +00:00
|
|
|
if let Some(idx) = self.ids.get(id) {
|
|
|
|
let mut proc = Status::Nested(proc);
|
2025-06-27 18:27:42 +00:00
|
|
|
swap(&mut proc, &mut self.procs[*idx]);
|
2025-06-26 05:28:33 +00:00
|
|
|
}
|
2025-06-27 18:27:42 +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
|
|
|
|
2025-06-27 18:27:42 +00:00
|
|
|
pub fn is_available(&self) -> bool {
|
|
|
|
match &self.procs[self.active_idx] {
|
|
|
|
Status::Empty => false,
|
|
|
|
Status::Borrowed => false,
|
2025-06-27 22:48:27 +00:00
|
|
|
Status::Nested(proc) => !self.sleeping.contains_key(proc.pid),
|
2025-06-26 20:11:35 +00:00
|
|
|
}
|
2025-06-27 18:27:42 +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
|
|
|
}
|
2025-06-27 18:27:42 +00:00
|
|
|
match &self.procs[self.active_idx] {
|
|
|
|
Status::Empty | Status::Borrowed => unreachable!(),
|
2025-06-27 22:48:27 +00:00
|
|
|
Status::Nested(proc) => proc.pid,
|
2025-06-26 20:11:35 +00:00
|
|
|
}
|
|
|
|
}
|
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-26 20:11:35 +00:00
|
|
|
fn next(&mut self) {
|
|
|
|
let mut active = None;
|
2025-06-27 18:27:42 +00:00
|
|
|
swap(&mut active, &mut self.active);
|
|
|
|
let mut zoo = self.zoo.as_ref().borrow_mut();
|
|
|
|
zoo.release(active.unwrap());
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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);
|
2025-06-26 21:15:00 +00:00
|
|
|
self.active = Some(main);
|
2025-06-27 00:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn active_id(&mut self) -> &'static str {
|
2025-06-27 22:48:27 +00:00
|
|
|
self.active.as_ref().unwrap().pid
|
2025-06-27 00:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2025-06-30 16:48:50 +00:00
|
|
|
// TODO: add memory io places to this signature
|
|
|
|
// * console
|
|
|
|
// * input
|
|
|
|
// * commands
|
|
|
|
// * slurp
|
|
|
|
pub async fn run(
|
|
|
|
&mut self,
|
|
|
|
console: Value,
|
|
|
|
// input: Value,
|
|
|
|
// commands: Value,
|
|
|
|
// slurp_out: Value,
|
|
|
|
// slurp_in: Value,
|
|
|
|
) {
|
2025-06-27 00:30:40 +00:00
|
|
|
self.activate_main();
|
2025-06-30 16:48:50 +00:00
|
|
|
// let Value::Box(input) = input else {unreachable!()};
|
|
|
|
// let Value::Box(commands) = commands else {unreachable!()};
|
|
|
|
// let Value::Box(slurp) = slurp else {
|
|
|
|
// unreachable!()};
|
|
|
|
let mut last_io = Instant::now();
|
|
|
|
let mut kill_signal = false;
|
2025-06-26 20:11:35 +00:00
|
|
|
loop {
|
2025-06-30 16:48:50 +00:00
|
|
|
if kill_signal {
|
|
|
|
// TODO: send a last message to the console
|
|
|
|
println!("received KILL signal");
|
|
|
|
return;
|
|
|
|
}
|
2025-06-27 18:27:42 +00:00
|
|
|
println!(
|
|
|
|
"entering world loop; active process is {}",
|
|
|
|
self.active_id()
|
|
|
|
);
|
2025-06-26 20:11:35 +00:00
|
|
|
self.active.as_mut().unwrap().interpret();
|
2025-06-27 18:27:42 +00:00
|
|
|
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 {
|
2025-06-30 16:48:50 +00:00
|
|
|
let result = self.active_result().clone().unwrap();
|
|
|
|
self.result = Some(result.clone());
|
|
|
|
|
|
|
|
//TODO: capture any remaining console or command values
|
|
|
|
do_io(vec![MsgOut::Complete(result)]);
|
2025-06-26 20:11:35 +00:00
|
|
|
return;
|
|
|
|
}
|
2025-06-27 22:48:27 +00:00
|
|
|
println!(
|
|
|
|
"process {} died with {:?}",
|
|
|
|
self.active_id(),
|
|
|
|
self.active_result().clone()
|
|
|
|
);
|
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();
|
2025-06-30 16:48:50 +00:00
|
|
|
// TODO:: if enough time has elapsed (how much?) run i/o
|
|
|
|
// 10 ms is 100hz, so that's a nice refresh rate
|
|
|
|
if Instant::now().duration_since(last_io) > Duration::from_millis(10) {
|
|
|
|
// gather io
|
|
|
|
// compile it into messages
|
|
|
|
// serialize it
|
|
|
|
let mut outbox = vec![];
|
|
|
|
if let Some(console) = flush_console(&console) {
|
|
|
|
outbox.push(console);
|
|
|
|
};
|
|
|
|
// TODO: slurp
|
|
|
|
// TODO: commands
|
|
|
|
// send it
|
|
|
|
// await the response
|
|
|
|
let inbox = do_io(outbox).await;
|
|
|
|
// unpack the response into messages
|
|
|
|
for msg in inbox {
|
|
|
|
match msg {
|
|
|
|
MsgIn::Kill => kill_signal = true,
|
|
|
|
_ => todo!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// update
|
|
|
|
last_io = Instant::now();
|
|
|
|
}
|
2025-06-26 20:11:35 +00:00
|
|
|
}
|
2025-06-26 05:28:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-06-30 16:48:50 +00:00
|
|
|
fn flush_console(console: &Value) -> Option<MsgOut> {
|
|
|
|
let console = console.as_box();
|
|
|
|
let working_copy = RefCell::new(Value::new_list());
|
|
|
|
console.swap(&working_copy);
|
|
|
|
let working_value = working_copy.borrow();
|
|
|
|
if working_value.as_list().is_empty() { return None; }
|
|
|
|
Some(MsgOut::Console(working_value.clone()))
|
|
|
|
}
|