Home | History | Annotate | Download | only in ref
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package java.lang.ref;
     19 
     20 /**
     21  * The {@code ReferenceQueue} is the container on which reference objects are
     22  * enqueued when the garbage collector detects the reachability type specified
     23  * for the referent.
     24  *
     25  * @since 1.2
     26  */
     27 public class ReferenceQueue<T> {
     28     private static final int NANOS_PER_MILLI = 1000000;
     29 
     30     private Reference<? extends T> head;
     31 
     32     /**
     33      * Constructs a new instance of this class.
     34      */
     35     public ReferenceQueue() {
     36     }
     37 
     38     /**
     39      * Returns the next available reference from the queue, removing it in the
     40      * process. Does not wait for a reference to become available.
     41      *
     42      * @return the next available reference, or {@code null} if no reference is
     43      *         immediately available
     44      */
     45     @SuppressWarnings("unchecked")
     46     public synchronized Reference<? extends T> poll() {
     47         if (head == null) {
     48             return null;
     49         }
     50 
     51         Reference<? extends T> ret;
     52 
     53         ret = head;
     54 
     55         if (head == head.queueNext) {
     56             head = null;
     57         } else {
     58             head = head.queueNext;
     59         }
     60 
     61         ret.queueNext = null;
     62 
     63         return ret;
     64     }
     65 
     66     /**
     67      * Returns the next available reference from the queue, removing it in the
     68      * process. Waits indefinitely for a reference to become available.
     69      *
     70      * @throws InterruptedException if the blocking call was interrupted
     71      */
     72     public Reference<? extends T> remove() throws InterruptedException {
     73         return remove(0L);
     74     }
     75 
     76     /**
     77      * Returns the next available reference from the queue, removing it in the
     78      * process. Waits for a reference to become available or the given timeout
     79      * period to elapse, whichever happens first.
     80      *
     81      * @param timeoutMillis maximum time to spend waiting for a reference object
     82      *     to become available. A value of {@code 0} results in the method
     83      *     waiting indefinitely.
     84      * @return the next available reference, or {@code null} if no reference
     85      *     becomes available within the timeout period
     86      * @throws IllegalArgumentException if {@code timeoutMillis < 0}.
     87      * @throws InterruptedException if the blocking call was interrupted
     88      */
     89     public synchronized Reference<? extends T> remove(long timeoutMillis)
     90             throws InterruptedException {
     91         if (timeoutMillis < 0) {
     92             throw new IllegalArgumentException("timeout < 0: " + timeoutMillis);
     93         }
     94 
     95         if (head != null) {
     96             return poll();
     97         }
     98 
     99         // avoid overflow: if total > 292 years, just wait forever
    100         if (timeoutMillis == 0 || (timeoutMillis > Long.MAX_VALUE / NANOS_PER_MILLI)) {
    101             do {
    102                 wait(0);
    103             } while (head == null);
    104             return poll();
    105         }
    106 
    107         // guaranteed to not overflow
    108         long nanosToWait = timeoutMillis * NANOS_PER_MILLI;
    109         int timeoutNanos = 0;
    110 
    111         // wait until notified or the timeout has elapsed
    112         long startTime = System.nanoTime();
    113         while (true) {
    114             wait(timeoutMillis, timeoutNanos);
    115             if (head != null) {
    116                 break;
    117             }
    118             long nanosElapsed = System.nanoTime() - startTime;
    119             long nanosRemaining = nanosToWait - nanosElapsed;
    120             if (nanosRemaining <= 0) {
    121                 break;
    122             }
    123             timeoutMillis = nanosRemaining / NANOS_PER_MILLI;
    124             timeoutNanos = (int) (nanosRemaining - timeoutMillis * NANOS_PER_MILLI);
    125         }
    126         return poll();
    127     }
    128 
    129     /**
    130      * Enqueue the reference object on the receiver.
    131      *
    132      * @param reference
    133      *            reference object to be enqueued.
    134      */
    135     synchronized void enqueue(Reference<? extends T> reference) {
    136         if (head == null) {
    137             reference.queueNext = reference;
    138         } else {
    139             reference.queueNext = head;
    140         }
    141         head = reference;
    142         notify();
    143     }
    144 
    145     /** @hide */
    146     public static Reference<?> unenqueued = null;
    147 
    148     static void add(Reference<?> list) {
    149         synchronized (ReferenceQueue.class) {
    150             if (unenqueued == null) {
    151                 unenqueued = list;
    152             } else {
    153                 Reference<?> next = unenqueued.pendingNext;
    154                 unenqueued.pendingNext = list.pendingNext;
    155                 list.pendingNext = next;
    156             }
    157             ReferenceQueue.class.notifyAll();
    158         }
    159     }
    160 }
    161