1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/test/compiler/6865031/Test.java Fri Jul 31 12:04:07 2009 -0700 1.3 @@ -0,0 +1,650 @@ 1.4 +/* 1.5 + * Copyright 2009 Goldman Sachs International. All Rights Reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. 1.11 + * 1.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.15 + * version 2 for more details (a copy is included in the LICENSE file that 1.16 + * accompanied this code). 1.17 + * 1.18 + * You should have received a copy of the GNU General Public License version 1.19 + * 2 along with this work; if not, write to the Free Software Foundation, 1.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.21 + * 1.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 1.23 + * CA 95054 USA or visit www.sun.com if you need additional information or 1.24 + * have any questions. 1.25 + * 1.26 + */ 1.27 + 1.28 +/* 1.29 + * @test 1.30 + * @bug 6865031 1.31 + * @summary Application gives bad result (throws bad exception) with compressed oops 1.32 + * @run main/othervm -XX:+UseCompressedOops -XX:HeapBaseMinAddress=32g -XX:-LoopUnswitching -XX:CompileCommand=inline,AbstractMemoryEfficientList.equals Test hello goodbye 1.33 + */ 1.34 + 1.35 +import java.lang.ref.ReferenceQueue; 1.36 +import java.lang.ref.WeakReference; 1.37 +import java.util.ArrayList; 1.38 +import java.util.Arrays; 1.39 +import java.util.List; 1.40 + 1.41 +interface MyList { 1.42 + public int size(); 1.43 + public Object set(final int index, final Object element); 1.44 + public Object get(final int index); 1.45 +} 1.46 + 1.47 +abstract class AbstractMemoryEfficientList implements MyList { 1.48 + abstract public int size(); 1.49 + abstract public Object get(final int index); 1.50 + abstract public Object set(final int index, final Object element); 1.51 + 1.52 + public boolean equals(Object o) { 1.53 + if (o == this) { 1.54 + return true; 1.55 + } 1.56 + 1.57 + if (!(o instanceof MyList)) { 1.58 + return false; 1.59 + } 1.60 + 1.61 + final MyList that = (MyList) o; 1.62 + if (this.size() != that.size()) { 1.63 + return false; 1.64 + } 1.65 + 1.66 + for (int i = 0; i < this.size(); i++) { 1.67 + try { 1.68 + if (!((this.get(i)).equals(that.get(i)))) { 1.69 + return false; 1.70 + } 1.71 + } catch (IndexOutOfBoundsException e) { 1.72 + System.out.println("THROWING RT EXC"); 1.73 + System.out.println("concurrent modification of this:" + this.getClass() + ":" + System.identityHashCode(this) + "; that:" + that.getClass() + ":" + System.identityHashCode(that) + "; i:" + i); 1.74 + e.printStackTrace(); 1.75 + System.exit(97); 1.76 + throw new RuntimeException("concurrent modification of this:" + this.getClass() + ":" + System.identityHashCode(this) + "; that:" + that.getClass() + ":" + System.identityHashCode(that) + "; i:" + i, e); 1.77 + } 1.78 + } 1.79 + return true; 1.80 + } 1.81 + 1.82 + public int hashCode() { 1.83 + int hashCode = 1; 1.84 + for (int i = 0; i < this.size(); i++) { 1.85 + Object obj = this.get(i); 1.86 + hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode()); 1.87 + } 1.88 + return hashCode; 1.89 + } 1.90 +} 1.91 + 1.92 +final class SingletonList extends AbstractMemoryEfficientList { 1.93 + private Object element1; 1.94 + 1.95 + SingletonList(final Object obj1) { 1.96 + super(); 1.97 + this.element1 = obj1; 1.98 + } 1.99 + 1.100 + public int size() { 1.101 + return 1; 1.102 + } 1.103 + 1.104 + public Object get(final int index) { 1.105 + if (index == 0) { 1.106 + return this.element1; 1.107 + } else { 1.108 + throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.size()); 1.109 + } 1.110 + } 1.111 + 1.112 + public Object set(final int index, final Object element) { 1.113 + if (index == 0) { 1.114 + final Object previousElement = this.element1; 1.115 + this.element1 = element; 1.116 + return previousElement; 1.117 + } else { 1.118 + throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.size()); 1.119 + } 1.120 + } 1.121 +} 1.122 + 1.123 +final class DoubletonList extends AbstractMemoryEfficientList { 1.124 + private Object element1; 1.125 + private Object element2; 1.126 + 1.127 + DoubletonList(final Object obj1, final Object obj2) { 1.128 + this.element1 = obj1; 1.129 + this.element2 = obj2; 1.130 + } 1.131 + 1.132 + public int size() { 1.133 + return 2; 1.134 + } 1.135 + 1.136 + public Object get(final int index) { 1.137 + switch (index) { 1.138 + case 0 : return this.element1; 1.139 + case 1 : return this.element2; 1.140 + default: throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.size()); 1.141 + } 1.142 + } 1.143 + 1.144 + public Object set(final int index, final Object element) { 1.145 + switch (index) { 1.146 + case 0 : 1.147 + { 1.148 + final Object previousElement = this.element1; 1.149 + this.element1 = element; 1.150 + return previousElement; 1.151 + } 1.152 + case 1 : 1.153 + { 1.154 + final Object previousElement = this.element2; 1.155 + this.element2 = element; 1.156 + return previousElement; 1.157 + } 1.158 + default : throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.size()); 1.159 + } 1.160 + } 1.161 +} 1.162 + 1.163 +class WeakPool<V> { 1.164 + protected static final int DEFAULT_INITIAL_CAPACITY = 16; 1.165 + private static final int MAXIMUM_CAPACITY = 1 << 30; 1.166 + private static final float DEFAULT_LOAD_FACTOR = 0.75f; 1.167 + 1.168 + protected Entry<V>[] table; 1.169 + 1.170 + private int size; 1.171 + protected int threshold; 1.172 + private final float loadFactor; 1.173 + private final ReferenceQueue<V> queue = new ReferenceQueue<V>(); 1.174 + 1.175 + public WeakPool() 1.176 + { 1.177 + this.loadFactor = DEFAULT_LOAD_FACTOR; 1.178 + threshold = DEFAULT_INITIAL_CAPACITY; 1.179 + table = new Entry[DEFAULT_INITIAL_CAPACITY]; 1.180 + } 1.181 + 1.182 + /** 1.183 + * Check for equality of non-null reference x and possibly-null y. By 1.184 + * default uses Object.equals. 1.185 + */ 1.186 + private boolean eq(Object x, Object y) 1.187 + { 1.188 + return x == y || x.equals(y); 1.189 + } 1.190 + 1.191 + /** 1.192 + * Return index for hash code h. 1.193 + */ 1.194 + private int indexFor(int h, int length) 1.195 + { 1.196 + return h & length - 1; 1.197 + } 1.198 + 1.199 + /** 1.200 + * Expunge stale entries from the table. 1.201 + */ 1.202 + private void expungeStaleEntries() 1.203 + { 1.204 + Object r; 1.205 + while ((r = queue.poll()) != null) 1.206 + { 1.207 + Entry e = (Entry) r; 1.208 + int h = e.hash; 1.209 + int i = indexFor(h, table.length); 1.210 + 1.211 + // System.out.println("EXPUNGING " + h); 1.212 + Entry<V> prev = table[i]; 1.213 + Entry<V> p = prev; 1.214 + while (p != null) 1.215 + { 1.216 + Entry<V> next = p.next; 1.217 + if (p == e) 1.218 + { 1.219 + if (prev == e) 1.220 + { 1.221 + table[i] = next; 1.222 + } 1.223 + else 1.224 + { 1.225 + prev.next = next; 1.226 + } 1.227 + e.next = null; // Help GC 1.228 + size--; 1.229 + break; 1.230 + } 1.231 + prev = p; 1.232 + p = next; 1.233 + } 1.234 + } 1.235 + } 1.236 + 1.237 + /** 1.238 + * Return the table after first expunging stale entries 1.239 + */ 1.240 + private Entry<V>[] getTable() 1.241 + { 1.242 + expungeStaleEntries(); 1.243 + return table; 1.244 + } 1.245 + 1.246 + /** 1.247 + * Returns the number of key-value mappings in this map. 1.248 + * This result is a snapshot, and may not reflect unprocessed 1.249 + * entries that will be removed before next attempted access 1.250 + * because they are no longer referenced. 1.251 + */ 1.252 + public int size() 1.253 + { 1.254 + if (size == 0) 1.255 + { 1.256 + return 0; 1.257 + } 1.258 + expungeStaleEntries(); 1.259 + return size; 1.260 + } 1.261 + 1.262 + /** 1.263 + * Returns <tt>true</tt> if this map contains no key-value mappings. 1.264 + * This result is a snapshot, and may not reflect unprocessed 1.265 + * entries that will be removed before next attempted access 1.266 + * because they are no longer referenced. 1.267 + */ 1.268 + public boolean isEmpty() 1.269 + { 1.270 + return size() == 0; 1.271 + } 1.272 + 1.273 + /** 1.274 + * Returns the value stored in the pool that equals the requested key 1.275 + * or <tt>null</tt> if the map contains no mapping for 1.276 + * this key (or the key is null) 1.277 + * 1.278 + * @param key the key whose equals value is to be returned. 1.279 + * @return the object that is equal the specified key, or 1.280 + * <tt>null</tt> if key is null or no object in the pool equals the key. 1.281 + */ 1.282 + public V get(V key) 1.283 + { 1.284 + if (key == null) 1.285 + { 1.286 + return null; 1.287 + } 1.288 + int h = key.hashCode(); 1.289 + Entry<V>[] tab = getTable(); 1.290 + int index = indexFor(h, tab.length); 1.291 + Entry<V> e = tab[index]; 1.292 + while (e != null) 1.293 + { 1.294 + V candidate = e.get(); 1.295 + if (e.hash == h && eq(key, candidate)) 1.296 + { 1.297 + return candidate; 1.298 + } 1.299 + e = e.next; 1.300 + } 1.301 + return null; 1.302 + } 1.303 + 1.304 + /** 1.305 + * Returns the entry associated with the specified key in the HashMap. 1.306 + * Returns null if the HashMap contains no mapping for this key. 1.307 + */ 1.308 + Entry getEntry(Object key) 1.309 + { 1.310 + int h = key.hashCode(); 1.311 + Entry[] tab = getTable(); 1.312 + int index = indexFor(h, tab.length); 1.313 + Entry e = tab[index]; 1.314 + while (e != null && !(e.hash == h && eq(key, e.get()))) 1.315 + { 1.316 + e = e.next; 1.317 + } 1.318 + return e; 1.319 + } 1.320 + 1.321 + /** 1.322 + * Places the object into the pool. If the object is null, nothing happens. 1.323 + * If an equal object already exists, it is not replaced. 1.324 + * 1.325 + * @param key the object to put into the pool. key may be null. 1.326 + * @return the object in the pool that is equal to the key, or the newly placed key if no such object existed when put was called 1.327 + */ 1.328 + public V put(V key) 1.329 + { 1.330 + if (key == null) 1.331 + { 1.332 + return null; 1.333 + } 1.334 + int h = key.hashCode(); 1.335 + Entry<V>[] tab = getTable(); 1.336 + int i = indexFor(h, tab.length); 1.337 + 1.338 + for (Entry<V> e = tab[i]; e != null; e = e.next) 1.339 + { 1.340 + V candidate = e.get(); 1.341 + if (h == e.hash && eq(key, candidate)) 1.342 + { 1.343 + return candidate; 1.344 + } 1.345 + } 1.346 + 1.347 + tab[i] = new Entry<V>(key, queue, h, tab[i]); 1.348 + 1.349 + if (++size >= threshold) 1.350 + { 1.351 + resize(tab.length * 2); 1.352 + } 1.353 + 1.354 + // System.out.println("Added " + key + " to pool"); 1.355 + return key; 1.356 + } 1.357 + 1.358 + /** 1.359 + * Rehashes the contents of this map into a new array with a 1.360 + * larger capacity. This method is called automatically when the 1.361 + * number of keys in this map reaches its threshold. 1.362 + * <p/> 1.363 + * If current capacity is MAXIMUM_CAPACITY, this method does not 1.364 + * resize the map, but but sets threshold to Integer.MAX_VALUE. 1.365 + * This has the effect of preventing future calls. 1.366 + * 1.367 + * @param newCapacity the new capacity, MUST be a power of two; 1.368 + * must be greater than current capacity unless current 1.369 + * capacity is MAXIMUM_CAPACITY (in which case value 1.370 + * is irrelevant). 1.371 + */ 1.372 + void resize(int newCapacity) 1.373 + { 1.374 + Entry<V>[] oldTable = getTable(); 1.375 + int oldCapacity = oldTable.length; 1.376 + if (oldCapacity == MAXIMUM_CAPACITY) 1.377 + { 1.378 + threshold = Integer.MAX_VALUE; 1.379 + return; 1.380 + } 1.381 + 1.382 + Entry<V>[] newTable = new Entry[newCapacity]; 1.383 + transfer(oldTable, newTable); 1.384 + table = newTable; 1.385 + 1.386 + /* 1.387 + * If ignoring null elements and processing ref queue caused massive 1.388 + * shrinkage, then restore old table. This should be rare, but avoids 1.389 + * unbounded expansion of garbage-filled tables. 1.390 + */ 1.391 + if (size >= threshold / 2) 1.392 + { 1.393 + threshold = (int) (newCapacity * loadFactor); 1.394 + } 1.395 + else 1.396 + { 1.397 + expungeStaleEntries(); 1.398 + transfer(newTable, oldTable); 1.399 + table = oldTable; 1.400 + } 1.401 + } 1.402 + 1.403 + /** 1.404 + * Transfer all entries from src to dest tables 1.405 + */ 1.406 + private void transfer(Entry[] src, Entry[] dest) 1.407 + { 1.408 + for (int j = 0; j < src.length; ++j) 1.409 + { 1.410 + Entry e = src[j]; 1.411 + src[j] = null; 1.412 + while (e != null) 1.413 + { 1.414 + Entry next = e.next; 1.415 + Object key = e.get(); 1.416 + if (key == null) 1.417 + { 1.418 + e.next = null; // Help GC 1.419 + size--; 1.420 + } 1.421 + else 1.422 + { 1.423 + int i = indexFor(e.hash, dest.length); 1.424 + e.next = dest[i]; 1.425 + dest[i] = e; 1.426 + } 1.427 + e = next; 1.428 + } 1.429 + } 1.430 + } 1.431 + 1.432 + /** 1.433 + * Removes the object in the pool that equals the key. 1.434 + * 1.435 + * @param key 1.436 + * @return previous value associated with specified key, or <tt>null</tt> 1.437 + * if there was no mapping for key or the key is null. 1.438 + */ 1.439 + public V removeFromPool(V key) 1.440 + { 1.441 + if (key == null) 1.442 + { 1.443 + return null; 1.444 + } 1.445 + int h = key.hashCode(); 1.446 + Entry<V>[] tab = getTable(); 1.447 + int i = indexFor(h, tab.length); 1.448 + Entry<V> prev = tab[i]; 1.449 + Entry<V> e = prev; 1.450 + 1.451 + while (e != null) 1.452 + { 1.453 + Entry<V> next = e.next; 1.454 + V candidate = e.get(); 1.455 + if (h == e.hash && eq(key, candidate)) 1.456 + { 1.457 + size--; 1.458 + if (prev == e) 1.459 + { 1.460 + tab[i] = next; 1.461 + } 1.462 + else 1.463 + { 1.464 + prev.next = next; 1.465 + } 1.466 + return candidate; 1.467 + } 1.468 + prev = e; 1.469 + e = next; 1.470 + } 1.471 + 1.472 + return null; 1.473 + } 1.474 + 1.475 + /** 1.476 + * Removes all mappings from this map. 1.477 + */ 1.478 + public void clear() 1.479 + { 1.480 + // clear out ref queue. We don't need to expunge entries 1.481 + // since table is getting cleared. 1.482 + while (queue.poll() != null) 1.483 + { 1.484 + // nop 1.485 + } 1.486 + 1.487 + table = new Entry[DEFAULT_INITIAL_CAPACITY]; 1.488 + threshold = DEFAULT_INITIAL_CAPACITY; 1.489 + size = 0; 1.490 + 1.491 + // Allocation of array may have caused GC, which may have caused 1.492 + // additional entries to go stale. Removing these entries from the 1.493 + // reference queue will make them eligible for reclamation. 1.494 + while (queue.poll() != null) 1.495 + { 1.496 + // nop 1.497 + } 1.498 + } 1.499 + 1.500 + /** 1.501 + * The entries in this hash table extend WeakReference, using its main ref 1.502 + * field as the key. 1.503 + */ 1.504 + protected static class Entry<V> 1.505 + extends WeakReference<V> 1.506 + { 1.507 + private final int hash; 1.508 + private Entry<V> next; 1.509 + 1.510 + /** 1.511 + * Create new entry. 1.512 + */ 1.513 + Entry(final V key, final ReferenceQueue<V> queue, final int hash, final Entry<V> next) 1.514 + { 1.515 + super(key, queue); 1.516 + this.hash = hash; 1.517 + this.next = next; 1.518 + } 1.519 + 1.520 + public V getKey() 1.521 + { 1.522 + return super.get(); 1.523 + } 1.524 + 1.525 + public boolean equals(Object o) 1.526 + { 1.527 + if (!(o instanceof WeakPool.Entry)) 1.528 + { 1.529 + return false; 1.530 + } 1.531 + WeakPool.Entry<V> that = (WeakPool.Entry<V>) o; 1.532 + V k1 = this.getKey(); 1.533 + V k2 = that.getKey(); 1.534 + return (k1==k2 || k1.equals(k2)); 1.535 + } 1.536 + 1.537 + public int hashCode() 1.538 + { 1.539 + return this.hash; 1.540 + } 1.541 + 1.542 + public String toString() 1.543 + { 1.544 + return String.valueOf(this.getKey()); 1.545 + } 1.546 + } 1.547 +} 1.548 + 1.549 +final class MultiSynonymKey { 1.550 + private List<MyList> keys; 1.551 + 1.552 + public MultiSynonymKey() { 1.553 + keys = new ArrayList<MyList>(); 1.554 + } 1.555 + 1.556 + public MultiSynonymKey(MyList... arg) { 1.557 + keys = Arrays.asList(arg); 1.558 + } 1.559 + 1.560 + public List<MyList> getKeys() { 1.561 + return keys; 1.562 + } 1.563 + 1.564 + public int hashCode() { 1.565 + return this.getKeys().hashCode(); 1.566 + } 1.567 + 1.568 + public boolean equals(Object obj) { 1.569 + if (this == obj) { 1.570 + return true; 1.571 + } 1.572 + 1.573 + if (!(obj instanceof MultiSynonymKey)) { 1.574 + return false; 1.575 + } 1.576 + 1.577 + MultiSynonymKey that = (MultiSynonymKey) obj; 1.578 + return this.getKeys().equals(that.getKeys()); 1.579 + } 1.580 + 1.581 + public String toString() { 1.582 + return this.getClass().getName() + this.getKeys().toString(); 1.583 + } 1.584 +} 1.585 + 1.586 +public class Test extends Thread { 1.587 + static public Test test; 1.588 + static private byte[] arg1; 1.589 + static private byte[] arg2; 1.590 + static public WeakPool<MultiSynonymKey> wp; 1.591 + public volatile MultiSynonymKey ml1; 1.592 + public volatile MultiSynonymKey ml2; 1.593 + private volatile MultiSynonymKey ml3; 1.594 + 1.595 + public void run() { 1.596 + int count=0; 1.597 + while (true) { 1.598 + try { 1.599 + Thread.sleep(10); 1.600 + } catch (Exception e) {} 1.601 + synchronized (wp) { 1.602 + ml2 = new MultiSynonymKey(new DoubletonList(new String(arg1), new String(arg2))); 1.603 + wp.put(ml2); 1.604 + ml3 = new MultiSynonymKey(new DoubletonList(new String(arg1), new String(arg2))); 1.605 + } 1.606 + try { 1.607 + Thread.sleep(10); 1.608 + } catch (Exception e) {} 1.609 + synchronized (wp) { 1.610 + ml1 = new MultiSynonymKey(new SingletonList(new String(arg1))); 1.611 + wp.put(ml1); 1.612 + ml3 = new MultiSynonymKey(new SingletonList(new String(arg1))); 1.613 + } 1.614 + if (count++==100) 1.615 + System.exit(95); 1.616 + } 1.617 + } 1.618 + 1.619 + public static void main(String[] args) throws Exception { 1.620 + wp = new WeakPool<MultiSynonymKey>(); 1.621 + test = new Test(); 1.622 + 1.623 + test.arg1 = args[0].getBytes(); 1.624 + test.arg2 = args[1].getBytes(); 1.625 + 1.626 + test.ml1 = new MultiSynonymKey(new SingletonList(new String(test.arg1))); 1.627 + test.ml2 = new MultiSynonymKey(new DoubletonList(new String(test.arg1), new String(test.arg2))); 1.628 + test.ml3 = new MultiSynonymKey(new DoubletonList(new String(test.arg1), new String(test.arg2))); 1.629 + 1.630 + wp.put(test.ml1); 1.631 + wp.put(test.ml2); 1.632 + 1.633 + test.setDaemon(true); 1.634 + test.start(); 1.635 + 1.636 + int counter = 0; 1.637 + while (true) { 1.638 + synchronized (wp) { 1.639 + MultiSynonymKey foo = test.ml3; 1.640 + 1.641 + if (wp.put(foo) == foo) { 1.642 + // System.out.println("foo " + counter); 1.643 + // System.out.println(foo); 1.644 + } 1.645 + } 1.646 + counter++; 1.647 + } 1.648 + } 1.649 + 1.650 + private boolean eq(Object x, Object y) { 1.651 + return x == y || x.equals(y); 1.652 + } 1.653 +}