1 /*
   2  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.lang.ref;
  27 
  28 /**
  29  * Reference queues, to which registered reference objects are appended by the
  30  * garbage collector after the appropriate reachability changes are detected.
  31  *
  32  * @author   Mark Reinhold
  33  * @since    1.2
  34  */
  35 
  36 public class ReferenceQueue<T> {
  37 
  38     /**
  39      * Constructs a new reference-object queue.
  40      */
  41     public ReferenceQueue() { }
  42 
  43     private static class Null<S> extends ReferenceQueue<S> {
  44         boolean enqueue(Reference<? extends S> r) {
  45             return false;
  46         }
  47     }
  48 
  49     static final ReferenceQueue<Object> NULL = new Null<>();
  50     static final ReferenceQueue<Object> ENQUEUED = new Null<>();
  51 
  52     static private class Lock { }
  53     private final Lock lock = new Lock();
  54     private volatile int waiters;
  55     @SuppressWarnings("unused")
  56     private volatile Reference<? extends T> head; // we assign using Unsafe CAS
  57 
  58     boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
  59         ReferenceQueue<?> queue;
  60         do {
  61             // Check that this reference hasn't already been
  62             // enqueued (and even then removed)
  63             queue = r.queue;
  64             if ((queue == NULL) || (queue == ENQUEUED)) {
  65                 return false;
  66             }
  67         } while (!r.casQueue(queue, ENQUEUED));
  68         assert queue == this;
  69         Reference<? extends T> h;
  70         do {
  71             h = head;
  72             r.next = (h == null) ? r : h;
  73         } while (!casHead(h, r));
  74 
  75         if (waiters > 0) {
  76             synchronized (lock) {
  77                 if (waiters > 0) {
  78                     lock.notifyAll();
  79                 }
  80             }
  81         }
  82         return true;
  83     }
  84 
  85     private Reference<? extends T> reallyPoll() {
  86         Reference<? extends T> r;
  87         while ((r = head) != null) {
  88             @SuppressWarnings("unchecked") // due to cast to raw type
  89                 Reference<? extends T> nh = (r.next == r)
  90                 ? null : (Reference) r.next;
  91             if (casHead(r, nh)) {
  92                 r.lazySetQueue(NULL);
  93                 UNSAFE.storeFence();
  94                 r.next = r;
  95                 if (r instanceof FinalReference) {
  96                     sun.misc.VM.addFinalRefCount(-1);
  97                 }
  98                 break;
  99             }
 100         }
 101         return r;
 102     }
 103 
 104     /**
 105      * Polls this queue to see if a reference object is available.  If one is
 106      * available without further delay then it is removed from the queue and
 107      * returned.  Otherwise this method immediately returns <tt>null</tt>.
 108      *
 109      * @return  A reference object, if one was immediately available,
 110      *          otherwise <code>null</code>
 111      */
 112     public Reference<? extends T> poll() {
 113         return reallyPoll();
 114     }
 115 
 116     /**
 117      * Removes the next reference object in this queue, blocking until either
 118      * one becomes available or the given timeout period expires.
 119      *
 120      * <p> This method does not offer real-time guarantees: It schedules the
 121      * timeout as if by invoking the {@link Object#wait(long)} method.
 122      *
 123      * @param  timeout  If positive, block for up to <code>timeout</code>
 124      *                  milliseconds while waiting for a reference to be
 125      *                  added to this queue.  If zero, block indefinitely.
 126      *
 127      * @return  A reference object, if one was available within the specified
 128      *          timeout period, otherwise <code>null</code>
 129      *
 130      * @throws  IllegalArgumentException
 131      *          If the value of the timeout argument is negative
 132      *
 133      * @throws  InterruptedException
 134      *          If the timeout wait is interrupted
 135      */
 136     public Reference<? extends T> remove(long timeout)
 137         throws IllegalArgumentException, InterruptedException
 138     {
 139         if (timeout < 0) {
 140             throw new IllegalArgumentException("Negative timeout value");
 141         }
 142         Reference<? extends T> r = reallyPoll();
 143         if (r != null) return r;
 144         return reallyRemove(timeout);
 145     }
 146 
 147     private Reference<? extends T> reallyRemove(long timeout) throws InterruptedException {
 148         long deadline = (timeout == 0)
 149                 ? 0 : System.nanoTime() + timeout * 1000_000L;
 150         synchronized (lock) {
 151             int w = waiters;
 152             waiters = w + 1;
 153             try {
 154                 for (;;) {
 155                     Reference<? extends T> r = reallyPoll();
 156                     if (r != null) return r;
 157                     if (timeout == 0) {
 158                         lock.wait(0);
 159                     } else {
 160                         long timeoutNanos = deadline - System.nanoTime();
 161                         if (timeoutNanos <= 0) return null;
 162                         lock.wait(timeoutNanos / 1000_000L, (int)(timeoutNanos % 1000_000L));
 163                     }
 164                 }
 165             } finally {
 166                 waiters = w;
 167             }
 168         }
 169     }
 170 
 171     /**
 172      * Removes the next reference object in this queue, blocking until one
 173      * becomes available.
 174      *
 175      * @return A reference object, blocking until one becomes available
 176      * @throws  InterruptedException  If the wait is interrupted
 177      */
 178     public Reference<? extends T> remove() throws InterruptedException {
 179         return remove(0);
 180     }
 181 
 182     // Unsafe machinery
 183 
 184     private boolean casHead(Reference<?> cmp, Reference<? extends T> val) {
 185         return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
 186     }
 187 
 188     private static final sun.misc.Unsafe UNSAFE;
 189     private static final long headOffset;
 190 
 191     static {
 192         try {
 193             UNSAFE = sun.misc.Unsafe.getUnsafe();
 194             Class<ReferenceQueue> rqc = ReferenceQueue.class;
 195             headOffset = UNSAFE.objectFieldOffset(rqc.getDeclaredField("head"));
 196         } catch (Exception e) {
 197             throw new Error(e);
 198         }
 199     }
 200 }