owlcs / owlapi

OWL API main repository
821 stars 315 forks source link

Concurrent modification exception in OWLManager.createConcurrentOWLOntologyManager() #1081

Closed fanavarro closed 1 year ago

fanavarro commented 1 year ago

Hi, I am developing an application that works with ontologies in a parallel way. I had a previous issue (#1078) in which I described the schema I follow. Concretely, I execute tasks with a thread pool executor, and each task consists in the following schema:

public Results call() throws Exception {
    /* Create ontology manager */
    OWLOntologyManager ontologyManager = OWLManager.createConcurrentOWLOntologyManager();

    /* Load ontology */
    ontology = ontologyManager.loadOntologyFromOntologyDocument(this.ontologyFile);

    /* Some stuff calculated from the ontology... */
    Results results = doSomethingWithOntology(ontology);

    return results;
}

Now, I am experiencing concurrent modification exceptions when I try to create the ontology manager. This seems to be a concurrency issue because the error sometimes appears and sometimes not. The stack trace is the following:

java.util.concurrent.ExecutionException: java.util.ConcurrentModificationException
    at java.util.concurrent.FutureTask.report(FutureTask.java:122) ~[na:1.8.0_352]
    at java.util.concurrent.FutureTask.get(FutureTask.java:192) ~[na:1.8.0_352]
    at es.um.dis.ontology_metrics_ws.services.CalculateMetricsServiceImpl.executeWithTaskExecutor(CalculateMetricsServiceImpl.java:173) [classes/:na]
    at es.um.dis.ontology_metrics_ws.services.CalculateMetricsServiceImpl.calculateMetrics(CalculateMetricsServiceImpl.java:68) [classes/:na]
    at es.um.dis.ontology_metrics_ws.services.CompletePipelineServiceImpl.calculateMetricsAndPerformAnalysisIfNeeded(CompletePipelineServiceImpl.java:41) [classes/:na]
    at es.um.dis.ontology_metrics_ws.services.CompletePipelineServiceImpl.calculateMetricsAndPerformAnalysisIfNeededEmail(CompletePipelineServiceImpl.java:66) [classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_352]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_352]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_352]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_352]
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) [spring-aop-5.3.24.jar:5.3.24]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) [spring-aop-5.3.24.jar:5.3.24]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) [spring-aop-5.3.24.jar:5.3.24]
    at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115) [spring-aop-5.3.24.jar:5.3.24]
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_352]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_352]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_352]
    at java.lang.Thread.run(Thread.java:750) ~[na:1.8.0_352]
Caused by: java.util.ConcurrentModificationException: null
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1390) ~[na:1.8.0_352]
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482) ~[na:1.8.0_352]
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472) ~[na:1.8.0_352]
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) ~[na:1.8.0_352]
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:1.8.0_352]
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:566) ~[na:1.8.0_352]
    at org.semanticweb.owlapi.utilities.Injector.inject(Injector.java:206) ~[owlapi-distribution-5.1.10.jar:5.1.10.2019-03-12T22:31:15Z]
    at org.semanticweb.owlapi.apibinding.OWLManager.createConcurrentOWLOntologyManager(OWLManager.java:128) ~[owlapi-distribution-5.1.10.jar:5.1.10.2019-03-12T22:31:15Z]
    at tasks.MetricCalculationTask.call(MetricCalculationTask.java:104) ~[classes/:na]
    at tasks.MetricCalculationTask.call(MetricCalculationTask.java:1) ~[classes/:na]
    ... 4 common frames omitted

I am using the OWLAPI v5.1.10 because I want to use the same version used by the ELK reasoner, taken from here. I am also compiling my project for Java 8.

Do you have any clue about what could be happening here? I have been modifying my code to replace the deprecated OWLAPI methods for the new ones that return streams, but I think that is not related with this issue. So, the exception comes from OWLManager.createConcurrentOWLOntologyManager(); but, are the calls to this method independent between them? It is possible that I have something strange in my code, but any hint is appreciated... I will be inspecting my code in detail tomorrow, maybe I am doing funny things in my doSomethingWithOntology method.

Thanks beforehand!

fanavarro commented 1 year ago

Hi again, I have been checking my code, but I do not see anything strange. The exception is being thrown by an ArrayList, as depicted in the stack trace of my previous message and in the following snapshot of my debugger:

imagen

If we go up until we reach the OWLAPI code, the method that is causing the exception is inject, from the Injector class:

imagen

Which is called by the OWLManager in createConcurrentOWLOntologyManager method:

imagen

So, in my code, I've included a mutex semaphore to prevent OWLManager.createConcurrentOWLOntologyManager(); being called from several threads at the same time and, for now, I am not getting those exceptions:

public Results call() throws Exception {
    /* Create ontology manager */
        MUTEX.acquire();
    OWLOntologyManager ontologyManager = OWLManager.createConcurrentOWLOntologyManager();
        MUTEX.release();

    /* Load ontology */
    ontology = ontologyManager.loadOntologyFromOntologyDocument(this.ontologyFile);

    /* Some stuff calculated from the ontology... */
    Results results = doSomethingWithOntology(ontology);

    return results;
}

But I thought this would not be needed, can you confirm that this is necessary in a parallel use of the OWLAPI?

Thanks in advance, Fran.

ignazio1977 commented 1 year ago

This looks like #883

Can you confirm which OWLAPI version you're using?

There's a simpler workaround described in the issue resolution: create a manager before entering the multithreaded part of your code, that will take care of initialisation. The exception you're seeing happens during first creation of injectors, follow up requests won't trigger it.

fanavarro commented 1 year ago

Hi @ignazio1977, thanks for your reply. I am using the version 5.1.10. And yes, this is related with #883 as I got that error from the parsers as well, but only a couple of times; the most part of the time I was getting the one referring to the concurrent modification exception.

With the semaphore, the issue seems to be is fixed, but I will try to create the manager outside the parallel task so I can remove that ugly sempahore...

I hope I can test it tomorrow, thanks for your dedication, Ignazio.

fanavarro commented 1 year ago

I confirm that I do not have any problem if I create the OWLOntologyManager outside the parallel code. I also have checked the commit 56d9feb2f520fcebad27ddff160644576f654bab, in which this issue is solved, and the version I am using (5.1.10) does not contain that update, so I think these exceptions are not appearing in newer OWLAPI versions. Thus, I am closing this issue, thanks again!