Uploaded image for project: 'Commons Collections'
  1. Commons Collections
  2. COLLECTIONS-776

Wrapping PassiveExpiringMap in a SynchonizedMap breaks expiration

    XMLWordPrintableJSON

    Details

    • Type: Bug
    • Status: Open
    • Priority: Major
    • Resolution: Unresolved
    • Affects Version/s: 4.4
    • Fix Version/s: None
    • Component/s: Collection
    • Labels:
      None
    • Environment:

      Java 8

      Built on mac OS Catalina 10.15 using gradle 5.2.1 in Intellij

      Description

      The documentation for PassiveExpiringMap says "If you wish to use this map from multiple threads concurrently, you must use appropriate synchronization. The simplest approach is to wrap this map using Collections.synchronizedMap(Map)."

      However, wrapping a PassiveExpiringMap in a Collections.synchronizedMap seems to break the PassiveExpiringMap's expiration. Specifically, the operation passiveExirpringMap.keySet() no longer removes expired entries prior to returning (which it should, according to the PassiveExpiringMap doc).

      I wrote this simple program to prove it. It puts a key into two PassiveExpiringMaps with the same expiration policy of 15 seconds, one of which is wrapped in a SyncronizedMap. Then it loops through the maps, calling keySet() on them. The entry disappears from the unwrapped map after 15 seconds, but NOT the wrapped map! Code and log output below.

       

      import java.text.SimpleDateFormat;
      import java.util.Collections;
      import java.util.Date;
      import java.util.Map;
      import java.util.Set;
      import java.util.UUID;
      import java.util.concurrent.TimeUnit;
      import org.apache.commons.collections4.map.PassiveExpiringMap;
      
      public class Main {
      
          // unwrapped
          static Map<String, String> unwrappedMap;
      
          // wrapped
          static Map<String, String> wrappedMap;
      
          public static void main(String[] args) throws InterruptedException {
              unwrappedMap = new PassiveExpiringMap<>(15, TimeUnit.SECONDS);
              wrappedMap = Collections.synchronizedMap(new PassiveExpiringMap<>(15, TimeUnit.SECONDS));
      
              // Put something in the maps
              String key = UUID.randomUUID().toString();
              logWithTimestamp("Putting key " + key);
              unwrappedMap.put(key, "");
              wrappedMap.put(key, "");
      
              // Check the map until the key has expired, sleeping 1 second between checks
              while (true) {
                  Set<String> keysInUnwrappedMap = unwrappedMap.keySet();
                  Set<String> keysInWrappedMap = wrappedMap.keySet();
                  logWithTimestamp("Keys in unwrapped map: " + String.join(", ", keysInUnwrappedMap));
                  logWithTimestamp("Keys in wrapped map: " + String.join(", ", keysInWrappedMap));
                  Thread.sleep(1000);
              }
          }
      
          private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
          private static void logWithTimestamp(String string) {
              Date resultdate = new Date(System.currentTimeMillis());
              System.out.println("[" + sdf.format(resultdate) + "] " + string);
          }
      
      }
      
      
      [10:02:41] Putting key fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:41] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:41] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:42] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:42] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:43] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:43] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:44] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:44] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:45] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:45] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:46] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:46] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:47] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:47] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:48] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:48] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:49] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:49] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:50] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:50] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:51] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:51] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:52] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:52] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:53] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:53] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:54] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:54] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:55] Keys in unwrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:55] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:56] Keys in unwrapped map: 
      [10:02:56] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:57] Keys in unwrapped map: 
      [10:02:57] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:58] Keys in unwrapped map: 
      [10:02:58] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      [10:02:59] Keys in unwrapped map: 
      [10:02:59] Keys in wrapped map: fce9d058-8f13-4e6d-814e-90e03f575678
      

        Attachments

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              amsadowski5 Aidan Sadowski
            • Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

              • Created:
                Updated: