diff options
author | jfrijters <jfrijters> | 2015-05-29 19:01:25 +0300 |
---|---|---|
committer | jfrijters <jfrijters> | 2015-05-29 19:01:25 +0300 |
commit | dd8612ba6cb52669d0a87015c8dfe61ba3b2271e (patch) | |
tree | be3d231941eee08dd2fa038f2f6d2163885e3119 | |
parent | 8c1f4b82c64ab915af3ca8edc68f449690599b36 (diff) |
Bug fix. ReferenceQueue should not keep registered (but not yet enqueued) Reference objects alive.
-rw-r--r-- | openjdk/java/lang/ref/ReferenceQueue.java | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/openjdk/java/lang/ref/ReferenceQueue.java b/openjdk/java/lang/ref/ReferenceQueue.java new file mode 100644 index 00000000..1d58d9bb --- /dev/null +++ b/openjdk/java/lang/ref/ReferenceQueue.java @@ -0,0 +1,208 @@ +/* + Copyright (C) 2014-2015 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ + +package java.lang.ref; + +public class ReferenceQueue<T> +{ + static final ReferenceQueue ENQUEUED = new ReferenceQueue(); + static final ReferenceQueue NULL = new ReferenceQueue(); + private volatile Link<T> activeHead; + private volatile Reference<T> head; + final Object lock = new Object(); + volatile boolean waitingForGC; + + private final class GCNotification { + protected void finalize() { + waitingForGC = false; + synchronized (lock) { + lock.notifyAll(); + } + } + } + + // NOTE a known problem with this approach is that the WeakReference will not be available + // after we've become only finalizer reachable + private static class Link<T> extends cli.System.WeakReference { + Link<T> next; + + Link(Reference<T> ref) { + super(ref); + } + + Reference<T> get() { + return (Reference<T>)get_Target(); + } + } + + public Reference<? extends T> poll() + { + if (head == null && (activeHead == null || waitingForGC)) { + return null; + } + synchronized (lock) { + return pollImpl(); + } + } + + private Reference<? extends T> pollImpl() + { + if (head == null) { + if (activeHead == null || waitingForGC) { + return null; + } + scanActiveList(); + if (head == null) { + waitingForGC = true; + new GCNotification(); + return null; + } + } + Reference<T> ref = head; + head = ref.next; + ref.next = null; + ref.queue = NULL; + return ref; + } + + public Reference<? extends T> remove(long timeout) throws IllegalArgumentException, InterruptedException + { + if (timeout < 0) + throw new IllegalArgumentException("Negative timeout value"); + + synchronized (lock) { + long expiration = 0; + for (;;) { + Reference<? extends T> ref = pollImpl(); + if (ref != null) + return ref; + + if (timeout == 0) { + lock.wait(); + } else { + long now = System.currentTimeMillis(); + if (expiration == 0) { + expiration = now + timeout; + if (expiration < 0) { + expiration = Long.MAX_VALUE; + } + } + if (now >= expiration) { + return null; + } + lock.wait(expiration - now); + } + } + } + } + + public Reference<? extends T> remove() throws InterruptedException + { + return remove(0); + } + + final void clear(Reference<T> ref) + { + synchronized (lock) { + if (ref.queue != ENQUEUED) { + ref.queue = NULL; + } + } + } + + final boolean enqueue(Reference<T> ref) + { + synchronized (lock) { + if (ref.queue != ENQUEUED && ref.queue != NULL) { + ref.queue = ENQUEUED; + + if (ref.isStrongOrNullRef()) { + ref.next = head; + head = ref; + lock.notifyAll(); + return true; + } + + Link<T> prev = null; + Link<T> curr = activeHead; + + while (curr != null) { + if (curr.get() == ref) { + if (prev == null) { + activeHead = curr.next; + } else { + prev.next = curr.next; + } + ref.next = head; + head = ref; + lock.notifyAll(); + return true; + } + prev = curr; + curr = curr.next; + } + } + } + return false; + } + + final void addToActiveList(Reference<T> ref) + { + Link<T> link = new Link<T>(ref); + synchronized (lock) { + link.next = activeHead; + activeHead = link; + if (link.next == null) { + lock.notifyAll(); + } + } + } + + private void scanActiveList() + { + Link<T> prev = null; + Link<T> curr = activeHead; + + while (curr != null) { + Reference<T> ref = curr.get(); + if (ref == null || !ref.isActive()) { + Link<T> next = curr.next; + if (prev == null) { + activeHead = next; + } else { + prev.next = next; + } + if (ref != null) { + ref.next = head; + ref.queue = ENQUEUED; + head = ref; + } + curr = next; + continue; + } + prev = curr; + curr = curr.next; + } + } +} |