[ 
https://issues.apache.org/jira/browse/JENA-904?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Stian Soiland-Reyes updated JENA-904:
-------------------------------------
    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.

{code}
        @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());
        }

{code}

When investigating this, I got as far as seeing that the activeInterpreters is 
normally closed from hasNext() when it reaches the end here:

https://github.com/apache/jena/blob/master/jena-core/src/main/java/com/hp/hpl/jena/reasoner/rulesys/impl/Generator.java#L303

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:

https://github.com/apache/jena/blob/master/jena-core/src/main/java/com/hp/hpl/jena/reasoner/rulesys/impl/LPTopGoalIterator.java#L199

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.



> LPBRuleEngine leaks activeInterpreters
> --------------------------------------
>
>                 Key: JENA-904
>                 URL: https://issues.apache.org/jira/browse/JENA-904
>             Project: Apache Jena
>          Issue Type: Bug
>          Components: Ontology API
>            Reporter: Stian Soiland-Reyes
>
> 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.
> {code}
>       @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());
>       }
> {code}
> When investigating this, I got as far as seeing that the activeInterpreters 
> is normally closed from hasNext() when it reaches the end here:
> https://github.com/apache/jena/blob/master/jena-core/src/main/java/com/hp/hpl/jena/reasoner/rulesys/impl/Generator.java#L303
> 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:
> https://github.com/apache/jena/blob/master/jena-core/src/main/java/com/hp/hpl/jena/reasoner/rulesys/impl/LPTopGoalIterator.java#L199
> 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.



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

Reply via email to