Deadlock on close when results are not consumed

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

1 Like

Hi Marvin, thanks for the very detailed report! We have identified the problem and it will be fixed for the next Stardog release

1 Like

Marvin,

Just an update, we have fixed this issue and as of yesterday afternoon released Stardog 6.2.1. Please take a look when you can!

Looks good. We can't reproduce the issue anymore. Thank you very much for the quick fix.

A short hint, your release notes have not yet been updated.