changeset 854:8b01a6e690ab

RT-21046: hold strong reference to StyleCacheKey
author David Grieve<david.grieve@oracle.com>
date Mon, 23 Apr 2012 13:54:45 -0400
parents a09aa02b8189
children 228024299e92
files javafx-ui-common/src/com/sun/javafx/css/StyleHelper.java javafx-ui-common/src/com/sun/javafx/css/StyleManager.java
diffstat 2 files changed, 51 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/javafx-ui-common/src/com/sun/javafx/css/StyleHelper.java	Fri Apr 20 10:59:37 2012 -0400
+++ b/javafx-ui-common/src/com/sun/javafx/css/StyleHelper.java	Mon Apr 23 13:54:45 2012 -0400
@@ -135,11 +135,6 @@
      *
      * The key is comprised of this helper's index, plus the
      * indices of all this node's parents' helper's indices.
-     * We know that the indices are contiguous so we can
-     * construct a unique key from the indices. If the indices
-     * are [1, 2, 3], then the unique key will be 123. If
-     * the indices are [50, 2, 18], then the unique key will be
-     *  50218.
      *
      * The values in the cache styles that apply are determined
      * by the node's state and the state of its parents. This unique combination
@@ -155,8 +150,19 @@
      *
      * Since all StyleHelpers are relevant to a Scene, valueCache is
      * created by StyleManager.StylesheetContainer and is passed in.
+     * Note that all StyleHelper instances within a given Scene all 
+     * share the same valueCache! 
      */
-    Map<Reference<StyleCacheKey>, List<CacheEntry>> valueCache;
+    Map<StyleCacheKey, List<CacheEntry>> valueCache;
+    
+    /**
+     * Outside of StyleHelper, StyleCacheKeys are Reference&lt;StyleCacheKey&gt;.
+     * This Map holds the StyleHelper as the key so the Reference won't get
+     * collected. At least, thats the theory. This is set from StyleManager.
+     * As with valueCache, there is only one shared instance of this Map
+     * for a given Scene.
+     */
+    Map<StyleCacheKey,Reference<StyleCacheKey>> keysInUse;
 
     /**
      * Called from Node.impl_createStyleHelper before a getting a new
@@ -197,15 +203,16 @@
 
         // If we encounter a key with a null referent, then the key is added
         // to a list and then removed later in order to avoid concurrent mod.
-        List<Reference<StyleCacheKey>> keysToRemove = null;
+        List<StyleCacheKey> keysToRemove = null;
 
         Reference<StyleCacheKey> existingKeyRef = null;
 
-        for(Reference<StyleCacheKey> keyRef : valueCache.keySet()) {
-            final StyleCacheKey key = keyRef.get();
-            if (key == null) {
-                if (keysToRemove == null) keysToRemove = new ArrayList<Reference<StyleCacheKey>>();
-                keysToRemove.add(keyRef);
+        for(Entry<StyleCacheKey,Reference<StyleCacheKey>> entry : keysInUse.entrySet()) {
+            final StyleCacheKey key = entry.getKey();
+            final Reference<StyleCacheKey> keyRef = entry.getValue();
+            if (keyRef.get() == null) {
+                if (keysToRemove == null) keysToRemove = new ArrayList<StyleCacheKey>();
+                keysToRemove.add(key);
                 continue;
             }
             if (Arrays.equals(key.indices, indices)) {
@@ -215,8 +222,9 @@
         }
 
         if (keysToRemove != null) {
-            for (Reference<StyleCacheKey> keyRef : keysToRemove) {
-                for(CacheEntry entry : valueCache.remove(keyRef)) {
+            for (StyleCacheKey key : keysToRemove) {
+                keysInUse.remove(key);
+                for(CacheEntry entry : valueCache.remove(key)) {
                     entry.values.clear();
                 }
             }
@@ -245,9 +253,10 @@
         }
 
         // No existing cache was found for this set of helpers.
-        Reference<StyleCacheKey> keyRef =
-            new WeakReference<StyleCacheKey>(new StyleCacheKey(indices, pclassMasks));
-        valueCache.put(keyRef, new ArrayList<CacheEntry>());
+        final StyleCacheKey key = new StyleCacheKey(indices, pclassMasks);
+        final Reference<StyleCacheKey> keyRef = new WeakReference(key);
+        valueCache.put(key, new ArrayList<CacheEntry>());
+        keysInUse.put(key, keyRef);
         return keyRef;
     }
 
@@ -303,18 +312,15 @@
         //
         final Reference<StyleCacheKey> keyRef = node.impl_getStyleCacheKey();
 
-        final List<CacheEntry> cachedValues = valueCache.get(keyRef);
+        final StyleCacheKey key = keyRef != null ? keyRef.get() : null;
+        if(key == null) return null;
+        
+        final List<CacheEntry> cachedValues = valueCache.get(key);
         if (cachedValues == null) return null;
 
         //
         // Find the entry in the list that matches the states
         //
-        StyleCacheKey key = keyRef.get();
-        if(key == null) {
-            for (CacheEntry ce : cachedValues) ce.values.clear();
-            valueCache.remove(keyRef);
-            return null;
-        }
         
         // pclassMask is the set of pseudoclasses that appear in the
         // style maps of this set of StyleHelpers. Calculated values are
--- a/javafx-ui-common/src/com/sun/javafx/css/StyleManager.java	Fri Apr 20 10:59:37 2012 -0400
+++ b/javafx-ui-common/src/com/sun/javafx/css/StyleManager.java	Mon Apr 23 13:54:45 2012 -0400
@@ -880,7 +880,19 @@
         // Since a StylesheetContainer is created for a Scene with stylesheets,
         // it makes sense that the container should own the valueCache. This
         // way, each scene gets its own valueCache.
-        private final Map<Reference<StyleHelper.StyleCacheKey>, List<StyleHelper.CacheEntry>> valueCache;
+        private final Map<StyleHelper.StyleCacheKey, List<StyleHelper.CacheEntry>> valueCache;
+
+        // For the Reference<StyleHelper.StyleCacheKey>, we manage the 
+        // Reference, not the garbage collector. Basically, when a StyleHelper's
+        // cache is cleared, we want to clear the ref right away which lets
+        // the node know immediately that the StyleCacheKey is no longer valid.
+        // This is bending the Reference paradigm a bit, but it does convey that 
+        // the referent is subject to anhilation. Anyway, for this to work,
+        // the same Reference needs to be returned and the StyleHelper needs
+        // a place to keep them. Like the valueCache, there is one of these
+        // mappings per scene.
+        private final Map<StyleHelper.StyleCacheKey, Reference<StyleHelper.StyleCacheKey>> keysInUse;
+        
         // ditto
         private int helperCount;
 
@@ -913,7 +925,8 @@
 
             StyleManager.getInstance().userAgentStylesheetMap.addListener(mapChangeListener);
 
-            valueCache = new HashMap<Reference<StyleHelper.StyleCacheKey>, List<StyleHelper.CacheEntry>>();
+            valueCache = new HashMap<StyleHelper.StyleCacheKey, List<StyleHelper.CacheEntry>>();
+            keysInUse = new HashMap<StyleHelper.StyleCacheKey, Reference<StyleHelper.StyleCacheKey>>();
             helperCount = 0;
         }
 
@@ -926,11 +939,13 @@
 
         private void clearCaches() {
 
-            for (Entry<Reference<StyleHelper.StyleCacheKey>, List<StyleHelper.CacheEntry>> entry : valueCache.entrySet()) {
+            for (Entry<StyleHelper.StyleCacheKey, List<StyleHelper.CacheEntry>> entry : valueCache.entrySet()) {
                 for (StyleHelper.CacheEntry cacheEntry : entry.getValue()) {
                     cacheEntry.values.clear();
                 }
-                entry.getKey().clear();
+                final Reference<StyleHelper.StyleCacheKey> ref = 
+                    keysInUse.remove(entry.getKey());
+                if (ref != null) ref.clear();
                 entry.getValue().clear();
             }
             valueCache.clear();
@@ -1179,6 +1194,7 @@
         StyleHelper getStyleHelper(Scene scene) {
             StyleHelper helper = StyleHelper.create(getStyles(scene), 0, ++helperCount);
             helper.valueCache = valueCache;
+            helper.keysInUse = keysInUse;
             return helper;
         }
 
@@ -1349,6 +1365,7 @@
                     ++(container.helperCount));
 
             helper.valueCache = container.valueCache;
+            helper.keysInUse = container.keysInUse;
             
             final Reference<StyleHelper> helperRef =
                 new WeakReference<StyleHelper>(helper);