aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-08-19 14:39:39 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2023-08-19 14:39:39 +0200
commitb7a46b805bd7fbb3b21a48a035698ab11fadcb7c (patch)
treeab8787dd0699cfb5a0fa1084719a0375e5b0c85b
parentrefactor: move ITC algorithms (diff)
downloadrefinery-b7a46b805bd7fbb3b21a48a035698ab11fadcb7c.tar.gz
refinery-b7a46b805bd7fbb3b21a48a035698ab11fadcb7c.tar.zst
refinery-b7a46b805bd7fbb3b21a48a035698ab11fadcb7c.zip
feat: interruptible VIATRA engine
Reduce server load by introducing a timeout for semantics analysis.
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsService.java129
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java133
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java46
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocument.java13
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentProvider.java9
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java14
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java9
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java5
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java10
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java9
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java9
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java38
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java18
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/CancellationToken.java13
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java156
15 files changed, 379 insertions, 232 deletions
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsService.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsService.java
index 2495430e..39191162 100644
--- a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsService.java
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsService.java
@@ -5,8 +5,6 @@
5 */ 5 */
6package tools.refinery.language.web.semantics; 6package tools.refinery.language.web.semantics;
7 7
8import com.google.gson.JsonArray;
9import com.google.gson.JsonObject;
10import com.google.inject.Inject; 8import com.google.inject.Inject;
11import com.google.inject.Provider; 9import com.google.inject.Provider;
12import com.google.inject.Singleton; 10import com.google.inject.Singleton;
@@ -14,43 +12,29 @@ import org.eclipse.xtext.service.OperationCanceledManager;
14import org.eclipse.xtext.util.CancelIndicator; 12import org.eclipse.xtext.util.CancelIndicator;
15import org.eclipse.xtext.web.server.model.AbstractCachedService; 13import org.eclipse.xtext.web.server.model.AbstractCachedService;
16import org.eclipse.xtext.web.server.model.IXtextWebDocument; 14import org.eclipse.xtext.web.server.model.IXtextWebDocument;
17import org.eclipse.xtext.web.server.model.XtextWebDocument;
18import org.eclipse.xtext.web.server.validation.ValidationService; 15import org.eclipse.xtext.web.server.validation.ValidationService;
19import org.jetbrains.annotations.Nullable; 16import org.jetbrains.annotations.Nullable;
20import org.slf4j.Logger; 17import org.slf4j.Logger;
21import org.slf4j.LoggerFactory; 18import org.slf4j.LoggerFactory;
22import tools.refinery.language.model.problem.Problem; 19import tools.refinery.language.model.problem.Problem;
23import tools.refinery.language.semantics.model.ModelInitializer; 20import tools.refinery.language.web.xtext.server.push.PushWebDocument;
24import tools.refinery.language.semantics.model.SemanticsUtils;
25import tools.refinery.store.model.Model;
26import tools.refinery.store.model.ModelStore;
27import tools.refinery.store.query.viatra.ViatraModelQueryAdapter;
28import tools.refinery.store.reasoning.ReasoningAdapter;
29import tools.refinery.store.reasoning.ReasoningStoreAdapter;
30import tools.refinery.store.reasoning.literal.Concreteness;
31import tools.refinery.store.reasoning.representation.PartialRelation;
32import tools.refinery.store.representation.TruthValue;
33import tools.refinery.store.tuple.Tuple;
34 21
35import java.util.Arrays; 22import java.util.concurrent.*;
36import java.util.List;
37import java.util.TreeMap;
38 23
39@Singleton 24@Singleton
40public class SemanticsService extends AbstractCachedService<SemanticsResult> { 25public class SemanticsService extends AbstractCachedService<SemanticsResult> {
41 private static final Logger LOG = LoggerFactory.getLogger(SemanticsService.class); 26 private static final Logger LOG = LoggerFactory.getLogger(SemanticsService.class);
42 27
43 @Inject 28 @Inject
44 private SemanticsUtils semanticsUtils; 29 private Provider<SemanticsWorker> workerProvider;
45 30
46 @Inject 31 @Inject
47 private ValidationService validationService; 32 private OperationCanceledManager operationCanceledManager;
48 33
49 @Inject 34 @Inject
50 private Provider<ModelInitializer> initializerProvider; 35 private ValidationService validationService;
51 36
52 @Inject 37 private final ExecutorService executorService = Executors.newCachedThreadPool();
53 private OperationCanceledManager operationCanceledManager;
54 38
55 @Override 39 @Override
56 public SemanticsResult compute(IXtextWebDocument doc, CancelIndicator cancelIndicator) { 40 public SemanticsResult compute(IXtextWebDocument doc, CancelIndicator cancelIndicator) {
@@ -58,44 +42,42 @@ public class SemanticsService extends AbstractCachedService<SemanticsResult> {
58 if (LOG.isTraceEnabled()) { 42 if (LOG.isTraceEnabled()) {
59 start = System.currentTimeMillis(); 43 start = System.currentTimeMillis();
60 } 44 }
61 Problem problem = getProblem(doc, cancelIndicator); 45 var problem = getProblem(doc, cancelIndicator);
62 if (problem == null) { 46 if (problem == null) {
63 return null; 47 return null;
64 } 48 }
65 var initializer = initializerProvider.get(); 49 var worker = workerProvider.get();
66 var builder = ModelStore.builder() 50 worker.setProblem(problem,cancelIndicator);
67 .with(ViatraModelQueryAdapter.builder()) 51 var future = executorService.submit(worker);
68 .with(ReasoningAdapter.builder() 52 SemanticsResult result = null;
69 .requiredInterpretations(Concreteness.PARTIAL));
70 operationCanceledManager.checkCanceled(cancelIndicator);
71 try { 53 try {
72 var modelSeed = initializer.createModel(problem, builder); 54 result = future.get(2, TimeUnit.SECONDS);
73 operationCanceledManager.checkCanceled(cancelIndicator); 55 } catch (InterruptedException e) {
74 var nodeTrace = getNodeTrace(initializer); 56 future.cancel(true);
75 operationCanceledManager.checkCanceled(cancelIndicator); 57 LOG.error("Semantics service interrupted", e);
76 var store = builder.build(); 58 Thread.currentThread().interrupt();
77 operationCanceledManager.checkCanceled(cancelIndicator); 59 } catch (ExecutionException e) {
78 var model = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(modelSeed); 60 operationCanceledManager.propagateAsErrorIfCancelException(e.getCause());
79 operationCanceledManager.checkCanceled(cancelIndicator); 61 throw new IllegalStateException(e);
80 var partialInterpretation = getPartialInterpretation(initializer, model, cancelIndicator); 62 } catch (TimeoutException e) {
81 if (LOG.isTraceEnabled()) { 63 future.cancel(true);
82 long end = System.currentTimeMillis(); 64 LOG.trace("Semantics service timeout", e);
83 LOG.trace("Computed semantics for {} ({}) in {}ms", doc.getResourceId(), doc.getStateId(), 65 return new SemanticsErrorResult("Partial interpretation timed out");
84 end - start);
85 }
86 return new SemanticsSuccessResult(nodeTrace, partialInterpretation);
87 } catch (RuntimeException e) {
88 LOG.debug("Error while computing semantics", e);
89 return new SemanticsErrorResult(e.getMessage());
90 } 66 }
67 if (LOG.isTraceEnabled()) {
68 long end = System.currentTimeMillis();
69 LOG.trace("Computed semantics for {} ({}) in {}ms", doc.getResourceId(), doc.getStateId(),
70 end - start);
71 }
72 return result;
91 } 73 }
92 74
93 @Nullable 75 @Nullable
94 private Problem getProblem(IXtextWebDocument doc, CancelIndicator cancelIndicator) { 76 private Problem getProblem(IXtextWebDocument doc, CancelIndicator cancelIndicator) {
95 if (!(doc instanceof XtextWebDocument webDoc)) { 77 if (!(doc instanceof PushWebDocument pushDoc)) {
96 throw new IllegalArgumentException("Unexpected IXtextWebDocument: " + doc); 78 throw new IllegalArgumentException("Unexpected IXtextWebDocument: " + doc);
97 } 79 }
98 var validationResult = webDoc.getCachedServiceResult(validationService, cancelIndicator, true); 80 var validationResult = pushDoc.getCachedServiceResult(validationService, cancelIndicator, true);
99 boolean hasError = validationResult.getIssues().stream() 81 boolean hasError = validationResult.getIssues().stream()
100 .anyMatch(issue -> "error".equals(issue.getSeverity())); 82 .anyMatch(issue -> "error".equals(issue.getSeverity()));
101 if (hasError) { 83 if (hasError) {
@@ -111,53 +93,4 @@ public class SemanticsService extends AbstractCachedService<SemanticsResult> {
111 } 93 }
112 return problem; 94 return problem;
113 } 95 }
114
115 private List<String> getNodeTrace(ModelInitializer initializer) {
116 var nodeTrace = new String[initializer.getNodeCount()];
117 for (var entry : initializer.getNodeTrace().keyValuesView()) {
118 var node = entry.getOne();
119 var index = entry.getTwo();
120 nodeTrace[index] = semanticsUtils.getName(node).orElse(null);
121 }
122 return Arrays.asList(nodeTrace);
123 }
124
125 private JsonObject getPartialInterpretation(ModelInitializer initializer, Model model,
126 CancelIndicator cancelIndicator) {
127 var adapter = model.getAdapter(ReasoningAdapter.class);
128 var json = new JsonObject();
129 for (var entry : initializer.getRelationTrace().entrySet()) {
130 var relation = entry.getKey();
131 var partialSymbol = entry.getValue();
132 var tuples = getTuplesJson(adapter, partialSymbol);
133 var name = semanticsUtils.getName(relation).orElse(partialSymbol.name());
134 json.add(name, tuples);
135 operationCanceledManager.checkCanceled(cancelIndicator);
136 }
137 return json;
138 }
139
140 private static JsonArray getTuplesJson(ReasoningAdapter adapter, PartialRelation partialSymbol) {
141 var interpretation = adapter.getPartialInterpretation(Concreteness.PARTIAL, partialSymbol);
142 var cursor = interpretation.getAll();
143 var map = new TreeMap<Tuple, TruthValue>();
144 while (cursor.move()) {
145 map.put(cursor.getKey(), cursor.getValue());
146 }
147 var tuples = new JsonArray();
148 for (var entry : map.entrySet()) {
149 tuples.add(toArray(entry.getKey(), entry.getValue()));
150 }
151 return tuples;
152 }
153
154 private static JsonArray toArray(Tuple tuple, TruthValue value) {
155 int arity = tuple.getSize();
156 var json = new JsonArray(arity + 1);
157 for (int i = 0; i < arity; i++) {
158 json.add(tuple.get(i));
159 }
160 json.add(value.toString());
161 return json;
162 }
163} 96}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java
new file mode 100644
index 00000000..25589260
--- /dev/null
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java
@@ -0,0 +1,133 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.web.semantics;
7
8import com.google.gson.JsonArray;
9import com.google.gson.JsonObject;
10import com.google.inject.Inject;
11import org.eclipse.xtext.service.OperationCanceledManager;
12import org.eclipse.xtext.util.CancelIndicator;
13import org.slf4j.Logger;
14import org.slf4j.LoggerFactory;
15import tools.refinery.language.model.problem.Problem;
16import tools.refinery.language.semantics.model.ModelInitializer;
17import tools.refinery.language.semantics.model.SemanticsUtils;
18import tools.refinery.store.model.Model;
19import tools.refinery.store.model.ModelStore;
20import tools.refinery.store.query.viatra.ViatraModelQueryAdapter;
21import tools.refinery.store.reasoning.ReasoningAdapter;
22import tools.refinery.store.reasoning.ReasoningStoreAdapter;
23import tools.refinery.store.reasoning.literal.Concreteness;
24import tools.refinery.store.reasoning.representation.PartialRelation;
25import tools.refinery.store.representation.TruthValue;
26import tools.refinery.store.tuple.Tuple;
27import tools.refinery.viatra.runtime.CancellationToken;
28
29import java.util.Arrays;
30import java.util.List;
31import java.util.TreeMap;
32import java.util.concurrent.Callable;
33
34class SemanticsWorker implements Callable<SemanticsResult> {
35 private static final Logger LOG = LoggerFactory.getLogger(SemanticsWorker.class);
36
37 @Inject
38 private SemanticsUtils semanticsUtils;
39
40 @Inject
41 private OperationCanceledManager operationCanceledManager;
42
43 @Inject
44 private ModelInitializer initializer;
45
46 private Problem problem;
47
48 private CancellationToken cancellationToken;
49
50 public void setProblem(Problem problem, CancelIndicator parentIndicator) {
51 this.problem = problem;
52 cancellationToken = () -> {
53 if (Thread.interrupted() || parentIndicator.isCanceled()) {
54 operationCanceledManager.throwOperationCanceledException();
55 }
56 };
57 }
58
59 @Override
60 public SemanticsResult call() {
61 var builder = ModelStore.builder()
62 .with(ViatraModelQueryAdapter.builder()
63 .cancellationToken(cancellationToken))
64 .with(ReasoningAdapter.builder()
65 .requiredInterpretations(Concreteness.PARTIAL));
66 cancellationToken.checkCancelled();
67 try {
68 var modelSeed = initializer.createModel(problem, builder);
69 cancellationToken.checkCancelled();
70 var nodeTrace = getNodeTrace(initializer);
71 cancellationToken.checkCancelled();
72 var store = builder.build();
73 cancellationToken.checkCancelled();
74 var model = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(modelSeed);
75 cancellationToken.checkCancelled();
76 var partialInterpretation = getPartialInterpretation(initializer, model);
77
78 return new SemanticsSuccessResult(nodeTrace, partialInterpretation);
79 } catch (RuntimeException e) {
80 LOG.debug("Error while computing semantics", e);
81 var message = e.getMessage();
82 return new SemanticsErrorResult(message == null ? "Partial interpretation error" : e.getMessage());
83 }
84 }
85
86 private List<String> getNodeTrace(ModelInitializer initializer) {
87 var nodeTrace = new String[initializer.getNodeCount()];
88 for (var entry : initializer.getNodeTrace().keyValuesView()) {
89 var node = entry.getOne();
90 var index = entry.getTwo();
91 nodeTrace[index] = semanticsUtils.getName(node).orElse(null);
92 }
93 return Arrays.asList(nodeTrace);
94 }
95
96 private JsonObject getPartialInterpretation(ModelInitializer initializer, Model model) {
97 var adapter = model.getAdapter(ReasoningAdapter.class);
98 var json = new JsonObject();
99 for (var entry : initializer.getRelationTrace().entrySet()) {
100 var relation = entry.getKey();
101 var partialSymbol = entry.getValue();
102 var tuples = getTuplesJson(adapter, partialSymbol);
103 var name = semanticsUtils.getName(relation).orElse(partialSymbol.name());
104 json.add(name, tuples);
105 cancellationToken.checkCancelled();
106 }
107 return json;
108 }
109
110 private static JsonArray getTuplesJson(ReasoningAdapter adapter, PartialRelation partialSymbol) {
111 var interpretation = adapter.getPartialInterpretation(Concreteness.PARTIAL, partialSymbol);
112 var cursor = interpretation.getAll();
113 var map = new TreeMap<Tuple, TruthValue>();
114 while (cursor.move()) {
115 map.put(cursor.getKey(), cursor.getValue());
116 }
117 var tuples = new JsonArray();
118 for (var entry : map.entrySet()) {
119 tuples.add(toArray(entry.getKey(), entry.getValue()));
120 }
121 return tuples;
122 }
123
124 private static JsonArray toArray(Tuple tuple, TruthValue value) {
125 int arity = tuple.getSize();
126 var json = new JsonArray(arity + 1);
127 for (int i = 0; i < arity; i++) {
128 json.add(tuple.get(i));
129 }
130 json.add(value.toString());
131 return json;
132 }
133}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java
index 2c0e9329..74456604 100644
--- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java
@@ -42,6 +42,8 @@ public class TransactionExecutor implements IDisposable, PrecomputationListener
42 42
43 private final List<XtextWebPushMessage> pendingPushMessages = new ArrayList<>(); 43 private final List<XtextWebPushMessage> pendingPushMessages = new ArrayList<>();
44 44
45 private volatile boolean disposed;
46
45 public TransactionExecutor(ISession session, IResourceServiceProvider.Registry resourceServiceProviderRegistry) { 47 public TransactionExecutor(ISession session, IResourceServiceProvider.Registry resourceServiceProviderRegistry) {
46 this.session = session; 48 this.session = session;
47 this.resourceServiceProviderRegistry = resourceServiceProviderRegistry; 49 this.resourceServiceProviderRegistry = resourceServiceProviderRegistry;
@@ -52,10 +54,13 @@ public class TransactionExecutor implements IDisposable, PrecomputationListener
52 } 54 }
53 55
54 public void handleRequest(XtextWebRequest request) throws ResponseHandlerException { 56 public void handleRequest(XtextWebRequest request) throws ResponseHandlerException {
57 if (disposed) {
58 return;
59 }
55 var serviceContext = new SimpleServiceContext(session, request.getRequestData()); 60 var serviceContext = new SimpleServiceContext(session, request.getRequestData());
56 var ping = serviceContext.getParameter("ping"); 61 var ping = serviceContext.getParameter("ping");
57 if (ping != null) { 62 if (ping != null) {
58 responseHandler.onResponse(new XtextWebOkResponse(request, new PongResult(ping))); 63 onResponse(new XtextWebOkResponse(request, new PongResult(ping)));
59 return; 64 return;
60 } 65 }
61 synchronized (callPendingLock) { 66 synchronized (callPendingLock) {
@@ -72,23 +77,36 @@ public class TransactionExecutor implements IDisposable, PrecomputationListener
72 var serviceDispatcher = injector.getInstance(XtextServiceDispatcher.class); 77 var serviceDispatcher = injector.getInstance(XtextServiceDispatcher.class);
73 var service = serviceDispatcher.getService(new SubscribingServiceContext(serviceContext, this)); 78 var service = serviceDispatcher.getService(new SubscribingServiceContext(serviceContext, this));
74 var serviceResult = service.getService().apply(); 79 var serviceResult = service.getService().apply();
75 responseHandler.onResponse(new XtextWebOkResponse(request, serviceResult)); 80 onResponse(new XtextWebOkResponse(request, serviceResult));
76 } catch (InvalidRequestException e) { 81 } catch (InvalidRequestException e) {
77 responseHandler.onResponse(new XtextWebErrorResponse(request, XtextWebErrorKind.REQUEST_ERROR, e)); 82 onResponse(new XtextWebErrorResponse(request, XtextWebErrorKind.REQUEST_ERROR, e));
78 } catch (RuntimeException e) { 83 } catch (RuntimeException e) {
79 responseHandler.onResponse(new XtextWebErrorResponse(request, XtextWebErrorKind.SERVER_ERROR, e)); 84 onResponse(new XtextWebErrorResponse(request, XtextWebErrorKind.SERVER_ERROR, e));
80 } finally { 85 } finally {
81 synchronized (callPendingLock) { 86 flushPendingPushMessages();
82 for (var message : pendingPushMessages) { 87 }
83 try { 88 }
84 responseHandler.onResponse(message); 89
85 } catch (ResponseHandlerException | RuntimeException e) { 90 private void onResponse(XtextWebResponse response) throws ResponseHandlerException {
86 LOG.error("Error while flushing push message", e); 91 if (!disposed) {
87 } 92 responseHandler.onResponse(response);
93 }
94 }
95
96 private void flushPendingPushMessages() {
97 synchronized (callPendingLock) {
98 for (var message : pendingPushMessages) {
99 if (disposed) {
100 return;
101 }
102 try {
103 responseHandler.onResponse(message);
104 } catch (ResponseHandlerException | RuntimeException e) {
105 LOG.error("Error while flushing push message", e);
88 } 106 }
89 pendingPushMessages.clear();
90 callPending = false;
91 } 107 }
108 pendingPushMessages.clear();
109 callPending = false;
92 } 110 }
93 } 111 }
94 112
@@ -164,10 +182,12 @@ public class TransactionExecutor implements IDisposable, PrecomputationListener
164 182
165 @Override 183 @Override
166 public void dispose() { 184 public void dispose() {
185 disposed = true;
167 for (var subscription : subscriptions.values()) { 186 for (var subscription : subscriptions.values()) {
168 var document = subscription.get(); 187 var document = subscription.get();
169 if (document != null) { 188 if (document != null) {
170 document.removePrecomputationListener(this); 189 document.removePrecomputationListener(this);
190 document.cancelBackgroundWork();
171 } 191 }
172 } 192 }
173 } 193 }
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocument.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocument.java
index dfbd4878..1542c694 100644
--- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocument.java
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocument.java
@@ -27,11 +27,11 @@ public class PushWebDocument extends XtextWebDocument {
27 27
28 private final Map<Class<?>, IServiceResult> precomputedServices = new HashMap<>(); 28 private final Map<Class<?>, IServiceResult> precomputedServices = new HashMap<>();
29 29
30 private final DocumentSynchronizer synchronizer;
31
30 public PushWebDocument(String resourceId, DocumentSynchronizer synchronizer) { 32 public PushWebDocument(String resourceId, DocumentSynchronizer synchronizer) {
31 super(resourceId, synchronizer); 33 super(resourceId, synchronizer);
32 if (resourceId == null) { 34 this.synchronizer = synchronizer;
33 throw new IllegalArgumentException("resourceId must not be null");
34 }
35 } 35 }
36 36
37 public void addPrecomputationListener(PrecomputationListener listener) { 37 public void addPrecomputationListener(PrecomputationListener listener) {
@@ -63,6 +63,9 @@ public class PushWebDocument extends XtextWebDocument {
63 63
64 private <T extends IServiceResult> void notifyPrecomputationListeners(String serviceName, T result) { 64 private <T extends IServiceResult> void notifyPrecomputationListeners(String serviceName, T result) {
65 var resourceId = getResourceId(); 65 var resourceId = getResourceId();
66 if (resourceId == null) {
67 return;
68 }
66 var stateId = getStateId(); 69 var stateId = getStateId();
67 List<PrecomputationListener> copyOfListeners; 70 List<PrecomputationListener> copyOfListeners;
68 synchronized (precomputationListeners) { 71 synchronized (precomputationListeners) {
@@ -83,4 +86,8 @@ public class PushWebDocument extends XtextWebDocument {
83 } 86 }
84 } 87 }
85 } 88 }
89
90 public void cancelBackgroundWork() {
91 synchronizer.setCanceled(true);
92 }
86} 93}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentProvider.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentProvider.java
index b6f4fb43..ec6204ef 100644
--- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentProvider.java
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentProvider.java
@@ -27,12 +27,7 @@ public class PushWebDocumentProvider implements IWebDocumentProvider {
27 27
28 @Override 28 @Override
29 public XtextWebDocument get(String resourceId, IServiceContext serviceContext) { 29 public XtextWebDocument get(String resourceId, IServiceContext serviceContext) {
30 if (resourceId == null) { 30 return new PushWebDocument(resourceId,
31 return new XtextWebDocument(null, synchronizerProvider.get()); 31 serviceContext.getSession().get(DocumentSynchronizer.class, () -> this.synchronizerProvider.get()));
32 } else {
33 // We only need to send push messages if a resourceId is specified.
34 return new PushWebDocument(resourceId,
35 serviceContext.getSession().get(DocumentSynchronizer.class, () -> this.synchronizerProvider.get()));
36 }
37 } 32 }
38} 33}
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java
index 043d318c..923fecd6 100644
--- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java
@@ -70,10 +70,11 @@ public class XtextWebSocket implements ResponseHandler {
70 70
71 @OnWebSocketError 71 @OnWebSocketError
72 public void onError(Throwable error) { 72 public void onError(Throwable error) {
73 executor.dispose();
73 if (webSocketSession == null) { 74 if (webSocketSession == null) {
74 return; 75 return;
75 } 76 }
76 LOG.error("Internal websocket error in connection from" + webSocketSession.getRemoteSocketAddress(), error); 77 LOG.error("Internal websocket error in connection from " + webSocketSession.getRemoteSocketAddress(), error);
77 } 78 }
78 79
79 @OnWebSocketMessage 80 @OnWebSocketMessage
@@ -86,14 +87,18 @@ public class XtextWebSocket implements ResponseHandler {
86 try { 87 try {
87 request = gson.fromJson(reader, XtextWebRequest.class); 88 request = gson.fromJson(reader, XtextWebRequest.class);
88 } catch (JsonIOException e) { 89 } catch (JsonIOException e) {
89 LOG.error("Cannot read from websocket from" + webSocketSession.getRemoteSocketAddress(), e); 90 LOG.error("Cannot read from websocket from " + webSocketSession.getRemoteSocketAddress(), e);
90 if (webSocketSession.isOpen()) { 91 if (webSocketSession.isOpen()) {
92 executor.dispose();
91 webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot read payload", Callback.NOOP); 93 webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot read payload", Callback.NOOP);
92 } 94 }
93 return; 95 return;
94 } catch (JsonParseException e) { 96 } catch (JsonParseException e) {
95 LOG.warn("Malformed websocket request from" + webSocketSession.getRemoteSocketAddress(), e); 97 LOG.warn("Malformed websocket request from " + webSocketSession.getRemoteSocketAddress(), e);
96 webSocketSession.close(XtextStatusCode.INVALID_JSON, "Invalid JSON payload", Callback.NOOP); 98 if (webSocketSession.isOpen()) {
99 executor.dispose();
100 webSocketSession.close(XtextStatusCode.INVALID_JSON, "Invalid JSON payload", Callback.NOOP);
101 }
97 return; 102 return;
98 } 103 }
99 try { 104 try {
@@ -101,6 +106,7 @@ public class XtextWebSocket implements ResponseHandler {
101 } catch (ResponseHandlerException e) { 106 } catch (ResponseHandlerException e) {
102 LOG.warn("Cannot write websocket response", e); 107 LOG.warn("Cannot write websocket response", e);
103 if (webSocketSession.isOpen()) { 108 if (webSocketSession.isOpen()) {
109 executor.dispose();
104 webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot write response", Callback.NOOP); 110 webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot write response", Callback.NOOP);
105 } 111 }
106 } 112 }
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java
index d31325f1..6b3be115 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java
@@ -5,14 +5,15 @@
5 */ 5 */
6package tools.refinery.store.query.viatra; 6package tools.refinery.store.query.viatra;
7 7
8import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions;
9import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
10import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
11import tools.refinery.store.model.ModelStore; 8import tools.refinery.store.model.ModelStore;
12import tools.refinery.store.query.ModelQueryBuilder; 9import tools.refinery.store.query.ModelQueryBuilder;
13import tools.refinery.store.query.dnf.AnyQuery; 10import tools.refinery.store.query.dnf.AnyQuery;
14import tools.refinery.store.query.dnf.Dnf; 11import tools.refinery.store.query.dnf.Dnf;
15import tools.refinery.store.query.rewriter.DnfRewriter; 12import tools.refinery.store.query.rewriter.DnfRewriter;
13import tools.refinery.viatra.runtime.CancellationToken;
14import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions;
15import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
16import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
16 17
17import java.util.Collection; 18import java.util.Collection;
18import java.util.function.Function; 19import java.util.function.Function;
@@ -29,6 +30,8 @@ public interface ViatraModelQueryBuilder extends ModelQueryBuilder {
29 30
30 ViatraModelQueryBuilder searchBackend(IQueryBackendFactory queryBackendFactory); 31 ViatraModelQueryBuilder searchBackend(IQueryBackendFactory queryBackendFactory);
31 32
33 ViatraModelQueryBuilder cancellationToken(CancellationToken cancellationToken);
34
32 @Override 35 @Override
33 default ViatraModelQueryBuilder queries(AnyQuery... queries) { 36 default ViatraModelQueryBuilder queries(AnyQuery... queries) {
34 ModelQueryBuilder.super.queries(queries); 37 ModelQueryBuilder.super.queries(queries);
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java
index f1209f69..ad754988 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java
@@ -18,6 +18,7 @@ import tools.refinery.store.query.viatra.ViatraModelQueryAdapter;
18import tools.refinery.store.query.viatra.internal.matcher.FunctionalViatraMatcher; 18import tools.refinery.store.query.viatra.internal.matcher.FunctionalViatraMatcher;
19import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher; 19import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher;
20import tools.refinery.store.query.viatra.internal.matcher.RelationalViatraMatcher; 20import tools.refinery.store.query.viatra.internal.matcher.RelationalViatraMatcher;
21import tools.refinery.viatra.runtime.CancellationToken;
21import tools.refinery.viatra.runtime.api.AdvancedViatraQueryEngine; 22import tools.refinery.viatra.runtime.api.AdvancedViatraQueryEngine;
22import tools.refinery.viatra.runtime.api.GenericQueryGroup; 23import tools.refinery.viatra.runtime.api.GenericQueryGroup;
23import tools.refinery.viatra.runtime.api.IQuerySpecification; 24import tools.refinery.viatra.runtime.api.IQuerySpecification;
@@ -81,6 +82,10 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter, Mod
81 return storeAdapter; 82 return storeAdapter;
82 } 83 }
83 84
85 public CancellationToken getCancellationToken() {
86 return storeAdapter.getCancellationToken();
87 }
88
84 @Override 89 @Override
85 public <T> ResultSet<T> getResultSet(Query<T> query) { 90 public <T> ResultSet<T> getResultSet(Query<T> query) {
86 var canonicalQuery = storeAdapter.getCanonicalQuery(query); 91 var canonicalQuery = storeAdapter.getCanonicalQuery(query);
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java
index cfdc43ba..bb0630f3 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java
@@ -17,6 +17,7 @@ import tools.refinery.store.query.viatra.ViatraModelQueryBuilder;
17import tools.refinery.store.query.viatra.internal.localsearch.FlatCostFunction; 17import tools.refinery.store.query.viatra.internal.localsearch.FlatCostFunction;
18import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher; 18import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher;
19import tools.refinery.store.query.viatra.internal.pquery.Dnf2PQuery; 19import tools.refinery.store.query.viatra.internal.pquery.Dnf2PQuery;
20import tools.refinery.viatra.runtime.CancellationToken;
20import tools.refinery.viatra.runtime.api.IQuerySpecification; 21import tools.refinery.viatra.runtime.api.IQuerySpecification;
21import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions; 22import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions;
22import tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchGenericBackendFactory; 23import tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchGenericBackendFactory;
@@ -35,6 +36,7 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder<Via
35 // Use a cost function that ignores the initial (empty) model but allows higher arity input keys. 36 // Use a cost function that ignores the initial (empty) model but allows higher arity input keys.
36 LocalSearchHintOptions.PLANNER_COST_FUNCTION, new FlatCostFunction() 37 LocalSearchHintOptions.PLANNER_COST_FUNCTION, new FlatCostFunction()
37 ), (IQueryBackendFactory) null); 38 ), (IQueryBackendFactory) null);
39 private CancellationToken cancellationToken = CancellationToken.NONE;
38 private final CompositeRewriter rewriter; 40 private final CompositeRewriter rewriter;
39 private final Dnf2PQuery dnf2PQuery = new Dnf2PQuery(); 41 private final Dnf2PQuery dnf2PQuery = new Dnf2PQuery();
40 private final Set<AnyQuery> queries = new LinkedHashSet<>(); 42 private final Set<AnyQuery> queries = new LinkedHashSet<>();
@@ -85,6 +87,12 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder<Via
85 } 87 }
86 88
87 @Override 89 @Override
90 public ViatraModelQueryBuilder cancellationToken(CancellationToken cancellationToken) {
91 this.cancellationToken = cancellationToken;
92 return this;
93 }
94
95 @Override
88 public ViatraModelQueryBuilder queries(Collection<? extends AnyQuery> queries) { 96 public ViatraModelQueryBuilder queries(Collection<? extends AnyQuery> queries) {
89 checkNotConfigured(); 97 checkNotConfigured();
90 this.queries.addAll(queries); 98 this.queries.addAll(queries);
@@ -136,7 +144,7 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder<Via
136 validateSymbols(store); 144 validateSymbols(store);
137 return new ViatraModelQueryStoreAdapterImpl(store, buildEngineOptions(), dnf2PQuery.getSymbolViews(), 145 return new ViatraModelQueryStoreAdapterImpl(store, buildEngineOptions(), dnf2PQuery.getSymbolViews(),
138 Collections.unmodifiableMap(canonicalQueryMap), Collections.unmodifiableMap(querySpecifications), 146 Collections.unmodifiableMap(canonicalQueryMap), Collections.unmodifiableMap(querySpecifications),
139 Collections.unmodifiableSet(vacuousQueries)); 147 Collections.unmodifiableSet(vacuousQueries), cancellationToken);
140 } 148 }
141 149
142 private ViatraQueryEngineOptions buildEngineOptions() { 150 private ViatraQueryEngineOptions buildEngineOptions() {
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java
index 5407ad01..f32e1cc6 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java
@@ -5,6 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal; 6package tools.refinery.store.query.viatra.internal;
7 7
8import tools.refinery.viatra.runtime.CancellationToken;
8import tools.refinery.viatra.runtime.api.IQuerySpecification; 9import tools.refinery.viatra.runtime.api.IQuerySpecification;
9import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions; 10import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions;
10import tools.refinery.viatra.runtime.matchers.context.IInputKey; 11import tools.refinery.viatra.runtime.matchers.context.IInputKey;
@@ -26,18 +27,20 @@ public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAd
26 private final Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications; 27 private final Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications;
27 private final Set<AnyQuery> vacuousQueries; 28 private final Set<AnyQuery> vacuousQueries;
28 private final Set<AnyQuery> allQueries; 29 private final Set<AnyQuery> allQueries;
30 private final CancellationToken cancellationToken;
29 31
30 ViatraModelQueryStoreAdapterImpl(ModelStore store, ViatraQueryEngineOptions engineOptions, 32 ViatraModelQueryStoreAdapterImpl(ModelStore store, ViatraQueryEngineOptions engineOptions,
31 Map<AnySymbolView, IInputKey> inputKeys, 33 Map<AnySymbolView, IInputKey> inputKeys,
32 Map<AnyQuery, AnyQuery> canonicalQueryMap, 34 Map<AnyQuery, AnyQuery> canonicalQueryMap,
33 Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications, 35 Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications,
34 Set<AnyQuery> vacuousQueries) { 36 Set<AnyQuery> vacuousQueries, CancellationToken cancellationToken) {
35 this.store = store; 37 this.store = store;
36 this.engineOptions = engineOptions; 38 this.engineOptions = engineOptions;
37 this.inputKeys = inputKeys; 39 this.inputKeys = inputKeys;
38 this.canonicalQueryMap = canonicalQueryMap; 40 this.canonicalQueryMap = canonicalQueryMap;
39 this.querySpecifications = querySpecifications; 41 this.querySpecifications = querySpecifications;
40 this.vacuousQueries = vacuousQueries; 42 this.vacuousQueries = vacuousQueries;
43 this.cancellationToken = cancellationToken;
41 var mutableAllQueries = new LinkedHashSet<AnyQuery>(querySpecifications.size() + vacuousQueries.size()); 44 var mutableAllQueries = new LinkedHashSet<AnyQuery>(querySpecifications.size() + vacuousQueries.size());
42 mutableAllQueries.addAll(querySpecifications.keySet()); 45 mutableAllQueries.addAll(querySpecifications.keySet());
43 mutableAllQueries.addAll(vacuousQueries); 46 mutableAllQueries.addAll(vacuousQueries);
@@ -62,6 +65,10 @@ public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAd
62 return allQueries; 65 return allQueries;
63 } 66 }
64 67
68 public CancellationToken getCancellationToken() {
69 return cancellationToken;
70 }
71
65 @Override 72 @Override
66 public <T> Query<T> getCanonicalQuery(Query<T> query) { 73 public <T> Query<T> getCanonicalQuery(Query<T> query) {
67 // We know that canonical forms of queries do not change output types. 74 // We know that canonical forms of queries do not change output types.
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java
index d1fa5239..dadab5dd 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java
@@ -5,6 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.context; 6package tools.refinery.store.query.viatra.internal.context;
7 7
8import tools.refinery.viatra.runtime.CancellationToken;
8import tools.refinery.viatra.runtime.matchers.context.*; 9import tools.refinery.viatra.runtime.matchers.context.*;
9import tools.refinery.viatra.runtime.matchers.tuple.ITuple; 10import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
10import tools.refinery.viatra.runtime.matchers.tuple.Tuple; 11import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
@@ -32,10 +33,13 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
32 33
33 private final Model model; 34 private final Model model;
34 35
36 private final CancellationToken cancellationToken;
37
35 RelationalRuntimeContext(ViatraModelQueryAdapterImpl adapter) { 38 RelationalRuntimeContext(ViatraModelQueryAdapterImpl adapter) {
36 model = adapter.getModel(); 39 model = adapter.getModel();
37 metaContext = new RelationalQueryMetaContext(adapter.getStoreAdapter().getInputKeys()); 40 metaContext = new RelationalQueryMetaContext(adapter.getStoreAdapter().getInputKeys());
38 modelUpdateListener = new ModelUpdateListener(adapter); 41 modelUpdateListener = new ModelUpdateListener(adapter);
42 cancellationToken = adapter.getCancellationToken();
39 } 43 }
40 44
41 @Override 45 @Override
@@ -192,4 +196,9 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
192 public void executeAfterTraversal(Runnable runnable) { 196 public void executeAfterTraversal(Runnable runnable) {
193 runnable.run(); 197 runnable.run();
194 } 198 }
199
200 @Override
201 public CancellationToken getCancellationToken() {
202 return cancellationToken;
203 }
195} 204}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java
index 16e290fd..79e0526d 100644
--- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java
@@ -1,26 +1,17 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro 2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools>
3 * This program and the accompanying materials are made available under the 4 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at 5 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html. 6 * http://www.eclipse.org/legal/epl-v20.html.
6 * 7 *
7 * SPDX-License-Identifier: EPL-2.0 8 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/ 9 *******************************************************************************/
9 10
10package tools.refinery.viatra.runtime.rete.network; 11package tools.refinery.viatra.runtime.rete.network;
11 12
12import java.util.ArrayDeque;
13import java.util.ArrayList;
14import java.util.Collection;
15import java.util.Deque;
16import java.util.HashSet;
17import java.util.LinkedHashSet;
18import java.util.LinkedList;
19import java.util.Map;
20import java.util.Set;
21import java.util.function.Function;
22
23import org.apache.log4j.Logger; 13import org.apache.log4j.Logger;
14import tools.refinery.viatra.runtime.CancellationToken;
24import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; 15import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
25import tools.refinery.viatra.runtime.matchers.tuple.Tuple; 16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
26import tools.refinery.viatra.runtime.matchers.util.Clearable; 17import tools.refinery.viatra.runtime.matchers.util.Clearable;
@@ -42,6 +33,9 @@ import tools.refinery.viatra.runtime.rete.single.SingleInputNode;
42import tools.refinery.viatra.runtime.rete.single.TrimmerNode; 33import tools.refinery.viatra.runtime.rete.single.TrimmerNode;
43import tools.refinery.viatra.runtime.rete.util.Options; 34import tools.refinery.viatra.runtime.rete.util.Options;
44 35
36import java.util.*;
37import java.util.function.Function;
38
45/** 39/**
46 * @author Gabor Bergmann 40 * @author Gabor Bergmann
47 * 41 *
@@ -79,6 +73,8 @@ public final class ReteContainer {
79 73
80 protected final TimelyConfiguration timelyConfiguration; 74 protected final TimelyConfiguration timelyConfiguration;
81 75
76 private final CancellationToken cancellationToken;
77
82 /** 78 /**
83 * @param threaded 79 * @param threaded
84 * false if operating in a single-threaded environment 80 * false if operating in a single-threaded environment
@@ -88,6 +84,7 @@ public final class ReteContainer {
88 this.network = network; 84 this.network = network;
89 this.backendContext = network.getEngine().getBackendContext(); 85 this.backendContext = network.getEngine().getBackendContext();
90 this.timelyConfiguration = network.getEngine().getTimelyConfiguration(); 86 this.timelyConfiguration = network.getEngine().getTimelyConfiguration();
87 cancellationToken = backendContext.getRuntimeContext().getCancellationToken();
91 88
92 this.delayedCommandQueue = new LinkedHashSet<DelayedCommand>(); 89 this.delayedCommandQueue = new LinkedHashSet<DelayedCommand>();
93 this.delayedCommandBuffer = new LinkedHashSet<DelayedCommand>(); 90 this.delayedCommandBuffer = new LinkedHashSet<DelayedCommand>();
@@ -395,10 +392,10 @@ public final class ReteContainer {
395 392
396 /** 393 /**
397 * Retrieves a safe copy of the contents of a supplier. 394 * Retrieves a safe copy of the contents of a supplier.
398 * 395 *
399 * <p> Note that there may be multiple copies of a Tuple in case of a {@link TrimmerNode}, so the result is not always a set. 396 * <p> Note that there may be multiple copies of a Tuple in case of a {@link TrimmerNode}, so the result is not always a set.
400 * 397 *
401 * @param flush if true, a flush is performed before pulling the contents 398 * @param flush if true, a flush is performed before pulling the contents
402 * @since 2.3 399 * @since 2.3
403 */ 400 */
404 public Collection<Tuple> pullContents(final Supplier supplier, final boolean flush) { 401 public Collection<Tuple> pullContents(final Supplier supplier, final boolean flush) {
@@ -424,7 +421,7 @@ public final class ReteContainer {
424 421
425 /** 422 /**
426 * Retrieves the contents of a SingleInputNode's parentage. 423 * Retrieves the contents of a SingleInputNode's parentage.
427 * 424 *
428 * @since 2.3 425 * @since 2.3
429 */ 426 */
430 public Collection<Tuple> pullPropagatedContents(final SingleInputNode supplier, final boolean flush) { 427 public Collection<Tuple> pullPropagatedContents(final SingleInputNode supplier, final boolean flush) {
@@ -438,7 +435,7 @@ public final class ReteContainer {
438 435
439 /** 436 /**
440 * Retrieves the timestamp-aware contents of a SingleInputNode's parentage. 437 * Retrieves the timestamp-aware contents of a SingleInputNode's parentage.
441 * 438 *
442 * @since 2.3 439 * @since 2.3
443 */ 440 */
444 public Map<Tuple, Timeline<Timestamp>> pullPropagatedContentsWithTimestamp(final SingleInputNode supplier, 441 public Map<Tuple, Timeline<Timestamp>> pullPropagatedContentsWithTimestamp(final SingleInputNode supplier,
@@ -541,7 +538,7 @@ public final class ReteContainer {
541 538
542 /** 539 /**
543 * Sends out all pending messages to their receivers. The delivery is governed by the communication tracker. 540 * Sends out all pending messages to their receivers. The delivery is governed by the communication tracker.
544 * 541 *
545 * @since 1.6 542 * @since 1.6
546 */ 543 */
547 public void deliverMessagesSingleThreaded() { 544 public void deliverMessagesSingleThreaded() {
@@ -620,7 +617,7 @@ public final class ReteContainer {
620 617
621 /** 618 /**
622 * Returns an addressed node at this container. 619 * Returns an addressed node at this container.
623 * 620 *
624 * @pre: address.container == this, e.g. address MUST be local 621 * @pre: address.container == this, e.g. address MUST be local
625 * @throws IllegalArgumentException 622 * @throws IllegalArgumentException
626 * if address is non-local 623 * if address is non-local
@@ -726,4 +723,7 @@ public final class ReteContainer {
726 return network.getInputConnector(); 723 return network.getInputConnector();
727 } 724 }
728 725
726 public void checkCancelled() {
727 cancellationToken.checkCancelled();
728 }
729} 729}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java
index e7ec36dc..7dc7c4bc 100644
--- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java
@@ -1,19 +1,15 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro 2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools>
3 * This program and the accompanying materials are made available under the 4 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at 5 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html. 6 * http://www.eclipse.org/legal/epl-v20.html.
6 * 7 *
7 * SPDX-License-Identifier: EPL-2.0 8 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/ 9 *******************************************************************************/
9 10
10package tools.refinery.viatra.runtime.rete.network; 11package tools.refinery.viatra.runtime.rete.network;
11 12
12import java.util.Collection;
13import java.util.HashSet;
14import java.util.List;
15import java.util.Set;
16
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple; 13import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; 14import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
19import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; 15import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
@@ -24,11 +20,16 @@ import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
24import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; 20import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
25import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; 21import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
26 22
23import java.util.Collection;
24import java.util.HashSet;
25import java.util.List;
26import java.util.Set;
27
27/** 28/**
28 * Base implementation for a supplier node. 29 * Base implementation for a supplier node.
29 * 30 *
30 * @author Gabor Bergmann 31 * @author Gabor Bergmann
31 * 32 *
32 */ 33 */
33public abstract class StandardNode extends BaseNode implements Supplier, NetworkStructureChangeSensitiveNode { 34public abstract class StandardNode extends BaseNode implements Supplier, NetworkStructureChangeSensitiveNode {
34 protected final List<Receiver> children = CollectionsFactory.createObserverList(); 35 protected final List<Receiver> children = CollectionsFactory.createObserverList();
@@ -45,6 +46,7 @@ public abstract class StandardNode extends BaseNode implements Supplier, Network
45 * @since 2.4 46 * @since 2.4
46 */ 47 */
47 protected void propagateUpdate(final Direction direction, final Tuple updateElement, final Timestamp timestamp) { 48 protected void propagateUpdate(final Direction direction, final Tuple updateElement, final Timestamp timestamp) {
49 reteContainer.checkCancelled();
48 for (final Mailbox childMailbox : childMailboxes) { 50 for (final Mailbox childMailbox : childMailboxes) {
49 childMailbox.postMessage(direction, updateElement, timestamp); 51 childMailbox.postMessage(direction, updateElement, timestamp);
50 } 52 }
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/CancellationToken.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/CancellationToken.java
new file mode 100644
index 00000000..a2ae41e3
--- /dev/null
+++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/CancellationToken.java
@@ -0,0 +1,13 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.viatra.runtime;
7
8@FunctionalInterface
9public interface CancellationToken {
10 CancellationToken NONE = () -> {};
11
12 void checkCancelled();
13}
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java
index c2e90614..61359c1b 100644
--- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java
+++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java
@@ -1,82 +1,84 @@
1/******************************************************************************* 1/*******************************************************************************
2 * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro 2 * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools>
3 * This program and the accompanying materials are made available under the 4 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at 5 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html. 6 * http://www.eclipse.org/legal/epl-v20.html.
6 * 7 *
7 * SPDX-License-Identifier: EPL-2.0 8 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/ 9 *******************************************************************************/
9package tools.refinery.viatra.runtime.matchers.context; 10package tools.refinery.viatra.runtime.matchers.context;
10 11
11import java.lang.reflect.InvocationTargetException; 12import tools.refinery.viatra.runtime.CancellationToken;
12import java.util.Optional;
13import java.util.concurrent.Callable;
14
15import tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper; 13import tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper;
16import tools.refinery.viatra.runtime.matchers.tuple.ITuple; 14import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple; 15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; 16import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
19import tools.refinery.viatra.runtime.matchers.util.Accuracy; 17import tools.refinery.viatra.runtime.matchers.util.Accuracy;
20 18
19import java.lang.reflect.InvocationTargetException;
20import java.util.Optional;
21import java.util.concurrent.Callable;
22
21/** 23/**
22 * Provides instance model information (relations corresponding to input keys) to query evaluator backends at runtime. 24 * Provides instance model information (relations corresponding to input keys) to query evaluator backends at runtime.
23 * Implementors shall extend {@link AbstractQueryRuntimeContext} instead directly this interface. 25 * Implementors shall extend {@link AbstractQueryRuntimeContext} instead directly this interface.
24 * 26 *
25 * @author Bergmann Gabor 27 * @author Bergmann Gabor
26 * @noimplement This interface is not intended to be implemented by clients. Extend {@link AbstractQueryRuntimeContext} instead. 28 * @noimplement This interface is not intended to be implemented by clients. Extend {@link AbstractQueryRuntimeContext} instead.
27 */ 29 */
28public interface IQueryRuntimeContext { 30public interface IQueryRuntimeContext {
29 /** 31 /**
30 * Provides metamodel-specific info independent of the runtime instance model. 32 * Provides metamodel-specific info independent of the runtime instance model.
31 */ 33 */
32 public IQueryMetaContext getMetaContext(); 34 public IQueryMetaContext getMetaContext();
33 35
34 36
35 /** 37 /**
36 * The given callable will be executed, and all model traversals will be delayed until the execution is done. If 38 * The given callable will be executed, and all model traversals will be delayed until the execution is done. If
37 * there are any outstanding information to be read from the model, a single coalesced model traversal will 39 * there are any outstanding information to be read from the model, a single coalesced model traversal will
38 * initialize the caches and deliver the notifications. 40 * initialize the caches and deliver the notifications.
39 * 41 *
40 * <p> Calls may be nested. A single coalesced traversal will happen at the end of the outermost call. 42 * <p> Calls may be nested. A single coalesced traversal will happen at the end of the outermost call.
41 * 43 *
42 * <p> <b>Caution: </b> results returned by the runtime context may be incomplete during the coalescing period, to be corrected by notifications sent during the final coalesced traversal. 44 * <p> <b>Caution: </b> results returned by the runtime context may be incomplete during the coalescing period, to be corrected by notifications sent during the final coalesced traversal.
43 * For example, if a certain input key is not cached yet, an empty relation may be reported during <code>callable.call()</code>; the cache will be constructed after the call terminates and notifications will deliver the entire content of the relation. 45 * For example, if a certain input key is not cached yet, an empty relation may be reported during <code>callable.call()</code>; the cache will be constructed after the call terminates and notifications will deliver the entire content of the relation.
44 * Non-incremental query backends should therefore never enumerate input keys while coalesced (verify using {@link #isCoalescing()}). 46 * Non-incremental query backends should therefore never enumerate input keys while coalesced (verify using {@link #isCoalescing()}).
45 * 47 *
46 * @param callable 48 * @param callable
47 */ 49 */
48 public abstract <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException; 50 public abstract <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException;
49 /** 51 /**
50 * @return true iff currently within a coalescing section (i.e. within the callable of a call to {@link #coalesceTraversals(Callable)}). 52 * @return true iff currently within a coalescing section (i.e. within the callable of a call to {@link #coalesceTraversals(Callable)}).
51 */ 53 */
52 public boolean isCoalescing(); 54 public boolean isCoalescing();
53 55
54 /** 56 /**
55 * Returns true if index is available for the given key providing the given service. 57 * Returns true if index is available for the given key providing the given service.
56 * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. 58 * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
57 * @since 1.4 59 * @since 1.4
58 */ 60 */
59 public boolean isIndexed(IInputKey key, IndexingService service); 61 public boolean isIndexed(IInputKey key, IndexingService service);
60 62
61 /** 63 /**
62 * If the given (enumerable) input key is not yet indexed, the model will be traversed 64 * If the given (enumerable) input key is not yet indexed, the model will be traversed
63 * (after the end of the outermost coalescing block, see {@link IQueryRuntimeContext#coalesceTraversals(Callable)}) 65 * (after the end of the outermost coalescing block, see {@link IQueryRuntimeContext#coalesceTraversals(Callable)})
64 * so that the index can be built. It is possible that the base indexer will select a higher indexing level merging 66 * so that the index can be built. It is possible that the base indexer will select a higher indexing level merging
65 * multiple indexing requests to an appropriate level. 67 * multiple indexing requests to an appropriate level.
66 * 68 *
67 * <p><b>Postcondition:</b> After invoking this method, {@link #getIndexed(IInputKey, IndexingService)} for the same key 69 * <p><b>Postcondition:</b> After invoking this method, {@link #getIndexed(IInputKey, IndexingService)} for the same key
68 * and service will be guaranteed to return the requested or a highing indexing level as soon as {@link #isCoalescing()} first returns false. 70 * and service will be guaranteed to return the requested or a highing indexing level as soon as {@link #isCoalescing()} first returns false.
69 * 71 *
70 * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. 72 * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
71 * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. 73 * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
72 * @since 1.4 74 * @since 1.4
73 */ 75 */
74 public void ensureIndexed(IInputKey key, IndexingService service); 76 public void ensureIndexed(IInputKey key, IndexingService service);
75 77
76 /** 78 /**
77 * Returns the number of tuples in the extensional relation identified by the input key seeded with the given mask and tuple. 79 * Returns the number of tuples in the extensional relation identified by the input key seeded with the given mask and tuple.
78 * 80 *
79 * @param key an input key 81 * @param key an input key
80 * @param seedMask 82 * @param seedMask
81 * a mask that extracts those parameters of the input key (from the entire parameter list) that should be 83 * a mask that extracts those parameters of the input key (from the entire parameter list) that should be
82 * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most once in seedMask. 84 * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most once in seedMask.
@@ -84,59 +86,59 @@ public interface IQueryRuntimeContext {
84 * the tuple of fixed values restricting the match set to be considered, in the same order as given in 86 * the tuple of fixed values restricting the match set to be considered, in the same order as given in
85 * parameterSeedMask, so that for each considered match tuple, 87 * parameterSeedMask, so that for each considered match tuple,
86 * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. 88 * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null.
87 * 89 *
88 * @return the number of tuples in the model for the given key and seed 90 * @return the number of tuples in the model for the given key and seed
89 * 91 *
90 * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. 92 * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
91 * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. 93 * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
92 * @since 1.7 94 * @since 1.7
93 */ 95 */
94 public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed); 96 public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed);
95 97
96 98
97 /** 99 /**
98 * Gives an estimate of the number of different groups the tuples of the given relation are projected into by the given mask 100 * Gives an estimate of the number of different groups the tuples of the given relation are projected into by the given mask
99 * (e.g. for an identity mask, this means the full relation size). The estimate must meet the required accuracy. 101 * (e.g. for an identity mask, this means the full relation size). The estimate must meet the required accuracy.
100 * 102 *
101 * <p> Must accept any input key, even non-enumerables or those not recognized by this runtime context. 103 * <p> Must accept any input key, even non-enumerables or those not recognized by this runtime context.
102 * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} is returned. 104 * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} is returned.
103 * 105 *
104 * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. 106 * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask.
105 * 107 *
106 * @return if available, an estimate of the cardinality of the projection of the given extensional relation, with the desired accuracy. 108 * @return if available, an estimate of the cardinality of the projection of the given extensional relation, with the desired accuracy.
107 * 109 *
108 * @since 2.1 110 * @since 2.1
109 */ 111 */
110 public Optional<Long> estimateCardinality(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy); 112 public Optional<Long> estimateCardinality(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy);
111 113
112 114
113 /** 115 /**
114 * Gives an estimate of the average size of different groups the tuples of the given relation are projected into by the given mask 116 * Gives an estimate of the average size of different groups the tuples of the given relation are projected into by the given mask
115 * (e.g. for an identity mask, this means 1, while for an empty mask, the result is the full relation size). 117 * (e.g. for an identity mask, this means 1, while for an empty mask, the result is the full relation size).
116 * The estimate must meet the required accuracy. 118 * The estimate must meet the required accuracy.
117 * 119 *
118 * <p> Must accept any input key, even non-enumerables or those not recognized by this runtime context. 120 * <p> Must accept any input key, even non-enumerables or those not recognized by this runtime context.
119 * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned. 121 * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned.
120 * 122 *
121 * <p> For an empty relation, zero is acceptable as an exact answer. 123 * <p> For an empty relation, zero is acceptable as an exact answer.
122 * 124 *
123 * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. 125 * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask.
124 * 126 *
125 * @return if available, an estimate of the average size of each projection group of the given extensional relation, with the desired accuracy. 127 * @return if available, an estimate of the average size of each projection group of the given extensional relation, with the desired accuracy.
126 * 128 *
127 * @since 2.1 129 * @since 2.1
128 */ 130 */
129 public default Optional<Double> estimateAverageBucketSize(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) { 131 public default Optional<Double> estimateAverageBucketSize(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) {
130 if (key.isEnumerable()) { 132 if (key.isEnumerable()) {
131 return StatisticsHelper.estimateAverageBucketSize(groupMask, requiredAccuracy, 133 return StatisticsHelper.estimateAverageBucketSize(groupMask, requiredAccuracy,
132 (mask, accuracy) -> this.estimateCardinality(key, mask, accuracy)); 134 (mask, accuracy) -> this.estimateCardinality(key, mask, accuracy));
133 } else return groupMask.isIdentity() ? Optional.of(1.0) : Optional.empty(); 135 } else return groupMask.isIdentity() ? Optional.of(1.0) : Optional.empty();
134 } 136 }
135 137
136 138
137 /** 139 /**
138 * Returns the tuples in the extensional relation identified by the input key, optionally seeded with the given tuple. 140 * Returns the tuples in the extensional relation identified by the input key, optionally seeded with the given tuple.
139 * 141 *
140 * @param key an input key 142 * @param key an input key
141 * @param seedMask 143 * @param seedMask
142 * a mask that extracts those parameters of the input key (from the entire parameter list) that should be 144 * a mask that extracts those parameters of the input key (from the entire parameter list) that should be
@@ -144,23 +146,23 @@ public interface IQueryRuntimeContext {
144 * @param seed 146 * @param seed
145 * the tuple of fixed values restricting the match set to be considered, in the same order as given in 147 * the tuple of fixed values restricting the match set to be considered, in the same order as given in
146 * parameterSeedMask, so that for each considered match tuple, 148 * parameterSeedMask, so that for each considered match tuple,
147 * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. 149 * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null.
148 * @return the tuples in the model for the given key and seed 150 * @return the tuples in the model for the given key and seed
149 * 151 *
150 * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. 152 * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
151 * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. 153 * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
152 * @since 1.7 154 * @since 1.7
153 */ 155 */
154 public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed); 156 public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed);
155 157
156 /** 158 /**
157 * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples 159 * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples
158 * are bound by the seed except for one. 160 * are bound by the seed except for one.
159 * 161 *
160 * <p> 162 * <p>
161 * Selects the tuples in the extensional relation identified by the input key, optionally seeded with the given 163 * Selects the tuples in the extensional relation identified by the input key, optionally seeded with the given
162 * tuple, and then returns the single value from each tuple which is not bound by the ssed mask. 164 * tuple, and then returns the single value from each tuple which is not bound by the ssed mask.
163 * 165 *
164 * @param key 166 * @param key
165 * an input key 167 * an input key
166 * @param seedMask 168 * @param seedMask
@@ -172,7 +174,7 @@ public interface IQueryRuntimeContext {
172 * parameterSeedMask, so that for each considered match tuple, 174 * parameterSeedMask, so that for each considered match tuple,
173 * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. 175 * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null.
174 * @return the objects in the model for the given key and seed 176 * @return the objects in the model for the given key and seed
175 * 177 *
176 * <p> 178 * <p>
177 * <b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. 179 * <b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
178 * @throws IllegalArgumentException 180 * @throws IllegalArgumentException
@@ -180,17 +182,17 @@ public interface IQueryRuntimeContext {
180 * @since 1.7 182 * @since 1.7
181 */ 183 */
182 public Iterable<? extends Object> enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed); 184 public Iterable<? extends Object> enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed);
183 185
184 /** 186 /**
185 * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples 187 * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples
186 * are bound by the seed. 188 * are bound by the seed.
187 * 189 *
188 * <p> 190 * <p>
189 * Returns whether the given tuple is in the extensional relation identified by the input key. 191 * Returns whether the given tuple is in the extensional relation identified by the input key.
190 * 192 *
191 * <p> 193 * <p>
192 * Note: this call works for non-enumerable input keys as well. 194 * Note: this call works for non-enumerable input keys as well.
193 * 195 *
194 * @param key 196 * @param key
195 * an input key 197 * an input key
196 * @param seed 198 * @param seed
@@ -202,31 +204,31 @@ public interface IQueryRuntimeContext {
202 */ 204 */
203 public boolean containsTuple(IInputKey key, ITuple seed); 205 public boolean containsTuple(IInputKey key, ITuple seed);
204 206
205 207
206 /** 208 /**
207 * Subscribes for updates in the extensional relation identified by the input key, optionally seeded with the given tuple. 209 * Subscribes for updates in the extensional relation identified by the input key, optionally seeded with the given tuple.
208 * <p> This should be called after invoking 210 * <p> This should be called after invoking
209 * 211 *
210 * @param key an input key 212 * @param key an input key
211 * @param seed can be null or a tuple with matching arity; 213 * @param seed can be null or a tuple with matching arity;
212 * if non-null, only those updates in the model are notified about 214 * if non-null, only those updates in the model are notified about
213 * that match the seed at positions where the seed is non-null. 215 * that match the seed at positions where the seed is non-null.
214 * @param listener will be notified of future changes 216 * @param listener will be notified of future changes
215 * 217 *
216 * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. 218 * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
217 * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. 219 * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
218 */ 220 */
219 public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener); 221 public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener);
220 222
221 /** 223 /**
222 * Unsubscribes from updates in the extensional relation identified by the input key, optionally seeded with the given tuple. 224 * Unsubscribes from updates in the extensional relation identified by the input key, optionally seeded with the given tuple.
223 * 225 *
224 * @param key an input key 226 * @param key an input key
225 * @param seed can be null or a tuple with matching arity; 227 * @param seed can be null or a tuple with matching arity;
226 * if non-null, only those updates in the model are notified about 228 * if non-null, only those updates in the model are notified about
227 * that match the seed at positions where the seed is non-null. 229 * that match the seed at positions where the seed is non-null.
228 * @param listener will no longer be notified of future changes 230 * @param listener will no longer be notified of future changes
229 * 231 *
230 * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. 232 * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
231 * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. 233 * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
232 */ 234 */
@@ -234,16 +236,16 @@ public interface IQueryRuntimeContext {
234 /* 236 /*
235 TODO: uniqueness 237 TODO: uniqueness
236 */ 238 */
237 239
238 /** 240 /**
239 * Wraps the external element into the internal representation that is to be used by the query backend 241 * Wraps the external element into the internal representation that is to be used by the query backend
240 * <p> model element -> internal object. 242 * <p> model element -> internal object.
241 * <p> null must be mapped to null. 243 * <p> null must be mapped to null.
242 */ 244 */
243 public Object wrapElement(Object externalElement); 245 public Object wrapElement(Object externalElement);
244 246
245 /** 247 /**
246 * Unwraps the internal representation of the element into its original form 248 * Unwraps the internal representation of the element into its original form
247 * <p> internal object -> model element 249 * <p> internal object -> model element
248 * <p> null must be mapped to null. 250 * <p> null must be mapped to null.
249 */ 251 */
@@ -269,13 +271,17 @@ public interface IQueryRuntimeContext {
269 * @since 1.4 271 * @since 1.4
270 */ 272 */
271 public void ensureWildcardIndexing(IndexingService service); 273 public void ensureWildcardIndexing(IndexingService service);
272 274
273 /** 275 /**
274 * Execute the given runnable after traversal. It is guaranteed that the runnable is executed as soon as 276 * Execute the given runnable after traversal. It is guaranteed that the runnable is executed as soon as
275 * the indexing is finished. The callback is executed only once, then is removed from the callback queue. 277 * the indexing is finished. The callback is executed only once, then is removed from the callback queue.
276 * @param traversalCallback 278 * @param traversalCallback
277 * @throws InvocationTargetException 279 * @throws InvocationTargetException
278 * @since 1.4 280 * @since 1.4
279 */ 281 */
280 public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException; 282 public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException;
283
284 default CancellationToken getCancellationToken() {
285 return CancellationToken.NONE;
286 }
281} 287}