1313 *******************************************************************************/
1414package org .eclipse .swt .graphics ;
1515
16+ import java .lang .ref .*;
17+ import java .lang .ref .Cleaner .*;
1618import java .util .*;
19+ import java .util .concurrent .*;
20+ import java .util .concurrent .atomic .*;
1721import java .util .function .*;
1822
1923import org .eclipse .swt .*;
4448public abstract class Resource {
4549
4650 /**
47- * Used to track not disposed SWT resource. A separate class allows
48- * not to have the {@link #finalize} when tracking is disabled, avoiding
49- * possible performance issues in GC.
51+ * Used to track not disposed SWT resource.
5052 */
51- private static class ResourceTracker {
53+ private static final class ResourceTracker implements Runnable {
5254 /**
53- * Resource that is tracked here
55+ * Invokes {@link #run()} once the resource is eligible for GC
5456 */
55- private Resource resource ;
57+ private static final Cleaner cleaner = Cleaner . create ( new ResourceTrackerThreadFactory ()) ;
5658
5759 /**
5860 * Recorded at Resource creation if {@link #setNonDisposeHandler} was
5961 * enabled, used to track resource disposal
6062 */
61- private Error allocationStack ;
63+ private final Error allocationStack ;
6264
6365 /**
64- * Allows to ignore specific Resources even if they are not disposed
65- * properly, used for example for Fonts that SWT doesn't own.
66+ * Controls whether the {@link Resource#nonDisposedReporter} should be notified
6667 */
67- boolean ignoreMe ;
68+ private final AtomicBoolean reporting = new AtomicBoolean ( false ) ;
6869
69- ResourceTracker (Resource resource , Error allocationStack ) {
70- this .resource = resource ;
70+ ResourceTracker (Error allocationStack ) {
7171 this .allocationStack = allocationStack ;
7272 }
7373
7474 @ Override
75- protected void finalize () {
76- if (ignoreMe ) return ;
75+ public void run () {
76+ if (! reporting . get () ) return ;
7777 if (nonDisposedReporter == null ) return ;
7878
79- // If the Resource is GC'ed before it was disposed, this is a leak.
80- if (!resource .isDisposed ())
81- nonDisposedReporter .accept (allocationStack );
79+ nonDisposedReporter .accept (allocationStack );
8280 }
8381 }
8482
@@ -97,6 +95,11 @@ protected void finalize() {
9795 */
9896 private ResourceTracker tracker ;
9997
98+ /**
99+ * Represents the {@link #tracker} registered as a cleaning action via the {@link ResourceTracker#cleaner}
100+ */
101+ private Cleanable cleanable ;
102+
100103 static {
101104 boolean trackingEnabled = Boolean .getBoolean ("org.eclipse.swt.graphics.Resource.reportNonDisposed" ); //$NON-NLS-1$
102105 if (trackingEnabled ) {
@@ -142,6 +145,8 @@ void destroyHandlesExcept(Set<Integer> zoomLevels) {
142145 * This method does nothing if the resource is already disposed.
143146 */
144147public void dispose () {
148+ if (tracker != null ) tracker .reporting .set (false );
149+ if (cleanable != null ) cleanable .clean ();
145150 if (device == null ) return ;
146151 if (device .isDisposed ()) return ;
147152 destroy ();
@@ -164,30 +169,29 @@ public Device getDevice() {
164169}
165170
166171void ignoreNonDisposed () {
167- if (tracker != null ) {
168- tracker .ignoreMe = true ;
169- }
172+ if (tracker != null ) tracker .reporting .set (false );
173+ if (cleanable != null ) cleanable .clean ();
170174}
171175
172176void init () {
173177 if (device .tracking ) device .new_Object (this );
178+ if (tracker != null && tracker .reporting .compareAndSet (false , true )) {
179+ cleanable = ResourceTracker .cleaner .register (this , tracker );
180+ }
174181}
175182
176183void initNonDisposeTracking () {
177184 // Color doesn't really have any resource to be leaked, ignore.
178185 if (this instanceof Color ) return ;
179186
180- // Avoid performance costs of having '.finalize()' when not tracking.
187+ // Avoid performance costs of gathering the current stack trace when not tracking.
181188 if (nonDisposedReporter == null ) return ;
182189
183190 // Capture a stack trace to help investigating the leak
184191 Error error = new Error ("SWT Resource was not properly disposed" ); //$NON-NLS-1$
185192
186- // Allocate a helper class with '.finalize()' in it, it will do the actual
187- // work of detecting and reporting errors. This works because Resource
188- // holds the only reference to 'ResourceTracker' and therefore the tracker
189- // is only GC'ed when Resource itself is ready to be GC'ed.
190- tracker = new ResourceTracker (this , error );
193+ // Create the tracker that will later be registered as a cleaning action for this resource.
194+ tracker = new ResourceTracker (error );
191195}
192196
193197/**
@@ -220,4 +224,25 @@ public static void setNonDisposeHandler(Consumer<Error> reporter) {
220224 nonDisposedReporter = reporter ;
221225}
222226
227+ private final static class ResourceTrackerThreadFactory implements ThreadFactory {
228+
229+ private final ThreadGroup group ;
230+
231+ public ResourceTrackerThreadFactory () {
232+ ThreadGroup root = Thread .currentThread ().getThreadGroup ();
233+ while (root .getParent () != null ) {
234+ root = root .getParent ();
235+ }
236+ this .group = new ThreadGroup (root , "SWTResourceTrackerThreadGroup" ); //$NON-NLS-1$
237+ }
238+
239+ @ Override
240+ public Thread newThread (Runnable r ) {
241+ Thread thread = new Thread (group , r , "SWTResourceTracker" , 0 , false ); //$NON-NLS-1$
242+ thread .setPriority (Thread .MAX_PRIORITY - 2 );
243+ thread .setContextClassLoader (ClassLoader .getSystemClassLoader ());
244+ return thread ;
245+ }
246+ }
247+
223248}
0 commit comments