Details
-
Bug
-
Status: Closed
-
Major
-
Resolution: Fixed
-
None
-
None
Description
I found that activeInterpreters in LPBRuleEngine leaks also if you don't iterate through to the end - calling .close() on the iterator is not enough. Clean-up seems to only happen when it.hasNext() is called and it returns false - so this could happen also in cases like where you return after getting the first hit.
@Test public void testNotLeakingActiveInterpreters() throws Exception { Graph data = Factory.createGraphMem(); data.add(new Triple(a, ty, C1)); data.add(new Triple(b, ty, C1)); List<Rule> rules = Rule .parseRules("[r1: (?x p ?t) <- (?x rdf:type C1), makeInstance(?x, p, C2, ?t)]" + "[r2: (?t rdf:type C2) <- (?x rdf:type C1), makeInstance(?x, p, C2, ?t)]"); FBRuleInfGraph infgraph = (FBRuleInfGraph) createReasoner(rules).bind( data); LPBRuleEngine engine = getEngineForGraph(infgraph); assertEquals(0, engine.activeInterpreters.size()); assertEquals(0, engine.tabledGoals.size()); // we ask for a non-hit -- it works, but only because we call it.hasNext() ExtendedIterator<Triple> it = infgraph.find(nohit, ty, C1); assertFalse(it.hasNext()); it.close(); assertEquals(0, engine.activeInterpreters.size()); // and again. // Ensure this is not cached by asking for a different triple pattern ExtendedIterator<Triple> it2 = infgraph.find(nohit, ty, C2); // uuups, forgot to call it.hasNext(). But .close() should tidy it2.close(); assertEquals(0, engine.activeInterpreters.size()); // OK, let's ask for something that is in the graph ExtendedIterator<Triple> it3 = infgraph.find(a, ty, C1); assertTrue(it3.hasNext()); assertEquals(a, it3.next().getMatchSubject()); // .. and what if we forget to call next() to consume b? // (e.g. return from a method with the first hit) // this should be enough it3.close(); // without leaks of activeInterpreters assertEquals(0, engine.activeInterpreters.size()); }
When investigating this, I got as far as seeing that the activeInterpreters is normally closed from hasNext() when it reaches the end here:
which is a zombie check.. but this doesn't work when hasNext() hasn't reach the end, even though it is also called from close() here:
Moving the checkForCompletions further down and calling .close() on any nextToRun or lookAhead was not sufficient - there is always a secondary Generator in the list which is not removed - only the top-level one is removed normally.
I was unable to investigate any further as I could not understand the classes that were creating the two Generators.
The inner LPInterpreter 147 is first created from generatorFor() call from ConsumerChoicePointFrame constructor within LPInterpreter.setupTabledCall which is coming from the ruleEngine.find.
This outer LPInterpreter is then added as part of the find().
In "normal operation" the inner one is removed through the zombie clearing, while the outer one is removed through .close()
If you don't iterate through to the end, the inner one is not removed.