Fail faster when something modifies the list of energy providers/requesters while an extract/inject operation is ongoing, since this would lead to ConcurrentModificationExceptions later, without a way of tracking down what caused the modification in the first place.

This commit is contained in:
Sebastian Hartte
2020-07-19 16:39:37 +02:00
parent 5a976ebfae
commit 1c88ea7b78
+68 -18
View File
@@ -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<EnergyThreshold> 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<IAEPowerStorage> 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<IAEPowerStorage> 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<IEnergyGridProvider> energyGridProviders = HashMultiset.create();
private final IGrid myGrid;
private final HashMap<IGridNode, IEnergyWatcher> 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<IAEPowerStorage> 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<IAEPowerStorage> 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);
}
}
}