From 4d365b54dad8d066bba2a2b1a05092b4802b9970 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Mon, 11 Sep 2023 19:22:26 +0200 Subject: feat: cancellation token for ModelStore --- .../main/java/tools/refinery/store/model/Model.java | 4 ++-- .../java/tools/refinery/store/model/ModelStore.java | 2 ++ .../refinery/store/model/ModelStoreBuilder.java | 3 +++ .../refinery/store/model/internal/ModelImpl.java | 16 ++++++++++++++-- .../store/model/internal/ModelStoreBuilderImpl.java | 21 ++++++++++++++++++--- .../store/model/internal/ModelStoreImpl.java | 15 ++++++++++++++- .../model/internal/VersionedInterpretation.java | 6 ++---- .../statecoding/StateCodeCalculatorFactory.java | 3 ++- .../internal/StateCoderStoreAdapterImpl.java | 2 +- .../AbstractNeighbourhoodCalculator.java | 6 +++++- .../neighbourhood/LazyNeighbourhoodCalculator.java | 6 ++++-- .../neighbourhood/NeighbourhoodCalculator.java | 8 ++++++-- .../refinery/store/util/CancellationToken.java | 13 +++++++++++++ 13 files changed, 86 insertions(+), 19 deletions(-) create mode 100644 subprojects/store/src/main/java/tools/refinery/store/util/CancellationToken.java (limited to 'subprojects/store/src/main/java/tools') diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/Model.java b/subprojects/store/src/main/java/tools/refinery/store/model/Model.java index e2ab72e7..c4ce5207 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/Model.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/Model.java @@ -8,11 +8,9 @@ package tools.refinery.store.model; import tools.refinery.store.adapter.ModelAdapter; import tools.refinery.store.map.Version; import tools.refinery.store.map.Versioned; -import tools.refinery.store.model.internal.VersionedInterpretation; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; -import java.util.Map; import java.util.Optional; public interface Model extends Versioned { @@ -38,4 +36,6 @@ public interface Model extends Versioned { void addListener(ModelListener listener); void removeListener(ModelListener listener); + + void checkCancelled(); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java index 89382b3a..61abf126 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java @@ -26,6 +26,8 @@ public interface ModelStore { T getAdapter(Class adapterType); + void checkCancelled(); + static ModelStoreBuilder builder() { return new ModelStoreBuilderImpl(); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreBuilder.java b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreBuilder.java index 8f652762..9b2b38c3 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreBuilder.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreBuilder.java @@ -8,12 +8,15 @@ package tools.refinery.store.model; import tools.refinery.store.adapter.ModelAdapterBuilder; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; +import tools.refinery.store.util.CancellationToken; import java.util.Collection; import java.util.List; import java.util.Optional; public interface ModelStoreBuilder { + ModelStoreBuilder cancellationToken(CancellationToken cancellationToken); + default ModelStoreBuilder symbols(AnySymbol... symbols) { return symbols(List.of(symbols)); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java index 2b12d5a6..d11e431b 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java @@ -13,23 +13,26 @@ import tools.refinery.store.model.*; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.util.CancellationToken; import java.util.*; public class ModelImpl implements Model { - private final ModelStore store; + private final ModelStoreImpl store; private Version state; private LinkedHashMap> interpretations; private final List adapters; private final List listeners = new ArrayList<>(); + private final CancellationToken cancellationToken; private boolean uncommittedChanges; private ModelAction pendingAction = ModelAction.NONE; private Version restoringToState = null; - ModelImpl(ModelStore store, Version state, int adapterCount) { + ModelImpl(ModelStoreImpl store, Version state, int adapterCount) { this.store = store; this.state = state; adapters = new ArrayList<>(adapterCount); + cancellationToken = store.getCancellationToken(); } void setInterpretations(LinkedHashMap> interpretations) { @@ -88,6 +91,7 @@ public class ModelImpl implements Model { @Override public Version commit() { + checkCancelled(); if (hasPendingAction()) { throw pendingActionError("commit"); } @@ -106,6 +110,7 @@ public class ModelImpl implements Model { Version[] interpretationVersions = new Version[interpretations.size()]; int j = 0; for (var interpretationEntry : interpretations.entrySet()) { + checkCancelled(); interpretationVersions[j++] = interpretationEntry.getValue().commit(); } ModelVersion modelVersion = new ModelVersion(interpretationVersions); @@ -125,6 +130,7 @@ public class ModelImpl implements Model { @Override public void restore(Version version) { + checkCancelled(); if (hasPendingAction()) { throw pendingActionError("restore to %s".formatted(version)); } @@ -140,6 +146,7 @@ public class ModelImpl implements Model { } int j = 0; for (var interpretation : interpretations.values()) { + checkCancelled(); interpretation.restore(ModelVersion.getInternalVersion(version, j++)); } @@ -187,4 +194,9 @@ public class ModelImpl implements Model { public void removeListener(ModelListener listener) { listeners.remove(listener); } + + @Override + public void checkCancelled() { + cancellationToken.checkCancelled(); + } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java index 2dde7a4c..53ecde5e 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java @@ -16,14 +16,28 @@ import tools.refinery.store.model.ModelStoreConfiguration; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.util.CancellationToken; import java.util.*; public class ModelStoreBuilderImpl implements ModelStoreBuilder { + private CancellationToken cancellationToken; private final LinkedHashSet allSymbols = new LinkedHashSet<>(); private final LinkedHashMap, List> equivalenceClasses = new LinkedHashMap<>(); private final List adapters = new ArrayList<>(); + @Override + public ModelStoreBuilder cancellationToken(CancellationToken cancellationToken) { + if (this.cancellationToken != null) { + throw new IllegalStateException("Cancellation token was already set"); + } + if (cancellationToken == null) { + throw new IllegalStateException("Cancellation token must not be null"); + } + this.cancellationToken = cancellationToken; + return this; + } + @Override public ModelStoreBuilder symbol(Symbol symbol) { if (!allSymbols.add(symbol)) { @@ -75,7 +89,8 @@ public class ModelStoreBuilderImpl implements ModelStoreBuilder { for (var entry : equivalenceClasses.entrySet()) { createStores(stores, entry.getKey(), entry.getValue()); } - var modelStore = new ModelStoreImpl(stores, adapters.size()); + var modelStore = new ModelStoreImpl(stores, adapters.size(), cancellationToken == null ? + CancellationToken.NONE : cancellationToken); for (var adapterBuilder : adapters) { var storeAdapter = adapterBuilder.build(modelStore); modelStore.addAdapter(storeAdapter); @@ -86,8 +101,8 @@ public class ModelStoreBuilderImpl implements ModelStoreBuilder { private void createStores(Map> stores, SymbolEquivalenceClass equivalenceClass, List symbols) { int size = symbols.size(); - VersionedMapStoreFactory mapFactory = VersionedMapStore - .builder() + VersionedMapStoreFactory mapFactory = VersionedMapStore + .builder() .strategy(VersionedMapStoreFactoryBuilder.StoreStrategy.DELTA) .defaultValue(equivalenceClass.defaultValue()) .build(); diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java index a320a618..fd4cc160 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java @@ -14,16 +14,20 @@ import tools.refinery.store.model.ModelDiffCursor; import tools.refinery.store.model.ModelStore; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.util.CancellationToken; import java.util.*; public class ModelStoreImpl implements ModelStore { private final LinkedHashMap> stores; private final List adapters; + private final CancellationToken cancellationToken; - ModelStoreImpl(LinkedHashMap> stores, int adapterCount) { + ModelStoreImpl(LinkedHashMap> stores, int adapterCount, + CancellationToken cancellationToken) { this.stores = stores; adapters = new ArrayList<>(adapterCount); + this.cancellationToken = cancellationToken; } @Override @@ -100,4 +104,13 @@ public class ModelStoreImpl implements ModelStore { void addAdapter(ModelStoreAdapter adapter) { adapters.add(adapter); } + + @Override + public void checkCancelled() { + cancellationToken.checkCancelled(); + } + + CancellationToken getCancellationToken() { + return cancellationToken; + } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java index 71df3962..dcf0ad08 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java @@ -75,6 +75,7 @@ public abstract class VersionedInterpretation implements Interpretation { @Override public T put(Tuple key, T value) { checkKey(key); + model.checkCancelled(); model.markAsChanged(); var oldValue = map.put(key, value); valueChanged(key, oldValue, value, false); @@ -83,15 +84,12 @@ public abstract class VersionedInterpretation implements Interpretation { @Override public void putAll(Cursor cursor) { - if (listeners.isEmpty()) { - map.putAll(cursor); - return; - } model.markAsChanged(); if (cursor.getDependingMaps().contains(map)) { List keys = new ArrayList<>(); List values = new ArrayList<>(); while (cursor.move()) { + model.checkCancelled(); keys.add(cursor.getKey()); values.add(cursor.getValue()); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculatorFactory.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculatorFactory.java index 04e17a13..809205e4 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculatorFactory.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculatorFactory.java @@ -7,9 +7,10 @@ package tools.refinery.store.statecoding; import org.eclipse.collections.api.set.primitive.IntSet; import tools.refinery.store.model.Interpretation; +import tools.refinery.store.model.Model; import java.util.List; public interface StateCodeCalculatorFactory { - StateCodeCalculator create(List> interpretations, IntSet individuals); + StateCodeCalculator create(Model model, List> interpretations, IntSet individuals); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderStoreAdapterImpl.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderStoreAdapterImpl.java index 89586bfb..2cb6f3c1 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderStoreAdapterImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderStoreAdapterImpl.java @@ -68,7 +68,7 @@ public class StateCoderStoreAdapterImpl implements StateCoderStoreAdapter { @Override public StateCoderAdapter createModelAdapter(Model model) { var interpretations = symbols.stream().map(model::getInterpretation).toList(); - var coder = codeCalculatorFactory.create(interpretations, individuals); + var coder = codeCalculatorFactory.create(model, interpretations, individuals); return new StateCoderAdapterImpl(this, coder, model); } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java index 5d390da2..4cef6786 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java @@ -10,6 +10,7 @@ import org.eclipse.collections.api.map.primitive.MutableIntLongMap; import org.eclipse.collections.api.set.primitive.IntSet; import tools.refinery.store.model.AnyInterpretation; import tools.refinery.store.model.Interpretation; +import tools.refinery.store.model.Model; import tools.refinery.store.statecoding.ObjectCode; import tools.refinery.store.tuple.Tuple; import tools.refinery.store.tuple.Tuple0; @@ -17,13 +18,16 @@ import tools.refinery.store.tuple.Tuple0; import java.util.*; public abstract class AbstractNeighbourhoodCalculator { + protected final Model model; protected final List nullImpactValues; protected final LinkedHashMap impactValues; protected final MutableIntLongMap individualHashValues = IntLongMaps.mutable.empty(); protected static final long PRIME = 31; - protected AbstractNeighbourhoodCalculator(List interpretations, IntSet individuals) { + protected AbstractNeighbourhoodCalculator(Model model, List interpretations, + IntSet individuals) { + this.model = model; this.nullImpactValues = new ArrayList<>(); this.impactValues = new LinkedHashMap<>(); // Random isn't used for cryptographical purposes but just to assign distinguishable identifiers to symbols. diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculator.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculator.java index c188a839..04335141 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculator.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculator.java @@ -12,6 +12,7 @@ import org.eclipse.collections.api.set.primitive.IntSet; import tools.refinery.store.map.Cursor; import tools.refinery.store.model.AnyInterpretation; import tools.refinery.store.model.Interpretation; +import tools.refinery.store.model.Model; import tools.refinery.store.statecoding.StateCodeCalculator; import tools.refinery.store.statecoding.StateCoderResult; import tools.refinery.store.tuple.Tuple; @@ -19,8 +20,9 @@ import tools.refinery.store.tuple.Tuple; import java.util.List; public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator implements StateCodeCalculator { - public LazyNeighbourhoodCalculator(List interpretations, IntSet individuals) { - super(interpretations, individuals); + public LazyNeighbourhoodCalculator(Model model, List interpretations, + IntSet individuals) { + super(model, interpretations, individuals); } public StateCoderResult calculateCodes() { diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java index 1442c915..5e6de53b 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java @@ -8,6 +8,7 @@ package tools.refinery.store.statecoding.neighbourhood; import org.eclipse.collections.api.set.primitive.IntSet; import tools.refinery.store.map.Cursor; import tools.refinery.store.model.Interpretation; +import tools.refinery.store.model.Model; import tools.refinery.store.statecoding.ObjectCode; import tools.refinery.store.statecoding.StateCodeCalculator; import tools.refinery.store.statecoding.StateCoderResult; @@ -21,17 +22,19 @@ public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator imp private ObjectCodeImpl previousObjectCode = new ObjectCodeImpl(); private ObjectCodeImpl nextObjectCode = new ObjectCodeImpl(); - public NeighbourhoodCalculator(List> interpretations, IntSet individuals) { - super(interpretations, individuals); + public NeighbourhoodCalculator(Model model, List> interpretations, IntSet individuals) { + super(model, interpretations, individuals); } public StateCoderResult calculateCodes() { + model.checkCancelled(); previousObjectCode.clear(); nextObjectCode.clear(); initializeWithIndividuals(previousObjectCode); int rounds = 0; do { + model.checkCancelled(); constructNextObjectCodes(previousObjectCode, nextObjectCode); var tempObjectCode = previousObjectCode; previousObjectCode = nextObjectCode; @@ -60,6 +63,7 @@ public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator imp private void constructNextObjectCodes(ObjectCodeImpl previous, ObjectCodeImpl next) { for (var impactValueEntry : this.impactValues.entrySet()) { + model.checkCancelled(); Interpretation interpretation = (Interpretation) impactValueEntry.getKey(); var cursor = interpretation.getAll(); int arity = interpretation.getSymbol().arity(); diff --git a/subprojects/store/src/main/java/tools/refinery/store/util/CancellationToken.java b/subprojects/store/src/main/java/tools/refinery/store/util/CancellationToken.java new file mode 100644 index 00000000..be294013 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/util/CancellationToken.java @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.util; + +@FunctionalInterface +public interface CancellationToken { + CancellationToken NONE = () -> {}; + + void checkCancelled(); +} -- cgit v1.2.3-70-g09d2