13
13
*******************************************************************************/
14
14
package org .eclipse .swt .graphics ;
15
15
16
+ import java .lang .ref .*;
17
+ import java .lang .ref .Cleaner .*;
16
18
import java .util .*;
19
+ import java .util .concurrent .*;
20
+ import java .util .concurrent .atomic .*;
17
21
import java .util .function .*;
18
22
19
23
import org .eclipse .swt .*;
44
48
public abstract class Resource {
45
49
46
50
/**
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.
50
52
*/
51
- private static class ResourceTracker {
53
+ private static final class ResourceTracker implements Runnable {
52
54
/**
53
- * Resource that is tracked here
55
+ * Invokes {@link #run()} once the resource is eligible for GC
54
56
*/
55
- private Resource resource ;
57
+ private static final Cleaner cleaner = Cleaner . create ( new ResourceTrackerThreadFactory ()) ;
56
58
57
59
/**
58
60
* Recorded at Resource creation if {@link #setNonDisposeHandler} was
59
61
* enabled, used to track resource disposal
60
62
*/
61
- private Error allocationStack ;
63
+ private final Error allocationStack ;
62
64
63
65
/**
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
66
67
*/
67
- boolean ignoreMe ;
68
+ private final AtomicBoolean reporting = new AtomicBoolean ( false ) ;
68
69
69
- ResourceTracker (Resource resource , Error allocationStack ) {
70
- this .resource = resource ;
70
+ ResourceTracker (Error allocationStack ) {
71
71
this .allocationStack = allocationStack ;
72
72
}
73
73
74
74
@ Override
75
- protected void finalize () {
76
- if (ignoreMe ) return ;
75
+ public void run () {
76
+ if (! reporting . get () ) return ;
77
77
if (nonDisposedReporter == null ) return ;
78
78
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 );
82
80
}
83
81
}
84
82
@@ -97,6 +95,11 @@ protected void finalize() {
97
95
*/
98
96
private ResourceTracker tracker ;
99
97
98
+ /**
99
+ * Represents the {@link #tracker} registered as a cleaning action via the {@link ResourceTracker#cleaner}
100
+ */
101
+ private Cleanable cleanable ;
102
+
100
103
static {
101
104
boolean trackingEnabled = Boolean .getBoolean ("org.eclipse.swt.graphics.Resource.reportNonDisposed" ); //$NON-NLS-1$
102
105
if (trackingEnabled ) {
@@ -142,6 +145,8 @@ void destroyHandlesExcept(Set<Integer> zoomLevels) {
142
145
* This method does nothing if the resource is already disposed.
143
146
*/
144
147
public void dispose () {
148
+ if (tracker != null ) tracker .reporting .set (false );
149
+ if (cleanable != null ) cleanable .clean ();
145
150
if (device == null ) return ;
146
151
if (device .isDisposed ()) return ;
147
152
destroy ();
@@ -164,30 +169,29 @@ public Device getDevice() {
164
169
}
165
170
166
171
void ignoreNonDisposed () {
167
- if (tracker != null ) {
168
- tracker .ignoreMe = true ;
169
- }
172
+ if (tracker != null ) tracker .reporting .set (false );
173
+ if (cleanable != null ) cleanable .clean ();
170
174
}
171
175
172
176
void init () {
173
177
if (device .tracking ) device .new_Object (this );
178
+ if (tracker != null && tracker .reporting .compareAndSet (false , true )) {
179
+ cleanable = ResourceTracker .cleaner .register (this , tracker );
180
+ }
174
181
}
175
182
176
183
void initNonDisposeTracking () {
177
184
// Color doesn't really have any resource to be leaked, ignore.
178
185
if (this instanceof Color ) return ;
179
186
180
- // Avoid performance costs of having '.finalize()' when not tracking.
187
+ // Avoid performance costs of gathering the current stack trace when not tracking.
181
188
if (nonDisposedReporter == null ) return ;
182
189
183
190
// Capture a stack trace to help investigating the leak
184
191
Error error = new Error ("SWT Resource was not properly disposed" ); //$NON-NLS-1$
185
192
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 );
191
195
}
192
196
193
197
/**
@@ -220,4 +224,25 @@ public static void setNonDisposeHandler(Consumer<Error> reporter) {
220
224
nonDisposedReporter = reporter ;
221
225
}
222
226
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
+
223
248
}
0 commit comments