--- old/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java 2015-07-03 16:04:57.458713794 +0200 +++ new/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java 2015-07-03 16:04:57.388713308 +0200 @@ -46,51 +46,70 @@ } } - static ReferenceQueue NULL = new Null<>(); - static ReferenceQueue ENQUEUED = new Null<>(); + static final ReferenceQueue NULL = new Null<>(); + static final ReferenceQueue ENQUEUED = new Null<>(); - static private class Lock { }; - private Lock lock = new Lock(); - private volatile Reference head = null; - private long queueLength = 0; + static private class Lock { } + private final Lock lock = new Lock(); + private volatile int waiters; + + @SuppressWarnings("unused") + private volatile Reference head; // we assign using Unsafe CAS boolean enqueue(Reference r) { /* Called only by Reference class */ - synchronized (lock) { - // Check that since getting the lock this reference hasn't already been + if (markEnqueued(r)) { // markEnqueued is atomic + addChunk(r, r); + return true; + } else { + return false; + } + } + + boolean markEnqueued(Reference r) { + ReferenceQueue queue; + do { + // Check that this reference hasn't already been // enqueued (and even then removed) - ReferenceQueue queue = r.queue; + queue = r.queue; if ((queue == NULL) || (queue == ENQUEUED)) { return false; } - assert queue == this; - r.queue = ENQUEUED; - r.next = (head == null) ? r : head; - head = r; - queueLength++; - if (r instanceof FinalReference) { - sun.misc.VM.addFinalRefCount(1); + } while (!r.casQueue(queue, ENQUEUED)); + assert queue == this; + return true; + } + + @SuppressWarnings("unchecked") + void addChunk(Reference chunkHead, Reference chunkTail) { + Reference h; + do { + h = head; + chunkTail.next = (h == null) ? chunkTail : h; + } while (!casHead(h, (Reference) chunkHead)); + // notify waiters + if (waiters > 0) { + synchronized (lock) { + if (waiters > 0) { + lock.notifyAll(); + } } - lock.notifyAll(); - return true; } } - @SuppressWarnings("unchecked") - private Reference reallyPoll() { /* Must hold lock */ - Reference r = head; - if (r != null) { - head = (r.next == r) ? - null : - r.next; // Unchecked due to the next field having a raw type in Reference - r.queue = NULL; - r.next = r; - queueLength--; - if (r instanceof FinalReference) { - sun.misc.VM.addFinalRefCount(-1); + private Reference reallyPoll() { + Reference r; + while ((r = head) != null) { + @SuppressWarnings("unchecked") // due to cast to raw type + Reference nh = (r.next == r) + ? null : (Reference) r.next; + if (casHead(r, nh)) { + r.lazySetQueue(NULL); + UNSAFE.storeFence(); + r.next = r; + break; } - return r; } - return null; + return r; } /** @@ -102,11 +121,7 @@ * otherwise null */ public Reference poll() { - if (head == null) - return null; - synchronized (lock) { - return reallyPoll(); - } + return reallyPoll(); } /** @@ -135,20 +150,31 @@ if (timeout < 0) { throw new IllegalArgumentException("Negative timeout value"); } + Reference r = reallyPoll(); + if (r != null) return r; + return reallyRemove(timeout); + } + + private Reference reallyRemove(long timeout) throws InterruptedException { + long deadline = (timeout == 0) + ? 0 : System.nanoTime() + timeout * 1000_000L; + synchronized (lock) { - Reference r = reallyPoll(); - if (r != null) return r; - long start = (timeout == 0) ? 0 : System.nanoTime(); - for (;;) { - lock.wait(timeout); - r = reallyPoll(); - if (r != null) return r; - if (timeout != 0) { - long end = System.nanoTime(); - timeout -= (end - start) / 1000_000; - if (timeout <= 0) return null; - start = end; + ++waiters; + try { + for (; ; ) { + Reference r = reallyPoll(); + if (r != null) return r; + if (timeout == 0) { + lock.wait(0); + } else { + long timeoutNanos = deadline - System.nanoTime(); + if (timeoutNanos <= 0) return null; + lock.wait(timeoutNanos / 1000_000L, (int) (timeoutNanos % 1000_000L)); + } } + } finally { + --waiters; } } } @@ -164,4 +190,22 @@ return remove(0); } + // Unsafe machinery + + private boolean casHead(Reference cmp, Reference val) { + return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val); + } + + private static final sun.misc.Unsafe UNSAFE; + private static final long headOffset; + + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class rqc = ReferenceQueue.class; + headOffset = UNSAFE.objectFieldOffset(rqc.getDeclaredField("head")); + } catch (Exception e) { + throw new Error(e); + } + } }