blob: 6c9b4d56b440213771400cca93dec76dade8b842 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.lang.ref;
/**
* The {@code ReferenceQueue} is the container on which reference objects are
* enqueued when the garbage collector detects the reachability type specified
* for the referent.
*
* @since 1.2
*/
public class ReferenceQueue<T> {
private static final int NANOS_PER_MILLI = 1000000;
private Reference<? extends T> head;
/**
* Constructs a new instance of this class.
*/
public ReferenceQueue() {
}
/**
* Returns the next available reference from the queue, removing it in the
* process. Does not wait for a reference to become available.
*
* @return the next available reference, or {@code null} if no reference is
* immediately available
*/
@SuppressWarnings("unchecked")
public synchronized Reference<? extends T> poll() {
if (head == null) {
return null;
}
Reference<? extends T> ret;
ret = head;
if (head == head.queueNext) {
head = null;
} else {
head = head.queueNext;
}
ret.queueNext = null;
return ret;
}
/**
* Returns the next available reference from the queue, removing it in the
* process. Waits indefinitely for a reference to become available.
*
* @throws InterruptedException if the blocking call was interrupted
*/
public Reference<? extends T> remove() throws InterruptedException {
return remove(0L);
}
/**
* Returns the next available reference from the queue, removing it in the
* process. Waits for a reference to become available or the given timeout
* period to elapse, whichever happens first.
*
* @param timeoutMillis maximum time to spend waiting for a reference object
* to become available. A value of {@code 0} results in the method
* waiting indefinitely.
* @return the next available reference, or {@code null} if no reference
* becomes available within the timeout period
* @throws IllegalArgumentException if {@code timeoutMillis < 0}.
* @throws InterruptedException if the blocking call was interrupted
*/
public synchronized Reference<? extends T> remove(long timeoutMillis)
throws InterruptedException {
if (timeoutMillis < 0) {
throw new IllegalArgumentException("timeout < 0: " + timeoutMillis);
}
if (head != null) {
return poll();
}
// avoid overflow: if total > 292 years, just wait forever
if (timeoutMillis == 0 || (timeoutMillis > Long.MAX_VALUE / NANOS_PER_MILLI)) {
do {
wait(0);
} while (head == null);
return poll();
}
// guaranteed to not overflow
long nanosToWait = timeoutMillis * NANOS_PER_MILLI;
int timeoutNanos = 0;
// wait until notified or the timeout has elapsed
long startTime = System.nanoTime();
while (true) {
wait(timeoutMillis, timeoutNanos);
if (head != null) {
break;
}
long nanosElapsed = System.nanoTime() - startTime;
long nanosRemaining = nanosToWait - nanosElapsed;
if (nanosRemaining <= 0) {
break;
}
timeoutMillis = nanosRemaining / NANOS_PER_MILLI;
timeoutNanos = (int) (nanosRemaining - timeoutMillis * NANOS_PER_MILLI);
}
return poll();
}
/**
* Enqueue the reference object on the receiver.
*
* @param reference
* reference object to be enqueued.
* @return boolean true if reference is enqueued. false if reference failed
* to enqueue.
*/
synchronized void enqueue(Reference<? extends T> reference) {
if (head == null) {
reference.queueNext = reference;
} else {
reference.queueNext = head;
}
head = reference;
notify();
}
/** @hide */
public static Reference unenqueued = null;
static void add(Reference<?> list) {
synchronized (ReferenceQueue.class) {
if (unenqueued == null) {
unenqueued = list;
} else {
Reference<?> next = unenqueued.pendingNext;
unenqueued.pendingNext = list.pendingNext;
list.pendingNext = next;
}
ReferenceQueue.class.notifyAll();
}
}
}