Aidan Sadowski created COLLECTIONS-776:
------------------------------------------

             Summary: Wrapping PassiveExpiringMap in a SynchonizedMap breaks 
expiration
                 Key: COLLECTIONS-776
                 URL: https://issues.apache.org/jira/browse/COLLECTIONS-776
             Project: Commons Collections
          Issue Type: Bug
          Components: Collection
    Affects Versions: 4.4
         Environment: Java 8

Built on mac OS Catalina 10.15 using gradle 5.2.1 in Intellij
            Reporter: Aidan Sadowski


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)}}|https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html?is-external=true#synchronizedMap-java.util.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.

 
{code:java}
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);
    }

}

{code}
{code:java}
[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
{code}



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to