Fixed deadlock on synchronized vcToCleanUp.

Change-Id: I563f8c897165e9124fa0edec49db1155a21933db
diff --git a/Changes b/Changes
index 068d9b0..9d34589 100644
--- a/Changes
+++ b/Changes
@@ -1,5 +1,6 @@
-0.64.2 2025-06-18
+0.64.2 2025-07-02
     - [bugfix] Restore rewrites in Meta. (diewald)
+    - [bugfix] Fixed deadlock on synchronized vcToCleanUp. (margaretha)
 
 0.64.1 2025-05-26
     - [bugfix] Ignore line endings in indexer tests. (diewald)
diff --git a/src/main/java/de/ids_mannheim/korap/cache/VirtualCorpusCache.java b/src/main/java/de/ids_mannheim/korap/cache/VirtualCorpusCache.java
index e1bef61..81e094c 100644
--- a/src/main/java/de/ids_mannheim/korap/cache/VirtualCorpusCache.java
+++ b/src/main/java/de/ids_mannheim/korap/cache/VirtualCorpusCache.java
@@ -60,6 +60,8 @@
 
     public static final Set<String> vcToCleanUp = Collections
             .synchronizedSet(new HashSet<>());
+    
+    public static volatile boolean isCleaning = false;
 
 
     public VirtualCorpusCache () {
@@ -86,13 +88,11 @@
 
         String[] parts = vcId.split("/");
         if (parts.length > 2) {
-            vcToCleanUp.remove(vcId);
             return false;
         }
 
         String vcName = parts.length == 2 ? parts[1] : parts[0];
         if (!vcNamePattern.matcher(vcName).matches()) {
-            vcToCleanUp.remove(vcId);
             return false;
         }
 
@@ -163,6 +163,11 @@
     }
 
 
+    /** Retrieve a VC from the cache, either from the memory map or disk.
+     * 
+     * @param vcId
+     * @return a map of index leaves and DocBits, otherwise null if not found.
+     */
     public static Map<String, DocBits> retrieve (String vcId) {
         Map<String, DocBits> vcData = map.get(vcId);
         if (vcData != null) {
@@ -218,8 +223,11 @@
             return;
         }
 
-        vcToCleanUp.remove(vcId);
         map.remove(vcId);
+        if (!isCleaning) { 
+        	vcToCleanUp.remove(vcId);
+        }
+        
         File vc = new File(CACHE_LOCATION + "/" + vcId);
         if (vc.exists()) {
             for (File f : vc.listFiles()) {
@@ -262,14 +270,20 @@
      */
     public static void setIndexInfo (IndexInfo indexInfo) {
         VirtualCorpusCache.indexInfo = indexInfo;
-        synchronized (vcToCleanUp) {
-            if (!vcToCleanUp.isEmpty()) {
+        //synchronized (vcToCleanUp) {
+            if (!vcToCleanUp.isEmpty() && !isCleaning) {
+            	isCleaning = true;
                 cleanup();
+                isCleaning = false;
             }
-        }
+        //}
     }
 
 
+    /** Remove out-dated leaves that are not used anymore due to index update 
+     * (i.e., by sending a close-index-reader-API request)
+     * 
+     */
     private static void cleanup () {
         final Set<String> currentLeafFingerprints = indexInfo
                 .getAllLeafFingerprints();
@@ -314,6 +328,8 @@
             Supplier<DocBits> calculateDocBits) {
         DocBits docBits = null;
         Map<String, DocBits> leafToDocBitMap = retrieve(vcId);
+        // if VC is not in the cache (both memory and disk), 
+        // put it in the memory map
         if (leafToDocBitMap == null) {
             leafToDocBitMap = Collections
                     .synchronizedMap(new HashMap<String, DocBits>());
@@ -321,11 +337,23 @@
         }
         else {
             docBits = leafToDocBitMap.get(leafFingerprint);
-            if (docBits == null) {
+            // VC-id is the cache but there is no data for the leaf
+            if (docBits == null && !isCleaning) {
                 vcToCleanUp.add(vcId);
             }
         }
         if (docBits == null) {
+        	/* Calculating docBits and storing in the cache
+        	 * 
+        	 * This process is triggered when finding a JSON-LD file at 
+        	 * the named-vc folder that doesn't exist in the cache.
+        	 * 
+        	 * It should only happens at server start-up, or index update
+        	 * for a small number of new leaves.
+        	 * 
+        	 * New named VC should *not* be added at a running instance, as 
+        	 * it would trigger this process.
+        	 */   
             docBits = calculateDocBits.get();
             leafToDocBitMap.put(leafFingerprint, docBits);
             storeOnDisk(vcId, leafFingerprint, docBits);