Fri, 31 Jul 2009 12:04:07 -0700
6865031: Application gives bad result (throws bad exception) with compressed oops
Summary: Produce narrow type for new Phi from the original Phi type.
Reviewed-by: cfang
src/share/vm/opto/cfgnode.cpp | file | annotate | diff | comparison | revisions | |
test/compiler/6865031/Test.java | file | annotate | diff | comparison | revisions |
1.1 --- a/src/share/vm/opto/cfgnode.cpp Thu Jul 30 16:05:56 2009 -0700 1.2 +++ b/src/share/vm/opto/cfgnode.cpp Fri Jul 31 12:04:07 2009 -0700 1.3 @@ -1792,15 +1792,12 @@ 1.4 if (UseCompressedOops && can_reshape && progress == NULL) { 1.5 bool may_push = true; 1.6 bool has_decodeN = false; 1.7 - Node* in_decodeN = NULL; 1.8 for (uint i=1; i<req(); ++i) {// For all paths in 1.9 Node *ii = in(i); 1.10 if (ii->is_DecodeN() && ii->bottom_type() == bottom_type()) { 1.11 - // Note: in_decodeN is used only to define the type of new phi. 1.12 - // Find a non dead path otherwise phi type will be wrong. 1.13 + // Do optimization if a non dead path exist. 1.14 if (ii->in(1)->bottom_type() != Type::TOP) { 1.15 has_decodeN = true; 1.16 - in_decodeN = ii->in(1); 1.17 } 1.18 } else if (!ii->is_Phi()) { 1.19 may_push = false; 1.20 @@ -1809,7 +1806,9 @@ 1.21 1.22 if (has_decodeN && may_push) { 1.23 PhaseIterGVN *igvn = phase->is_IterGVN(); 1.24 - PhiNode *new_phi = PhiNode::make_blank(in(0), in_decodeN); 1.25 + // Make narrow type for new phi. 1.26 + const Type* narrow_t = TypeNarrowOop::make(this->bottom_type()->is_ptr()); 1.27 + PhiNode* new_phi = new (phase->C, r->req()) PhiNode(r, narrow_t); 1.28 uint orig_cnt = req(); 1.29 for (uint i=1; i<req(); ++i) {// For all paths in 1.30 Node *ii = in(i); 1.31 @@ -1822,7 +1821,7 @@ 1.32 if (ii->as_Phi() == this) { 1.33 new_ii = new_phi; 1.34 } else { 1.35 - new_ii = new (phase->C, 2) EncodePNode(ii, in_decodeN->bottom_type()); 1.36 + new_ii = new (phase->C, 2) EncodePNode(ii, narrow_t); 1.37 igvn->register_new_node_with_optimizer(new_ii); 1.38 } 1.39 }
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/test/compiler/6865031/Test.java Fri Jul 31 12:04:07 2009 -0700 2.3 @@ -0,0 +1,650 @@ 2.4 +/* 2.5 + * Copyright 2009 Goldman Sachs International. All Rights Reserved. 2.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.7 + * 2.8 + * This code is free software; you can redistribute it and/or modify it 2.9 + * under the terms of the GNU General Public License version 2 only, as 2.10 + * published by the Free Software Foundation. 2.11 + * 2.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 2.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 2.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 2.15 + * version 2 for more details (a copy is included in the LICENSE file that 2.16 + * accompanied this code). 2.17 + * 2.18 + * You should have received a copy of the GNU General Public License version 2.19 + * 2 along with this work; if not, write to the Free Software Foundation, 2.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2.21 + * 2.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 2.23 + * CA 95054 USA or visit www.sun.com if you need additional information or 2.24 + * have any questions. 2.25 + * 2.26 + */ 2.27 + 2.28 +/* 2.29 + * @test 2.30 + * @bug 6865031 2.31 + * @summary Application gives bad result (throws bad exception) with compressed oops 2.32 + * @run main/othervm -XX:+UseCompressedOops -XX:HeapBaseMinAddress=32g -XX:-LoopUnswitching -XX:CompileCommand=inline,AbstractMemoryEfficientList.equals Test hello goodbye 2.33 + */ 2.34 + 2.35 +import java.lang.ref.ReferenceQueue; 2.36 +import java.lang.ref.WeakReference; 2.37 +import java.util.ArrayList; 2.38 +import java.util.Arrays; 2.39 +import java.util.List; 2.40 + 2.41 +interface MyList { 2.42 + public int size(); 2.43 + public Object set(final int index, final Object element); 2.44 + public Object get(final int index); 2.45 +} 2.46 + 2.47 +abstract class AbstractMemoryEfficientList implements MyList { 2.48 + abstract public int size(); 2.49 + abstract public Object get(final int index); 2.50 + abstract public Object set(final int index, final Object element); 2.51 + 2.52 + public boolean equals(Object o) { 2.53 + if (o == this) { 2.54 + return true; 2.55 + } 2.56 + 2.57 + if (!(o instanceof MyList)) { 2.58 + return false; 2.59 + } 2.60 + 2.61 + final MyList that = (MyList) o; 2.62 + if (this.size() != that.size()) { 2.63 + return false; 2.64 + } 2.65 + 2.66 + for (int i = 0; i < this.size(); i++) { 2.67 + try { 2.68 + if (!((this.get(i)).equals(that.get(i)))) { 2.69 + return false; 2.70 + } 2.71 + } catch (IndexOutOfBoundsException e) { 2.72 + System.out.println("THROWING RT EXC"); 2.73 + System.out.println("concurrent modification of this:" + this.getClass() + ":" + System.identityHashCode(this) + "; that:" + that.getClass() + ":" + System.identityHashCode(that) + "; i:" + i); 2.74 + e.printStackTrace(); 2.75 + System.exit(97); 2.76 + throw new RuntimeException("concurrent modification of this:" + this.getClass() + ":" + System.identityHashCode(this) + "; that:" + that.getClass() + ":" + System.identityHashCode(that) + "; i:" + i, e); 2.77 + } 2.78 + } 2.79 + return true; 2.80 + } 2.81 + 2.82 + public int hashCode() { 2.83 + int hashCode = 1; 2.84 + for (int i = 0; i < this.size(); i++) { 2.85 + Object obj = this.get(i); 2.86 + hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode()); 2.87 + } 2.88 + return hashCode; 2.89 + } 2.90 +} 2.91 + 2.92 +final class SingletonList extends AbstractMemoryEfficientList { 2.93 + private Object element1; 2.94 + 2.95 + SingletonList(final Object obj1) { 2.96 + super(); 2.97 + this.element1 = obj1; 2.98 + } 2.99 + 2.100 + public int size() { 2.101 + return 1; 2.102 + } 2.103 + 2.104 + public Object get(final int index) { 2.105 + if (index == 0) { 2.106 + return this.element1; 2.107 + } else { 2.108 + throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.size()); 2.109 + } 2.110 + } 2.111 + 2.112 + public Object set(final int index, final Object element) { 2.113 + if (index == 0) { 2.114 + final Object previousElement = this.element1; 2.115 + this.element1 = element; 2.116 + return previousElement; 2.117 + } else { 2.118 + throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.size()); 2.119 + } 2.120 + } 2.121 +} 2.122 + 2.123 +final class DoubletonList extends AbstractMemoryEfficientList { 2.124 + private Object element1; 2.125 + private Object element2; 2.126 + 2.127 + DoubletonList(final Object obj1, final Object obj2) { 2.128 + this.element1 = obj1; 2.129 + this.element2 = obj2; 2.130 + } 2.131 + 2.132 + public int size() { 2.133 + return 2; 2.134 + } 2.135 + 2.136 + public Object get(final int index) { 2.137 + switch (index) { 2.138 + case 0 : return this.element1; 2.139 + case 1 : return this.element2; 2.140 + default: throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.size()); 2.141 + } 2.142 + } 2.143 + 2.144 + public Object set(final int index, final Object element) { 2.145 + switch (index) { 2.146 + case 0 : 2.147 + { 2.148 + final Object previousElement = this.element1; 2.149 + this.element1 = element; 2.150 + return previousElement; 2.151 + } 2.152 + case 1 : 2.153 + { 2.154 + final Object previousElement = this.element2; 2.155 + this.element2 = element; 2.156 + return previousElement; 2.157 + } 2.158 + default : throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.size()); 2.159 + } 2.160 + } 2.161 +} 2.162 + 2.163 +class WeakPool<V> { 2.164 + protected static final int DEFAULT_INITIAL_CAPACITY = 16; 2.165 + private static final int MAXIMUM_CAPACITY = 1 << 30; 2.166 + private static final float DEFAULT_LOAD_FACTOR = 0.75f; 2.167 + 2.168 + protected Entry<V>[] table; 2.169 + 2.170 + private int size; 2.171 + protected int threshold; 2.172 + private final float loadFactor; 2.173 + private final ReferenceQueue<V> queue = new ReferenceQueue<V>(); 2.174 + 2.175 + public WeakPool() 2.176 + { 2.177 + this.loadFactor = DEFAULT_LOAD_FACTOR; 2.178 + threshold = DEFAULT_INITIAL_CAPACITY; 2.179 + table = new Entry[DEFAULT_INITIAL_CAPACITY]; 2.180 + } 2.181 + 2.182 + /** 2.183 + * Check for equality of non-null reference x and possibly-null y. By 2.184 + * default uses Object.equals. 2.185 + */ 2.186 + private boolean eq(Object x, Object y) 2.187 + { 2.188 + return x == y || x.equals(y); 2.189 + } 2.190 + 2.191 + /** 2.192 + * Return index for hash code h. 2.193 + */ 2.194 + private int indexFor(int h, int length) 2.195 + { 2.196 + return h & length - 1; 2.197 + } 2.198 + 2.199 + /** 2.200 + * Expunge stale entries from the table. 2.201 + */ 2.202 + private void expungeStaleEntries() 2.203 + { 2.204 + Object r; 2.205 + while ((r = queue.poll()) != null) 2.206 + { 2.207 + Entry e = (Entry) r; 2.208 + int h = e.hash; 2.209 + int i = indexFor(h, table.length); 2.210 + 2.211 + // System.out.println("EXPUNGING " + h); 2.212 + Entry<V> prev = table[i]; 2.213 + Entry<V> p = prev; 2.214 + while (p != null) 2.215 + { 2.216 + Entry<V> next = p.next; 2.217 + if (p == e) 2.218 + { 2.219 + if (prev == e) 2.220 + { 2.221 + table[i] = next; 2.222 + } 2.223 + else 2.224 + { 2.225 + prev.next = next; 2.226 + } 2.227 + e.next = null; // Help GC 2.228 + size--; 2.229 + break; 2.230 + } 2.231 + prev = p; 2.232 + p = next; 2.233 + } 2.234 + } 2.235 + } 2.236 + 2.237 + /** 2.238 + * Return the table after first expunging stale entries 2.239 + */ 2.240 + private Entry<V>[] getTable() 2.241 + { 2.242 + expungeStaleEntries(); 2.243 + return table; 2.244 + } 2.245 + 2.246 + /** 2.247 + * Returns the number of key-value mappings in this map. 2.248 + * This result is a snapshot, and may not reflect unprocessed 2.249 + * entries that will be removed before next attempted access 2.250 + * because they are no longer referenced. 2.251 + */ 2.252 + public int size() 2.253 + { 2.254 + if (size == 0) 2.255 + { 2.256 + return 0; 2.257 + } 2.258 + expungeStaleEntries(); 2.259 + return size; 2.260 + } 2.261 + 2.262 + /** 2.263 + * Returns <tt>true</tt> if this map contains no key-value mappings. 2.264 + * This result is a snapshot, and may not reflect unprocessed 2.265 + * entries that will be removed before next attempted access 2.266 + * because they are no longer referenced. 2.267 + */ 2.268 + public boolean isEmpty() 2.269 + { 2.270 + return size() == 0; 2.271 + } 2.272 + 2.273 + /** 2.274 + * Returns the value stored in the pool that equals the requested key 2.275 + * or <tt>null</tt> if the map contains no mapping for 2.276 + * this key (or the key is null) 2.277 + * 2.278 + * @param key the key whose equals value is to be returned. 2.279 + * @return the object that is equal the specified key, or 2.280 + * <tt>null</tt> if key is null or no object in the pool equals the key. 2.281 + */ 2.282 + public V get(V key) 2.283 + { 2.284 + if (key == null) 2.285 + { 2.286 + return null; 2.287 + } 2.288 + int h = key.hashCode(); 2.289 + Entry<V>[] tab = getTable(); 2.290 + int index = indexFor(h, tab.length); 2.291 + Entry<V> e = tab[index]; 2.292 + while (e != null) 2.293 + { 2.294 + V candidate = e.get(); 2.295 + if (e.hash == h && eq(key, candidate)) 2.296 + { 2.297 + return candidate; 2.298 + } 2.299 + e = e.next; 2.300 + } 2.301 + return null; 2.302 + } 2.303 + 2.304 + /** 2.305 + * Returns the entry associated with the specified key in the HashMap. 2.306 + * Returns null if the HashMap contains no mapping for this key. 2.307 + */ 2.308 + Entry getEntry(Object key) 2.309 + { 2.310 + int h = key.hashCode(); 2.311 + Entry[] tab = getTable(); 2.312 + int index = indexFor(h, tab.length); 2.313 + Entry e = tab[index]; 2.314 + while (e != null && !(e.hash == h && eq(key, e.get()))) 2.315 + { 2.316 + e = e.next; 2.317 + } 2.318 + return e; 2.319 + } 2.320 + 2.321 + /** 2.322 + * Places the object into the pool. If the object is null, nothing happens. 2.323 + * If an equal object already exists, it is not replaced. 2.324 + * 2.325 + * @param key the object to put into the pool. key may be null. 2.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 2.327 + */ 2.328 + public V put(V key) 2.329 + { 2.330 + if (key == null) 2.331 + { 2.332 + return null; 2.333 + } 2.334 + int h = key.hashCode(); 2.335 + Entry<V>[] tab = getTable(); 2.336 + int i = indexFor(h, tab.length); 2.337 + 2.338 + for (Entry<V> e = tab[i]; e != null; e = e.next) 2.339 + { 2.340 + V candidate = e.get(); 2.341 + if (h == e.hash && eq(key, candidate)) 2.342 + { 2.343 + return candidate; 2.344 + } 2.345 + } 2.346 + 2.347 + tab[i] = new Entry<V>(key, queue, h, tab[i]); 2.348 + 2.349 + if (++size >= threshold) 2.350 + { 2.351 + resize(tab.length * 2); 2.352 + } 2.353 + 2.354 + // System.out.println("Added " + key + " to pool"); 2.355 + return key; 2.356 + } 2.357 + 2.358 + /** 2.359 + * Rehashes the contents of this map into a new array with a 2.360 + * larger capacity. This method is called automatically when the 2.361 + * number of keys in this map reaches its threshold. 2.362 + * <p/> 2.363 + * If current capacity is MAXIMUM_CAPACITY, this method does not 2.364 + * resize the map, but but sets threshold to Integer.MAX_VALUE. 2.365 + * This has the effect of preventing future calls. 2.366 + * 2.367 + * @param newCapacity the new capacity, MUST be a power of two; 2.368 + * must be greater than current capacity unless current 2.369 + * capacity is MAXIMUM_CAPACITY (in which case value 2.370 + * is irrelevant). 2.371 + */ 2.372 + void resize(int newCapacity) 2.373 + { 2.374 + Entry<V>[] oldTable = getTable(); 2.375 + int oldCapacity = oldTable.length; 2.376 + if (oldCapacity == MAXIMUM_CAPACITY) 2.377 + { 2.378 + threshold = Integer.MAX_VALUE; 2.379 + return; 2.380 + } 2.381 + 2.382 + Entry<V>[] newTable = new Entry[newCapacity]; 2.383 + transfer(oldTable, newTable); 2.384 + table = newTable; 2.385 + 2.386 + /* 2.387 + * If ignoring null elements and processing ref queue caused massive 2.388 + * shrinkage, then restore old table. This should be rare, but avoids 2.389 + * unbounded expansion of garbage-filled tables. 2.390 + */ 2.391 + if (size >= threshold / 2) 2.392 + { 2.393 + threshold = (int) (newCapacity * loadFactor); 2.394 + } 2.395 + else 2.396 + { 2.397 + expungeStaleEntries(); 2.398 + transfer(newTable, oldTable); 2.399 + table = oldTable; 2.400 + } 2.401 + } 2.402 + 2.403 + /** 2.404 + * Transfer all entries from src to dest tables 2.405 + */ 2.406 + private void transfer(Entry[] src, Entry[] dest) 2.407 + { 2.408 + for (int j = 0; j < src.length; ++j) 2.409 + { 2.410 + Entry e = src[j]; 2.411 + src[j] = null; 2.412 + while (e != null) 2.413 + { 2.414 + Entry next = e.next; 2.415 + Object key = e.get(); 2.416 + if (key == null) 2.417 + { 2.418 + e.next = null; // Help GC 2.419 + size--; 2.420 + } 2.421 + else 2.422 + { 2.423 + int i = indexFor(e.hash, dest.length); 2.424 + e.next = dest[i]; 2.425 + dest[i] = e; 2.426 + } 2.427 + e = next; 2.428 + } 2.429 + } 2.430 + } 2.431 + 2.432 + /** 2.433 + * Removes the object in the pool that equals the key. 2.434 + * 2.435 + * @param key 2.436 + * @return previous value associated with specified key, or <tt>null</tt> 2.437 + * if there was no mapping for key or the key is null. 2.438 + */ 2.439 + public V removeFromPool(V key) 2.440 + { 2.441 + if (key == null) 2.442 + { 2.443 + return null; 2.444 + } 2.445 + int h = key.hashCode(); 2.446 + Entry<V>[] tab = getTable(); 2.447 + int i = indexFor(h, tab.length); 2.448 + Entry<V> prev = tab[i]; 2.449 + Entry<V> e = prev; 2.450 + 2.451 + while (e != null) 2.452 + { 2.453 + Entry<V> next = e.next; 2.454 + V candidate = e.get(); 2.455 + if (h == e.hash && eq(key, candidate)) 2.456 + { 2.457 + size--; 2.458 + if (prev == e) 2.459 + { 2.460 + tab[i] = next; 2.461 + } 2.462 + else 2.463 + { 2.464 + prev.next = next; 2.465 + } 2.466 + return candidate; 2.467 + } 2.468 + prev = e; 2.469 + e = next; 2.470 + } 2.471 + 2.472 + return null; 2.473 + } 2.474 + 2.475 + /** 2.476 + * Removes all mappings from this map. 2.477 + */ 2.478 + public void clear() 2.479 + { 2.480 + // clear out ref queue. We don't need to expunge entries 2.481 + // since table is getting cleared. 2.482 + while (queue.poll() != null) 2.483 + { 2.484 + // nop 2.485 + } 2.486 + 2.487 + table = new Entry[DEFAULT_INITIAL_CAPACITY]; 2.488 + threshold = DEFAULT_INITIAL_CAPACITY; 2.489 + size = 0; 2.490 + 2.491 + // Allocation of array may have caused GC, which may have caused 2.492 + // additional entries to go stale. Removing these entries from the 2.493 + // reference queue will make them eligible for reclamation. 2.494 + while (queue.poll() != null) 2.495 + { 2.496 + // nop 2.497 + } 2.498 + } 2.499 + 2.500 + /** 2.501 + * The entries in this hash table extend WeakReference, using its main ref 2.502 + * field as the key. 2.503 + */ 2.504 + protected static class Entry<V> 2.505 + extends WeakReference<V> 2.506 + { 2.507 + private final int hash; 2.508 + private Entry<V> next; 2.509 + 2.510 + /** 2.511 + * Create new entry. 2.512 + */ 2.513 + Entry(final V key, final ReferenceQueue<V> queue, final int hash, final Entry<V> next) 2.514 + { 2.515 + super(key, queue); 2.516 + this.hash = hash; 2.517 + this.next = next; 2.518 + } 2.519 + 2.520 + public V getKey() 2.521 + { 2.522 + return super.get(); 2.523 + } 2.524 + 2.525 + public boolean equals(Object o) 2.526 + { 2.527 + if (!(o instanceof WeakPool.Entry)) 2.528 + { 2.529 + return false; 2.530 + } 2.531 + WeakPool.Entry<V> that = (WeakPool.Entry<V>) o; 2.532 + V k1 = this.getKey(); 2.533 + V k2 = that.getKey(); 2.534 + return (k1==k2 || k1.equals(k2)); 2.535 + } 2.536 + 2.537 + public int hashCode() 2.538 + { 2.539 + return this.hash; 2.540 + } 2.541 + 2.542 + public String toString() 2.543 + { 2.544 + return String.valueOf(this.getKey()); 2.545 + } 2.546 + } 2.547 +} 2.548 + 2.549 +final class MultiSynonymKey { 2.550 + private List<MyList> keys; 2.551 + 2.552 + public MultiSynonymKey() { 2.553 + keys = new ArrayList<MyList>(); 2.554 + } 2.555 + 2.556 + public MultiSynonymKey(MyList... arg) { 2.557 + keys = Arrays.asList(arg); 2.558 + } 2.559 + 2.560 + public List<MyList> getKeys() { 2.561 + return keys; 2.562 + } 2.563 + 2.564 + public int hashCode() { 2.565 + return this.getKeys().hashCode(); 2.566 + } 2.567 + 2.568 + public boolean equals(Object obj) { 2.569 + if (this == obj) { 2.570 + return true; 2.571 + } 2.572 + 2.573 + if (!(obj instanceof MultiSynonymKey)) { 2.574 + return false; 2.575 + } 2.576 + 2.577 + MultiSynonymKey that = (MultiSynonymKey) obj; 2.578 + return this.getKeys().equals(that.getKeys()); 2.579 + } 2.580 + 2.581 + public String toString() { 2.582 + return this.getClass().getName() + this.getKeys().toString(); 2.583 + } 2.584 +} 2.585 + 2.586 +public class Test extends Thread { 2.587 + static public Test test; 2.588 + static private byte[] arg1; 2.589 + static private byte[] arg2; 2.590 + static public WeakPool<MultiSynonymKey> wp; 2.591 + public volatile MultiSynonymKey ml1; 2.592 + public volatile MultiSynonymKey ml2; 2.593 + private volatile MultiSynonymKey ml3; 2.594 + 2.595 + public void run() { 2.596 + int count=0; 2.597 + while (true) { 2.598 + try { 2.599 + Thread.sleep(10); 2.600 + } catch (Exception e) {} 2.601 + synchronized (wp) { 2.602 + ml2 = new MultiSynonymKey(new DoubletonList(new String(arg1), new String(arg2))); 2.603 + wp.put(ml2); 2.604 + ml3 = new MultiSynonymKey(new DoubletonList(new String(arg1), new String(arg2))); 2.605 + } 2.606 + try { 2.607 + Thread.sleep(10); 2.608 + } catch (Exception e) {} 2.609 + synchronized (wp) { 2.610 + ml1 = new MultiSynonymKey(new SingletonList(new String(arg1))); 2.611 + wp.put(ml1); 2.612 + ml3 = new MultiSynonymKey(new SingletonList(new String(arg1))); 2.613 + } 2.614 + if (count++==100) 2.615 + System.exit(95); 2.616 + } 2.617 + } 2.618 + 2.619 + public static void main(String[] args) throws Exception { 2.620 + wp = new WeakPool<MultiSynonymKey>(); 2.621 + test = new Test(); 2.622 + 2.623 + test.arg1 = args[0].getBytes(); 2.624 + test.arg2 = args[1].getBytes(); 2.625 + 2.626 + test.ml1 = new MultiSynonymKey(new SingletonList(new String(test.arg1))); 2.627 + test.ml2 = new MultiSynonymKey(new DoubletonList(new String(test.arg1), new String(test.arg2))); 2.628 + test.ml3 = new MultiSynonymKey(new DoubletonList(new String(test.arg1), new String(test.arg2))); 2.629 + 2.630 + wp.put(test.ml1); 2.631 + wp.put(test.ml2); 2.632 + 2.633 + test.setDaemon(true); 2.634 + test.start(); 2.635 + 2.636 + int counter = 0; 2.637 + while (true) { 2.638 + synchronized (wp) { 2.639 + MultiSynonymKey foo = test.ml3; 2.640 + 2.641 + if (wp.put(foo) == foo) { 2.642 + // System.out.println("foo " + counter); 2.643 + // System.out.println(foo); 2.644 + } 2.645 + } 2.646 + counter++; 2.647 + } 2.648 + } 2.649 + 2.650 + private boolean eq(Object x, Object y) { 2.651 + return x == y || x.equals(y); 2.652 + } 2.653 +}