diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b0b0a336..a96d26b22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. - [#1994] Replace command in commandline with three argument causing replacements file load - [#1477] Open file (Context menu) with unicode characters, unicode in paths, on Windows - Starting app with parameters causing wrong GUI init +- [#1991] ConcurrentModificationException on clearing cache thread ## [18.4.0] - 2023-03-19 ### Added @@ -3019,6 +3020,7 @@ All notable changes to this project will be documented in this file. [#1993]: https://www.free-decompiler.com/flash/issues/1993 [#1994]: https://www.free-decompiler.com/flash/issues/1994 [#1477]: https://www.free-decompiler.com/flash/issues/1477 +[#1991]: https://www.free-decompiler.com/flash/issues/1991 [#1888]: https://www.free-decompiler.com/flash/issues/1888 [#1892]: https://www.free-decompiler.com/flash/issues/1892 [#355]: https://www.free-decompiler.com/flash/issues/355 diff --git a/libsrc/ffdec_lib/src/com/jpexs/helpers/Cache.java b/libsrc/ffdec_lib/src/com/jpexs/helpers/Cache.java index 7a928cc80..e4fb32b4b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/helpers/Cache.java +++ b/libsrc/ffdec_lib/src/com/jpexs/helpers/Cache.java @@ -22,7 +22,6 @@ import java.io.File; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -43,6 +42,8 @@ public class Cache implements Freed { private Map cache; private Map lastAccessed; + private static final Object instancesLock = new Object(); + private static final List> instances = new ArrayList<>(); public static final int STORAGE_FILES = 1; @@ -54,27 +55,28 @@ public class Cache implements Freed { private final boolean memoryOnly; private final String name; - + private final boolean temporary; - + private static final long CLEAN_INTERVAL = 5 * 1000; //5 seconds - - private static Thread oldCleaner = null; + + private static Thread oldCleaner = null; static { Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { - for (WeakReference cw : instances) { - Cache c = cw.get(); - if (c != null) { - c.clear(); - c.free(); + synchronized (instancesLock) { + for (WeakReference cw : instances) { + Cache c = cw.get(); + if (c != null) { + c.clear(); + c.free(); + } } } } - }); } @@ -83,37 +85,41 @@ public class Cache implements Freed { oldCleaner = new Thread("Old cache cleaner") { @Override public void run() { - while(!Thread.interrupted()) { + while (!Thread.interrupted()) { try { Thread.sleep(CLEAN_INTERVAL); } catch (InterruptedException ex) { return; } try { - clearAllOld(); + clearAllOld(); } catch (Exception cme) { Logger.getLogger(Cache.class.getSimpleName()).log(Level.SEVERE, "Error during clearing cache thread", cme); } } - } + } }; oldCleaner.setDaemon(true); oldCleaner.setPriority(Thread.MIN_PRIORITY); oldCleaner.start(); } Cache instance = new Cache<>(weak, memoryOnly, name, temporary); - instances.add(new WeakReference<>(instance)); + synchronized (instancesLock) { + instances.add(new WeakReference<>(instance)); + } return instance; } private static int storageType = STORAGE_FILES; public static void clearAll() { - for (WeakReference cw : instances) { - Cache c = cw.get(); - if (c != null) { - c.clear(); - c.initCache(); + synchronized (instancesLock) { + for (WeakReference cw : instances) { + Cache c = cw.get(); + if (c != null) { + c.clear(); + c.initCache(); + } } } } @@ -174,7 +180,7 @@ public class Cache implements Freed { initCache(); } - public synchronized boolean contains(K key) { + public synchronized boolean contains(K key) { boolean ret = cache.containsKey(key); if (ret) { lastAccessed.put(key, System.currentTimeMillis()); @@ -208,7 +214,7 @@ public class Cache implements Freed { @Override public boolean isFreeing() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new UnsupportedOperationException("Not supported yet."); } @Override @@ -223,7 +229,7 @@ public class Cache implements Freed { ret.addAll(cache.keySet()); return ret; } - + private synchronized int clearOld() { long currentTime = System.currentTimeMillis(); Set keys = new HashSet<>(lastAccessed.keySet()); @@ -232,26 +238,28 @@ public class Cache implements Freed { return 0; } int num = 0; - for(K key:keys) { + for (K key : keys) { long time = lastAccessed.get(key); if (time < currentTime - temporaryThreshold) { remove(key); num++; } - } + } return num; } - + private static void clearAllOld() { int num = 0; - for (WeakReference cw : instances) { - Cache c = cw.get(); - if (c != null) { - if (c.temporary) { - num += c.clearOld(); + synchronized (instancesLock) { + for (WeakReference cw : instances) { + Cache c = cw.get(); + if (c != null) { + if (c.temporary) { + num += c.clearOld(); + } } } - } + } if (num > 0) { System.gc(); }