1
Fork 0

averted turtle deadlock with liberal use of smart pointers

This commit is contained in:
Andy Killorin 2023-12-20 00:18:05 -06:00
parent 1694c461ff
commit 69e7d20df1
Signed by: ank
GPG key ID: B6241CA3B552BCA4
5 changed files with 47 additions and 30 deletions

View file

@ -89,7 +89,7 @@ end
local commands = {
["Wait"] = sleep,
["Forward"] = cyclefn(turtle.forward),
["Backward"] = cyclefn(turtle.backward),
["Backward"] = cyclefn(turtle.back),
["Up"] = cyclefn(turtle.up),
["Down"] = cyclefn(turtle.down),
["DropFront"] = turtle.dropfront,

View file

@ -30,6 +30,7 @@ impl World {
self.state.write().await.insert(block);
}
/// Returns true if a known non-air block exists at the point
pub async fn occupied(&self, block: Vec3) -> bool {
self.state.read().await.locate_at_point(&block.into()).is_some_and(|b| b.name != "minecraft:air")
}

View file

@ -171,8 +171,7 @@ async fn dig(
let turtle = state.read().await.get_turtle(id).await.unwrap();
tokio::spawn(
async move {
let pos = turtle.goto_adjacent(req).await.unwrap();
turtle.execute(pos.dig(req).unwrap()).await
mine::mine_chunk(turtle.clone(), req).await
}
);
@ -230,8 +229,6 @@ async fn command(
) -> Json<turtle::TurtleCommand> {
let mut state = &mut state.write().await;
println!("{:?}", &req);
if id as usize > state.turtles.len() {
return Json(turtle::TurtleCommand::Update);
}

View file

@ -16,6 +16,12 @@ pub async fn route_facing(from: Position, to: Vec3, world: &World) -> Option<Vec
}
pub async fn route(from: Position, to: Position, world: &World) -> Option<Vec<Position>> {
// attempt at not crashing by looking infinitely into the abyss
if world.get(to.pos).await
.is_some_and(|b| difficulty(&b.name).is_none())
{
return None;
}
route_to(from, to.pos, |p| p == &to, world).await
}
@ -24,13 +30,6 @@ where D: FnMut(&Position) -> bool {
// lock once, we'll be doing a lot of lookups
let world = world.clone().lock().await;
// attempt at not crashing by looking infinitely into the abyss
if world
.locate_at_point(&to.into())
.is_some_and(|b| difficulty(&b.name).is_none())
{
return None;
}
let route = astar(
&from,
move |p| next(p, &world),
@ -63,8 +62,8 @@ fn next(from: &Position, world: &WorldReadLock) -> Vec<(Position, u32)> {
let ahead = from.pos + from.dir.unit();
insert(&mut vec, ahead, from.dir, world, UNKNOWN);
//let behind = from.pos - from.dir.unit();
//insert(&mut vec, behind, from.dir, world, None);
let behind = from.pos - from.dir.unit();
insert(&mut vec, behind, from.dir, world, None);
let above = from.pos + Vec3::y();
insert(&mut vec, above, from.dir, world, UNKNOWN);

View file

@ -25,6 +25,7 @@ use super::LiveState;
use std::collections::VecDeque;
use std::future::Ready;
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::time::Duration;
@ -143,16 +144,24 @@ impl Turtle {
pub struct TurtleCommander {
sender: Arc<Sender>,
world: World,
turtle: Arc<RwLock<Turtle>>,
// everything below is best-effort
// TODO: make not bad
pos: Arc<RwLock<Position>>,
fuel: Arc<AtomicUsize>,
max_fuel: Arc<AtomicUsize>,
}
impl TurtleCommander {
pub async fn new(turtle: Name, state: &LiveState) -> Option<TurtleCommander> {
let turtle = state.turtles.get(turtle.to_num() as usize)?.clone();
let turtle = turtle.read().await;
Some(TurtleCommander {
sender: turtle.clone().read().await.sender.as_ref().unwrap().clone(),
sender: turtle.sender.as_ref().unwrap().clone(),
world: state.world.clone(),
turtle,
pos: Arc::new(RwLock::new(turtle.position)),
fuel: Arc::new(AtomicUsize::new(turtle.fuel)),
max_fuel: Arc::new(AtomicUsize::new(turtle.fuel_limit)),
})
}
@ -161,22 +170,26 @@ impl TurtleCommander {
self.sender.to_owned().send((command,send)).await.unwrap();
recv.await.unwrap()
let resp = recv.await.unwrap();
let mut pos = self.pos.write().await;
*pos = resp.pos;
self.fuel.store(resp.fuel, std::sync::atomic::Ordering::SeqCst);
resp
}
pub async fn pos(&self) -> Position {
self.turtle.read().await.position
self.pos.read().await.clone()
}
pub async fn fuel(&self) -> usize {
self.turtle.read().await.fuel
self.fuel.load(std::sync::atomic::Ordering::SeqCst)
}
pub async fn fuel_limit(&self) -> usize {
self.turtle.read().await.fuel_limit
self.max_fuel.load(std::sync::atomic::Ordering::SeqCst)
}
pub async fn world(&self) -> World {
pub fn world(&self) -> World {
self.world.clone()
}
@ -188,7 +201,9 @@ impl TurtleCommander {
break;
}
let route = route(recent, pos, &world).await?;
// easiest way to not eventually take over all memory
let routing = timeout(Duration::from_secs(1), route(recent, pos, &world));
let route = routing.await.ok()??;
let steps: Vec<TurtleCommand> = route.iter().map_windows(|[from,to]| from.difference(**to).unwrap()).collect();
@ -212,18 +227,19 @@ impl TurtleCommander {
let world = self.world.clone();
loop {
if pos == recent.dir.unit() + recent.pos {
if pos == recent.dir.unit() + recent.pos
|| pos == recent.pos + Vec3::y()
|| pos == recent.pos - Vec3::y()
{
break;
}
let route = route_facing(recent, pos, &world).await?;
let routing = timeout(Duration::from_secs(1), route_facing(recent, pos, &world));
let route = routing.await.ok()??;
let steps: Vec<TurtleCommand> = route.iter().map_windows(|[from,to]| from.difference(**to).unwrap()).collect();
'route: for (next_position, command) in route.into_iter().skip(1).zip(steps) {
// reroute if the goal point is not empty before moving
// valid routes will explicitly tell you to break ground
if world.occupied(next_position.pos).await {
if world.garbage(next_position.pos).await {
self.execute(dbg!(recent.dig(next_position.pos))?).await;
@ -232,9 +248,13 @@ impl TurtleCommander {
}
}
let state = self.execute(command).await;
let state = self.execute(command.clone()).await;
if let TurtleCommandResponse::Failure = state.ret {
if let TurtleCommand::Backward(_) = command {
// turn around if you bump your rear on something
self.goto(Position::new(recent.pos, recent.dir.left().left())).await?
}
break 'route;
}
@ -309,7 +329,7 @@ pub(crate) async fn process_turtle_update(
_ => {}
}
turtle.queued_movement = cmd.unit(turtle.position.dir);
println!("Turtle {}: {cmd:?}", turtle.name.to_str());
println!("{}: {cmd:?}", turtle.name.to_str());
return Ok(cmd);
}
}