diff --git a/src/main/java/appeng/me/cache/EnergyGridCache.java b/src/main/java/appeng/me/cache/EnergyGridCache.java index db92ac10a..9a2ce7e64 100644 --- a/src/main/java/appeng/me/cache/EnergyGridCache.java +++ b/src/main/java/appeng/me/cache/EnergyGridCache.java @@ -29,6 +29,7 @@ import java.util.Queue; import java.util.Set; import java.util.SortedSet; +import com.google.common.base.Preconditions; import com.google.common.collect.HashMultiset; import com.google.common.collect.Multiset; import com.google.common.collect.Sets; @@ -84,8 +85,23 @@ public class EnergyGridCache implements IEnergyGrid { private final NavigableSet interests = Sets.newTreeSet(); private final double averageLength = 40.0; + + // Should only be modified from the add/remove methods below to guard against + // concurrent modifications private final SortedSet providers = new ObjectRBTreeSet<>(COMPARATOR_HIGHEST_PRIORITY_FIRST); + // Used to track whether an extraction is currently in progress, to fail fast + // when something externally + // modifies the energy grid. + private boolean ongoingExtractOperation = false; + + // Should only be modified from the add/remove methods below to guard against + // concurrent modifications private final SortedSet requesters = new ObjectRBTreeSet<>(COMPARATOR_LOWEST_PRIORITY_FIRST); + // Used to track whether an injection is currently in progress, to fail fast + // when something externally + // modifies the energy grid. + private boolean ongoingInjectOperation = false; + private final Multiset energyGridProviders = HashMultiset.create(); private final IGrid myGrid; private final HashMap watchers = new HashMap<>(); @@ -148,12 +164,12 @@ public class EnergyGridCache implements IEnergyGrid { switch (ev.type) { case PROVIDE_POWER: if (ev.storage.getPowerFlow() != AccessRestriction.WRITE) { - this.providers.add(ev.storage); + addProvider(ev.storage); } break; case REQUEST_POWER: if (ev.storage.getPowerFlow() != AccessRestriction.READ) { - this.requesters.add(ev.storage); + addRequester(ev.storage); } break; } @@ -281,16 +297,21 @@ public class EnergyGridCache implements IEnergyGrid { final Iterator it = this.providers.iterator(); - while (extractedPower < amt && it.hasNext()) { - final IAEPowerStorage node = it.next(); + ongoingExtractOperation = true; + try { + while (extractedPower < amt && it.hasNext()) { + final IAEPowerStorage node = it.next(); - final double req = amt - extractedPower; - final double newPower = node.extractAEPower(req, mode, PowerMultiplier.ONE); - extractedPower += newPower; + final double req = amt - extractedPower; + final double newPower = node.extractAEPower(req, mode, PowerMultiplier.ONE); + extractedPower += newPower; - if (newPower < req && mode == Actionable.MODULATE) { - it.remove(); + if (newPower < req && mode == Actionable.MODULATE) { + it.remove(); + } } + } finally { + ongoingExtractOperation = false; } final double result = Math.min(extractedPower, amt); @@ -313,13 +334,18 @@ public class EnergyGridCache implements IEnergyGrid { final Iterator it = this.requesters.iterator(); - while (amt > 0 && it.hasNext()) { - final IAEPowerStorage node = it.next(); - amt = node.injectAEPower(amt, mode); + ongoingInjectOperation = true; + try { + while (amt > 0 && it.hasNext()) { + final IAEPowerStorage node = it.next(); + amt = node.injectAEPower(amt, mode); - if (amt > 0 && mode == Actionable.MODULATE) { - it.remove(); + if (amt > 0 && mode == Actionable.MODULATE) { + it.remove(); + } } + } finally { + ongoingInjectOperation = false; } final double overflow = Math.max(0.0, amt); @@ -452,8 +478,8 @@ public class EnergyGridCache implements IEnergyGrid { this.globalAvailablePower -= ps.getAECurrentPower(); } - this.providers.remove(ps); - this.requesters.remove(ps); + removeProvider(ps); + removeRequester(ps); } } @@ -467,6 +493,30 @@ public class EnergyGridCache implements IEnergyGrid { } } + private void addRequester(IAEPowerStorage requester) { + Preconditions.checkState(!ongoingInjectOperation, + "Cannot modify energy requesters while energy is being injected."); + this.requesters.add(requester); + } + + private void removeRequester(IAEPowerStorage requester) { + Preconditions.checkState(!ongoingInjectOperation, + "Cannot modify energy requesters while energy is being injected."); + this.requesters.add(requester); + } + + private void addProvider(IAEPowerStorage provider) { + Preconditions.checkState(!ongoingExtractOperation, + "Cannot modify energy providers while energy is being extracted."); + this.providers.add(provider); + } + + private void removeProvider(IAEPowerStorage provider) { + Preconditions.checkState(!ongoingExtractOperation, + "Cannot modify energy providers while energy is being extracted."); + this.providers.remove(provider); + } + @Override public void addNode(final IGridNode node, final IGridHost machine) { if (machine instanceof IEnergyGridProvider) { @@ -492,11 +542,11 @@ public class EnergyGridCache implements IEnergyGrid { if (current > 0 && ps.getPowerFlow() != AccessRestriction.WRITE) { this.globalAvailablePower += current; - this.providers.add(ps); + addProvider(ps); } if (current < max && ps.getPowerFlow() != AccessRestriction.READ) { - this.requesters.add(ps); + addRequester(ps); } } }