Hello!
We stumbled upon a deadlock when using the Java API. Code says more than 1000 words, so we created a minimal example showing the problem:
import com.complexible.stardog.api.Connection;
import com.complexible.stardog.api.ConnectionConfiguration;
import com.stardog.stark.query.SelectQueryResult;
public class ConnectionDeadlock {
public static void main(String[] args) throws InterruptedException {
ConnectionConfiguration config = ConnectionConfiguration
.to("stardog")
.credentials("admin", "admin")
.server("http://docker.local:5820");
Connection connection = config.connect();
SelectQueryResult result = connection
.select("SELECT ?s WHERE { ?s ?p ?o . } LIMIT 1001")
.execute();
if (result.hasNext()) {
System.out.println("We have a result...");
Thread.sleep(100);
}
result.close();
connection.close();
}
}
If the query returns more than 1000 results, a deadlock occurs when calling result.close()
before consuming the results. Here are the two threads in WAITING
state:
-
main execution thread:
"main@1" prio=5 tid=0x1 nid=NA waiting java.lang.Thread.State: WAITING at sun.misc.Unsafe.park(Unsafe.java:-1) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitUninterruptibly(AbstractQueuedSynchronizer.java:1976) at com.google.common.util.concurrent.Monitor.awaitUninterruptibly(Monitor.java:1106) at com.google.common.util.concurrent.Monitor.enterWhenUninterruptibly(Monitor.java:546) at com.google.common.util.concurrent.AbstractService.awaitTerminated(AbstractService.java:305) at com.google.common.util.concurrent.AbstractExecutionThreadService.awaitTerminated(AbstractExecutionThreadService.java:223) at com.complexible.common.rdf.query.IOBindingSetIterator.close(IOBindingSetIterator.java:137) at com.complexible.common.rdf.query.IteratorAsTupleQueryResult.close(IteratorAsTupleQueryResult.java:64) at com.stardog.stark.query.impl.ForwardingSelectQueryResult.close(ForwardingSelectQueryResult.java:53) at com.complexible.stardog.protocols.http.client.HttpClientImpl$2.close(HttpClientImpl.java:351) at com.complexible.stardog.api.impl.AbstractConnection$OnCloseQueryResult.close(AbstractConnection.java:720) at PoolDeadlock.main(PoolDeadlock.java:31)
-
asynchronous parser thread:
"Stardog.IOBindingSetIteration.ParseService@3092" prio=5 tid=0xf nid=NA waiting java.lang.Thread.State: WAITING at sun.misc.Unsafe.park(Unsafe.java:-1) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) at java.util.concurrent.ArrayBlockingQueue.put(ArrayBlockingQueue.java:353) at com.complexible.common.rdf.query.IOBindingSetIterator$QueueingTupleQueryResultHandler.handle(IOBindingSetIterator.java:221) at com.complexible.common.rdf.query.IOBindingSetIterator$QueueingTupleQueryResultHandler.handle(IOBindingSetIterator.java:209) at com.stardog.stark.query.io.binary.BinarySelectQueryResultParser.parse(BinarySelectQueryResultParser.java:167) at com.stardog.stark.query.io.binary.BinarySelectQueryResultParser.parse(BinarySelectQueryResultParser.java:60) at com.stardog.stark.query.io.QueryResultParser.parse(QueryResultParser.java:56) at com.complexible.common.rdf.query.IOBindingSetIterator$ParseService.run(IOBindingSetIterator.java:185) at com.google.common.util.concurrent.AbstractExecutionThreadService$1$2.run(AbstractExecutionThreadService.java:66) at com.google.common.util.concurrent.Callables$4.run(Callables.java:119) at java.lang.Thread.run(Thread.java:748)
This is critical, because our client acts as a proxy and due to the nature of HTTP it can always happen that the other client to whom we forward the results no longer listens. If that's the case, we stop consumption of the results and end up in this deadlock.
We tested reproduced this behavior with Stardog 6.1.1 and 6.2.0 (server + Java libraries).
Kind regards,
Marvin