changeset 417:e27e7efee021

meth: integrate possible fix for 7177472
author jrose
date Fri, 20 Jul 2012 01:46:55 -0700
parents 971e1b4846fc
children 4a0cedf169a2
files meth-7177472.patch series
diffstat 2 files changed, 313 insertions(+), 50 deletions(-) [+]
line wrap: on
line diff
--- a/meth-7177472.patch	Thu Jul 19 19:28:11 2012 -0700
+++ b/meth-7177472.patch	Fri Jul 20 01:46:55 2012 -0700
@@ -3,64 +3,327 @@
 Reviewed-by: ?
 Contributed-by: aleksey.shipilev@oracle.com
 
-diff -r 16d0b8b9a29f src/share/classes/java/lang/invoke/MethodType.java
---- a/src/share/classes/java/lang/invoke/MethodType.java	Thu Jun 14 15:45:24 2012 -0700
-+++ b/src/share/classes/java/lang/invoke/MethodType.java	Sat Jun 16 00:06:18 2012 +0400
-@@ -30,6 +30,9 @@
+diff --git a/src/share/classes/java/lang/invoke/MethodType.java b/src/share/classes/java/lang/invoke/MethodType.java
+--- a/src/share/classes/java/lang/invoke/MethodType.java
++++ b/src/share/classes/java/lang/invoke/MethodType.java
+@@ -28,11 +28,13 @@
+ import sun.invoke.util.Wrapper;
+ import java.lang.ref.WeakReference;
+ import java.lang.ref.ReferenceQueue;
++import java.util.concurrent.ConcurrentHashMap;
+ import java.util.Arrays;
  import java.util.Collections;
- import java.util.HashMap;
  import java.util.List;
-+import java.util.concurrent.ConcurrentHashMap;
-+import java.util.concurrent.ConcurrentMap;
-+
  import sun.invoke.util.BytecodeDescriptor;
  import static java.lang.invoke.MethodHandleStatics.*;
++import java.lang.ref.Reference;
  
-@@ -135,8 +138,8 @@
-         return new IndexOutOfBoundsException(num.toString());
-     }
- 
--    static final HashMap<MethodType,MethodType> internTable
--            = new HashMap<MethodType, MethodType>();
-+    static final ConcurrentMap<MethodType,MethodType> internTable
-+            = new ConcurrentHashMap<MethodType, MethodType>();
- 
-     static final Class<?>[] NO_PTYPES = {};
- 
-@@ -239,11 +242,12 @@
+ /**
+  * A method type represents the arguments and return type accepted and
+@@ -240,8 +242,7 @@
+             ptypes = NO_PTYPES; trusted = true;
          }
          MethodType mt1 = new MethodType(rtype, ptypes);
-         MethodType mt0;
--        synchronized (internTable) {
--            mt0 = internTable.get(mt1);
--            if (mt0 != null)
--                return mt0;
+-        MethodType mt0;
+-        mt0 = internTable.get(mt1);
++        MethodType mt0 = internTable.get(mt1);
+         if (mt0 != null)
+             return mt0;
+         if (!trusted)
+@@ -939,84 +940,17 @@
+      * @see         java.util.HashSet
+      * @see         java.util.WeakHashMap
+      * @see         java.lang.ref.WeakReference
++     * @see         concurrent.ConcurrentHashMap
+      */
+     private static class WeakInternSet {
+-        // The default initial capacity -- MUST be a power of two.
+-        private static final int DEFAULT_INITIAL_CAPACITY = 16;
+-
+-        // The maximum capacity, used if a higher value is implicitly specified
+-        // by either of the constructors with arguments.
+-        // MUST be a power of two <= 1<<30.
+-        private static final int MAXIMUM_CAPACITY = 1 << 30;
+-
+-        // The load factor used when none specified in constructor.
+-        private static final float DEFAULT_LOAD_FACTOR = 0.75f;
+-
+-        // The table, resized as necessary. Length MUST Always be a power of two.
+-        private Entry[] table;
+-
+-        // The number of entries contained in this set.
+-        private int size;
+-
+-        // The next size value at which to resize (capacity * load factor).
+-        private int threshold;
+-
+-        // The load factor for the hash table.
+-        private final float loadFactor;
+-
+-        // Reference queue for cleared WeakEntries
++        private final ConcurrentHashMap<Entry, Entry> map = new ConcurrentHashMap<Entry, Entry>();
+         private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
+ 
+-        private Entry[] newTable(int n) {
+-            return new Entry[n];
+-        }
+-
+         /**
+          * Constructs a new, empty <tt>WeakInternSet</tt> with the default initial
+-         * capacity (16) and load factor (0.75).
++         * capacity and load factor, inherited from {@link ConcurrentHashMap}.
+          */
+         WeakInternSet() {
+-            this.loadFactor = DEFAULT_LOAD_FACTOR;
+-            threshold = DEFAULT_INITIAL_CAPACITY;
+-            table = newTable(DEFAULT_INITIAL_CAPACITY);
+-        }
+-
+-        /**
+-         * Applies a supplemental hash function to a given hashCode, which
+-         * defends against poor quality hash functions.  This is critical
+-         * because hashing uses power-of-two length hash tables, that
+-         * otherwise encounter collisions for hashCodes that do not differ
+-         * in lower bits.
+-         * @param h preliminary hash code value
+-         * @return supplemental hash code value
+-         */
+-        private static int hash(int h) {
+-            // This function ensures that hashCodes that differ only by
+-            // constant multiples at each bit position have a bounded
+-            // number of collisions (approximately 8 at default load factor).
+-            h ^= (h >>> 20) ^ (h >>> 12);
+-            return h ^ (h >>> 7) ^ (h >>> 4);
+-        }
+-
+-        /**
+-         * Checks for equality of non-null reference x and possibly-null y.  By
+-         * default uses Object.equals.
+-         * @param x first object to compare
+-         * @param y second object to compare
+-         * @return <tt>true</tt> if objects are equal
+-         */
+-        private static boolean eq(Object x, Object y) {
+-            return x == y || x.equals(y);
+-        }
+-
+-        /**
+-         * Returns index for hash code h.
+-         * @param h      raw hash code
+-         * @param length length of table (power of 2)
+-         * @return index in table
+-         */
+-        private static int indexFor(int h, int length) {
+-            return h & (length-1);
+         }
+ 
+         /**
+@@ -1024,39 +958,11 @@
+          */
+         private void expungeStaleEntries() {
+             for (Object x; (x = queue.poll()) != null; ) {
+-                synchronized (queue) {
+-                    Entry entry = (Entry) x;
+-                    int i = indexFor(entry.hash, table.length);
+-                    Entry prev = table[i];
+-                    Entry p = prev;
+-                    while (p != null) {
+-                        Entry next = p.next;
+-                        if (p == entry) {
+-                            if (prev == entry)
+-                                table[i] = next;
+-                            else
+-                                prev.next = next;
+-                            entry.next = null;
+-                            size--;
+-                            break;
+-                        }
+-                        prev = p;
+-                        p = next;
+-                    }
+-                }
++                map.remove((Entry) x);
+             }
+         }
+ 
+         /**
+-         * Returns the table after first expunging stale entries.
+-         * @return an expunged hash table
+-         */
+-        private Entry[] getTable() {
+-            expungeStaleEntries();
+-            return table;
+-        }
+-
+-        /**
+          * Returns the entry to which the specified value is mapped,
+          * or {@code null} if this set contains no entry for the value.
+          *
+@@ -1068,16 +974,15 @@
+          * @param value value to search for in set
+          * @return interned value if in set, otherwise <tt>null</tt>
+          */
+-        synchronized MethodType get(MethodType value) {
+-            int h = hash(value.hashCode());
+-            Entry[] tab = getTable();
+-            int index = indexFor(h, tab.length);
+-            Entry e = tab[index];
+-            MethodType g;
+-            while (e != null) {
+-                if (e.hash == h && eq(value, g = e.get()))
+-                    return g;
+-                e = e.next;
++        MethodType get(MethodType elem) {
++            expungeStaleEntries();
 +
-+        mt0 = internTable.get(mt1);
-+        if (mt0 != null) {
-+            return mt0;
++            Entry value = map.get(new Entry(elem));
++            if (value != null) {
++                MethodType res = value.get();
++                if (res != null) {
++                    return res;
++                }
+             }
+             return null;
          }
+@@ -1091,86 +996,20 @@
+          * @return the previous entry associated with <tt>value</tt>, or
+          *         <tt>value</tt> if there was no previous entry found
+          */
+-        synchronized MethodType add(MethodType value) {
+-            int h = hash(value.hashCode());
+-            Entry[] tab = getTable();
+-            int i = indexFor(h, tab.length);
+-            MethodType g;
+-            for (Entry e = tab[i]; e != null; e = e.next) {
+-                if (h == e.hash && eq(value, g = e.get())) {
+-                    return g;
+-                }
+-            }
+-            Entry e = tab[i];
+-            tab[i] = new Entry(value, queue, h, e);
+-            if (++size >= threshold)
+-                resize(tab.length * 2);
+-            return value;
+-        }
+-
+-        /**
+-         * Rehashes the contents of this set into a new array with a
+-         * larger capacity.  This method is called automatically when the
+-         * number of keys in this set reaches its threshold.
+-         *
+-         * If current capacity is MAXIMUM_CAPACITY, this method does not
+-         * resize the set, but sets threshold to Integer.MAX_VALUE.
+-         * This has the effect of preventing future calls.
+-         *
+-         * @param newCapacity the new capacity, MUST be a power of two;
+-         *        must be greater than current capacity unless current
+-         *        capacity is MAXIMUM_CAPACITY (in which case value
+-         *        is irrelevant)
+-         */
+-        private void resize(int newCapacity) {
+-            Entry[] oldTable = getTable();
+-            int oldCapacity = oldTable.length;
+-            if (oldCapacity == MAXIMUM_CAPACITY) {
+-                threshold = Integer.MAX_VALUE;
+-                return;
+-            }
+-
+-            Entry[] newTable = newTable(newCapacity);
+-            transfer(oldTable, newTable);
+-            table = newTable;
+-
+-            /*
+-             * If ignoring null elements and processing ref queue caused massive
+-             * shrinkage, then restore old table.  This should be rare, but avoids
+-             * unbounded expansion of garbage-filled tables.
+-             */
+-            if (size >= threshold / 2) {
+-                threshold = (int)(newCapacity * loadFactor);
+-            } else {
++        MethodType add(MethodType elem) {
++            // Playing double race here, and so spinloop is required.
++            // First race is with two concurrent updaters.
++            // Second race is with GC purging weak ref under our feet.
++            // Hopefully, we always end up with single spin.
++            MethodType interned;
++            do {
+                 expungeStaleEntries();
+-                transfer(newTable, oldTable);
+-                table = oldTable;
+-            }
+-        }
+-
+-        /**
+-         * Transfers all entries from src to dest tables
+-         * @param src  original table
+-         * @param dest new table
+-         */
+-        private void transfer(Entry[] src, Entry[] dest) {
+-            for (int j = 0; j < src.length; ++j) {
+-                Entry e = src[j];
+-                src[j] = null;
+-                while (e != null) {
+-                    Entry next = e.next;
+-                    MethodType key = e.get();
+-                    if (key == null) {
+-                        e.next = null;  // Help GC
+-                        size--;
+-                    } else {
+-                        int i = indexFor(e.hash, dest.length);
+-                        e.next = dest[i];
+-                        dest[i] = e;
+-                    }
+-                    e = next;
+-                }
+-            }
++                Entry e = new Entry(elem, queue);
++                Entry exist = map.putIfAbsent(e, e);
++                Entry winner = (exist != null ? exist : e);
++                interned = winner.get();
++            } while (interned == null);
++            return interned;
+         }
+ 
+         /**
+@@ -1179,18 +1018,36 @@
+          */
+         private static class Entry extends WeakReference<MethodType> {
+             final int hash;
+-            Entry next;
+ 
+             /**
+              * Creates new entry.
+              */
++            Entry(MethodType key) {
++                super(key);
++                hash = key.hashCode();
++            }
 +
-         if (!trusted)
-             // defensively copy the array passed in by the user
-             mt1 = new MethodType(rtype, ptypes.clone());
-@@ -254,13 +258,13 @@
-             // This is a principal (erased) type; show it to the JVM.
-             MethodHandleNatives.init(mt1);
+             Entry(MethodType key,
+-                  ReferenceQueue<Object> queue,
+-                  int hash, Entry next) {
++                  ReferenceQueue<Object> queue) {
+                 super(key, queue);
+-                this.hash  = hash;
+-                this.next  = next;
++                hash = key.hashCode();
+             }
++
++            @Override
++            public boolean equals(Object obj) {
++                if ((obj != null) && (obj instanceof Entry)) {
++                    Object that = ((Entry) obj).get();
++                    Object mine = get();
++                    return (that == null || mine == null) ? (this == obj) : mine.equals(that);
++                }
++                return false;
++            }
++
++            @Override
++            public int hashCode() {
++                return hash;
++            }
++
          }
--        synchronized (internTable) {
--            mt0 = internTable.get(mt1);
--            if (mt0 != null)
--                return mt0;
--            internTable.put(mt1, mt1);
-+
-+        MethodType exist = internTable.putIfAbsent(mt1, mt1);
-+        if (exist != null) {
-+            return exist;
-+        } else {
-+            return mt1;
-         }
--        return mt1;
      }
- 
-     private static final MethodType[] objectOnlyTypes = new MethodType[20];
+ }
--- a/series	Thu Jul 19 19:28:11 2012 -0700
+++ b/series	Fri Jul 20 01:46:55 2012 -0700
@@ -9,7 +9,7 @@
 meth-lazy-7023639.bmh.patch     #-/meth #+78f1f4e4e9c7
 
 # non-pushed files are under review or development, or merely experimental:
-meth-7177472.patch              #(78f1f4e4e9c7) #-buildable
+meth-7177472.patch              #-/meth #+78f1f4e4e9c7
 indy.pack.patch                 #-/meth #+78f1f4e4e9c7 #-buildable
 meth.patch                      #-/meth #+78f1f4e4e9c7