1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * Copyright (c) 1997, 2005, Oracle and/or its affiliates. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.  Oracle designates this
9 * particular file as subject to the "Classpath" exception as provided
10 * by Oracle in the LICENSE file that accompanied this code.
11 *
12 * This code is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 * version 2 for more details (a copy is included in the LICENSE file that
16 * accompanied this code).
17 *
18 * You should have received a copy of the GNU General Public License version
19 * 2 along with this work; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23 * or visit www.oracle.com if you need additional information or have any
24 * questions.
25 */
26
27package java.lang.ref;
28
29import sun.misc.Cleaner;
30
31/**
32 * Reference queues, to which registered reference objects are appended by the
33 * garbage collector after the appropriate reachability changes are detected.
34 *
35 * @author   Mark Reinhold
36 * @since    1.2
37 */
38public class ReferenceQueue<T> {
39
40    // Reference.queueNext will be set to sQueueNextUnenqueued to indicate
41    // when a reference has been enqueued and removed from its queue.
42    private static final Reference sQueueNextUnenqueued = new PhantomReference(null, null);
43
44    // NOTE: This implementation of ReferenceQueue is FIFO (queue-like) whereas
45    // the OpenJdk implementation is LIFO (stack-like).
46    private Reference<? extends T> head = null;
47    private Reference<? extends T> tail = null;
48
49    private final Object lock = new Object();
50
51    /**
52     * Constructs a new reference-object queue.
53     */
54    public ReferenceQueue() { }
55
56    /**
57     * Enqueue the given reference onto this queue.
58     * The caller is responsible for ensuring the lock is held on this queue,
59     * and for calling notifyAll on this queue after the reference has been
60     * enqueued. Returns true if the reference was enqueued successfully,
61     * false if the reference had already been enqueued.
62     * @GuardedBy("lock")
63     */
64    private boolean enqueueLocked(Reference<? extends T> r) {
65        // Verify the reference has not already been enqueued.
66        if (r.queueNext != null) {
67            return false;
68        }
69
70        if (r instanceof Cleaner) {
71            // If this reference is a Cleaner, then simply invoke the clean method instead
72            // of enqueueing it in the queue. Cleaners are associated with dummy queues that
73            // are never polled and objects are never enqueued on them.
74            Cleaner cl = (sun.misc.Cleaner) r;
75            cl.clean();
76
77            // Update queueNext to indicate that the reference has been
78            // enqueued, but is now removed from the queue.
79            r.queueNext = sQueueNextUnenqueued;
80            return true;
81        }
82
83        if (tail == null) {
84            head = r;
85        } else {
86            tail.queueNext = r;
87        }
88        tail = r;
89        tail.queueNext = r;
90        return true;
91    }
92
93    /**
94     * Test if the given reference object has been enqueued but not yet
95     * removed from the queue, assuming this is the reference object's queue.
96     */
97    boolean isEnqueued(Reference<? extends T> reference) {
98        synchronized (lock) {
99            return reference.queueNext != null && reference.queueNext != sQueueNextUnenqueued;
100        }
101    }
102
103    /**
104     * Enqueue the reference object on the receiver.
105     *
106     * @param reference
107     *            reference object to be enqueued.
108     * @return true if the reference was enqueued.
109     */
110    boolean enqueue(Reference<? extends T> reference) {
111        synchronized (lock) {
112            if (enqueueLocked(reference)) {
113                lock.notifyAll();
114                return true;
115            }
116            return false;
117        }
118    }
119
120    // @GuardedBy("lock")
121    private Reference<? extends T> reallyPollLocked() {
122        if (head != null) {
123            Reference<? extends T> r = head;
124            if (head == tail) {
125                tail = null;
126                head = null;
127            } else {
128                head = head.queueNext;
129            }
130
131            // Update queueNext to indicate that the reference has been
132            // enqueued, but is now removed from the queue.
133            r.queueNext = sQueueNextUnenqueued;
134            return r;
135        }
136
137        return null;
138    }
139
140    /**
141     * Polls this queue to see if a reference object is available.  If one is
142     * available without further delay then it is removed from the queue and
143     * returned.  Otherwise this method immediately returns <tt>null</tt>.
144     *
145     * @return  A reference object, if one was immediately available,
146     *          otherwise <code>null</code>
147     */
148    public Reference<? extends T> poll() {
149        synchronized (lock) {
150            if (head == null)
151                return null;
152
153            return reallyPollLocked();
154        }
155    }
156
157    /**
158     * Removes the next reference object in this queue, blocking until either
159     * one becomes available or the given timeout period expires.
160     *
161     * <p> This method does not offer real-time guarantees: It schedules the
162     * timeout as if by invoking the {@link Object#wait(long)} method.
163     *
164     * @param  timeout  If positive, block for up to <code>timeout</code>
165     *                  milliseconds while waiting for a reference to be
166     *                  added to this queue.  If zero, block indefinitely.
167     *
168     * @return  A reference object, if one was available within the specified
169     *          timeout period, otherwise <code>null</code>
170     *
171     * @throws  IllegalArgumentException
172     *          If the value of the timeout argument is negative
173     *
174     * @throws  InterruptedException
175     *          If the timeout wait is interrupted
176     */
177    public Reference<? extends T> remove(long timeout)
178        throws IllegalArgumentException, InterruptedException
179    {
180        if (timeout < 0) {
181            throw new IllegalArgumentException("Negative timeout value");
182        }
183        synchronized (lock) {
184            Reference<? extends T> r = reallyPollLocked();
185            if (r != null) return r;
186            long start = (timeout == 0) ? 0 : System.nanoTime();
187            for (;;) {
188                lock.wait(timeout);
189                r = reallyPollLocked();
190                if (r != null) return r;
191                if (timeout != 0) {
192                    long end = System.nanoTime();
193                    timeout -= (end - start) / 1000_000;
194                    if (timeout <= 0) return null;
195                    start = end;
196                }
197            }
198        }
199    }
200
201    /**
202     * Removes the next reference object in this queue, blocking until one
203     * becomes available.
204     *
205     * @return A reference object, blocking until one becomes available
206     * @throws  InterruptedException  If the wait is interrupted
207     */
208    public Reference<? extends T> remove() throws InterruptedException {
209        return remove(0);
210    }
211
212    /**
213     * Enqueue the given list of currently pending (unenqueued) references.
214     *
215     * @hide
216     */
217    public static void enqueuePending(Reference<?> list) {
218        Reference<?> start = list;
219        do {
220            ReferenceQueue queue = list.queue;
221            if (queue == null) {
222                Reference<?> next = list.pendingNext;
223
224                // Make pendingNext a self-loop to preserve the invariant that
225                // once enqueued, pendingNext is non-null -- without leaking
226                // the object pendingNext was previously pointing to.
227                list.pendingNext = list;
228                list = next;
229            } else {
230                // To improve performance, we try to avoid repeated
231                // synchronization on the same queue by batching enqueue of
232                // consecutive references in the list that have the same
233                // queue.
234                synchronized (queue.lock) {
235                    do {
236                        Reference<?> next = list.pendingNext;
237
238                        // Make pendingNext a self-loop to preserve the
239                        // invariant that once enqueued, pendingNext is
240                        // non-null -- without leaking the object pendingNext
241                        // was previously pointing to.
242                        list.pendingNext = list;
243                        queue.enqueueLocked(list);
244                        list = next;
245                    } while (list != start && list.queue == queue);
246                    queue.lock.notifyAll();
247                }
248            }
249        } while (list != start);
250    }
251
252    /**
253     * List of references that the GC says need to be enqueued.
254     * Protected by ReferenceQueue.class lock.
255     * @hide
256     */
257    public static Reference<?> unenqueued = null;
258
259    static void add(Reference<?> list) {
260        synchronized (ReferenceQueue.class) {
261            if (unenqueued == null) {
262                unenqueued = list;
263            } else {
264                // Find the last element in unenqueued.
265                Reference<?> last = unenqueued;
266                while (last.pendingNext != unenqueued) {
267                  last = last.pendingNext;
268                }
269                // Add our list to the end. Update the pendingNext to point back to enqueued.
270                last.pendingNext = list;
271                last = list;
272                while (last.pendingNext != list) {
273                    last = last.pendingNext;
274                }
275                last.pendingNext = unenqueued;
276            }
277            ReferenceQueue.class.notifyAll();
278        }
279    }
280}
281