001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.tools; 003 004import java.lang.ref.Reference; 005import java.lang.ref.ReferenceQueue; 006import java.lang.ref.WeakReference; 007 008import org.openstreetmap.josm.tools.bugreport.BugReport; 009 010/** 011 * This is a special weak reference that notifies a listener when it is no longer available. 012 * 013 * A special dereferenced-thread is used for this, so make sure your code is thread-safe. 014 * @author Michael Zangl 015 * @param <T> The weak reference 016 * @since 12181 017 */ 018public class ListenableWeakReference<T> extends WeakReference<T> { 019 private static final ReferenceQueue<Object> GLOBAL_QUEUE = new ReferenceQueue<>(); 020 private static Thread thread; 021 private Runnable runOnDereference; 022 023 /** 024 * Create a new {@link ListenableWeakReference} 025 * @param referent The object that is referenced 026 */ 027 public ListenableWeakReference(T referent) { 028 this(referent, () -> { }); 029 } 030 031 /** 032 * Create a new {@link ListenableWeakReference} 033 * @param referent The object that is referenced 034 * @param runOnDereference The runnable to run when the object is no longer referenced. 035 */ 036 public ListenableWeakReference(T referent, Runnable runOnDereference) { 037 super(referent, GLOBAL_QUEUE); 038 this.runOnDereference = runOnDereference; 039 ensureQueueStarted(); 040 } 041 042 /** 043 * This method is called after the object is dereferenced. 044 */ 045 protected void onDereference() { 046 this.runOnDereference.run(); 047 } 048 049 private static synchronized void ensureQueueStarted() { 050 if (thread == null) { 051 thread = new Thread(ListenableWeakReference::clean, "Weak reference cleaner"); 052 thread.start(); 053 } 054 } 055 056 private static void clean() { 057 boolean running = true; 058 try { 059 while (running) { 060 Reference<? extends Object> ref = GLOBAL_QUEUE.remove(); 061 if (ref instanceof ListenableWeakReference) { 062 ((ListenableWeakReference<?>) ref).onDereference(); 063 } 064 } 065 } catch (InterruptedException e) { 066 running = false; 067 BugReport.intercept(e).warn(); 068 Thread.currentThread().interrupt(); 069 } 070 } 071}