Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoerg Kubitz2021-10-03 06:20:20 +0000
committerJörg Kubitz2021-11-01 14:01:55 +0000
commitdd760156c66832fac12586d4c7a38c80c1657afc (patch)
tree8ce24a23d8a25013d1d721125cef91a47819ab5a
parent7cddf9b5fe0cbb3b29588276d552da6a75b4de3b (diff)
downloadeclipse.platform.ui-dd760156c66832fac12586d4c7a38c80c1657afc.tar.gz
eclipse.platform.ui-dd760156c66832fac12586d4c7a38c80c1657afc.tar.xz
eclipse.platform.ui-dd760156c66832fac12586d4c7a38c80c1657afc.zip
Bug 576740 - Add a LRU resource cache for imagesI20211101-1800
Can be disable with system property: "org.eclipse.jface.resource.cacheSize=0" Default cacheSize=300. Removes all hotspots from org.eclipse.jface.resource.ResourceManager Does not only affect ResourceManager but allows also repeated calls of setImage() to check for same Image instance and avoid that NO-OP. Especially from loading images for toolbar: The problem was that the former reference counting did destroy the images just before reloading them again. Caches all Images loaded from filesystem/url during startup of org.eclipse.sdk.ide. Only the light weight RGBColorDescriptor is destroyed during startup of org.eclipse.sdk.ide. Change-Id: I5bfd38dce66ebcc062e140098316fda826e7bf5e Signed-off-by: Joerg Kubitz <jkubitz-eclipse@gmx.de> Reviewed-on: https://git.eclipse.org/r/c/platform/eclipse.platform.ui/+/186086 Tested-by: Platform Bot <platform-bot@eclipse.org> Reviewed-by: Sebastian Zarnekow <sebastian.zarnekow@gmail.com>
-rw-r--r--bundles/org.eclipse.jface/build.properties1
-rw-r--r--bundles/org.eclipse.jface/src/org/eclipse/jface/resource/DeferredImageDescriptor.java2
-rw-r--r--bundles/org.eclipse.jface/src/org/eclipse/jface/resource/DerivedImageDescriptor.java1
-rw-r--r--bundles/org.eclipse.jface/src/org/eclipse/jface/resource/DeviceResourceDescriptor.java41
-rw-r--r--bundles/org.eclipse.jface/src/org/eclipse/jface/resource/FileImageDescriptor.java1
-rw-r--r--bundles/org.eclipse.jface/src/org/eclipse/jface/resource/ImageDescriptor.java3
-rw-r--r--bundles/org.eclipse.jface/src/org/eclipse/jface/resource/JFaceResources.java18
-rw-r--r--bundles/org.eclipse.jface/src/org/eclipse/jface/resource/LazyResourceManager.java124
-rw-r--r--bundles/org.eclipse.jface/src/org/eclipse/jface/resource/URLImageDescriptor.java4
-rw-r--r--tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/images/LazyResourceManagerTest.java362
10 files changed, 552 insertions, 5 deletions
diff --git a/bundles/org.eclipse.jface/build.properties b/bundles/org.eclipse.jface/build.properties
index 9b175c1f340..dfed40ab86d 100644
--- a/bundles/org.eclipse.jface/build.properties
+++ b/bundles/org.eclipse.jface/build.properties
@@ -19,3 +19,4 @@ bin.includes = plugin.properties,\
.options
src.includes = about.html
source.. = src/
+additional.bundles = org.eclipse.pde.api.tools.annotations
diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/DeferredImageDescriptor.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/DeferredImageDescriptor.java
index c1ff14c11a4..a94b003c039 100644
--- a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/DeferredImageDescriptor.java
+++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/DeferredImageDescriptor.java
@@ -57,6 +57,7 @@ final class DeferredImageDescriptor extends ImageDescriptor {
* @param supplier the supplier of the URL
*/
DeferredImageDescriptor(boolean useMissingImage, Supplier<URL> supplier) {
+ super(true);
this.supplier = Objects.requireNonNull(supplier);
this.useMissingImage = useMissingImage;
}
@@ -78,4 +79,5 @@ final class DeferredImageDescriptor extends ImageDescriptor {
}
return ImageDescriptor.createFromURL(url).createImage(returnMissingImageOnError, device);
}
+
}
diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/DerivedImageDescriptor.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/DerivedImageDescriptor.java
index 998843c425e..59c1f907125 100644
--- a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/DerivedImageDescriptor.java
+++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/DerivedImageDescriptor.java
@@ -40,6 +40,7 @@ final class DerivedImageDescriptor extends ImageDescriptor {
* @see SWT#IMAGE_GRAY
*/
public DerivedImageDescriptor(ImageDescriptor original, int swtFlags) {
+ super(true);
this.original = original;
flags = swtFlags;
}
diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/DeviceResourceDescriptor.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/DeviceResourceDescriptor.java
index ff09b416c76..3d0bfb0b843 100644
--- a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/DeviceResourceDescriptor.java
+++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/DeviceResourceDescriptor.java
@@ -30,6 +30,47 @@ import org.eclipse.swt.graphics.Device;
* @since 3.1
*/
public abstract class DeviceResourceDescriptor {
+ private final boolean shouldBeCached;
+
+ /**
+ * default constructor with shouldBeCached=false
+ */
+ public DeviceResourceDescriptor() {
+ this(false);
+ }
+
+ /**
+ * @param shouldBeCached Indicates if the resource instance described by the
+ * descriptor should to be kept by {@link ResourceManager}
+ * even if all references to the resource are lost (due to
+ * {@link ResourceManager#destroy(DeviceResourceDescriptor)}).<br>
+ * Should return true for resources that are costly to
+ * create (for example by involving I/O Operation). Has
+ * only an effect if caching is enabled (see
+ * org.eclipse.jface.resource.JFaceResources#cacheSize).
+ * Caching relies on {@link #equals(Object)} and
+ * {@link #hashCode()}. For equal
+ * DeviceResourceDescriptors the same Resource instance is
+ * returned by the {@link ResourceManager} instance return
+ * by
+ * {@link org.eclipse.jface.resource.JFaceResources#getResources(org.eclipse.swt.widgets.Display)}
+ * as long as the cache is big enough to cache all
+ * resources.<br>
+ * Instances which equal (in terms of
+ * {@link #equals(Object)}) must have the same
+ * shouldBeCached mode.
+ * @see LazyResourceManager
+ * @since 3.24
+ */
+ protected DeviceResourceDescriptor(boolean shouldBeCached) {
+ this.shouldBeCached = shouldBeCached;
+
+ }
+
+ final boolean shouldBeCached() {
+ return shouldBeCached;
+ }
+
/**
* Creates the resource described by this descriptor
*
diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/FileImageDescriptor.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/FileImageDescriptor.java
index 01ec171d0ad..a5251f4828f 100644
--- a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/FileImageDescriptor.java
+++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/FileImageDescriptor.java
@@ -81,6 +81,7 @@ class FileImageDescriptor extends ImageDescriptor {
* the name of the file
*/
FileImageDescriptor(Class<?> clazz, String filename) {
+ super(true);
this.location = clazz;
this.name = filename;
}
diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/ImageDescriptor.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/ImageDescriptor.java
index 21e21d4e576..11caab71401 100644
--- a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/ImageDescriptor.java
+++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/ImageDescriptor.java
@@ -73,6 +73,9 @@ public abstract class ImageDescriptor extends DeviceResourceDescriptor {
// do nothing
}
+ ImageDescriptor(boolean shouldBeCached) {
+ super(shouldBeCached);
+ }
/**
* Creates and returns a new image descriptor from a file.
*
diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/JFaceResources.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/JFaceResources.java
index 773e5a553ca..d087a4dacec 100644
--- a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/JFaceResources.java
+++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/JFaceResources.java
@@ -66,7 +66,7 @@ public class JFaceResources {
* Map of Display onto DeviceResourceManager. Holds all the resources for
* the associated display.
*/
- private static final Map<Display,DeviceResourceManager> registries = new HashMap<>();
+ private static final Map<Display, ResourceManager> registries = new HashMap<>();
/**
* The symbolic font name for the banner font (value
@@ -196,19 +196,29 @@ public class JFaceResources {
}
/**
+ * 300 is big enough to cache common images of eclipse IDE, and small enough to
+ * not blow OS.
+ */
+ private static final int cacheSize = Integer.getInteger("org.eclipse.jface.resource.cacheSize", 300).intValue(); //$NON-NLS-1$
+
+ /**
* Returns the global resource manager for the given display
*
* @since 3.1
*
- * @param toQuery
- * display to query
+ * @param toQuery display to query
* @return the global resource manager for the given display
*/
public static ResourceManager getResources(final Display toQuery) {
ResourceManager reg = registries.get(toQuery);
if (reg == null) {
- final DeviceResourceManager mgr = new DeviceResourceManager(toQuery);
+ final ResourceManager mgr;
+ if (cacheSize == 0) {
+ mgr = new DeviceResourceManager(toQuery);
+ } else {
+ mgr = new LazyResourceManager(cacheSize, new DeviceResourceManager(toQuery));
+ }
reg = mgr;
registries.put(toQuery, mgr);
toQuery.disposeExec(() -> {
diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/LazyResourceManager.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/LazyResourceManager.java
new file mode 100644
index 00000000000..939da7fa00c
--- /dev/null
+++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/LazyResourceManager.java
@@ -0,0 +1,124 @@
+package org.eclipse.jface.resource;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.pde.api.tools.annotations.NoReference;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * A LRU based ResourceManager Wrapper. Not to be used by clients.
+ */
+@NoReference
+public class LazyResourceManager extends ResourceManager {
+ /**
+ * This LRU Map only holds the DeviceResourceDescriptors which are not
+ * referenced otherwise anymore. The Resources itself are only cached by the
+ * parent ResourceManager.
+ */
+ private static class LruMap extends LinkedHashMap<DeviceResourceDescriptor, ResourceManager> {
+ private static final long serialVersionUID = 1L;
+ int cacheSize;
+
+ LruMap(int cacheSize) {
+ super(cacheSize, 0.75f, true); // last access-order
+ this.cacheSize = cacheSize;
+ }
+
+ @Override
+ protected boolean removeEldestEntry(java.util.Map.Entry<DeviceResourceDescriptor, ResourceManager> eldest) {
+ boolean remove = size() > cacheSize;
+ if (remove) {
+ // destroy resource which was not used recently:
+ eldest.getValue().destroy(eldest.getKey());
+ }
+ return remove;
+ }
+ }
+
+ private final ResourceManager parent;
+ private final LruMap unreferenced;
+ private final Map<DeviceResourceDescriptor, Integer> refCount;
+
+ /**
+ * @param cacheSize the lru cache size
+ * @param parent ResourceManager
+ */
+ public LazyResourceManager(int cacheSize, ResourceManager parent) {
+ this.parent = parent;
+ this.unreferenced = new LruMap(cacheSize);
+ this.refCount = new HashMap<>();
+ }
+
+ @Override
+ public Device getDevice() {
+ return parent.getDevice();
+ }
+
+ /**
+ * @param descriptor
+ * @return if a resource based on this descriptor should be cached.
+ */
+ private boolean shouldBeCached(DeviceResourceDescriptor descriptor) {
+ return descriptor.shouldBeCached();
+ }
+
+ @Override
+ protected Image getDefaultImage() {
+ return parent.getDefaultImage();
+ }
+
+ private static Integer createOrIncrease(@SuppressWarnings("unused") DeviceResourceDescriptor key, Integer refs) {
+ return Integer.valueOf(refs == null ? 1 : refs.intValue() + 1);
+ }
+
+ private static Integer decreaseOrRemove(@SuppressWarnings("unused") DeviceResourceDescriptor key, Integer refs) {
+ return refs.intValue() == 1 ? null : Integer.valueOf(refs.intValue() - 1);
+
+ }
+ @Override
+ public Object create(DeviceResourceDescriptor descriptor) {
+ if (!shouldBeCached(descriptor)) {
+ return parent.create(descriptor);
+ }
+ int updatedRefs = refCount.compute(descriptor, LazyResourceManager::createOrIncrease).intValue();
+ if (updatedRefs == 1) {
+ ResourceManager cached = unreferenced.remove(descriptor);
+ if (cached == null) {
+ return parent.create(descriptor);
+ }
+ // referenced again
+ } else {
+ assert !unreferenced.containsKey(descriptor);
+ }
+ return parent.find(descriptor);
+ }
+
+ @Override
+ public void destroy(DeviceResourceDescriptor descriptor) {
+ if (!shouldBeCached(descriptor)) {
+ parent.destroy(descriptor);
+ return;
+ }
+ Integer refsLeft = refCount.computeIfPresent(descriptor, LazyResourceManager::decreaseOrRemove);
+ if (refsLeft == null) {
+ // defer destroy:
+ ResourceManager old = unreferenced.put(descriptor, parent);
+ assert old == null;
+ }
+ }
+
+ @Override
+ public Object find(DeviceResourceDescriptor descriptor) {
+ if (!shouldBeCached(descriptor)) {
+ return parent.find(descriptor);
+ }
+ if (refCount.containsKey(descriptor)) {
+ return parent.find(descriptor);
+ }
+ return null;
+ }
+
+}
diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/URLImageDescriptor.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/URLImageDescriptor.java
index d5b577bf7e6..c150acd09f6 100644
--- a/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/URLImageDescriptor.java
+++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/resource/URLImageDescriptor.java
@@ -11,7 +11,7 @@
* Contributors:
* IBM Corporation - initial API and implementation
* Patrik Suzzi <psuzzi@gmail.com> - Bug 483465
- * Christoph Läubrich - Bug 567898 - [JFace][HiDPI] ImageDescriptor support alternative naming scheme for high dpi
+ * Christoph Läubrich - Bug 567898 - [JFace][HiDPI] ImageDescriptor support alternative naming scheme for high dpi
*******************************************************************************/
package org.eclipse.jface.resource;
@@ -107,6 +107,7 @@ class URLImageDescriptor extends ImageDescriptor {
* The URL to load the image from. Must be non-null.
*/
URLImageDescriptor(URL url) {
+ super(true);
this.url = url.toExternalForm();
}
@@ -333,4 +334,5 @@ class URLImageDescriptor extends ImageDescriptor {
}
return result;
}
+
}
diff --git a/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/images/LazyResourceManagerTest.java b/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/images/LazyResourceManagerTest.java
new file mode 100644
index 00000000000..980ba82cbab
--- /dev/null
+++ b/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/images/LazyResourceManagerTest.java
@@ -0,0 +1,362 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2016 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Patrik Suzzi <psuzzi@gmail.com> - Bug 490700
+ *******************************************************************************/
+package org.eclipse.jface.tests.images;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.resource.DeviceResourceDescriptor;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.resource.LazyResourceManager;
+import org.eclipse.jface.resource.ResourceManager;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.Image;
+
+import junit.framework.TestCase;
+
+public class LazyResourceManagerTest extends TestCase {
+ private static class CachableTestDescriptor extends DeviceResourceDescriptor {
+ CachableTestDescriptor() {
+ super(true);
+ }
+
+ @Override
+ public Object createResource(Device device) {
+ return null;
+ }
+
+ @Override
+ public void destroyResource(Object previouslyCreatedObject) {
+ }
+ }
+
+ private static class UncachableTestDescriptor extends DeviceResourceDescriptor {
+ UncachableTestDescriptor() {
+ super(false);
+ }
+
+ @Override
+ public Object createResource(Device device) {
+ return null;
+ }
+
+ @Override
+ public void destroyResource(Object previouslyCreatedObject) {
+ }
+ }
+
+ private static class TestResourceManager extends ResourceManager {
+ private Device device;
+ private Map<DeviceResourceDescriptor, AtomicReference<DeviceResourceDescriptor>> objects = new HashMap<>();
+ private Map<DeviceResourceDescriptor, Integer> refCount = new HashMap<>();
+ private Image defaultImage;
+
+ @Override
+ public Device getDevice() {
+ return device;
+ }
+
+ @Override
+ public AtomicReference<DeviceResourceDescriptor> create(DeviceResourceDescriptor descriptor) {
+ AtomicReference<DeviceResourceDescriptor> newInstance = new AtomicReference<>(descriptor);
+ AtomicReference<DeviceResourceDescriptor> previous = objects.putIfAbsent(descriptor, newInstance);
+ refCount.compute(descriptor, (k, refs) -> Integer.valueOf((refs == null ? 0 : refs.intValue()) + 1));
+ return previous == null ? newInstance : previous;
+ }
+
+ @Override
+ public void destroy(DeviceResourceDescriptor descriptor) {
+ if (!refCount.containsKey(descriptor)) {
+ throw new RuntimeException("not created");
+ }
+ int refs = refCount.get(descriptor).intValue();
+ refs--;
+ refCount.put(descriptor, Integer.valueOf(refs));
+ if (refs == 0) {
+ objects.remove(descriptor);
+ }
+
+ }
+
+ @Override
+ protected Image getDefaultImage() {
+ return defaultImage;
+ }
+
+ @Override
+ public AtomicReference<DeviceResourceDescriptor> find(DeviceResourceDescriptor descriptor) {
+ return objects.get(descriptor);
+ }
+
+ public void setDevice(Device device) {
+ this.device = device;
+ }
+
+ public void setDefaultImage(Image defaultImage) {
+ this.defaultImage = defaultImage;
+ }
+
+ }
+
+ public LazyResourceManagerTest(String name) {
+ super(name);
+ }
+
+ public void testDefaultImage() {
+ // note, we must touch the class to ensure the static initialer runs
+ // so the image registry is up to date
+ Dialog.getBlockedHandler();
+
+ @SuppressWarnings("deprecation")
+ String[] imageNames = new String[] { Dialog.DLG_IMG_ERROR, Dialog.DLG_IMG_INFO, Dialog.DLG_IMG_QUESTION,
+ Dialog.DLG_IMG_WARNING, Dialog.DLG_IMG_MESSAGE_ERROR, Dialog.DLG_IMG_MESSAGE_INFO,
+ Dialog.DLG_IMG_MESSAGE_WARNING };
+
+ ImageRegistry reg = JFaceResources.getImageRegistry();
+
+ TestResourceManager tst = new TestResourceManager();
+ LazyResourceManager mgr = new LazyResourceManager(2, tst) {
+ {
+ // getDefaultImage() is not not public
+ assertSame(tst.getDefaultImage(), getDefaultImage());
+ }
+ };
+ for (String imageName : imageNames) {
+ Image image = reg.get(imageName);
+ tst.setDefaultImage(image);
+ tst.setDevice(image.getDevice());
+ }
+ assertNotNull(tst.getDevice());
+ assertSame(tst.getDevice(), mgr.getDevice());
+
+ }
+
+ public void testUncachable() {
+ TestResourceManager tst = new TestResourceManager();
+ LazyResourceManager mgr = new LazyResourceManager(2, tst);
+
+ assertSame(tst.getDevice(), mgr.getDevice());
+ DeviceResourceDescriptor descriptor1 = new UncachableTestDescriptor();
+ {
+ Object created1 = mgr.create(descriptor1);
+ AtomicReference<DeviceResourceDescriptor> expected1 = tst.find(descriptor1);
+ assertSame(expected1, created1);
+ assertSame(expected1, mgr.find(descriptor1));
+ mgr.destroy(descriptor1);
+ assertDestroyed(expected1, mgr, tst, descriptor1);
+ }
+ }
+
+ void assertCached(AtomicReference<DeviceResourceDescriptor> previousInstance, LazyResourceManager mgr,
+ TestResourceManager tst, DeviceResourceDescriptor rd) {
+ assertNotNull(previousInstance);
+ assertNull(mgr.find(rd)); // destroyed but
+ assertNotNull(tst.find(rd)); // cached
+// would be best to ask mgr to creat a new resource, but that would influence the LRU order:
+// Object created = mgr.create(rd);
+// mgr.destroy(rd);
+ // assume mgr would create the instance given by parent tst:
+ AtomicReference<DeviceResourceDescriptor> created = tst.find(rd);
+ assertSame(previousInstance, created);
+ }
+
+ void assertAlife(AtomicReference<DeviceResourceDescriptor> previousInstance, LazyResourceManager mgr,
+ TestResourceManager tst, DeviceResourceDescriptor rd) {
+ assertNotNull(previousInstance);
+ assertSame(previousInstance, mgr.find(rd));
+ assertSame(previousInstance, tst.find(rd));
+ }
+
+ void assertDestroyed(AtomicReference<DeviceResourceDescriptor> previousInstance, LazyResourceManager mgr,
+ TestResourceManager tst, DeviceResourceDescriptor rd) {
+ assertNotNull(previousInstance);
+ assertNull(mgr.find(rd)); // destroyed and
+ assertNull(tst.find(rd)); // cached
+// would be best to ask mgr to creat a new resource, but that would influence the LRU order:
+// Object created = mgr.create(rd);
+// mgr.destroy(rd);
+// assertNotSame(previousInstance, created);
+ // assume mgr would create the instance given by parent tst:
+ AtomicReference<DeviceResourceDescriptor> created = tst.find(rd);
+ assertNotSame(previousInstance, created);
+ }
+
+ /**
+ * Creates multiple resources for 2 Descriptors. Only 1 of them can be cached
+ **/
+ @SuppressWarnings("unchecked")
+ public void testLazyResourceManagerRefCounting() {
+ TestResourceManager tst = new TestResourceManager();
+ LazyResourceManager mgr = new LazyResourceManager(1, tst);
+
+ assertSame(tst.getDevice(), mgr.getDevice());
+ DeviceResourceDescriptor descriptor1 = new CachableTestDescriptor();
+ AtomicReference<DeviceResourceDescriptor> expected1;
+ {
+ expected1 = (AtomicReference<DeviceResourceDescriptor>) mgr.create(descriptor1); // create first ref
+ assertAlife(expected1, mgr, tst, descriptor1);
+ mgr.create(descriptor1); // create second ref
+ assertAlife(expected1, mgr, tst, descriptor1);
+ mgr.destroy(descriptor1); // destroy second
+ assertAlife(expected1, mgr, tst, descriptor1);
+ mgr.destroy(descriptor1); // destroy first
+ }
+ assertCached(expected1, mgr, tst, descriptor1);
+ DeviceResourceDescriptor descriptor2 = new CachableTestDescriptor();
+ {
+ // exceeded cache capacity:
+ mgr.create(descriptor2);
+ mgr.destroy(descriptor2);
+ }
+ assertDestroyed(expected1, mgr, tst, descriptor1);
+ {
+ Object created1_ = mgr.create(descriptor1);
+ AtomicReference<DeviceResourceDescriptor> expected1_ = tst.find(descriptor1); // == descriptor1
+ assertNotSame(expected1, expected1_);
+ assertSame((expected1).get(), (expected1_).get()); // same descriptor
+ assertSame(expected1_, created1_);
+ assertAlife(expected1_, mgr, tst, descriptor1);
+ mgr.destroy(descriptor1);
+ assertCached(expected1_, mgr, tst, descriptor1);
+ }
+ }
+
+ /** Creates resources for 3 Descriptors. Only 2 of them can be cached **/
+ public void testLazyResourceManager() {
+ TestResourceManager tst = new TestResourceManager();
+ LazyResourceManager mgr = new LazyResourceManager(2, tst);
+
+ assertSame(tst.getDevice(), mgr.getDevice());
+ DeviceResourceDescriptor descriptor1 = new CachableTestDescriptor();
+ AtomicReference<DeviceResourceDescriptor> expected1;
+ {
+ Object created1 = mgr.create(descriptor1);
+ expected1 = tst.find(descriptor1);
+ assertSame(expected1, created1);
+ assertSame(expected1, mgr.find(descriptor1));
+ mgr.destroy(descriptor1);
+ }
+ assertCached(expected1, mgr, tst, descriptor1);
+ DeviceResourceDescriptor descriptor2 = new CachableTestDescriptor();
+ AtomicReference<DeviceResourceDescriptor> expected2;
+ {
+ Object created = mgr.create(descriptor2);
+ expected2 = tst.find(descriptor2);
+ assertSame(expected2, created);
+ assertSame(expected2, mgr.find(descriptor2));
+ mgr.destroy(descriptor2);
+ assertCached(expected2, mgr, tst, descriptor2);
+ mgr.create(descriptor2);
+ mgr.destroy(descriptor2);
+ assertCached(expected2, mgr, tst, descriptor2);
+ mgr.create(descriptor2);
+ mgr.destroy(descriptor2);
+ assertCached(expected2, mgr, tst, descriptor2);
+ }
+ assertCached(expected1, mgr, tst, descriptor1);
+ DeviceResourceDescriptor descriptor3 = new CachableTestDescriptor();
+ AtomicReference<DeviceResourceDescriptor> expected3;
+ {
+ Object created = mgr.create(descriptor3);
+ expected3 = tst.find(descriptor3);
+ assertSame(expected3, created);
+ assertSame(expected3, mgr.find(descriptor3));
+ mgr.destroy(descriptor3);
+ assertCached(expected3, mgr, tst, descriptor3);
+ }
+ assertDestroyed(expected1, mgr, tst, descriptor1); // lru size exceeded: not cached anymore
+ assertCached(expected2, mgr, tst, descriptor2);
+ assertCached(expected3, mgr, tst, descriptor3);
+ {
+ Object created1_ = mgr.create(descriptor1);
+ AtomicReference<DeviceResourceDescriptor> expected1_ = tst.find(descriptor1); // == descriptor1
+ assertNotSame(expected1, expected1_); // different resources
+ assertSame(((AtomicReference<?>) expected1).get(), ((AtomicReference<?>) expected1_).get()); // same
+ // descriptor
+ assertSame(expected1_, created1_);
+ assertSame(expected1_, mgr.find(descriptor1));
+ mgr.destroy(descriptor1);
+ assertCached(expected1_, mgr, tst, descriptor1);// cached again
+ }
+ assertCached(expected3, mgr, tst, descriptor3);
+ assertDestroyed(expected2, mgr, tst, descriptor2); // lru size exceeded: not cached anymore
+ }
+
+ /**
+ * Creates resources for 3 Descriptors. Only the 2 last recently used should be
+ * cached
+ **/
+ public void testLazyResourceManagerLRU() {
+ TestResourceManager tst = new TestResourceManager();
+ LazyResourceManager mgr = new LazyResourceManager(2, tst);
+
+ assertSame(tst.getDevice(), mgr.getDevice());
+ DeviceResourceDescriptor descriptor1 = new CachableTestDescriptor();
+ AtomicReference<DeviceResourceDescriptor> expected1;
+ {
+ Object created1 = mgr.create(descriptor1);
+ expected1 = tst.find(descriptor1);
+ assertSame(expected1, created1);
+ assertSame(expected1, mgr.find(descriptor1));
+ mgr.destroy(descriptor1);
+ }
+ assertCached(expected1, mgr, tst, descriptor1);
+ DeviceResourceDescriptor descriptor2 = new CachableTestDescriptor();
+ AtomicReference<DeviceResourceDescriptor> expected2;
+ {
+ Object created = mgr.create(descriptor2);
+ expected2 = tst.find(descriptor2);
+ assertSame(expected2, created);
+ assertSame(expected2, mgr.find(descriptor2));
+ mgr.destroy(descriptor2);
+ assertCached(expected2, mgr, tst, descriptor2);
+ }
+ assertCached(expected1, mgr, tst, descriptor1);
+ DeviceResourceDescriptor descriptor3 = new CachableTestDescriptor();
+ AtomicReference<DeviceResourceDescriptor> expected3;
+ {
+ Object created = mgr.create(descriptor3);
+ expected3 = tst.find(descriptor3);
+ assertSame(expected3, created);
+ assertSame(expected3, mgr.find(descriptor3));
+ mgr.destroy(descriptor3);
+ assertCached(expected3, mgr, tst, descriptor3);
+ }
+ { // *use* 2 again
+ Object created = mgr.create(descriptor2);
+ AtomicReference<DeviceResourceDescriptor> expected = tst.find(descriptor2);
+ assertSame(expected, created);
+ assertSame(expected, mgr.find(descriptor2));
+ mgr.destroy(descriptor2);
+ assertCached(expected, mgr, tst, descriptor2);
+ }
+ assertDestroyed(expected1, mgr, tst, descriptor1); // lru size exceeded: not cached anymore
+ assertCached(expected2, mgr, tst, descriptor2);
+ assertCached(expected3, mgr, tst, descriptor3);
+ {
+ Object created1_ = mgr.create(descriptor1);
+ AtomicReference<DeviceResourceDescriptor> expected1_ = tst.find(descriptor1); // == descriptor1
+ assertSame(expected1_, created1_);
+ assertSame(expected1_, mgr.find(descriptor1));
+ mgr.destroy(descriptor1);
+ assertCached(expected1_, mgr, tst, descriptor1);
+ }
+ assertDestroyed(expected3, mgr, tst, descriptor3); // lru size exceeded: not cached anymore
+ assertCached(expected2, mgr, tst, descriptor2); // 2 still cached, because recently used
+ }
+}

Back to the top