ReferenceQueue.java revision 12597:eab40d8ea2e5
1292706Spkelsey/*
2292706Spkelsey * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
3292706Spkelsey * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4292706Spkelsey *
5292706Spkelsey * This code is free software; you can redistribute it and/or modify it
6292706Spkelsey * under the terms of the GNU General Public License version 2 only, as
7292706Spkelsey * published by the Free Software Foundation.  Oracle designates this
8292706Spkelsey * particular file as subject to the "Classpath" exception as provided
9292706Spkelsey * by Oracle in the LICENSE file that accompanied this code.
10292706Spkelsey *
11292706Spkelsey * This code is distributed in the hope that it will be useful, but WITHOUT
12292706Spkelsey * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13292706Spkelsey * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14292706Spkelsey * version 2 for more details (a copy is included in the LICENSE file that
15292706Spkelsey * accompanied this code).
16292706Spkelsey *
17292706Spkelsey * You should have received a copy of the GNU General Public License version
18292706Spkelsey * 2 along with this work; if not, write to the Free Software Foundation,
19292706Spkelsey * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20292706Spkelsey *
21292706Spkelsey * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22292706Spkelsey * or visit www.oracle.com if you need additional information or have any
23292706Spkelsey * questions.
24292706Spkelsey */
25292706Spkelsey
26292706Spkelseypackage java.lang.ref;
27292706Spkelsey
28292706Spkelseyimport java.util.function.Consumer;
29292706Spkelsey
30292706Spkelsey/**
31292706Spkelsey * Reference queues, to which registered reference objects are appended by the
32292706Spkelsey * garbage collector after the appropriate reachability changes are detected.
33292706Spkelsey *
34292706Spkelsey * @author   Mark Reinhold
35292706Spkelsey * @since    1.2
36292706Spkelsey */
37292706Spkelsey
38292706Spkelseypublic class ReferenceQueue<T> {
39292706Spkelsey
40292706Spkelsey    /**
41292706Spkelsey     * Constructs a new reference-object queue.
42292706Spkelsey     */
43292706Spkelsey    public ReferenceQueue() { }
44292706Spkelsey
45292706Spkelsey    private static class Null<S> extends ReferenceQueue<S> {
46292706Spkelsey        boolean enqueue(Reference<? extends S> r) {
47292706Spkelsey            return false;
48292706Spkelsey        }
49292706Spkelsey    }
50292706Spkelsey
51292706Spkelsey    static ReferenceQueue<Object> NULL = new Null<>();
52292706Spkelsey    static ReferenceQueue<Object> ENQUEUED = new Null<>();
53292706Spkelsey
54292706Spkelsey    static private class Lock { };
55292706Spkelsey    private Lock lock = new Lock();
56292706Spkelsey    private volatile Reference<? extends T> head = null;
57292706Spkelsey    private long queueLength = 0;
58292706Spkelsey
59292706Spkelsey    boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
60292706Spkelsey        synchronized (lock) {
61292706Spkelsey            // Check that since getting the lock this reference hasn't already been
62292706Spkelsey            // enqueued (and even then removed)
63292706Spkelsey            ReferenceQueue<?> queue = r.queue;
64292706Spkelsey            if ((queue == NULL) || (queue == ENQUEUED)) {
65292706Spkelsey                return false;
66292706Spkelsey            }
67292706Spkelsey            assert queue == this;
68292706Spkelsey            r.next = (head == null) ? r : head;
69292706Spkelsey            head = r;
70292706Spkelsey            queueLength++;
71292706Spkelsey            // Update r.queue *after* adding to list, to avoid race
72292706Spkelsey            // with concurrent enqueued checks and fast-path poll().
73292706Spkelsey            // Volatiles ensure ordering.
74292706Spkelsey            r.queue = ENQUEUED;
75292706Spkelsey            if (r instanceof FinalReference) {
76292706Spkelsey                sun.misc.VM.addFinalRefCount(1);
77292706Spkelsey            }
78292706Spkelsey            lock.notifyAll();
79292706Spkelsey            return true;
80292706Spkelsey        }
81292706Spkelsey    }
82292706Spkelsey
83292706Spkelsey    private Reference<? extends T> reallyPoll() {       /* Must hold lock */
84292706Spkelsey        Reference<? extends T> r = head;
85292706Spkelsey        if (r != null) {
86292706Spkelsey            r.queue = NULL;
87292706Spkelsey            // Update r.queue *before* removing from list, to avoid
88292706Spkelsey            // race with concurrent enqueued checks and fast-path
89292706Spkelsey            // poll().  Volatiles ensure ordering.
90292706Spkelsey            @SuppressWarnings("unchecked")
91292706Spkelsey            Reference<? extends T> rn = r.next;
92292706Spkelsey            head = (rn == r) ? null : rn;
93292706Spkelsey            r.next = r;
94292706Spkelsey            queueLength--;
95292706Spkelsey            if (r instanceof FinalReference) {
96292706Spkelsey                sun.misc.VM.addFinalRefCount(-1);
97292706Spkelsey            }
98292706Spkelsey            return r;
99292706Spkelsey        }
100292706Spkelsey        return null;
101292706Spkelsey    }
102292706Spkelsey
103292706Spkelsey    /**
104292706Spkelsey     * Polls this queue to see if a reference object is available.  If one is
105292706Spkelsey     * available without further delay then it is removed from the queue and
106292706Spkelsey     * returned.  Otherwise this method immediately returns <tt>null</tt>.
107292706Spkelsey     *
108292706Spkelsey     * @return  A reference object, if one was immediately available,
109292706Spkelsey     *          otherwise <code>null</code>
110292706Spkelsey     */
111304088Skarels    public Reference<? extends T> poll() {
112292706Spkelsey        if (head == null)
113292706Spkelsey            return null;
114292706Spkelsey        synchronized (lock) {
115292706Spkelsey            return reallyPoll();
116292706Spkelsey        }
117292706Spkelsey    }
118292706Spkelsey
119292706Spkelsey    /**
120292706Spkelsey     * Removes the next reference object in this queue, blocking until either
121292706Spkelsey     * one becomes available or the given timeout period expires.
122292706Spkelsey     *
123292706Spkelsey     * <p> This method does not offer real-time guarantees: It schedules the
124292706Spkelsey     * timeout as if by invoking the {@link Object#wait(long)} method.
125292706Spkelsey     *
126292706Spkelsey     * @param  timeout  If positive, block for up to <code>timeout</code>
127292706Spkelsey     *                  milliseconds while waiting for a reference to be
128292706Spkelsey     *                  added to this queue.  If zero, block indefinitely.
129292706Spkelsey     *
130292706Spkelsey     * @return  A reference object, if one was available within the specified
131292706Spkelsey     *          timeout period, otherwise <code>null</code>
132292706Spkelsey     *
133292706Spkelsey     * @throws  IllegalArgumentException
134292706Spkelsey     *          If the value of the timeout argument is negative
135292706Spkelsey     *
136292706Spkelsey     * @throws  InterruptedException
137292706Spkelsey     *          If the timeout wait is interrupted
138292706Spkelsey     */
139292706Spkelsey    public Reference<? extends T> remove(long timeout)
140292706Spkelsey        throws IllegalArgumentException, InterruptedException
141292706Spkelsey    {
142292706Spkelsey        if (timeout < 0) {
143292706Spkelsey            throw new IllegalArgumentException("Negative timeout value");
144292706Spkelsey        }
145292706Spkelsey        synchronized (lock) {
146292706Spkelsey            Reference<? extends T> r = reallyPoll();
147292706Spkelsey            if (r != null) return r;
148292706Spkelsey            long start = (timeout == 0) ? 0 : System.nanoTime();
149292706Spkelsey            for (;;) {
150292706Spkelsey                lock.wait(timeout);
151292706Spkelsey                r = reallyPoll();
152292706Spkelsey                if (r != null) return r;
153292706Spkelsey                if (timeout != 0) {
154292706Spkelsey                    long end = System.nanoTime();
155292706Spkelsey                    timeout -= (end - start) / 1000_000;
156292706Spkelsey                    if (timeout <= 0) return null;
157292706Spkelsey                    start = end;
158292706Spkelsey                }
159292706Spkelsey            }
160292706Spkelsey        }
161292706Spkelsey    }
162292706Spkelsey
163292706Spkelsey    /**
164292706Spkelsey     * Removes the next reference object in this queue, blocking until one
165292706Spkelsey     * becomes available.
166292706Spkelsey     *
167292706Spkelsey     * @return A reference object, blocking until one becomes available
168292706Spkelsey     * @throws  InterruptedException  If the wait is interrupted
169292706Spkelsey     */
170292706Spkelsey    public Reference<? extends T> remove() throws InterruptedException {
171292706Spkelsey        return remove(0);
172292706Spkelsey    }
173292706Spkelsey
174292706Spkelsey    /**
175292706Spkelsey     * Iterate queue and invoke given action with each Reference.
176292706Spkelsey     * Suitable for diagnostic purposes.
177292706Spkelsey     * WARNING: any use of this method should make sure to not
178292706Spkelsey     * retain the referents of iterated references (in case of
179292706Spkelsey     * FinalReference(s)) so that their life is not prolonged more
180292706Spkelsey     * than necessary.
181292706Spkelsey     */
182292706Spkelsey    void forEach(Consumer<? super Reference<? extends T>> action) {
183292706Spkelsey        for (Reference<? extends T> r = head; r != null;) {
184292706Spkelsey            action.accept(r);
185292706Spkelsey            @SuppressWarnings("unchecked")
186292706Spkelsey            Reference<? extends T> rn = r.next;
187292706Spkelsey            if (rn == r) {
188292706Spkelsey                if (r.queue == ENQUEUED) {
189292706Spkelsey                    // still enqueued -> we reached end of chain
190292706Spkelsey                    r = null;
191292706Spkelsey                } else {
192292706Spkelsey                    // already dequeued: r.queue == NULL; ->
193292706Spkelsey                    // restart from head when overtaken by queue poller(s)
194292706Spkelsey                    r = head;
195292706Spkelsey                }
196292706Spkelsey            } else {
197292706Spkelsey                // next in chain
198292706Spkelsey                r = rn;
199292706Spkelsey            }
200292706Spkelsey        }
201292706Spkelsey    }
202292706Spkelsey}
203292706Spkelsey