[
https://issues.apache.org/jira/browse/COLLECTIONS-776?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17426654#comment-17426654
]
ckurz commented on COLLECTIONS-776:
-----------------------------------
Hi, I belive I can fix this, does anybody mind if I do?
> 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
> Priority: Major
>
> 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)