1 | // License: GPL. For details, see LICENSE file.
|
---|
2 | package org.openstreetmap.josm.tools;
|
---|
3 |
|
---|
4 | import java.lang.ref.Reference;
|
---|
5 | import java.lang.ref.ReferenceQueue;
|
---|
6 | import java.lang.ref.WeakReference;
|
---|
7 |
|
---|
8 | import org.openstreetmap.josm.tools.bugreport.BugReport;
|
---|
9 |
|
---|
10 | /**
|
---|
11 | * This is a special weak reference that notifies a listener when it is no longer available.
|
---|
12 | *
|
---|
13 | * A special dereferenced-thread is used for this, so make sure your code is thread-safe.
|
---|
14 | * @author Michael Zangl
|
---|
15 | * @since 12181
|
---|
16 | *
|
---|
17 | * @param <T> The weak reference
|
---|
18 | */
|
---|
19 | public class ListenableWeakReference<T> extends WeakReference<T> {
|
---|
20 | private static final ReferenceQueue<Object> GLOBAL_QUEUE = new ReferenceQueue<>();
|
---|
21 | private static Thread thread;
|
---|
22 | private Runnable runOnDereference;
|
---|
23 |
|
---|
24 | /**
|
---|
25 | * Create a new {@link ListenableWeakReference}
|
---|
26 | * @param referent The object that is referenced
|
---|
27 | */
|
---|
28 | public ListenableWeakReference(T referent) {
|
---|
29 | this(referent, () -> { });
|
---|
30 | }
|
---|
31 |
|
---|
32 | /**
|
---|
33 | * Create a new {@link ListenableWeakReference}
|
---|
34 | * @param referent The object that is referenced
|
---|
35 | * @param runOnDereference The runnable to run when the object is no longer referenced.
|
---|
36 | */
|
---|
37 | public ListenableWeakReference(T referent, Runnable runOnDereference) {
|
---|
38 | super(referent, GLOBAL_QUEUE);
|
---|
39 | this.runOnDereference = runOnDereference;
|
---|
40 | ensureQueueStarted();
|
---|
41 | }
|
---|
42 |
|
---|
43 | /**
|
---|
44 | * This method is called after the object is dereferenced.
|
---|
45 | */
|
---|
46 | protected void onDereference() {
|
---|
47 | this.runOnDereference.run();
|
---|
48 | }
|
---|
49 |
|
---|
50 | private static synchronized void ensureQueueStarted() {
|
---|
51 | if (thread == null) {
|
---|
52 | thread = new Thread(ListenableWeakReference::clean, "Weak reference cleaner");
|
---|
53 | thread.start();
|
---|
54 | }
|
---|
55 | }
|
---|
56 |
|
---|
57 | private static void clean() {
|
---|
58 | boolean running = true;
|
---|
59 | try {
|
---|
60 | while (running) {
|
---|
61 | Reference<? extends Object> ref = GLOBAL_QUEUE.remove();
|
---|
62 | if (ref instanceof ListenableWeakReference) {
|
---|
63 | ((ListenableWeakReference<?>) ref).onDereference();
|
---|
64 | }
|
---|
65 | }
|
---|
66 | } catch (InterruptedException e) {
|
---|
67 | running = false;
|
---|
68 | BugReport.intercept(e).warn();
|
---|
69 | Thread.currentThread().interrupt();
|
---|
70 | }
|
---|
71 | }
|
---|
72 | }
|
---|