JervenBolleman / void-generator

Calculate statistics for use in a Service Description or Void file
MIT License
6 stars 4 forks source link

Deadlock in the QueryCallable.call method #17

Closed galgonek closed 1 month ago

galgonek commented 1 month ago

I discovered a deadlock in the current version of the generator (fe5b7f84a10d062e1d79e17a8d21f860ab8b2aa0).

The method call in the class QueryCallable needs to acquire a permit from the semaphore limiter to be executed. However, this code can recursively call the call method again, which again needs to acquire a permit. If the semaphore limit is exhausted and all running call methods try to acquire permits for their recursive calls, then the deadlock occurs.

This is exactly what happened in my case, all the pool threads got deadlocked in this state:

"pool-115-thread-1" #224 [53758] prio=5 os_prio=0 cpu=1.14ms elapsed=8034.60s tid=0x00007f8d345c49e0 nid=53758 waiting on condition  [0x00007f8a2c4fe000]
   java.lang.Thread.State: WAITING (parking)
        at jdk.internal.misc.Unsafe.park(java.base@21/Native Method)
        - parking to wait for  <0x000000011a00ae38> (a java.util.concurrent.Semaphore$NonfairSync)
        at java.util.concurrent.locks.LockSupport.park(java.base@21/LockSupport.java:221)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(java.base@21/AbstractQueuedSynchronizer.java:754)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireShared(java.base@21/AbstractQueuedSynchronizer.java:1079)
        at java.util.concurrent.Semaphore.acquireUninterruptibly(java.base@21/Semaphore.java:341)
        at swiss.sib.swissprot.voidcounter.QueryCallable.call(QueryCallable.java:27)
        at swiss.sib.swissprot.servicedescription.FindPredicatesAndClasses.run(FindPredicatesAndClasses.java:64)
        at swiss.sib.swissprot.servicedescription.FindPredicatesAndClasses.run(FindPredicatesAndClasses.java:23)
        at swiss.sib.swissprot.voidcounter.QueryCallable.call(QueryCallable.java:31)
        at swiss.sib.swissprot.voidcounter.QueryCallable.call(QueryCallable.java:11)
        at java.util.concurrent.FutureTask.run(java.base@21/FutureTask.java:317)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@21/ThreadPoolExecutor.java:1144)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@21/ThreadPoolExecutor.java:642)
        at java.lang.Thread.runWith(java.base@21/Thread.java:1596)
        at java.lang.Thread.run(java.base@21/Thread.java:1583)
JervenBolleman commented 1 month ago

Fixed this by no longer recursively calling call. Instead passing in a Supplier for the relevant QueryCallable to schedule more work upon completion. 393bdb490a605239856135fb0dd52f18329229b7 should fix this