[
https://issues.apache.org/jira/browse/JENA-2356?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Andy Seaborne updated JENA-2356:
--------------------------------
Fix Version/s: Jena 4.10.0
> Race condition with QueryEngineRegistry and UpdateEngineRegistry init()
> -----------------------------------------------------------------------
>
> Key: JENA-2356
> URL: https://issues.apache.org/jira/browse/JENA-2356
> Project: Apache Jena
> Issue Type: Bug
> Components: ARQ
> Affects Versions: Jena 4.9.0
> Environment: Java 17 on Linux.
> Reporter: Shawn Smith
> Assignee: Andy Seaborne
> Priority: Minor
> Fix For: Jena 4.10.0
>
>
> I encountered this exception in an application where two threads concurrently
> tried to execute a SPARQL query for the first time since the application
> started:
>
> {code:java}
> java.util.ConcurrentModificationException: null
> at
> java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
> at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967)
> at
> org.apache.jena.sparql.engine.QueryEngineRegistry.find(QueryEngineRegistry.java:94)
> at
> org.apache.jena.query.QueryExecutionBuilder.build(QueryExecutionBuilder.java:98)
> at
> org.apache.jena.query.QueryExecutionFactory.make(QueryExecutionFactory.java:622)
> at
> org.apache.jena.query.QueryExecutionFactory.make(QueryExecutionFactory.java:602)
> at
> org.apache.jena.query.QueryExecutionFactory.create(QueryExecutionFactory.java:146)
> at
> org.apache.jena.query.QueryExecutionFactory.create(QueryExecutionFactory.java:158)
> ... {code}
> The application doesn't use {{QueryEngineRegistry}} directly in any way, and
> the relevant code hasn't changed recently. I clearly just got unlucky due to
> a race condition in how {{QueryEngineRegistry}} initializes itself.
> At first glance, {{QueryEngineRegistry}} initialization looks fine due to the
> use of a static initializer:
>
> {code:java}
> public class QueryEngineRegistry
> {
> ...
> static { init(); }
> ...
> static QueryEngineRegistry registry = null;{code}
> But it turns out that the relative location of the {{init()}} and the field
> declaration matters. Once compiled, the code ^^ is equivalent to this:
>
> {code:java}
> public class QueryEngineRegistry
> {
> ...
> static { init(); }
> ...
> static QueryEngineRegistry registry;
> static { registry = null; } <== UNDOES THE EFFECT OF THE static
> init(){code}
> So the static initializer call to {{init()}} ends up having no effect because
> the field initialization happens afterwards, nulling out the registry created
> by {{{}init(){}}}.
> Instead, initialization falls to the code in the {{get()}} method:
>
> {code:java}
> static public QueryEngineRegistry get()
> {
> if ( registry == null )
> init();
> return registry;
> }
> ...
> private static synchronized void init()
> {
> registry = new QueryEngineRegistry();
> registry.add(QueryEngineMain.getFactory());
> registry.add(QueryEngineFactoryWrapper.get());
> }{code}
> Despite using {{synchronized}} in {{{}init(){}}}, this initialization
> strategy is not thread-safe. Once the first line of {{init()}} has executed,
> a concurrent thread that calls {{get()}} may receive a partially-initialized
> instance of {{{}QueryEngineRegistry{}}}. I believe that is what happened to
> me to trigger the stack trace above.
>
> Fixing the race condition can be as simple as the following change, which
> fixes the issue with the static initializer:
> {code:java}
> < static QueryEngineRegistry registry = null;
> > static QueryEngineRegistry registry; {code}
> But it seems like a good idea to cleanup the broken initialization code in
> get(), probably by simply removing it and relying on the static initializer.
> And note {{UpdateEngineRegistry}} uses the same initialization pattern.
>
--
This message was sent by Atlassian Jira
(v8.20.10#820010)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]