aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteEngine.java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteEngine.java')
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteEngine.java579
1 files changed, 579 insertions, 0 deletions
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteEngine.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteEngine.java
new file mode 100644
index 00000000..9bd499f4
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteEngine.java
@@ -0,0 +1,579 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * 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 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.matcher;
11
12import java.lang.reflect.InvocationTargetException;
13import java.util.Collection;
14import java.util.LinkedList;
15import java.util.Map;
16import java.util.concurrent.Callable;
17
18import org.apache.log4j.Logger;
19import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
20import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend;
21import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
22import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider;
23import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
24import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
25import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
26import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
27import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
28import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
29import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
30import tools.refinery.viatra.runtime.rete.boundary.Disconnectable;
31import tools.refinery.viatra.runtime.rete.boundary.ReteBoundary;
32import tools.refinery.viatra.runtime.rete.construction.RetePatternBuildException;
33import tools.refinery.viatra.runtime.rete.construction.plancompiler.ReteRecipeCompiler;
34import tools.refinery.viatra.runtime.rete.index.Indexer;
35import tools.refinery.viatra.runtime.rete.network.Network;
36import tools.refinery.viatra.runtime.rete.network.NodeProvisioner;
37import tools.refinery.viatra.runtime.rete.network.ReteContainer;
38import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo;
39
40/**
41 * @author Gabor Bergmann
42 *
43 */
44public class ReteEngine implements IQueryBackend {
45
46 protected Network reteNet;
47 protected final int reteThreads;
48 protected ReteBoundary boundary;
49
50 /**
51 * @since 2.2
52 */
53 protected final boolean deleteAndRederiveEvaluation;
54 /**
55 * @since 2.4
56 */
57 protected final TimelyConfiguration timelyConfiguration;
58
59 private IQueryBackendContext context;
60 private Logger logger;
61 protected IQueryRuntimeContext runtimeContext;
62
63 protected Collection<Disconnectable> disconnectables;
64
65 protected Map<PQuery, RetePatternMatcher> matchers;
66
67 protected ReteRecipeCompiler compiler;
68
69 protected final boolean parallelExecutionEnabled; // TRUE if model manipulation can go on
70
71 private boolean disposedOrUninitialized = true;
72
73 private HintConfigurator hintConfigurator;
74
75 /**
76 * @param context
77 * the context of the pattern matcher, conveying all information from the outside world.
78 * @param reteThreads
79 * the number of threads to operate the RETE network with; 0 means single-threaded operation, 1 starts an
80 * asynchronous thread to operate the RETE net, >1 uses multiple RETE containers.
81 */
82 public ReteEngine(IQueryBackendContext context, int reteThreads) {
83 this(context, reteThreads, false, null);
84 }
85
86 /**
87 * @since 2.4
88 */
89 public ReteEngine(IQueryBackendContext context, int reteThreads, boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyConfiguration) {
90 super();
91 this.context = context;
92 this.logger = context.getLogger();
93 this.runtimeContext = context.getRuntimeContext();
94 this.reteThreads = reteThreads;
95 this.parallelExecutionEnabled = reteThreads > 0;
96 this.deleteAndRederiveEvaluation = deleteAndRederiveEvaluation;
97 this.timelyConfiguration = timelyConfiguration;
98 initEngine();
99 this.compiler = null;
100 }
101
102 /**
103 * @since 1.6
104 */
105 public IQueryBackendContext getBackendContext() {
106 return context;
107 }
108
109 /**
110 * @since 2.2
111 */
112 public boolean isDeleteAndRederiveEvaluation() {
113 return this.deleteAndRederiveEvaluation;
114 }
115
116 /**
117 * @since 2.4
118 */
119 public TimelyConfiguration getTimelyConfiguration() {
120 return this.timelyConfiguration;
121 }
122
123 /**
124 * initializes engine components
125 */
126 private synchronized void initEngine() {
127 this.disposedOrUninitialized = false;
128 this.disconnectables = new LinkedList<Disconnectable>();
129 // this.caughtExceptions = new LinkedBlockingQueue<Throwable>();
130
131
132 this.hintConfigurator = new HintConfigurator(context.getHintProvider());
133
134 this.reteNet = new Network(reteThreads, this);
135 this.boundary = new ReteBoundary(this); // prerequisite: network
136
137 this.matchers = CollectionsFactory.createMap();
138 /* this.matchersScoped = new HashMap<PatternDescription, Map<Map<Integer,Scope>,RetePatternMatcher>>(); */
139
140 // prerequisite: network, framework, boundary, disconnectables
141 //context.subscribeBackendForUpdates(this.boundary);
142 // prerequisite: boundary, disconnectables
143// this.traceListener = context.subscribePatternMatcherForTraceInfluences(this);
144
145 }
146
147 @Override
148 public void flushUpdates() {
149 for (ReteContainer container : this.reteNet.getContainers()) {
150 container.deliverMessagesSingleThreaded();
151 }
152 }
153
154 /**
155 * deconstructs engine components
156 */
157 private synchronized void deconstructEngine() {
158 ensureInitialized();
159 reteNet.kill();
160
161 //context.unSubscribeBackendFromUpdates(this.boundary);
162 for (Disconnectable disc : disconnectables) {
163 disc.disconnect();
164 }
165
166 this.matchers = null;
167 this.disconnectables = null;
168
169 this.reteNet = null;
170 this.boundary = null;
171
172 this.hintConfigurator = null;
173
174 // this.machineListener = new MachineListener(this); // prerequisite:
175 // framework, disconnectables
176// this.traceListener = null;
177
178 this.disposedOrUninitialized = true;
179 }
180
181 /**
182 * Deconstructs the engine to get rid of it finally
183 */
184 public void killEngine() {
185 deconstructEngine();
186 // this.framework = null;
187 this.compiler = null;
188 this.logger = null;
189 }
190
191 /**
192 * Resets the engine to an after-initialization phase
193 *
194 */
195 public void reset() {
196 deconstructEngine();
197
198 initEngine();
199
200 compiler.reset();
201 }
202
203 /**
204 * Accesses the patternmatcher for a given pattern, constructs one if a matcher is not available yet.
205 *
206 * @pre: builder is set.
207 * @param query
208 * the pattern to be matched.
209 * @return a patternmatcher object that can match occurences of the given pattern.
210 * @throws ViatraQueryRuntimeException
211 * if construction fails.
212 */
213 public synchronized RetePatternMatcher accessMatcher(final PQuery query) {
214 ensureInitialized();
215 RetePatternMatcher matcher;
216 // String namespace = gtPattern.getNamespace().getName();
217 // String name = gtPattern.getName();
218 // String fqn = namespace + "." + name;
219 matcher = matchers.get(query);
220 if (matcher == null) {
221 constructionWrapper(() -> {
222 RecipeTraceInfo prodNode;
223 prodNode = boundary.accessProductionTrace(query);
224
225 RetePatternMatcher retePatternMatcher = new RetePatternMatcher(ReteEngine.this,
226 prodNode);
227 retePatternMatcher.setTag(query);
228 matchers.put(query, retePatternMatcher);
229 return null;
230 });
231 matcher = matchers.get(query);
232 }
233
234 executeDelayedCommands();
235
236 return matcher;
237 }
238
239
240 /**
241 * Constructs RETE pattern matchers for a collection of patterns, if they are not available yet. Model traversal
242 * during the whole construction period is coalesced (which may have an effect on performance, depending on the
243 * matcher context).
244 *
245 * @pre: builder is set.
246 * @param specifications
247 * the patterns to be matched.
248 * @throws ViatraQueryRuntimeException
249 * if construction fails.
250 */
251 public synchronized void buildMatchersCoalesced(final Collection<PQuery> specifications) {
252 ensureInitialized();
253 constructionWrapper(() -> {
254 for (PQuery specification : specifications) {
255 boundary.accessProductionNode(specification);
256 }
257 return null;
258 });
259 }
260
261 /**
262 * @since 2.4
263 */
264 public <T> T constructionWrapper(final Callable<T> payload) {
265 T result = null;
266// context.modelReadLock();
267// try {
268 if (parallelExecutionEnabled)
269 reteNet.getStructuralChangeLock().lock();
270 try {
271 try {
272 result = runtimeContext.coalesceTraversals(() -> {
273 T innerResult = payload.call();
274 this.executeDelayedCommands();
275 return innerResult;
276 });
277 } catch (InvocationTargetException ex) {
278 final Throwable cause = ex.getCause();
279 if (cause instanceof RetePatternBuildException)
280 throw (RetePatternBuildException) cause;
281 if (cause instanceof RuntimeException)
282 throw (RuntimeException) cause;
283 assert (false);
284 }
285 } finally {
286 if (parallelExecutionEnabled)
287 reteNet.getStructuralChangeLock().unlock();
288 reteNet.waitForReteTermination();
289 }
290// } finally {
291// context.modelReadUnLock();
292// }
293 return result;
294 }
295
296 // /**
297 // * Accesses the patternmatcher for a given pattern with additional scoping, constructs one if
298 // * a matcher is not available yet.
299 // *
300 // * @param gtPattern
301 // * the pattern to be matched.
302 // * @param additionalScopeMap
303 // * additional, optional scopes for the symbolic parameters
304 // * maps the position of the symbolic parameter to its additional scope (if any)
305 // * @pre: scope.parent is non-root, i.e. this is a nontrivial constraint
306 // * use the static method RetePatternMatcher.buildAdditionalScopeMap() to create from PatternCallSignature
307 // * @return a patternmatcher object that can match occurences of the given
308 // * pattern.
309 // * @throws PatternMatcherCompileTimeException
310 // * if construction fails.
311 // */
312 // public synchronized RetePatternMatcher accessMatcherScoped(PatternDescription gtPattern, Map<Integer, Scope>
313 // additionalScopeMap)
314 // throws PatternMatcherCompileTimeException {
315 // if (additionalScopeMap.isEmpty()) return accessMatcher(gtPattern);
316 //
317 // RetePatternMatcher matcher;
318 //
319 // Map<Map<Integer, Scope>, RetePatternMatcher> scopes = matchersScoped.get(gtPattern);
320 // if (scopes == null) {
321 // scopes = new HashMap<Map<Integer, Scope>, RetePatternMatcher>();
322 // matchersScoped.put(gtPattern, scopes);
323 // }
324 //
325 // matcher = scopes.get(additionalScopeMap);
326 // if (matcher == null) {
327 // context.modelReadLock();
328 // try {
329 // reteNet.getStructuralChangeLock().lock();
330 // try {
331 // Address<? extends Production> prodNode;
332 // prodNode = boundary.accessProductionScoped(gtPattern, additionalScopeMap);
333 //
334 // matcher = new RetePatternMatcher(this, prodNode);
335 // scopes.put(additionalScopeMap, matcher);
336 // } finally {
337 // reteNet.getStructuralChangeLock().unlock();
338 // }
339 // } finally {
340 // context.modelReadUnLock();
341 // }
342 // // reteNet.flushUpdates();
343 // }
344 //
345 // return matcher;
346 // }
347
348 /**
349 * Returns an indexer that groups the contents of this Production node by their projections to a given mask.
350 * Designed to be called by a RetePatternMatcher.
351 *
352 * @param production
353 * the production node to be indexed.
354 * @param mask
355 * the mask that defines the projection.
356 * @return the Indexer.
357 */
358 synchronized Indexer accessProjection(RecipeTraceInfo production, TupleMask mask) {
359 ensureInitialized();
360 NodeProvisioner nodeProvisioner = reteNet.getHeadContainer().getProvisioner();
361 Indexer result = nodeProvisioner.peekProjectionIndexer(production, mask);
362 if (result == null) {
363 result = constructionWrapper(() ->
364 nodeProvisioner.accessProjectionIndexerOnetime(production, mask)
365 );
366 }
367
368 return result;
369 }
370
371 // /**
372 // * Retrieves the patternmatcher for a given pattern fqn, returns null if
373 // the matching network hasn't been constructed yet.
374 // *
375 // * @param fqn the fully qualified name of the pattern to be matched.
376 // * @return the previously constructed patternmatcher object that can match
377 // occurences of the given pattern, or null if it doesn't exist.
378 // */
379 // public RetePatternMatcher getMatcher(String fqn)
380 // {
381 // RetePatternMatcher matcher = matchersByFqn.get(fqn);
382 // if (matcher == null)
383 // {
384 // Production prodNode = boundary.getProduction(fqn);
385 //
386 // matcher = new RetePatternMatcher(this, prodNode);
387 // matchersByFqn.put(fqn, matcher);
388 // }
389 //
390 // return matcher;
391 // }
392
393 /**
394 * @since 2.3
395 */
396 public void executeDelayedCommands() {
397 for (final ReteContainer container : this.reteNet.getContainers()) {
398 container.executeDelayedCommands();
399 }
400 }
401
402 /**
403 * Waits until the pattern matcher is in a steady state and output can be retrieved.
404 */
405 public void settle() {
406 ensureInitialized();
407 reteNet.waitForReteTermination();
408 }
409
410 /**
411 * Waits until the pattern matcher is in a steady state and output can be retrieved. When steady state is reached, a
412 * retrieval action is executed before the steady state ceases.
413 *
414 * @param action
415 * the action to be run when reaching the steady-state.
416 */
417 public void settle(Runnable action) {
418 ensureInitialized();
419 reteNet.waitForReteTermination(action);
420 }
421
422 // /**
423 // * @return the framework
424 // */
425 // public IFramework getFramework() {
426 // return framework.get();
427 // }
428
429 /**
430 * @return the reteNet
431 */
432 public Network getReteNet() {
433 ensureInitialized();
434 return reteNet;
435 }
436
437 /**
438 * @return the boundary
439 */
440 public ReteBoundary getBoundary() {
441 ensureInitialized();
442 return boundary;
443 }
444
445 // /**
446 // * @return the pattern matcher builder
447 // */
448 // public IRetePatternBuilder getBuilder() {
449 // return builder;
450 // }
451
452 /**
453 * @param builder
454 * the pattern matcher builder to set
455 */
456 public void setCompiler(ReteRecipeCompiler builder) {
457 ensureInitialized();
458 this.compiler = builder;
459 }
460
461// /**
462// * @return the manipulationListener
463// */
464// public IManipulationListener getManipulationListener() {
465// ensureInitialized();
466// return manipulationListener;
467// }
468
469// /**
470// * @return the traceListener
471// */
472// public IPredicateTraceListener geTraceListener() {
473// ensureInitialized();
474// return traceListener;
475// }
476
477 /**
478 * @param disc
479 * the new Disconnectable adapter.
480 */
481 public void addDisconnectable(Disconnectable disc) {
482 ensureInitialized();
483 disconnectables.add(disc);
484 }
485
486 /**
487 * @return the parallelExecutionEnabled
488 */
489 public boolean isParallelExecutionEnabled() {
490 return parallelExecutionEnabled;
491 }
492
493
494 public Logger getLogger() {
495 ensureInitialized();
496 return logger;
497 }
498
499 public IQueryRuntimeContext getRuntimeContext() {
500 ensureInitialized();
501 return runtimeContext;
502 }
503
504 public ReteRecipeCompiler getCompiler() {
505 ensureInitialized();
506 return compiler;
507 }
508
509 // /**
510 // * For internal use only: logs exceptions occurring during term evaluation inside the RETE net.
511 // * @param e
512 // */
513 // public void logEvaluatorException(Throwable e) {
514 // try {
515 // caughtExceptions.put(e);
516 // } catch (InterruptedException e1) {
517 // logEvaluatorException(e);
518 // }
519 // }
520 // /**
521 // * Polls the exceptions caught and logged during term evaluation by this RETE engine.
522 // * Recommended usage: iterate polling until null is returned.
523 // *
524 // * @return the next caught exception, or null if there are no more.
525 // */
526 // public Throwable getNextLoggedEvaluatorException() {
527 // return caughtExceptions.poll();
528 // }
529
530 void ensureInitialized() {
531 if (disposedOrUninitialized)
532 throw new IllegalStateException("Trying to use a Rete engine that has been disposed or has not yet been initialized.");
533
534 }
535
536 @Override
537 public IQueryResultProvider getResultProvider(PQuery query) {
538 return accessMatcher(query);
539 }
540
541 /**
542 * @since 1.4
543 */
544 @Override
545 public IQueryResultProvider getResultProvider(PQuery query, QueryEvaluationHint hints) {
546 hintConfigurator.storeHint(query, hints);
547 return accessMatcher(query);
548 }
549
550 @Override
551 public IQueryResultProvider peekExistingResultProvider(PQuery query) {
552 ensureInitialized();
553 return matchers.get(query);
554 }
555
556 @Override
557 public void dispose() {
558 killEngine();
559 }
560
561 @Override
562 public boolean isCaching() {
563 return true;
564 }
565
566 /**
567 * @since 1.5
568 * @noreference Internal API, subject to change
569 */
570 public IQueryBackendHintProvider getHintConfiguration() {
571 return hintConfigurator;
572 }
573
574 @Override
575 public IQueryBackendFactory getFactory() {
576 return ReteBackendFactory.INSTANCE;
577 }
578
579}