Deal with a race condition.
1) A car tries to spawn, but fails because there's something in the way
2) The player makes live edits
3) The retry occurs, but the path has become invalid due to edits
Need to make the detection of this more efficient later.
@@ -435,9 +435,40 @@ impl Sim {
self.trips.start_trip(self.time, id, trip_spec, &mut ctx);
}
Command::SpawnCar(create_car, retry_if_no_room) => {
- // create_car contains a Path, which is expensive to clone. We need different parts
- // of create_car after attempting start_car_on_lane. Make copies just of those
- // here. In no case do we ever clone the path.
+ // If this SpawnCar is being retried and the map was live-edited since the first
+ // attempt, the path might've become invalid. TODO Skip this check
+ // most of the time.
+ let constraints = create_car.vehicle.vehicle_type.to_constraints();
+ let mut ok = true;
+ for step in create_car.router.get_path().get_steps() {
+ match step.as_traversable() {
+ Traversable::Lane(l) => {
+ if !constraints.can_use(ctx.map.get_l(l), ctx.map) {
+ ok = false;
+ break;
+ }
+ }
+ Traversable::Turn(t) => {
+ if ctx.map.maybe_get_t(t).is_none() {
+ ok = false;
+ break;
+ }
+ }
+ }
+ }
+ if !ok {
+ self.trips.cancel_trip(
+ self.time,
+ create_car.trip_and_person.unwrap().0,
+ "path is no longer valid after map edits".to_string(),
+ Some(create_car.vehicle),
+ &mut ctx,
+ );
+ } else {
+ // create_car contains a Path, which is expensive to clone. We need different
+ // parts of create_car after attempting start_car_on_lane.
+ // Make copies just of those here. In no case do we ever
+ // clone the path.
let id = create_car.vehicle.id;
let maybe_route = create_car.maybe_route;
let trip_and_person = create_car.trip_and_person;
@@ -499,7 +530,10 @@ impl Sim {
if let Some(parked_car) = maybe_parked_car {
if let ParkingSpot::Offstreet(b, _) = parked_car.spot {
// Buses don't start in parking garages, so trip must exist
- events.push(Event::PersonLeavesBuilding(trip_and_person.unwrap().1, b));
+ events.push(Event::PersonLeavesBuilding(
+ trip_and_person.unwrap().1,
+ b,
+ ));
}
self.parking.remove_parked_car(parked_car);
}
@@ -510,6 +544,7 @@ impl Sim {
.record_demand(self.driving.get_path(id).unwrap(), map);
}
}
+ }
Command::SpawnPed(create_ped) => {
// Do the order a bit backwards so we don't have to clone the
// CreatePedestrian. spawn_ped can't fail.
