From bdd9feb3ea5825d267e97876b5c9ff0c2ca2a461 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 11 Oct 2019 20:56:26 -0400 Subject: [PATCH] async buffered image: fix listener leak from subscribing to already loaded images Listeners are never removed from the image, and for hot images in the image cache, the listeners are leaking. Since the images only change once after loading, do not queue any further listeners once it has been loaded. --- .../client/game/AsyncBufferedImage.java | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/game/AsyncBufferedImage.java b/runelite-client/src/main/java/net/runelite/client/game/AsyncBufferedImage.java index 0290c7856a..669ce38b8a 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/AsyncBufferedImage.java +++ b/runelite-client/src/main/java/net/runelite/client/game/AsyncBufferedImage.java @@ -26,8 +26,8 @@ package net.runelite.client.game; import java.awt.image.BufferedImage; +import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; @@ -35,7 +35,9 @@ import javax.swing.JLabel; public class AsyncBufferedImage extends BufferedImage { - private final List listeners = new CopyOnWriteArrayList<>(); + private final List listeners = new ArrayList<>(); + private boolean loaded; + public AsyncBufferedImage(int width, int height, int imageType) { super(width, height, imageType); @@ -44,19 +46,28 @@ public class AsyncBufferedImage extends BufferedImage /** * Call when the buffer has been changed */ - public void changed() + public synchronized void changed() { + loaded = true; for (Runnable r : listeners) { r.run(); } + listeners.clear(); } /** * Register a function to be ran when the buffer has changed */ - public void onChanged(Runnable r) + public synchronized void onChanged(Runnable r) { + if (loaded) + { + // If the image has already been loaded, further listeners will not fire. Do not + // queue them to avoid leaking listeners. + return; + } + listeners.add(r); } @@ -78,7 +89,13 @@ public class AsyncBufferedImage extends BufferedImage private ImageIcon makeIcon(JComponent c) { - listeners.add(c::repaint); + synchronized (this) + { + if (!loaded) + { + listeners.add(c::repaint); + } + } return new ImageIcon(this); } }